OAuth flow almost fully implemented (thanks to AppAuth). Persistence of AuthState is still to do.

This commit is contained in:
AlbYoda 2025-03-11 17:52:14 +01:00
parent db2c96ef48
commit a218600761
Signed by untrusted user who does not match committer: AlbYoda
GPG key ID: A539D876452F16FF
14 changed files with 441 additions and 66 deletions

View file

@ -16,6 +16,7 @@ android {
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders["appAuthRedirectScheme"] = "com.bbc.denadrive"
}
buildTypes {
@ -37,6 +38,9 @@ android {
buildFeatures {
compose = true
}
packaging {
resources.excludes.add("META-INF-DEPENDENCIES")
}
}
dependencies {
@ -59,6 +63,14 @@ dependencies {
implementation(libs.androidx.navigation.compose)
implementation(libs.okhttp)
implementation(libs.retrofit)
implementation(libs.converter.gson)
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)

View file

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
@ -14,16 +14,43 @@
android:supportsRtl="true"
android:theme="@style/Theme.DeNaDrive"
tools:targetApi="31">
<activity
android:name=".HomeNotAuthActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.DeNaDrive" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".TestBah"
android:exported="false"
android:label="@string/title_activity_test_bah"
android:theme="@style/Theme.DeNaDrive" />
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.DeNaDrive">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- <intent-filter android:autoVerify="true"> -->
<!-- <action android:name="android.intent.action.VIEW" /> -->
<!-- <category android:name="android.intent.category.DEFAULT" /> -->
<!-- <category android:name="android.intent.category.BROWSABLE" /> -->
<!-- <data android:scheme="https" /> -->
<!-- <data android:host="denadrive.bbc.com" /> -->
<!-- </intent-filter> -->
</activity>
</application>

View file

@ -0,0 +1,128 @@
package com.bbc.denadrive
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
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.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
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 com.bbc.denadrive.oauth.retrieveAuthServConf
import com.bbc.denadrive.ui.theme.DeNaDriveTheme
import net.openid.appauth.AuthorizationRequest
import net.openid.appauth.AuthorizationService
import net.openid.appauth.ResponseTypeValues
class HomeNotAuthActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
DeNaDriveTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
val logo = painterResource(id = R.drawable.vecchiaicona_)
LogoWithCaptionAndButton (
logo = logo,
caption = "Android",
buttonText = "PressMe",
modifier = Modifier.padding(innerPadding)
) { handleAuthStuff() }
}
}
}
}
private fun handleAuthStuff() {
val serviceConfig = retrieveAuthServConf()
// val authState = AuthState(serviceConfig)
val authRequestBuilder = AuthorizationRequest.Builder(
serviceConfig,
"722393551256-pum20gbvb1es723kt3ek9lmtrdimr0os.apps.googleusercontent.com",
ResponseTypeValues.CODE,
"com.bbc.denadrive:/oauth2redirect".toUri()
)
val authRequest = authRequestBuilder
.setScope("https://www.googleapis.com/auth/drive")
.build()
val authService = AuthorizationService(this)
authService.performAuthorizationRequest(
authRequest,
PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java),
PendingIntent.FLAG_MUTABLE),
PendingIntent.getActivity(this, 0, Intent(this, HomeNotAuthActivity::class.java),
PendingIntent.FLAG_MUTABLE)
)
}
}
@Composable
fun Greeting4(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview4() {
DeNaDriveTheme {
Greeting4("Android")
}
}
@Composable
fun LogoWithCaptionAndButton(
logo: Painter, // This will be your SVG logo
caption: String,
buttonText: String,
modifier: Modifier,
onButtonClick: () -> Unit
) {
Column(
modifier = modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
// Logo
Image(
painter = logo,
contentDescription = "Logo",
modifier = Modifier.size(100.dp) // Adjust size as needed
)
Spacer(modifier = Modifier.height(16.dp)) // Space between logo and caption
// Caption
Text(text = caption)
Spacer(modifier = Modifier.height(16.dp)) // Space between caption and button
// Button
Button(onClick = onButtonClick) {
Text(text = buttonText)
}
}
}

View file

@ -1,26 +1,36 @@
package com.bbc.denadrive
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
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.core.net.toUri
import androidx.navigation.compose.rememberNavController
import com.bbc.denadrive.home.ScaffoldWithSidebar
import com.bbc.denadrive.oauth.retrieveAuthServConf
import com.bbc.denadrive.ui.theme.DeNaDriveTheme
import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationRequest
import net.openid.appauth.AuthorizationResponse
import net.openid.appauth.AuthorizationService
import net.openid.appauth.ResponseTypeValues
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// handleAuthStuff()
setContent {
val navController = rememberNavController()
NavigationGraph(navController)
ScaffoldWithSidebar(navController) { }
// NavigationGraph(navController)
}
// enableEdgeToEdge()
// setContent {
@ -34,6 +44,49 @@ class MainActivity : ComponentActivity() {
// }
// }
}
private fun handleAuthStuff() {
val serviceConfig = retrieveAuthServConf()
// val authState = AuthState(serviceConfig)
val authRequestBuilder = AuthorizationRequest.Builder(
serviceConfig,
"722393551256-pum20gbvb1es723kt3ek9lmtrdimr0os.apps.googleusercontent.com",
ResponseTypeValues.CODE,
"com.bbc.denadrive:/oauth2redirect".toUri()
)
val authRequest = authRequestBuilder
.setScope("https://www.googleapis.com/auth/drive")
.build()
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)
)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val resp = AuthorizationResponse.fromIntent(intent)
val ex = AuthorizationException.fromIntent(intent)
if (resp != null) {
Toast.makeText(this, "YES", Toast.LENGTH_SHORT).show()
}
else {
if (ex != null) {
Toast.makeText(this, "NOPE ${ex.error}", Toast.LENGTH_SHORT).show()
}
}
setContent {
val navController = rememberNavController()
ScaffoldWithSidebar(navController) { }
}
}
}
@Composable

View file

@ -6,7 +6,6 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.bbc.denadrive.home.DetailsScreen
import com.bbc.denadrive.home.HomeScreen
import com.bbc.denadrive.home.ScaffoldWithSidebar
import com.bbc.denadrive.home.MyListVisualizer
@Composable
@ -24,6 +23,6 @@ fun NavigationGraph(navController: NavHostController) {
DetailsScreen(itemId, navController)
}
composable("prova") { ScaffoldWithSidebar { lista -> MyListVisualizer(lista) } }
composable("prova") { MyListVisualizer() }
}
}

View file

@ -0,0 +1,86 @@
package com.bbc.denadrive
import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
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.core.net.toUri
import com.bbc.denadrive.oauth.retrieveAuthServConf
import com.bbc.denadrive.ui.theme.DeNaDriveTheme
import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationRequest
import net.openid.appauth.AuthorizationResponse
import net.openid.appauth.AuthorizationService
import net.openid.appauth.ResponseTypeValues
class TestActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val resp = AuthorizationResponse.fromIntent(intent)
val ex = AuthorizationException.fromIntent(intent)
if (resp != null) {
Toast.makeText(this, "YES", Toast.LENGTH_SHORT).show()
}
else {
if (ex != null) {
Toast.makeText(this, "NOPE ${ex.error}", Toast.LENGTH_SHORT).show()
}
}
enableEdgeToEdge()
setContent {
DeNaDriveTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting2(
name = "Android2",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
enableEdgeToEdge()
setContent {
DeNaDriveTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting2(
name = "Android45",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
@Composable
fun Greeting2(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview2() {
DeNaDriveTheme {
Greeting2("Android")
}
}

View file

@ -0,0 +1,87 @@
package com.bbc.denadrive
import android.content.Intent
import android.os.Bundle
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 com.bbc.denadrive.ui.theme.DeNaDriveTheme
import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationResponse
class TestBah : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val resp = AuthorizationResponse.fromIntent(intent)
val ex = AuthorizationException.fromIntent(intent)
if (resp != null) {
Toast.makeText(this, "YES", Toast.LENGTH_SHORT).show()
}
else {
if (ex != null) {
Toast.makeText(this, "NOPE ${ex.error}", Toast.LENGTH_SHORT).show()
}
}
enableEdgeToEdge()
setContent {
DeNaDriveTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting3(
name = "Android4",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val resp = AuthorizationResponse.fromIntent(intent)
val ex = AuthorizationException.fromIntent(intent)
if (resp != null) {
Toast.makeText(this, "YES", Toast.LENGTH_SHORT).show()
}
else {
if (ex != null) {
Toast.makeText(this, "NOPE ${ex.error}", Toast.LENGTH_SHORT).show()
}
}
setContent {
DeNaDriveTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting3(
name = "Android12",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
@Composable
fun Greeting3(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview3() {
DeNaDriveTheme {
Greeting3("Android")
}
}

View file

@ -6,18 +6,6 @@ import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
//import androidx.compose.foundation.background
//import androidx.compose.foundation.clickable
//import androidx.compose.foundation.layout.*
//import androidx.compose.material3.*
//import androidx.compose.runtime.*
//import androidx.compose.ui.Alignment
//import androidx.compose.ui.Modifier
//import androidx.compose.ui.graphics.Color
//import androidx.compose.ui.unit.dp
//import androidx.compose.ui.unit.sp
//import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
@ -35,10 +23,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import androidx.compose.foundation.lazy.items
//import androidx.compose.material3.icons.Icons
//import androidx.compose.material3.icons.filled.Menu
//import androidx.compose.material3.icons.filled.MoreVert
import com.bbc.denadrive.NavigationGraph
@Composable
fun HomeScreen(navController: NavHostController) {
@ -56,14 +41,14 @@ fun DetailsScreen(itemId: String?, navController: NavHostController) {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ScaffoldWithSidebar(
toBeDisplayed: @Composable (List<String>) -> Unit
navController: NavHostController,
doIt: () -> Unit,
// toBeDisplayed: @Composable () -> Unit
) {
// State to control the drawer
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope()
val lista = listOf<String>("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",)
// ModalNavigationDrawer
ModalNavigationDrawer(
drawerState = drawerState,
@ -99,7 +84,7 @@ fun ScaffoldWithSidebar(
}
},
actions = {
IconButton(onClick = { /* Handle settings click */ }) {
IconButton(onClick = { doIt() }) {
// Icon for the settings (three vertical dots)
Icon(Icons.Filled.MoreVert, contentDescription = "Settings")
}
@ -114,7 +99,8 @@ fun ScaffoldWithSidebar(
.padding(16.dp),
contentAlignment = Alignment.Center
) {
toBeDisplayed(lista)
NavigationGraph(navController)
// toBeDisplayed()
// Text("Main Content", fontSize = 24.sp)
}
}
@ -158,9 +144,8 @@ fun RectangularButton(
}
@Composable
fun MyListVisualizer (
lista: List<String>
) {
fun MyListVisualizer () {
val lista = listOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z")
LazyColumn {
items(lista) { messaggio -> Text(text=messaggio, fontSize = 30.sp) }
}

View file

@ -0,0 +1,11 @@
package com.bbc.denadrive.oauth
import androidx.core.net.toUri
import net.openid.appauth.AuthorizationServiceConfiguration
fun retrieveAuthServConf(): AuthorizationServiceConfiguration {
return AuthorizationServiceConfiguration(
"https://accounts.google.com/o/oauth2/v2/auth".toUri(),
"https://oauth2.googleapis.com/token".toUri()
)
}

View file

@ -0,0 +1,2 @@
package com.bbc.denadrive.oauth

View file

@ -1,19 +0,0 @@
package com.bbc.denadrive.oauth
data class OAuthConfiguration(
val clientId: String,
val scopes: List<String>,
val authorizationEndpoint: String,
val tokenEndpoint: String,
val redirectUri: String,
)
//private fun createOAuthConfiguration(): OAuthConfiguration {
// return OAuthConfiguration(
// clientId = "722393551256-0v563ati4pc9rjc9p948sg418trqq998.apps.googleusercontent.com",
// scopes = listOf("https://mail.google.com/"),
// authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth",
// tokenEndpoint = "https://oauth2.googleapis.com/token",
// redirectUri = "${BuildConfig.APPLICATION_ID}:/oauth2redirect",
// )
//}

View file

@ -1,3 +1,7 @@
<resources>
<string name="app_name">DeNa Drive</string>
<string name="title_activity_test">TestActivity</string>
<string name="title_activity_test_bah">TestBah</string>
<string name="title_activity_speremm">Speremm</string>
<string name="title_activity_home_not_auth">HomeNotAuthActivity</string>
</resources>

View file

@ -1,8 +1,7 @@
[versions]
agp = "8.9.0"
googleApiClientAndroid = "1.32.1"
googleApiServicesDrive = "v3-rev20210830-1.32.1"
googleOauthClientJetty = "1.32.1"
appauth = "0.11.1"
converterGson = "2.9.0"
kotlin = "2.0.21"
coreKtx = "1.15.0"
junit = "4.13.2"
@ -10,16 +9,17 @@ junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.1"
composeBom = "2024.09.00"
composeBom = "2025.02.00"
navigationCompose = "2.8.8"
playServicesAuth = "21.3.0"
okhttp = "4.9.1"
securityCrypto = "1.1.0-alpha06"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
google-api-client-android = { module = "com.google.api-client:google-api-client-android", version.ref = "googleApiClientAndroid" }
google-api-services-drive = { module = "com.google.apis:google-api-services-drive", version.ref = "googleApiServicesDrive" }
google-oauth-client-jetty = { module = "com.google.oauth-client:google-oauth-client-jetty", version.ref = "googleOauthClientJetty" }
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" }
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,7 +33,8 @@ 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" }
play-services-auth = { module = "com.google.android.gms:play-services-auth", version.ref = "playServicesAuth" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "converterGson" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

View file

@ -5,7 +5,6 @@ pluginManagement {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
includeGroupByRegex(".*drive.*")
}
}
mavenCentral()