various changes

This commit is contained in:
Owen LeJeune
2024-01-23 11:04:54 -05:00
parent 8ea66ac3c3
commit a6e0f71229
8 changed files with 116 additions and 5 deletions

View File

@@ -93,6 +93,8 @@ dependencies {
implementation "androidx.paging:paging-compose:$compose_paging"
implementation "androidx.constraintlayout:constraintlayout-compose:$compose_constraint_layout"
implementation "com.google.accompanist:accompanist-webview:$compose_accompanist"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
// material you
def monet_compat = "0.4.1"

View File

@@ -1,7 +1,6 @@
package com.owenlejeune.tvtime.api.tmdb.api
import android.content.Context
import android.widget.Toast
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
@@ -12,7 +11,6 @@ import androidx.paging.PagingData
import androidx.paging.PagingSource
import androidx.paging.PagingState
import androidx.paging.cachedIn
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.ui.viewmodel.ViewModelConstants
import kotlinx.coroutines.flow.Flow
import org.koin.core.component.KoinComponent

View File

@@ -40,6 +40,7 @@ class AppPreferences(context: Context) {
private val FLOATING_BOTTOM_BAR = "floating_bottom_bar"
private val RECENT_SEARCHES = "recent_searches"
private val SHOW_GOT_QUOTES = "show_got_quotes"
private val USE_ACCOUNT_BIOMETRICS = "use_account_biometrics"
}
private val preferences: SharedPreferences = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE)
@@ -81,6 +82,11 @@ class AppPreferences(context: Context) {
get() = preferences.getInt(SELECTED_COLOR, selectedColorDefault)
set(value) { preferences.put(SELECTED_COLOR, value) }
val accountBiometricsDefault: Boolean = false
var accountBiometrics: Boolean
get() = preferences.getBoolean(USE_ACCOUNT_BIOMETRICS, accountBiometricsDefault)
set(value) { preferences.put(USE_ACCOUNT_BIOMETRICS, value) }
/******* Session Tokens ********/
var authorizedSessionValues: SessionManager.AuthorizedSessionValues?
get() = preferences.getString(AUTHORIZED_SESSION_VALUES, null)?.let {

View File

@@ -13,10 +13,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.paging.compose.LazyPagingItems
@@ -34,6 +36,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.model.RatedTv
import com.owenlejeune.tvtime.extensions.getCalendarYear
import com.owenlejeune.tvtime.extensions.lazyPagingItems
import com.owenlejeune.tvtime.extensions.unlessEmpty
import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.AccountIcon
import com.owenlejeune.tvtime.ui.components.BackButton
import com.owenlejeune.tvtime.ui.components.MediaResultCard
@@ -44,16 +47,38 @@ import com.owenlejeune.tvtime.ui.navigation.AppNavItem
import com.owenlejeune.tvtime.ui.screens.tabs.AccountTabNavItem
import com.owenlejeune.tvtime.ui.viewmodel.AccountViewModel
import com.owenlejeune.tvtime.ui.viewmodel.ApplicationViewModel
import com.owenlejeune.tvtime.utils.BiometricUtils
import com.owenlejeune.tvtime.utils.SessionManager
import com.owenlejeune.tvtime.utils.TmdbUtils
import com.owenlejeune.tvtime.utils.types.MediaViewType
import kotlinx.coroutines.launch
import org.koin.java.KoinJavaComponent.get
import java.util.Date
import kotlin.reflect.KClass
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AccountScreen(
appNavController: NavHostController,
doSignInPartTwo: Boolean = false,
appPreferences: AppPreferences = get(AppPreferences::class.java)
) {
var proceed by remember { mutableStateOf(!appPreferences.accountBiometrics) }
if (!proceed) {
val activity = LocalContext.current as FragmentActivity
BiometricUtils.showBiometricPrompt(
activity,
onFailed = { appNavController.popBackStack() },
onError = { appNavController.popBackStack() },
onSuccess = { proceed = true }
)
} else {
AccountView(appNavController = appNavController, doSignInPartTwo = doSignInPartTwo)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AccountView(
appNavController: NavHostController,
doSignInPartTwo: Boolean = false
) {

View File

@@ -1115,7 +1115,7 @@ private fun ReviewsCard(
if (isExpanded) {
reviews
} else {
reviews.subList(0, 3)
reviews.subList(0, minOf(reviews.size, 3))
}
} ?: emptyList()
Column(

View File

@@ -60,6 +60,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.recyclerview.widget.ItemTouchHelper
@@ -85,7 +86,9 @@ import com.owenlejeune.tvtime.ui.viewmodel.ApplicationViewModel
import com.owenlejeune.tvtime.ui.viewmodel.SettingsViewModel
import com.owenlejeune.tvtime.ui.views.HomeTabRecyclerAdapter
import com.owenlejeune.tvtime.ui.views.ItemMoveCallback
import com.owenlejeune.tvtime.utils.BiometricUtils
import com.owenlejeune.tvtime.utils.SessionManager
import com.owenlejeune.tvtime.utils.canShowBiometricsPrompt
import kotlinx.coroutines.launch
import org.koin.java.KoinJavaComponent.get
@@ -237,7 +240,7 @@ fun DesignPreferences(
settingsNavController: NavController,
) {
val settingsViewModel = viewModel<SettingsViewModel>()
val activity = LocalContext.current as Activity
val activity = LocalContext.current as FragmentActivity
Column(
modifier = Modifier
@@ -315,6 +318,24 @@ fun DesignPreferences(
if (showWallpaperPicker.value) {
WallpaperPicker(showPopup = showWallpaperPicker)
}
if (canShowBiometricsPrompt()) {
SwitchPreference(
titleText = "Biometrics",
subtitleText = "###",
checkState = settingsViewModel.useAccountBiometrics.collectAsState(),
onCheckedChange = {
settingsViewModel.toggleUseAccountBiometrics()
if (it) {
BiometricUtils.showBiometricPrompt(
activity,
onFailed = { settingsViewModel.setUseAccountBiometrics(false) },
onError = { settingsViewModel.setUseAccountBiometrics(false) }
)
}
}
)
}
}
}

View File

@@ -26,6 +26,7 @@ class SettingsViewModel: ViewModel() {
private val _showBackdropGallery = MutableStateFlow(preferences.showBackdropGallery)
private val _showNextMcuProduction = MutableStateFlow(preferences.showNextMcuProduction)
private val _showGotQuotes = MutableStateFlow(preferences.showGotQuotes)
private val _useAccountBiometrics = MutableStateFlow(preferences.accountBiometrics)
}
val showSearchBar = _showSearchBar.asStateFlow()
@@ -42,6 +43,7 @@ class SettingsViewModel: ViewModel() {
val showBackdropGallery = _showBackdropGallery.asStateFlow()
val showNextMcuProduction = _showNextMcuProduction.asStateFlow()
val showGotQuotes = _showGotQuotes.asStateFlow()
val useAccountBiometrics = _useAccountBiometrics.asStateFlow()
fun toggleShowSearchBar() {
_showSearchBar.value = _showSearchBar.value.not()
@@ -168,4 +170,14 @@ class SettingsViewModel: ViewModel() {
preferences.showGotQuotes = value
}
fun toggleUseAccountBiometrics() {
_useAccountBiometrics.value = _useAccountBiometrics.value.not()
preferences.accountBiometrics = _useAccountBiometrics.value
}
fun setUseAccountBiometrics(value: Boolean) {
_useAccountBiometrics.value = value
preferences.accountBiometrics = value
}
}

View File

@@ -0,0 +1,47 @@
package com.owenlejeune.tvtime.utils
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
object BiometricUtils {
fun showBiometricPrompt(activity: FragmentActivity, onError: () -> Unit = {}, onFailed: () -> Unit = {}, onSuccess: () -> Unit = {}) {
val executor = ContextCompat.getMainExecutor(activity)
val biometricPrompt = BiometricPrompt(activity, executor, object: BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
onError()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
onFailed()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
onSuccess()
}
})
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Title")
.setSubtitle("Subtitle")
.setNegativeButtonText("Cancel")
.build()
biometricPrompt.authenticate(promptInfo)
}
}
@Composable
fun canShowBiometricsPrompt(): Boolean {
val context = LocalContext.current
val biometricManager = BiometricManager.from(context)
return biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS
}