Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
ViratAtAndroid committed Mar 1, 2024
2 parents b14ea95 + 913f347 commit 95c808d
Show file tree
Hide file tree
Showing 15 changed files with 357 additions and 5 deletions.
8 changes: 8 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ dependencies {
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.benchmark:benchmark-macro:1.2.0'
implementation 'com.google.firebase:firebase-firestore:24.10.1'
implementation "androidx.biometric:biometric:1.2.0-alpha05"
implementation platform('androidx.compose:compose-bom:2023.03.00')
implementation 'androidx.media3:media3-exoplayer:1.2.1'
implementation 'androidx.media3:media3-ui:1.2.1'
// implementation 'com.google.android.ads:mediation-test-suite:3.0.0'


Expand All @@ -117,6 +121,7 @@ dependencies {
androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
androidTestImplementation "io.mockk:mockk-android:1.13.4"
androidTestImplementation "io.mockk:mockk-agent:1.13.4"
androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"

Expand Down Expand Up @@ -211,6 +216,9 @@ dependencies {

testImplementation("org.mockito:mockito-core:5.3.1")
testImplementation ("app.cash.turbine:turbine:1.0.0")

// Data Store
implementation ("androidx.datastore:datastore-preferences:1.0.0")
}

// Remember to add this to local.properties
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission
android:name="android.permission.READ_MEDIA_IMAGES"
android:minSdkVersion="33" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />

<application
android:name=".di.MainApp"
Expand Down Expand Up @@ -64,7 +65,7 @@
android:label="@string/title_activity_quote"
android:theme="@style/Theme.JetpackComposeAllInOne" />

<service
<service
android:name=".application_components.services.counter.CounterAppService"
android:exported="false" />
<service
Expand Down Expand Up @@ -95,6 +96,7 @@
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.example.jetpack_compose_all_in_one.lessons.lesson_20

import android.util.Log
import android.widget.Toast
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.fragment.app.FragmentActivity
import java.util.concurrent.Executors

@Composable
fun BiometricAuthentication() {
val context = LocalContext.current

val biometricManager = BiometricManager.from(context)
when (biometricManager.canAuthenticate()) {
BiometricManager.BIOMETRIC_SUCCESS ->
Log.d("BiometricAuth", "App can authenticate using biometrics.")
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
Log.e("BiometricAuth", "No biometric features available on this device.")
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->
Log.e("BiometricAuth", "Biometric features are currently unavailable.")
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->
Log.e("BiometricAuth", "The user hasn't associated any biometric credentials with their account.")
}

val biometricPrompt: BiometricPrompt?

if (context is FragmentActivity) {
biometricPrompt = BiometricPrompt(context,
Executors.newSingleThreadExecutor(),
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Log.i("Authentication error:", errString.toString())
}

override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
Toast.makeText(context, "Authentication succeeded!", Toast.LENGTH_SHORT).show()
}

override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Toast.makeText(context, "Authentication failed. Try again.", Toast.LENGTH_SHORT).show()
}
}
)
} else {
biometricPrompt = null
}

Button(onClick = {
biometricPrompt?.let { prompt ->
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login")
.setSubtitle("Log in using your biometric credential")
.setNegativeButtonText("Use account password")
.build()

prompt.authenticate(promptInfo)
}
}) {
Text("Authenticate")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.example.jetpack_compose_all_in_one.lessons.lesson_21

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.launch

@Composable
fun DashboardScreen() {

val context = LocalContext.current
val dataStore = UserDataStore(context)
val scope = rememberCoroutineScope()
val savedName = dataStore.getName.collectAsState(initial = "Empty")
val savedEmail = dataStore.getEmail.collectAsState(initial = "Empty")

Box(Modifier.fillMaxSize()) {
Column(Modifier.align(Alignment.Center)) {
Text(text = savedName.value!!)
Text(text = savedEmail.value!!)

Button(onClick = {
scope.launch {
dataStore.clearData()
}
}) {
Text(text = "Clear data store")
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.example.jetpack_compose_all_in_one.lessons.lesson_21

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import com.example.jetpack_compose_all_in_one.lessons.lesson_21.navigation.Routes.DASHBOARD
import kotlinx.coroutines.launch

@Composable
fun LoginScreen(navController: NavController) {

var name by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }

val context = LocalContext.current
val scope = rememberCoroutineScope()
val dataStore = UserDataStore(context)

Box(Modifier.fillMaxSize()) {
Column(Modifier.align(Alignment.Center)) {
TextField(
value = name,
onValueChange = { name = it },
label = { Text(text = "Name") }
)

TextField(
value = email,
onValueChange = { email = it },
label = { Text(text = "Email") }
)

TextField(
value = password,
onValueChange = { password = it },
label = { Text(text = "Password") }
)

Button(onClick = {
scope.launch {
dataStore.saveName(name)
dataStore.saveEmail(email)
}
navController.navigate(DASHBOARD)
},
modifier = Modifier.align(Alignment.CenterHorizontally)) {
Text(text = "LOGIN")
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.example.jetpack_compose_all_in_one.lessons.lesson_21

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class UserDataStore(private val context: Context) {

companion object {
private val Context.dataStore : DataStore<Preferences> by preferencesDataStore("User")
val NAME_KEY = stringPreferencesKey("name")
val EMAIL_KEY = stringPreferencesKey("email")
}

val getName: Flow<String?> = context.dataStore.data
.map { preferences ->
preferences [NAME_KEY] ?: "Empty"
}

suspend fun saveName(name : String) {
context.dataStore.edit { preferences ->
preferences[NAME_KEY] = name
}
}

val getEmail: Flow<String?> = context.dataStore.data
.map { preferences ->
preferences [EMAIL_KEY] ?: "Empty"
}

suspend fun saveEmail(email : String) {
context.dataStore.edit { preferences ->
preferences[EMAIL_KEY] = email
}
}

suspend fun clearData() {
context.dataStore.edit { preferences ->
preferences.remove(NAME_KEY)
preferences.remove(EMAIL_KEY)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.jetpack_compose_all_in_one.lessons.lesson_21.navigation

import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.jetpack_compose_all_in_one.lessons.lesson_21.DashboardScreen
import com.example.jetpack_compose_all_in_one.lessons.lesson_21.LoginScreen

@Composable
fun DataStoreDemoUI(){
val navController = rememberNavController()

NavHost(navController = navController,
startDestination = Routes.LOGIN_SCREEN
){
composable(route = Routes.LOGIN_SCREEN) { LoginScreen(navController = navController) }
composable(route = Routes.DASHBOARD) { DashboardScreen() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.jetpack_compose_all_in_one.lessons.lesson_21.navigation

object Routes {

const val LOGIN_SCREEN = "LoginScreen"
const val DASHBOARD = "Dashboard"

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.example.jetpack_compose_all_in_one.lessons.lesson_22

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView

@Composable
fun ExoPlayerView() {
val videoUrl = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
val context = LocalContext.current
val exoPlayer = ExoPlayer.Builder(context).build()

val mediaSource = remember(videoUrl) {
MediaItem.fromUri(videoUrl)
}

LaunchedEffect(mediaSource) {
exoPlayer.setMediaItem(mediaSource)
exoPlayer.prepare()
}

DisposableEffect(Unit) {
onDispose {
exoPlayer.release()
}
}

AndroidView(
factory = { ctx ->
PlayerView(ctx).apply {
player = exoPlayer
}
},
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
)
}
Loading

0 comments on commit 95c808d

Please sign in to comment.