mirror of
https://github.com/owenlejeune/TVTime.git
synced 2025-11-18 17:50:56 -05:00
move auth to internal webview
This commit is contained in:
@@ -90,6 +90,7 @@ dependencies {
|
|||||||
implementation "androidx.paging:paging-compose:$compose_paging"
|
implementation "androidx.paging:paging-compose:$compose_paging"
|
||||||
implementation "androidx.constraintlayout:constraintlayout-compose:$compose_constraint_layout"
|
implementation "androidx.constraintlayout:constraintlayout-compose:$compose_constraint_layout"
|
||||||
implementation "androidx.paging:paging-compose:$compose_paging"
|
implementation "androidx.paging:paging-compose:$compose_paging"
|
||||||
|
implementation "com.google.accompanist:accompanist-webview:0.28.0"
|
||||||
|
|
||||||
// material you
|
// material you
|
||||||
def monet_compat = "0.4.1"
|
def monet_compat = "0.4.1"
|
||||||
@@ -124,10 +125,10 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||||
|
|
||||||
// youtube player
|
// youtube player
|
||||||
def player_core = "11.0.1"
|
// def player_core = "11.0.1"
|
||||||
def chromecast_sender = "0.26"
|
// def chromecast_sender = "0.26"
|
||||||
implementation "com.pierfrancescosoffritti.androidyoutubeplayer:core:$player_core"
|
// implementation "com.pierfrancescosoffritti.androidyoutubeplayer:core:$player_core"
|
||||||
implementation "com.pierfrancescosoffritti.androidyoutubeplayer:chromecast-sender:$chromecast_sender"
|
// implementation "com.pierfrancescosoffritti.androidyoutubeplayer:chromecast-sender:$chromecast_sender"
|
||||||
|
|
||||||
// markdown
|
// markdown
|
||||||
def markdown = "0.2.1"
|
def markdown = "0.2.1"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,5 +10,6 @@ sealed class MainNavItem(val route: String) {
|
|||||||
object DetailView: MainNavItem("detail_route")
|
object DetailView: MainNavItem("detail_route")
|
||||||
object SettingsView: MainNavItem("settings_route")
|
object SettingsView: MainNavItem("settings_route")
|
||||||
object SearchView: MainNavItem("search_route")
|
object SearchView: MainNavItem("search_route")
|
||||||
|
object WebLinkView: MainNavItem("web_link_route")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens.main
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
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.material.icons.filled.AccountCircle
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -56,11 +57,17 @@ fun AccountTab(
|
|||||||
val currentSessionState = remember { SessionManager.currentSession }
|
val currentSessionState = remember { SessionManager.currentSession }
|
||||||
val currentSession = currentSessionState.value
|
val currentSession = currentSessionState.value
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
if (currentSession?.isAuthorized == false) {
|
if (currentSession?.isAuthorized == false) {
|
||||||
appBarTitle.value = stringResource(id = R.string.account_not_logged_in)
|
appBarTitle.value = stringResource(id = R.string.account_not_logged_in)
|
||||||
if (doSignInPartTwo) {
|
if (doSignInPartTwo) {
|
||||||
AccountLoadingView()
|
AccountLoadingView()
|
||||||
signInPart2()
|
LaunchedEffect(Unit) {
|
||||||
|
scope.launch {
|
||||||
|
SessionManager.singInPart2()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentSession?.isAuthorized == true) {
|
if (currentSession?.isAuthorized == true) {
|
||||||
@@ -76,7 +83,8 @@ fun AccountTab(
|
|||||||
|
|
||||||
appBarActions.value = {
|
appBarActions.value = {
|
||||||
AccountDropdownMenu(
|
AccountDropdownMenu(
|
||||||
session = currentSession
|
session = currentSession,
|
||||||
|
appNavController = appNavController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +293,7 @@ private fun MediaItemRow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AccountDropdownMenu(session: SessionManager.Session?) {
|
private fun AccountDropdownMenu(session: SessionManager.Session?, appNavController: NavHostController) {
|
||||||
val expanded = remember { mutableStateOf(false) }
|
val expanded = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -301,7 +309,7 @@ private fun AccountDropdownMenu(session: SessionManager.Session?) {
|
|||||||
if(session?.isAuthorized == true) {
|
if(session?.isAuthorized == true) {
|
||||||
AuthorizedSessionMenuItems(expanded = expanded)
|
AuthorizedSessionMenuItems(expanded = expanded)
|
||||||
} else {
|
} else {
|
||||||
NoSessionMenuItems(expanded = expanded)
|
NoSessionMenuItems(expanded = expanded, appNavController = appNavController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,29 +326,21 @@ private fun AuthorizedSessionMenuItems(expanded: MutableState<Boolean>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NoSessionMenuItems(expanded: MutableState<Boolean>) {
|
private fun NoSessionMenuItems(expanded: MutableState<Boolean>, appNavController: NavHostController) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(text = stringResource(id = R.string.action_sign_in)) },
|
text = { Text(text = stringResource(id = R.string.action_sign_in)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
signInPart1(context)
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
SessionManager.signInPart1(context) {
|
||||||
|
appNavController.navigate(MainNavItem.WebLinkView.route.plus("/$it"))
|
||||||
|
}
|
||||||
|
}
|
||||||
expanded.value = false
|
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
|
@Composable
|
||||||
private fun AuthorizedSessionIcon() {
|
private fun AuthorizedSessionIcon() {
|
||||||
val accountDetails = SessionManager.currentSession.value?.accountDetails?.value
|
val accountDetails = SessionManager.currentSession.value?.accountDetails?.value
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ class ItemMoveCallback(private val mAdapter: ItemTouchHelperContract): ItemTouch
|
|||||||
viewHolder: RecyclerView.ViewHolder,
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
target: RecyclerView.ViewHolder
|
target: RecyclerView.ViewHolder
|
||||||
): Boolean {
|
): Boolean {
|
||||||
mAdapter.onRowMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
mAdapter.onRowMoved(viewHolder.adapterPosition, target.adapterPosition)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ object NavConstants {
|
|||||||
const val SEARCH_TITLE_KEY = "search_title_key"
|
const val SEARCH_TITLE_KEY = "search_title_key"
|
||||||
const val ACCOUNT_KEY = "account_key"
|
const val ACCOUNT_KEY = "account_key"
|
||||||
const val AUTH_REDIRECT_PAGE = "return"
|
const val AUTH_REDIRECT_PAGE = "return"
|
||||||
|
const val WEB_LINK_KEY = "web_link_key"
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,8 @@ import kotlinx.coroutines.withContext
|
|||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.koin.java.KoinJavaComponent.get
|
import org.koin.java.KoinJavaComponent.get
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
object SessionManager: KoinComponent {
|
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 service = AuthenticationV4Service()
|
||||||
val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = "app://tvtime.auth.return"))
|
val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = "app://tvtime.auth.return"))
|
||||||
if (requestTokenResponse.isSuccessful) {
|
if (requestTokenResponse.isSuccessful) {
|
||||||
requestTokenResponse.body()?.let { ctr ->
|
requestTokenResponse.body()?.let { ctr ->
|
||||||
currentSession.value = InProgressSession(ctr.requestToken)
|
currentSession.value = InProgressSession(ctr.requestToken)
|
||||||
val browserIntent = Intent(
|
val url = context.getString(R.string.tmdb_auth_url, ctr.requestToken)
|
||||||
Intent.ACTION_VIEW,
|
val encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8.toString())
|
||||||
Uri.parse(
|
withContext(Dispatchers.Main) {
|
||||||
context.getString(R.string.tmdb_auth_url, ctr.requestToken)
|
onRedirect(encodedUrl)
|
||||||
)
|
}
|
||||||
)
|
|
||||||
context.startActivity(browserIntent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user