-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add Google Drive support #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…rieval for Google Drive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds Google Drive integration to the multiplatform application, enabling cloud storage and synchronization capabilities. The implementation includes platform-specific clients for Android and JVM (desktop), with stub implementations for Web and a missing iOS implementation.
Changes:
- Added a multiplatform
CloudDriveClientinterface with platform-specific implementations - Integrated Google Drive SDK dependencies for Android and JVM targets
- Updated SettingsScreen UI to support Google Drive authorization and account management
- Updated application namespace and package identifiers to
io.github.smiling_pixel.mark_day
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| gradle/libs.versions.toml | Added Google Drive and OAuth client library dependencies |
| composeApp/src/commonMain/kotlin/io/github/smiling_pixel/client/CloudDriveClient.kt | Defined common CloudDriveClient interface with DriveFile and UserInfo models |
| composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt | Android implementation using Google Play Services Auth and Drive API |
| composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleSignInHelper.kt | Helper object for managing Google Sign-In flow via ActivityResult API |
| composeApp/src/jvmMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt | JVM implementation using OAuth2 with browser-based authorization |
| composeApp/src/wasmJsMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt | Stub implementation with NotImplementedError responses |
| composeApp/src/commonMain/kotlin/io/github/smiling_pixel/SettingsScreen.kt | Added Google Drive authorization UI with sign-in/sign-out functionality |
| composeApp/src/androidMain/kotlin/io/github/smiling_pixel/MainActivity.kt | Registered ActivityResultLauncher for Google Sign-In flow |
| composeApp/src/androidMain/AndroidManifest.xml | Updated activity name to use fully qualified package |
| composeApp/build.gradle.kts | Added Google Drive dependencies and updated namespace/applicationId |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
composeApp/src/jvmMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
composeApp/src/jvmMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleSignInHelper.kt
Show resolved
Hide resolved
composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleSignInHelper.kt
Outdated
Show resolved
Hide resolved
composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleSignInHelper.kt
Outdated
Show resolved
Hide resolved
composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
| expect fun getCloudDriveClient(): CloudDriveClient | ||
|
|
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The iOS target is missing an implementation of the getCloudDriveClient() expected function. The common code defines expect fun getCloudDriveClient(): CloudDriveClient, and while Android, JVM, and wasmJs provide actual implementations, iOS does not. This will cause a compilation error for iOS targets. You need to add an iOS-specific implementation file similar to the wasmJs stub implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 28 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
composeApp/src/jvmMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Show resolved
Hide resolved
composeApp/src/jvmMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
composeApp/src/jvmMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
composeApp/src/jvmMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| override suspend fun isAuthorized(): Boolean { | ||
| return false |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is trailing whitespace after "false" on this line. This is a minor code style issue that should be cleaned up.
| return false | |
| return false |
| } | ||
|
|
||
| companion object { | ||
| private const val MIME_TYPE_FOLDER = "application/vnd.google-apps.folder" |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MIME_TYPE_FOLDER constant is duplicated across JVM (line 257) and Android (line 28) implementations with identical values. This violates the DRY (Don't Repeat Yourself) principle and could lead to inconsistencies if one is updated but not the other. Consider moving this constant to the common CloudDriveClient interface or creating a companion object in the common module that both implementations can reference.
| val folderId = parentId ?: "root" | ||
| val query = "'$folderId' in parents and trashed = false" |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The query string is constructed using string interpolation with the folderId parameter (line 101). While Google Drive's API likely handles this safely, and the folderId comes from trusted sources (Drive itself or "root"), this pattern could be vulnerable if the folderId is ever sourced from user input. Consider using parameterized queries or clearly documenting that folderId must be validated/sanitized if it ever comes from user input. The same pattern appears in the Android implementation at line 127.
| package io.github.smiling_pixel.client | ||
|
|
||
| /** | ||
| * Implementation of [CloudDriveClient] for Google Drive on Web. | ||
| * CURRENTLY NOT IMPLEMENTED. | ||
| */ | ||
| class GoogleDriveClient : CloudDriveClient { | ||
|
|
||
| override suspend fun listFiles(parentId: String?): List<DriveFile> { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun createFile(name: String, content: ByteArray, mimeType: String, parentId: String?): DriveFile { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun createFolder(name: String, parentId: String?): DriveFile { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun deleteFile(fileId: String) { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun downloadFile(fileId: String): ByteArray { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun updateFile(fileId: String, content: ByteArray): DriveFile { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun isAuthorized(): Boolean { | ||
| return false | ||
| } | ||
|
|
||
| override suspend fun authorize(): Boolean { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun signOut() { | ||
| throw NotImplementedError("Google Drive is not supported on Web target yet.") | ||
| } | ||
|
|
||
| override suspend fun getUserInfo(): UserInfo? { | ||
| return null | ||
| } | ||
| } | ||
|
|
||
| actual fun getCloudDriveClient(): CloudDriveClient = GoogleDriveClient() |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wasmJsMain implementation is missing a corresponding iOS implementation. All other platform-specific implementations (Android, JVM, WasmJS) have been provided, but there is no GoogleDriveClient.kt file in the iosMain source set. This will cause compilation errors for iOS targets since the expect fun getCloudDriveClient(): CloudDriveClient declaration in commonMain requires an actual implementation for all configured targets (including iosArm64 and iosSimulatorArm64 as defined in build.gradle.kts lines 27-28).
| class GoogleDriveClient : CloudDriveClient { | ||
|
|
||
| private val context: Context | ||
| get() = AndroidContextProvider.context |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The AndroidContextProvider.context is accessed on the first call to getService() or any Drive operation, but if MainActivity hasn't been fully initialized yet, this could throw an exception or use an uninitialized lateinit property. The GoogleDriveClient instance is created lazily (line 219), and if it's accessed before AndroidContextProvider.context is set in MainActivity.onCreate (line 29), this will crash. Consider adding validation to ensure context is initialized before accessing it, or document the initialization order requirement clearly.
| get() = AndroidContextProvider.context | |
| get() = try { | |
| AndroidContextProvider.context | |
| } catch (e: UninitializedPropertyAccessException) { | |
| throw IllegalStateException( | |
| "AndroidContextProvider.context is not initialized. " + | |
| "Ensure it is set before using GoogleDriveClient.", | |
| e | |
| ) | |
| } |
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| enableEdgeToEdge() | ||
| super.onCreate(savedInstanceState) | ||
|
|
||
| GoogleSignInHelper.registerLauncher( | ||
| registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> | ||
| GoogleSignInHelper.onActivityResult(result) | ||
| } | ||
| ) | ||
|
|
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GoogleSignInHelper.registerLauncher is called in MainActivity.onCreate before super.onCreate() is called (line 21). This violates Android best practices and could lead to issues because the Activity isn't fully initialized. The registration should happen after super.onCreate() and enableEdgeToEdge() calls. Additionally, since onCreate can be called multiple times (e.g., on configuration changes), registering the launcher multiple times could cause issues. Consider checking if the launcher is already registered or handle this more carefully.
| override fun onCreate(savedInstanceState: Bundle?) { | |
| enableEdgeToEdge() | |
| super.onCreate(savedInstanceState) | |
| GoogleSignInHelper.registerLauncher( | |
| registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> | |
| GoogleSignInHelper.onActivityResult(result) | |
| } | |
| ) | |
| companion object { | |
| private var launcherRegistered: Boolean = false | |
| } | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| enableEdgeToEdge() | |
| if (!launcherRegistered) { | |
| GoogleSignInHelper.registerLauncher( | |
| registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> | |
| GoogleSignInHelper.onActivityResult(result) | |
| } | |
| ) | |
| launcherRegistered = true | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This pull request introduces Google Drive integration into the application, enabling users to authorize, access, and manage their files in Google Drive from within the app. The changes include new client interfaces, Android implementation, UI updates for authorization, and necessary dependency and configuration updates.
Google Drive Integration
CloudDriveClientinterface, including models forDriveFileandUserInfo, to abstract cloud drive operations (composeApp/src/commonMain/kotlin/io/github/smiling_pixel/client/CloudDriveClient.kt).GoogleDriveClientwith full support for authorization, file/folder management, and user info retrieval, using Google APIs (composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleDriveClient.kt).GoogleSignInHelperto handle Google Sign-In flows and activity result management on Android (composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleSignInHelper.kt).UI Enhancements
SettingsScreento allow users to authorize Google Drive, view account info, and revoke access, reflecting authorization state and user details in the UI (composeApp/src/commonMain/kotlin/io/github/smiling_pixel/SettingsScreen.kt). [1] [2] [3]MainActivityto support the authorization flow (composeApp/src/androidMain/kotlin/io/github/smiling_pixel/MainActivity.kt).Dependency and Configuration Updates
build.gradle.kts(composeApp/build.gradle.kts). [1] [2]io.github.smiling_pixel.mark_dayfor consistency across Android and desktop builds (composeApp/build.gradle.kts,AndroidManifest.xml). [1] [2] [3]