diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a78e517..11529b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -39,7 +39,7 @@ android { compose = true } packaging { - resources.excludes.add("META-INF-DEPENDENCIES") + resources.pickFirsts.add("META-INF/DEPENDENCIES") } } @@ -69,11 +69,7 @@ dependencies { implementation(libs.appauth) implementation(libs.androidx.security.crypto) - - -// implementation(libs.google.api.client.android) -// implementation(libs.google.api.services.drive) -// implementation(libs.google.oauth.client.jetty) - + implementation(libs.google.auth.library.oauth2.http) + implementation(libs.kotlinx.serialization.json) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1857486..c0be5f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,16 +15,26 @@ android:theme="@style/Theme.DeNaDrive" tools:targetApi="31"> + - - + android:theme="@style/Theme.DeNaDrive"> + + - - + + + - - - - + + + + + + diff --git a/app/src/main/java/com/bbc/denadrive/HomeNotAuthActivity.kt b/app/src/main/java/com/bbc/denadrive/HomeNotAuthActivity.kt index 5060902..07c54b4 100644 --- a/app/src/main/java/com/bbc/denadrive/HomeNotAuthActivity.kt +++ b/app/src/main/java/com/bbc/denadrive/HomeNotAuthActivity.kt @@ -2,6 +2,7 @@ package com.bbc.denadrive import android.app.PendingIntent import android.content.Intent +import android.content.SharedPreferences import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -25,16 +26,36 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.net.toUri +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import com.bbc.denadrive.oauth.LoadingActivity import com.bbc.denadrive.oauth.retrieveAuthServConf import com.bbc.denadrive.ui.theme.DeNaDriveTheme +import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationRequest import net.openid.appauth.AuthorizationService import net.openid.appauth.ResponseTypeValues class HomeNotAuthActivity : ComponentActivity() { + + private lateinit var preferences: SharedPreferences + private lateinit var masterKey: MasterKey + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + masterKey = MasterKey.Builder(this) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + preferences = EncryptedSharedPreferences.create( + this, + "authState", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + enableEdgeToEdge() setContent { DeNaDriveTheme { @@ -42,18 +63,22 @@ class HomeNotAuthActivity : ComponentActivity() { val logo = painterResource(id = R.drawable.vecchiaicona_) LogoWithCaptionAndButton ( logo = logo, - caption = "Android", - buttonText = "PressMe", + caption = "DeNa Drive", + buttonText = "Log in", modifier = Modifier.padding(innerPadding) - ) { handleAuthStuff() } + ) { handleAuthStuff(preferences) } } } } } - private fun handleAuthStuff() { + private fun handleAuthStuff(preferences: SharedPreferences) { val serviceConfig = retrieveAuthServConf() -// val authState = AuthState(serviceConfig) + val authState = AuthState(serviceConfig) + with (preferences.edit()) { + this.putString("authState", authState.jsonSerializeString()) + this.apply() + } val authRequestBuilder = AuthorizationRequest.Builder( serviceConfig, "722393551256-pum20gbvb1es723kt3ek9lmtrdimr0os.apps.googleusercontent.com", @@ -67,7 +92,7 @@ class HomeNotAuthActivity : ComponentActivity() { val authService = AuthorizationService(this) authService.performAuthorizationRequest( authRequest, - PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java), + PendingIntent.getActivity(this, 0, Intent(this, LoadingActivity::class.java), PendingIntent.FLAG_MUTABLE), PendingIntent.getActivity(this, 0, Intent(this, HomeNotAuthActivity::class.java), PendingIntent.FLAG_MUTABLE) @@ -110,7 +135,7 @@ fun LogoWithCaptionAndButton( Image( painter = logo, contentDescription = "Logo", - modifier = Modifier.size(100.dp) // Adjust size as needed + modifier = Modifier.size(200.dp) // Adjust size as needed ) Spacer(modifier = Modifier.height(16.dp)) // Space between logo and caption diff --git a/app/src/main/java/com/bbc/denadrive/MainActivity.kt b/app/src/main/java/com/bbc/denadrive/MainActivity.kt index ef5a777..377d1a0 100644 --- a/app/src/main/java/com/bbc/denadrive/MainActivity.kt +++ b/app/src/main/java/com/bbc/denadrive/MainActivity.kt @@ -2,7 +2,9 @@ package com.bbc.denadrive import android.app.PendingIntent import android.content.Intent +import android.content.SharedPreferences import android.os.Bundle +import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -12,9 +14,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.core.net.toUri import androidx.navigation.compose.rememberNavController +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import com.bbc.denadrive.apihandlers.makeDriveApiCall import com.bbc.denadrive.home.ScaffoldWithSidebar import com.bbc.denadrive.oauth.retrieveAuthServConf import com.bbc.denadrive.ui.theme.DeNaDriveTheme +import net.openid.appauth.AuthState import net.openid.appauth.AuthorizationException import net.openid.appauth.AuthorizationRequest import net.openid.appauth.AuthorizationResponse @@ -22,27 +28,65 @@ import net.openid.appauth.AuthorizationService import net.openid.appauth.ResponseTypeValues class MainActivity : ComponentActivity() { -override fun onCreate(savedInstanceState: Bundle?) { + + private lateinit var masterKey: MasterKey + private lateinit var preferences: SharedPreferences + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) -// handleAuthStuff() + masterKey = MasterKey.Builder(this) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + preferences = EncryptedSharedPreferences.create( + this, + "authState", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + val authState = preferences.getString("authState", "bah") + ?.let { AuthState.jsonDeserialize(it) } + + val authService = AuthorizationService(this) + +// authState?.let { +// it.performActionWithFreshTokens(authService) { accessToken, idToken, ex -> +// if (ex != null) { +// Log.e("TELLINGYA", "${ex.error}") +// return@performActionWithFreshTokens +// } +// Log.e("TOLD", "okay I guess") +// } +// } + + authState?.let { + val myUrl = "https://www.googleapis.com/drive/v3/files?trashed=false&orderBy=folder" + makeDriveApiCall(this, it, myUrl, "root") { + response, exception -> + if (exception != null) { + Log.e("ECCOLO", "${exception.cause}") + Log.e("ECCOLO", "${exception.message}") + } + else { + Log.e("UOOOO", "$response") + } + } + with(preferences.edit()) { + this.remove("authState") + this.putString("authState", it.jsonSerializeString()) + this.apply() + } + } setContent { val navController = rememberNavController() - ScaffoldWithSidebar(navController) { } -// NavigationGraph(navController) + ScaffoldWithSidebar(navController) { + Log.e("LETTINGYOUKNOW", "authstate: ${authState?.jsonSerializeString()}") + } } -// enableEdgeToEdge() -// setContent { -// DeNaDriveTheme { -// Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> -// Greeting( -// name = "Android", -// modifier = Modifier.padding(innerPadding) -// ) -// } -// } -// } } @@ -62,10 +106,10 @@ override fun onCreate(savedInstanceState: Bundle?) { val authService = AuthorizationService(this) authService.performAuthorizationRequest( authRequest, - PendingIntent.getActivity(this, 0, Intent(this, TestActivity::class.java), - PendingIntent.FLAG_MUTABLE), - PendingIntent.getActivity(this, 0, Intent(this, TestBah::class.java), - PendingIntent.FLAG_MUTABLE) + PendingIntent.getActivity(this, 0, Intent(this, + TestActivity::class.java), PendingIntent.FLAG_MUTABLE), + PendingIntent.getActivity(this, 0, Intent(this, + TestBah::class.java), PendingIntent.FLAG_MUTABLE) ) } @@ -83,8 +127,9 @@ override fun onCreate(savedInstanceState: Bundle?) { } setContent { - val navController = rememberNavController() - ScaffoldWithSidebar(navController) { } +// val navController = rememberNavController() +// ScaffoldWithSidebar(navController) { } + Greeting("bah") } } } diff --git a/app/src/main/java/com/bbc/denadrive/apihandlers/ApiCalls.kt b/app/src/main/java/com/bbc/denadrive/apihandlers/ApiCalls.kt new file mode 100644 index 0000000..35024d5 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/apihandlers/ApiCalls.kt @@ -0,0 +1,45 @@ +package com.bbc.denadrive.apihandlers + +import android.content.Context +import net.openid.appauth.AuthState +import net.openid.appauth.AuthorizationService +import okhttp3.Call +import okhttp3.Callback +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okhttp3.HttpUrl.Companion.toHttpUrl +import java.io.IOException + +fun makeDriveApiCall(context: Context, authState: AuthState, url: String, folderId: String, + callback: (String?, Exception?) -> Unit) { + val authService = AuthorizationService(context) + // _ = idToken + authState.performActionWithFreshTokens(authService) { accessToken, _, ex -> + authService.dispose() + if (ex != null) { + callback(null, ex) + return@performActionWithFreshTokens + } + + val urlBuilder = url.toHttpUrl().newBuilder() + urlBuilder.addQueryParameter("q", "'$folderId' in parents") + val updatedUrl = urlBuilder.build() + + val request = Request.Builder() + .url(updatedUrl) + .header("Authorization", "Bearer $accessToken") + .build() + + OkHttpClient().newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + callback(null, e) + } + + override fun onResponse(call: Call, response: Response) { + val responseBody = response.body?.string() + callback(responseBody, null) + } + }) + } +} diff --git a/app/src/main/java/com/bbc/denadrive/apihandlers/DriveResponseBody.kt b/app/src/main/java/com/bbc/denadrive/apihandlers/DriveResponseBody.kt new file mode 100644 index 0000000..3f70d23 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/apihandlers/DriveResponseBody.kt @@ -0,0 +1,8 @@ +package com.bbc.denadrive.apihandlers + +data class DriveResponseBody( + val nextPageToken: String?, + val kind: String, + val incompleteSearch: Boolean, + val files: List +) diff --git a/app/src/main/java/com/bbc/denadrive/apihandlers/MyFile.kt b/app/src/main/java/com/bbc/denadrive/apihandlers/MyFile.kt new file mode 100644 index 0000000..22dec49 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/apihandlers/MyFile.kt @@ -0,0 +1,8 @@ +package com.bbc.denadrive.apihandlers + +data class MyFile( + val kind: String, + val mimeType: String, + val id: String, + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/apihandlers/ResponsesHandler.kt b/app/src/main/java/com/bbc/denadrive/apihandlers/ResponsesHandler.kt new file mode 100644 index 0000000..b140809 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/apihandlers/ResponsesHandler.kt @@ -0,0 +1,42 @@ +package com.bbc.denadrive.apihandlers + +import androidx.lifecycle.ViewModel +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json +import okhttp3.Response + +class ResponsesHandler: ViewModel() { + + var lastResponse: String = "" + var isComplete: Boolean = true + private lateinit var respBuilder: StringBuilder + private lateinit var lastResponseBody: DriveResponseBody + + fun newResponseArrived(response: Response) { + if (isComplete) { + respBuilder = StringBuilder("$response") + } + else { + respBuilder.append("$response") + updateCompleteness() + } + } + + private fun checkForCompleteness(): Pair { + return try { + Pair(true, Json.decodeFromString(respBuilder.toString())) + } catch (e: SerializationException) { + Pair(false, null) + } + } + + private fun updateCompleteness() { + val pair = checkForCompleteness() + isComplete = pair.first + if (isComplete) { + lastResponse = respBuilder.toString() + lastResponseBody = pair.second!! + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/home/NeverLoggedInLogic.kt b/app/src/main/java/com/bbc/denadrive/home/NeverLoggedInLogic.kt new file mode 100644 index 0000000..59567e6 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/home/NeverLoggedInLogic.kt @@ -0,0 +1,92 @@ +package com.bbc.denadrive.home + +import android.content.Intent +import android.content.SharedPreferences +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import com.bbc.denadrive.HomeNotAuthActivity +import com.bbc.denadrive.MainActivity +import com.bbc.denadrive.home.ui.theme.DeNaDriveTheme +import net.openid.appauth.AuthState + +class NeverLoggedInLogic : ComponentActivity() { + + private lateinit var preferences: SharedPreferences + private lateinit var masterKey: MasterKey + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + masterKey = MasterKey.Builder(this) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + preferences = EncryptedSharedPreferences.create( + this, + "authState", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + val authStateString = preferences.getString("authState", "unset") + + val intent: Intent + if (authStateString == "unset") { + intent = Intent(this, HomeNotAuthActivity::class.java) + } else { + val authState = authStateString?.let { AuthState.jsonDeserialize(it) } + intent = if (authState?.isAuthorized == true) { + Intent(this, MainActivity::class.java) + } + else { + Toast.makeText(this, "Session expired, please log in again", + Toast.LENGTH_SHORT).show() + Intent(this, HomeNotAuthActivity::class.java) + } + } + startActivity(intent) +// Toast.makeText(this, "should not make it here", Toast.LENGTH_SHORT).show() + + enableEdgeToEdge() + setContent { + DeNaDriveTheme { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + Greeting5( + name = "Android", + modifier = Modifier.padding(innerPadding) + ) + } + } + } + } +} + +@Composable +fun Greeting5(name: String, modifier: Modifier = Modifier) { + Text( + text = "Hello $name!", + modifier = modifier + ) +} + +@Preview(showBackground = true) +@Composable +fun GreetingPreview5() { + DeNaDriveTheme { + Greeting5("Android") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/home/ui/theme/Color.kt b/app/src/main/java/com/bbc/denadrive/home/ui/theme/Color.kt new file mode 100644 index 0000000..6fe42a1 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/home/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.bbc.denadrive.home.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/home/ui/theme/Theme.kt b/app/src/main/java/com/bbc/denadrive/home/ui/theme/Theme.kt new file mode 100644 index 0000000..8466dab --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/home/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package com.bbc.denadrive.home.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun DeNaDriveTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/home/ui/theme/Type.kt b/app/src/main/java/com/bbc/denadrive/home/ui/theme/Type.kt new file mode 100644 index 0000000..1942d92 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/home/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.bbc.denadrive.home.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/oauth/AuthStatePersistence.kt b/app/src/main/java/com/bbc/denadrive/oauth/AuthStatePersistence.kt index 80b0e7d..3c3c974 100644 --- a/app/src/main/java/com/bbc/denadrive/oauth/AuthStatePersistence.kt +++ b/app/src/main/java/com/bbc/denadrive/oauth/AuthStatePersistence.kt @@ -1,2 +1,29 @@ package com.bbc.denadrive.oauth +import android.content.Context +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import net.openid.appauth.AuthState + +object AuthStatePersistence { + + fun retrieveAuthState(context: Context): AuthState { + val masterKey = MasterKey.Builder(context) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + val preferences = EncryptedSharedPreferences.create( + context, + "authState", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + preferences.getString("authState", null)?.let { + return AuthState.jsonDeserialize(it) + } + return AuthState(retrieveAuthServConf()) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/oauth/LoadingActivity.kt b/app/src/main/java/com/bbc/denadrive/oauth/LoadingActivity.kt new file mode 100644 index 0000000..5c8cc1e --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/oauth/LoadingActivity.kt @@ -0,0 +1,175 @@ +package com.bbc.denadrive.oauth + +import android.content.Intent +import android.content.SharedPreferences +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import com.bbc.denadrive.HomeNotAuthActivity +import com.bbc.denadrive.MainActivity +import com.bbc.denadrive.oauth.ui.theme.DeNaDriveTheme +import net.openid.appauth.AuthState +import net.openid.appauth.AuthorizationException +import net.openid.appauth.AuthorizationResponse +import net.openid.appauth.AuthorizationService + +class LoadingActivity : ComponentActivity() { + + private lateinit var masterKey: MasterKey + private lateinit var preferences: SharedPreferences + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + enableEdgeToEdge() + setContent { + DeNaDriveTheme { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + LoadingScreen(modifier = Modifier.padding(innerPadding)) + } + } + } + + masterKey = MasterKey.Builder(this) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + preferences = EncryptedSharedPreferences.create( + this, + "authState", + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + val authState = preferences.getString("authState", "bah") + ?.let { AuthState.jsonDeserialize(it) } + + val response = AuthorizationResponse.fromIntent(intent) + val exception = AuthorizationException.fromIntent(intent) + if (response != null) { + Toast.makeText(this, "Done!", Toast.LENGTH_SHORT).show() + authState?.update(response, exception) + Log.e("MainActivity", "authState: ${authState?.jsonSerializeString()}", ) + with (preferences.edit()) { + this.remove("authState") + this.putString("authState", "${authState?.jsonSerializeString()}") + this.apply() + } + } else { + Toast.makeText(this, "Not done :( ${exception?.error}", Toast.LENGTH_SHORT).show() + } + + val authService = AuthorizationService(this) + response?.createTokenExchangeRequest()?.let { + authService.performTokenRequest( + it + ) { resp, ex -> + if (resp != null) { + authState?.update(resp, ex) + with(preferences.edit()) { + this.remove("authState") + this.putString("authState", "${authState?.jsonSerializeString()}") + this.apply() + } + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + } else { + val intent = Intent(this, HomeNotAuthActivity::class.java) + startActivity(intent) + } + } + } + + } +} + +@Composable +fun LoadingScreen( + modifier: Modifier = Modifier +) { + // State to control the rotation animation + val rotation = remember { mutableFloatStateOf(0f) } + + // Animate the rotation + LaunchedEffect(Unit) { + while (true) { + rotation.floatValue += 10f + if (rotation.floatValue >= 360f) rotation.floatValue = 0f + kotlinx.coroutines.delay(16) // Approximately 60 FPS + } + } + + Box( + modifier = modifier + .fillMaxSize() + .padding(16.dp), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + // Rotating Circular Progress Indicator + CircularProgressIndicator( + modifier = Modifier + .size(64.dp) + .graphicsLayer(rotationZ = rotation.floatValue), + color = Color.Blue + ) + Spacer(modifier = Modifier.height(16.dp)) + // Loading text + Text( + text = "Loading", + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = Color.Black + ) + } + } +} + +@Composable +fun Greeting6(name: String, modifier: Modifier = Modifier) { + Text( + text = "Hello $name!", + modifier = modifier + ) +} + +@Preview(showBackground = true) +@Composable +fun GreetingPreview6() { + DeNaDriveTheme { + Greeting6("Android") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Color.kt b/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Color.kt new file mode 100644 index 0000000..39fed0d --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.bbc.denadrive.oauth.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Theme.kt b/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Theme.kt new file mode 100644 index 0000000..f42f26c --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package com.bbc.denadrive.oauth.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun DeNaDriveTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Type.kt b/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Type.kt new file mode 100644 index 0000000..91e7124 --- /dev/null +++ b/app/src/main/java/com/bbc/denadrive/oauth/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.bbc.denadrive.oauth.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7a4ba83..79a47a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,4 +4,6 @@ TestBah Speremm HomeNotAuthActivity + NeverLoggedInLogic + LoadingActivity \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c35a82c..52d922a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,11 +2,13 @@ agp = "8.9.0" appauth = "0.11.1" converterGson = "2.9.0" +googleAuthLibraryOauth2Http = "1.15.0" kotlin = "2.0.21" coreKtx = "1.15.0" junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" +kotlinxSerializationJson = "1.5.0" lifecycleRuntimeKtx = "2.8.7" activityCompose = "1.10.1" composeBom = "2025.02.00" @@ -20,6 +22,7 @@ androidx-navigation-compose = { module = "androidx.navigation:navigation-compose androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" } appauth = { module = "net.openid:appauth", version.ref = "appauth" } converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } +google-auth-library-oauth2-http = { module = "com.google.auth:google-auth-library-oauth2-http", version.ref = "googleAuthLibraryOauth2Http" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } @@ -33,6 +36,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "converterGson" }