move auth to internal webview

This commit is contained in:
Owen LeJeune
2023-06-02 17:24:12 -04:00
parent 20390b0d50
commit fbc1719523
8 changed files with 148 additions and 32 deletions

View File

@@ -90,6 +90,7 @@ dependencies {
implementation "androidx.paging:paging-compose:$compose_paging"
implementation "androidx.constraintlayout:constraintlayout-compose:$compose_constraint_layout"
implementation "androidx.paging:paging-compose:$compose_paging"
implementation "com.google.accompanist:accompanist-webview:0.28.0"
// material you
def monet_compat = "0.4.1"
@@ -124,10 +125,10 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
// youtube player
def player_core = "11.0.1"
def chromecast_sender = "0.26"
implementation "com.pierfrancescosoffritti.androidyoutubeplayer:core:$player_core"
implementation "com.pierfrancescosoffritti.androidyoutubeplayer:chromecast-sender:$chromecast_sender"
// def player_core = "11.0.1"
// def chromecast_sender = "0.26"
// implementation "com.pierfrancescosoffritti.androidyoutubeplayer:core:$player_core"
// implementation "com.pierfrancescosoffritti.androidyoutubeplayer:chromecast-sender:$chromecast_sender"
// markdown
def markdown = "0.2.1"

View File

@@ -446,6 +446,17 @@ class MainActivity : MonetCompatActivity() {
)
}
}
composable(
route = MainNavItem.WebLinkView.route.plus("/{${NavConstants.WEB_LINK_KEY}}"),
arguments = listOf(
navArgument(NavConstants.WEB_LINK_KEY) { type = NavType.StringType }
)
) {
val url = it.arguments?.getString(NavConstants.WEB_LINK_KEY)
url?.let {
WebLinkView(url = url, appNavController = appNavController)
}
}
}
}

View File

@@ -10,5 +10,6 @@ sealed class MainNavItem(val route: String) {
object DetailView: MainNavItem("detail_route")
object SettingsView: MainNavItem("settings_route")
object SearchView: MainNavItem("search_route")
object WebLinkView: MainNavItem("web_link_route")
}

View File

@@ -1,6 +1,5 @@
package com.owenlejeune.tvtime.ui.screens.main
import android.content.Context
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
@@ -8,9 +7,11 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -56,11 +57,17 @@ fun AccountTab(
val currentSessionState = remember { SessionManager.currentSession }
val currentSession = currentSessionState.value
val scope = rememberCoroutineScope()
if (currentSession?.isAuthorized == false) {
appBarTitle.value = stringResource(id = R.string.account_not_logged_in)
if (doSignInPartTwo) {
AccountLoadingView()
signInPart2()
LaunchedEffect(Unit) {
scope.launch {
SessionManager.singInPart2()
}
}
}
} else {
if (currentSession?.isAuthorized == true) {
@@ -76,7 +83,8 @@ fun AccountTab(
appBarActions.value = {
AccountDropdownMenu(
session = currentSession
session = currentSession,
appNavController = appNavController
)
}
@@ -285,7 +293,7 @@ private fun MediaItemRow(
}
@Composable
private fun AccountDropdownMenu(session: SessionManager.Session?) {
private fun AccountDropdownMenu(session: SessionManager.Session?, appNavController: NavHostController) {
val expanded = remember { mutableStateOf(false) }
IconButton(
@@ -301,7 +309,7 @@ private fun AccountDropdownMenu(session: SessionManager.Session?) {
if(session?.isAuthorized == true) {
AuthorizedSessionMenuItems(expanded = expanded)
} else {
NoSessionMenuItems(expanded = expanded)
NoSessionMenuItems(expanded = expanded, appNavController = appNavController)
}
}
}
@@ -318,29 +326,21 @@ private fun AuthorizedSessionMenuItems(expanded: MutableState<Boolean>) {
}
@Composable
private fun NoSessionMenuItems(expanded: MutableState<Boolean>) {
private fun NoSessionMenuItems(expanded: MutableState<Boolean>, appNavController: NavHostController) {
val context = LocalContext.current
DropdownMenuItem(
text = { Text(text = stringResource(id = R.string.action_sign_in)) },
onClick = {
signInPart1(context)
CoroutineScope(Dispatchers.IO).launch {
SessionManager.signInPart1(context) {
appNavController.navigate(MainNavItem.WebLinkView.route.plus("/$it"))
}
}
expanded.value = false
}
)
}
private fun signInPart1(context: Context) {
CoroutineScope(Dispatchers.IO).launch {
SessionManager.signInPart1(context)
}
}
private fun signInPart2() {
CoroutineScope(Dispatchers.IO).launch {
SessionManager.singInPart2()
}
}
@Composable
private fun AuthorizedSessionIcon() {
val accountDetails = SessionManager.currentSession.value?.accountDetails?.value

View File

@@ -0,0 +1,99 @@
package com.owenlejeune.tvtime.ui.screens.main
import android.content.Context
import android.content.Intent
import android.view.View
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SmallTopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import com.google.accompanist.web.AccompanistWebViewClient
import com.google.accompanist.web.WebView
import com.google.accompanist.web.rememberWebViewState
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WebLinkView(
url: String,
appNavController: NavController
) {
Scaffold(
topBar = {
SmallTopAppBar(
title = {},
navigationIcon = {
IconButton(
onClick = { appNavController.popBackStack() }
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = "Close"
)
}
}
)
}
) {
Box(modifier = Modifier.padding(it)) {
val webViewState = rememberWebViewState(url = url)
WebView(
state = webViewState,
modifier = Modifier.fillMaxSize(),
client = MYmdbWebClient(),
onCreated = {
it.settings.javaScriptCanOpenWindowsAutomatically = true
it.settings.useWideViewPort = false
it.settings.allowContentAccess = true
it.settings.blockNetworkImage = false
it.settings.blockNetworkLoads = false
it.settings.loadsImagesAutomatically = true
it.clipToOutline = false
it.isNestedScrollingEnabled = true
it.settings.displayZoomControls = true
it.settings.setSupportZoom(true)
it.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
it.isVerticalScrollBarEnabled = true
it.isHorizontalScrollBarEnabled = true
it.settings.domStorageEnabled = true
it.settings.databaseEnabled = true
it.settings.javaScriptEnabled = true
it.settings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
it.setLayerType(View.LAYER_TYPE_HARDWARE,null)
it.postInvalidate()
it.isClickable = true
}
)
}
}
}
class MYmdbWebClient: AccompanistWebViewClient(), KoinComponent {
private val context: Context by inject()
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
if (request?.url.toString().contains("tvtime")) {
val uri = request!!.url
val intent = Intent(Intent.ACTION_VIEW, uri).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
context.startActivity(intent)
return true
}
return super.shouldOverrideUrlLoading(view, request)
}
}

View File

@@ -30,7 +30,7 @@ class ItemMoveCallback(private val mAdapter: ItemTouchHelperContract): ItemTouch
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
mAdapter.onRowMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
mAdapter.onRowMoved(viewHolder.adapterPosition, target.adapterPosition)
return true
}

View File

@@ -8,4 +8,5 @@ object NavConstants {
const val SEARCH_TITLE_KEY = "search_title_key"
const val ACCOUNT_KEY = "account_key"
const val AUTH_REDIRECT_PAGE = "return"
const val WEB_LINK_KEY = "web_link_key"
}

View File

@@ -27,6 +27,8 @@ import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koin.java.KoinJavaComponent.get
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
object SessionManager: KoinComponent {
@@ -69,19 +71,20 @@ object SessionManager: KoinComponent {
}
}
suspend fun signInPart1(context: Context) {
suspend fun signInPart1(
context: Context,
onRedirect: (url: String) -> Unit
) {
val service = AuthenticationV4Service()
val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = "app://tvtime.auth.return"))
if (requestTokenResponse.isSuccessful) {
requestTokenResponse.body()?.let { ctr ->
currentSession.value = InProgressSession(ctr.requestToken)
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse(
context.getString(R.string.tmdb_auth_url, ctr.requestToken)
)
)
context.startActivity(browserIntent)
val url = context.getString(R.string.tmdb_auth_url, ctr.requestToken)
val encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8.toString())
withContext(Dispatchers.Main) {
onRedirect(encodedUrl)
}
}
}
}