Skip to content

Conversation

@SmilingPixel
Copy link
Owner

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

  • Added a multiplatform CloudDriveClient interface, including models for DriveFile and UserInfo, to abstract cloud drive operations (composeApp/src/commonMain/kotlin/io/github/smiling_pixel/client/CloudDriveClient.kt).
  • Implemented the Android-specific GoogleDriveClient with 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).
  • Added GoogleSignInHelper to handle Google Sign-In flows and activity result management on Android (composeApp/src/androidMain/kotlin/io/github/smiling_pixel/client/GoogleSignInHelper.kt).

UI Enhancements

  • Updated the SettingsScreen to 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]
  • Registered the Google Sign-In launcher in MainActivity to support the authorization flow (composeApp/src/androidMain/kotlin/io/github/smiling_pixel/MainActivity.kt).

Dependency and Configuration Updates

  • Added required Google Drive and Google Sign-In libraries to Android and JVM targets in build.gradle.kts (composeApp/build.gradle.kts). [1] [2]
  • Updated application namespace, package name, and application ID to io.github.smiling_pixel.mark_day for consistency across Android and desktop builds (composeApp/build.gradle.kts, AndroidManifest.xml). [1] [2] [3]

Copilot AI review requested due to automatic review settings January 22, 2026 01:32
Copy link
Contributor

Copilot AI left a 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 CloudDriveClient interface 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.

Comment on lines +75 to +76
expect fun getCloudDriveClient(): CloudDriveClient

Copy link

Copilot AI Jan 22, 2026

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.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a 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.

}

override suspend fun isAuthorized(): Boolean {
return false
Copy link

Copilot AI Jan 24, 2026

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.

Suggested change
return false
return false

Copilot uses AI. Check for mistakes.
}

companion object {
private const val MIME_TYPE_FOLDER = "application/vnd.google-apps.folder"
Copy link

Copilot AI Jan 24, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +101
val folderId = parentId ?: "root"
val query = "'$folderId' in parents and trashed = false"
Copy link

Copilot AI Jan 24, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +50
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()
Copy link

Copilot AI Jan 24, 2026

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).

Copilot uses AI. Check for mistakes.
class GoogleDriveClient : CloudDriveClient {

private val context: Context
get() = AndroidContextProvider.context
Copy link

Copilot AI Jan 24, 2026

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.

Suggested change
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
)
}

Copilot uses AI. Check for mistakes.
Comment on lines 16 to +28
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)

GoogleSignInHelper.registerLauncher(
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
GoogleSignInHelper.onActivityResult(result)
}
)

Copy link

Copilot AI Jan 24, 2026

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.

Suggested change
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
}

Copilot uses AI. Check for mistakes.
SmilingPixel and others added 4 commits January 25, 2026 15:48
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants