I didn't proceed with this approach due to the following reasons
- OAuth Secret will be exposed on Android, which is a security risk
google-photos-library-client
usesprotobuf-java
which conflictprotobuf-lite
used by Firestore, and excluding either one causes issues with its respective library. This is officially a Java library (not Android library).
In the end, I use a Python Cloud Functions to access the Google Photos API instead.
The following is some code snippets which I try on, but never successfully tested them as I couldn't proceed further due to the protobuf
library conflict with Firestore.
Enable Google Photos API
https://console.cloud.google.com/apis/library/photoslibrary.googleapis.com
OAuth Client Id and Secret
Goto Google Cloud Console -> Credentials
Create credentials -> OAuth Client Id
- Application Type: Android
- Name
- SHA-1
- Package Name
Download JSON and save to /app/src/main/assets/oauth2-secret.json
Module:app build.gradle
android {
// Duplicate files copied in APK META-INF/* caused by google-photos-library-client
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
// possible alternative solutions
// pickFirst 'META-INF/*'
}
// OPTIONAL: conflict with google-photos-library-client
/*
configurations {
// implementation.exclude module:'proto-google-common-protos'
implementation.exclude module:'protolite-well-known-types'
// exclude 'protobuf-lite' will cause firestore "com.google.protobuf.Timestamp but expected Reference" error
implementation.exclude module:'protobuf-lite'
}
*/
}
dependencies {
// to enable GoogleAccountCredential.usingOAuth2
implementation('com.google.api-client:google-api-client-android:1.23.0') {
exclude group: 'org.apache.httpcomponents'
exclude module: 'guava-jdk5'
}
implementation('com.google.photos.library:google-photos-library-client:1.4.0') {
// exclude module: 'proto-google-common-protos'
// if exclude 'protobuf-java', unresolved supertypes: com.google.protobuf.GeneratedMessageV3
exclude module: 'protobuf-java'
exclude module: 'threetenbp'
}
}
Code
I didn't get the chance to test if the following code works without error (use it only for reference)
class TestFragment : Fragment() { companion object { fun newInstance() = TestFragment() private const val REQUEST_GOOGLE_PHOTOS_SIGN_IN = 1 } fun signIn() { val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestScopes(Scope("https://www.googleapis.com/auth/photoslibrary.readonly")) .build() val client = GoogleSignIn.getClient(context!!, signInOptions) startActivityForResult(client.signInIntent, REQUEST_GOOGLE_PHOTOS_SIGN_IN) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_GOOGLE_PHOTOS_SIGN_IN) { if (resultCode == RESULT_OK) { GoogleSignIn.getSignedInAccountFromIntent(data) .addOnSuccessListener { account -> val scopes = listOf("https://www.googleapis.com/auth/photoslibrary.readonly") val credential = GoogleAccountCredential.usingOAuth2(context, scopes) credential.selectedAccount = account.account val clientSecrets = GoogleClientSecrets.load( jsonFactory, InputStreamReader( context!!.assets.open("oauth2-secret.json") ) ) val clientId = clientSecrets.details.clientId val clientSecret = clientSecrets.details.clientSecret val userCredentials = UserCredentials.newBuilder() .setClientId(clientId) .setClientSecret(clientSecret) .setAccessToken(credential.token) // AccessToken(credential.token, null) .build() val settings = PhotosLibrarySettings.newBuilder() .setCredentialsProvider( FixedCredentialsProvider.create(userCredentials) ) .build() val client = PhotosLibraryClient.initialize(settings) for (album in client.listAlbums().iterateAll()) { Timber.d("Album: ${album.title}") } } } } }}
References: