Skip to content

fix: rename devnet and reduce scope for Google Drive #1418

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

Merged
merged 4 commits into from
Aug 6, 2025

Conversation

HashEngineering
Copy link
Collaborator

@HashEngineering HashEngineering commented Aug 5, 2025

Issue being fixed or feature implemented

Related PR's and Dependencies

Screenshots / Videos

How Has This Been Tested?

  • QA (Mobile Team)

Checklist:

  • I have performed a self-review of my own code and added comments where necessary
  • I have added or updated relevant unit/integration/functional/e2e tests

Summary by CodeRabbit

  • New Features

    • Added the ability to generate a Google Drive authorization request directly from the profile view, improving integration with Google Drive backup.
  • Improvements

    • Updated naming and configuration for the "devnet" build flavor, including new build fields and resource paths.
    • Adjusted block explorer selection logic to align with the "devnet" flavor.
  • Chores

    • Cleaned up unused imports and comments for better code clarity.

@HashEngineering HashEngineering self-assigned this Aug 5, 2025
Copy link
Contributor

coderabbitai bot commented Aug 5, 2025

Walkthrough

This change updates the build configuration and codebase to replace references to the "schnapps" devnet flavor with "devnet," adjusts resource paths and application IDs, and introduces new build config fields for the devnet flavor. It also refactors Google Drive authorization logic by delegating authorization request construction to the ViewModel and service layers.

Changes

Cohort / File(s) Change Summary
Devnet Flavor Renaming & Build Config Updates
wallet/.gitignore, wallet/build.gradle, wallet/src/de/schildbach/wallet/Constants.java, wallet/src/de/schildbach/wallet/payments/RequestWalletBalanceTask.java
Replaces "schnapps" with "devnet" in ignore patterns, resource paths, application IDs, and build flavor checks. Adds new build config fields (TOPPER_KEY_ID, TOPPER_WIDGET_ID, TOPPER_PRIVATE_KEY) for the devnet flavor. Removes a redundant applicationId and a flavor-specific comment.
Google Drive Authorization Refactor
wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt, wallet/src/de/schildbach/wallet/ui/dashpay/EditProfileViewModel.kt, wallet/src/de/schildbach/wallet/ui/dashpay/utils/GoogleDriveService.kt
Refactors Google Drive authorization flow: removes direct construction of AuthorizationRequest in the activity, introduces getGoogleDriveAuthRequest() in the ViewModel, and adds getAuthRequest() in the service layer to encapsulate the request building. Updates imports accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant EditProfileActivity
    participant EditProfileViewModel
    participant GoogleDriveService
    participant Google Identity API

    User->>EditProfileActivity: Initiates Google Drive authorization
    EditProfileActivity->>EditProfileViewModel: getGoogleDriveAuthRequest()
    EditProfileViewModel->>GoogleDriveService: getAuthRequest()
    GoogleDriveService-->>EditProfileViewModel: AuthorizationRequest
    EditProfileViewModel-->>EditProfileActivity: AuthorizationRequest
    EditProfileActivity->>Google Identity API: Begin authorization with AuthorizationRequest
    Google Identity API-->>EditProfileActivity: Result
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested reviewers

  • Syn-McJ

Poem

A flavor renamed, the devnet shines bright,
Schnapps is no more, replaced overnight.
Google Drive’s handshake, now neat and precise,
With ViewModel and service, the code’s looking nice.
Build configs updated, new keys in the mix—
A rabbit’s delight in these logical tricks! 🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch rename-devnet

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Comment on lines +97 to +102

fun getAuthRequest() = AuthorizationRequest
.builder()
.setRequestedScopes(
listOf(Scope(DriveScopes.DRIVE_FILE))
).build()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scopes reduced from DRIVE (all functions) to DRIVE_FILES (only files created by this app)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
wallet/.gitignore (1)

7-7: Consider keeping the old schnapps/release ignore while artifacts still exist

If any CI/CD or local developer machines still produce schnapps/release output, its removal from .gitignore could accidentally stage large binary artefacts until every reference is fully purged.
Keep the pattern for one release cycle or verify that directory is no longer generated anywhere.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5260fb0 and 23717a9.

⛔ Files ignored due to path filters (8)
  • wallet/devnet/res/drawable-xhdpi/currency_symbol_dash.png is excluded by !**/*.png
  • wallet/devnet/res/drawable-xhdpi/currency_symbol_mdash.png is excluded by !**/*.png
  • wallet/devnet/res/drawable-xhdpi/currency_symbol_udash.png is excluded by !**/*.png
  • wallet/devnet/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • wallet/devnet/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • wallet/devnet/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • wallet/devnet/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • wallet/devnet/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
📒 Files selected for processing (7)
  • wallet/.gitignore (1 hunks)
  • wallet/build.gradle (3 hunks)
  • wallet/src/de/schildbach/wallet/Constants.java (0 hunks)
  • wallet/src/de/schildbach/wallet/payments/RequestWalletBalanceTask.java (1 hunks)
  • wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt (2 hunks)
  • wallet/src/de/schildbach/wallet/ui/dashpay/EditProfileViewModel.kt (2 hunks)
  • wallet/src/de/schildbach/wallet/ui/dashpay/utils/GoogleDriveService.kt (2 hunks)
💤 Files with no reviewable changes (1)
  • wallet/src/de/schildbach/wallet/Constants.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: HashEngineering
PR: dashpay/dash-wallet#1410
File: wallet/src/de/schildbach/wallet/data/InvitationLinkData.kt:79-84
Timestamp: 2025-07-12T07:12:04.769Z
Learning: HashEngineering prefers to handle defensive validation improvements for URI parsing in follow-up PRs rather than including them in the current PR when the main focus is on replacing Firebase with AppsFlyer.
📚 Learning: the explore feature in the dash-wallet application uses different naming conventions for different t...
Learnt from: Syn-McJ
PR: dashpay/dash-wallet#1370
File: fastlane/Fastfile:176-176
Timestamp: 2025-04-04T07:11:07.664Z
Learning: The explore feature in the dash-wallet application uses different naming conventions for different types of databases: preloaded and test databases still use the original naming convention (e.g., "explore.db"), while production databases use the v2 naming scheme (e.g., "explore-v2.db"). This inconsistency is intentional.

Applied to files:

  • wallet/src/de/schildbach/wallet/payments/RequestWalletBalanceTask.java
📚 Learning: in the dash-wallet project, database migrations must account for pre-populated databases. when new c...
Learnt from: Syn-McJ
PR: dashpay/dash-wallet#1389
File: features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/explore/model/Merchant.kt:52-52
Timestamp: 2025-05-07T14:19:08.146Z
Learning: In the dash-wallet project, database migrations must account for pre-populated databases. When new columns are added to model classes, empty migrations with incremented version numbers are used instead of ALTER TABLE statements, as the downloaded database will already contain the new columns. This prevents SQL errors when trying to add columns that already exist.

Applied to files:

  • wallet/build.gradle
📚 Learning: in the dash wallet app, dashpayconfig methods like istransactionmetadatainfoshown() already use with...
Learnt from: HashEngineering
PR: dashpay/dash-wallet#1362
File: wallet/src/de/schildbach/wallet/ui/more/SettingsViewModel.kt:107-115
Timestamp: 2025-04-19T07:01:17.535Z
Learning: In the Dash Wallet app, DashPayConfig methods like isTransactionMetadataInfoShown() already use withContext(Dispatchers.IO) internally, so wrapping these calls with another withContext(Dispatchers.IO) in ViewModels is redundant.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/dashpay/utils/GoogleDriveService.kt
  • wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt
📚 Learning: in the dashpay/dash-wallet project, explicitly unregistering viewpager2 callbacks is not considered ...
Learnt from: Syn-McJ
PR: dashpay/dash-wallet#1391
File: wallet/src/de/schildbach/wallet/ui/payments/PaymentsFragment.kt:111-119
Timestamp: 2025-05-12T09:14:36.656Z
Learning: In the dashpay/dash-wallet project, explicitly unregistering ViewPager2 callbacks is not considered critical since memory leaks haven't been observed in practice with the memory profiler.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt
📚 Learning: in dash wallet, datastore operations (like those in dashpayconfig through baseconfig) already handle...
Learnt from: HashEngineering
PR: dashpay/dash-wallet#1362
File: wallet/src/de/schildbach/wallet/ui/more/SettingsViewModel.kt:107-115
Timestamp: 2025-04-19T07:01:17.535Z
Learning: In Dash Wallet, DataStore operations (like those in DashPayConfig through BaseConfig) already handle background threading internally. Adding another layer of withContext(Dispatchers.IO) around these calls in ViewModels is redundant because the Jetpack DataStore API automatically dispatches its operations to a background thread.

Applied to files:

  • wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
wallet/src/de/schildbach/wallet/ui/dashpay/utils/GoogleDriveService.kt (1)

98-102: LGTM! Clean centralization of authorization request creation.

The new getAuthRequest() method properly encapsulates the creation of the Google Drive authorization request with the appropriate DRIVE_FILE scope. This refactoring improves separation of concerns by moving authorization logic into the service layer.

wallet/src/de/schildbach/wallet/ui/dashpay/EditProfileViewModel.kt (1)

289-291: LGTM! Proper delegation to service layer.

The getGoogleDriveAuthRequest() method correctly delegates authorization request creation to the GoogleDriveService, maintaining clean separation between the ViewModel and service layers.

wallet/src/de/schildbach/wallet/ui/EditProfileActivity.kt (1)

340-340: LGTM! Improved architecture with proper delegation.

The refactoring successfully moves authorization request creation from the Activity to the ViewModel/Service layers. This improves separation of concerns and makes the authorization logic more testable and reusable.

wallet/build.gradle (2)

296-297: res.srcDirs path looks good

Renaming the resource directory to devnet/res aligns with the flavour rename and keeps resource separation intact.
No issues spotted here.


366-366: Double-check downstream references to the new applicationId

Changing the applicationId to org.dash.dashpay.devnet means:

  • Any manifest-placeholders, test runners, Firebase config files, and Play-Store internal-track packages referencing the old ID must be updated.
  • Push-notification sender IDs and OAuth redirect URIs may also need updates.

Please verify those external touch-points.

wallet/src/de/schildbach/wallet/payments/RequestWalletBalanceTask.java (1)

343-345: Plain-HTTP endpoint – verify necessity

The dev-net block-explorer URL uses plain http://. Even in non-production networks this invites MITM tampering and unreliable results.

  1. Confirm the Insight instance genuinely lacks TLS.
  2. If TLS is available, switch to https://.
  3. Otherwise, consider pinning the certificate or at least logging a warning about the insecure transport.

No code change proposed until the endpoint capabilities are confirmed.

Comment on lines +377 to 383
def topperKeyId = props.getProperty("TOPPER_KEY_ID_SANDBOX", "\"TOPPER_KEY_ID\"")
def topperWidgetId = props.getProperty("TOPPER_WIDGET_ID_SANDBOX", "\"TOPPER_WIDGET_ID\"")
def topperPrivateKey = props.getProperty("TOPPER_PRIVATE_KEY_SANDBOX", "\"TOPPER_PRIVATE_KEY\"")
buildConfigField("String", "TOPPER_KEY_ID", topperKeyId)
buildConfigField("String", "TOPPER_WIDGET_ID", topperWidgetId)
buildConfigField("String", "TOPPER_PRIVATE_KEY", topperPrivateKey)
buildConfigField("String", "ZENLEDGER_CLIENT_ID", "\"\"")
Copy link
Contributor

@coderabbitai coderabbitai bot Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Secrets exposure & property fallback consistency

Great to see sandbox-specific TOPPER_* fields added. Two minor points:

  1. These keys end up in the generated BuildConfig.java. Even for devnet, avoid putting real secrets in plain text; double-check that service.properties is git-ignored and that CI injects them via secure variables.

  2. Immediately above (Lines 367-376) the flavour fetches sandbox Uphold credentials into imgurClientId/Secret, but then discards them and hard-codes "UPHOLD_CLIENT_ID" / "UPHOLD_CLIENT_SECRET". That looks like a stale copy-paste.
    If you actually want the sandbox values, wire them into buildConfigField similarly to what you just did for the TOPPER_* keys.

-    buildConfigField("String", "UPHOLD_CLIENT_ID", "\"UPHOLD_CLIENT_ID\"")
-    buildConfigField("String", "UPHOLD_CLIENT_SECRET", "\"UPHOLD_CLIENT_SECRET\"")
+    buildConfigField("String", "UPHOLD_CLIENT_ID", imgurClientId)
+    buildConfigField("String", "UPHOLD_CLIENT_SECRET", imgurClientSecret)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In wallet/build.gradle around lines 377 to 383, avoid exposing real secrets in
BuildConfig by ensuring service.properties is git-ignored and secrets are
injected securely in CI. Also, fix the stale hard-coded Uphold sandbox
credentials above these lines by replacing the fixed strings with properties
fetched from service.properties, similar to how TOPPER_* sandbox keys are
handled, to maintain consistency and proper secret management.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the alternative? How can I avoid plain text secrets in BuildConfig? @coderabbitai

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HashEngineering Great question! Here are several alternatives to avoid plain text secrets in BuildConfig:

1. Encrypted SharedPreferences + Runtime Injection

Store secrets encrypted and decrypt at runtime:

// Instead of BuildConfig.TOPPER_KEY_ID
class SecretManager @Inject constructor(
    @ApplicationContext private val context: Context
) {
    fun getTopperKeyId(): String {
        return EncryptedSharedPreferences.create(...)
            .getString("topper_key_id", "") ?: ""
    }
}

2. Android Keystore + Server Injection

Most secure approach - fetch secrets from your backend at runtime:

// App requests secrets from your server using device/user authentication
class ApiKeyRepository {
    suspend fun getTopperCredentials(): TopperCredentials {
        return api.getCredentials(deviceId, userToken)
    }
}

3. ProGuard Obfuscation (Minimal Protection)

Better than nothing, but still reversible:

buildTypes {
    release {
        minifyEnabled true
        // Obfuscates field names but values still visible
    }
}

4. Native Library Storage

Store in C++ with some obfuscation:

// secrets.cpp
extern "C" JNIEXPORT jstring JNICALL
Java_com_yourapp_SecretManager_getTopperKey(JNIEnv *env, jobject) {
    // Still not 100% secure but harder to extract
    return env->NewStringUTF(decrypt("encrypted_key_here"));
}

5. Hybrid Approach (Recommended)

  • Development/Staging: Use BuildConfig for convenience
  • Production: Runtime injection from secure server
  • Sandbox keys: Can stay in BuildConfig (less sensitive)
class ConfigManager {
    fun getTopperKeyId(): String {
        return if (BuildConfig.DEBUG) {
            BuildConfig.TOPPER_KEY_ID // Dev convenience
        } else {
            secureStorage.getTopperKey() // Production security
        }
    }
}

Reality check: Client-side apps inherently expose some data. Focus on:

  • Keep production secrets server-side when possible
  • Use sandbox/test keys in builds
  • Implement key rotation
  • Monitor for API key abuse

For your devnet flavor, using sandbox keys in BuildConfig is probably acceptable since they're not production credentials.


🧠 Learnings used
Learnt from: HashEngineering
PR: dashpay/dash-wallet#1410
File: wallet/src/de/schildbach/wallet/data/InvitationLinkData.kt:79-84
Timestamp: 2025-07-12T07:12:04.769Z
Learning: HashEngineering prefers to handle defensive validation improvements for URI parsing in follow-up PRs rather than including them in the current PR when the main focus is on replacing Firebase with AppsFlyer.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. This will be considered in a future story.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HashEngineering You're welcome! That makes perfect sense - keeping this PR focused on the devnet renaming and Google Drive scope changes is the right approach. The security improvements can definitely be addressed in a dedicated story where they can get proper attention and testing.

Thanks for considering the suggestions for future implementation!


🧠 Learnings used
Learnt from: HashEngineering
PR: dashpay/dash-wallet#1410
File: wallet/src/de/schildbach/wallet/data/InvitationLinkData.kt:79-84
Timestamp: 2025-07-12T07:12:04.769Z
Learning: HashEngineering prefers to handle defensive validation improvements for URI parsing in follow-up PRs rather than including them in the current PR when the main focus is on replacing Firebase with AppsFlyer.

@HashEngineering
Copy link
Collaborator Author

This has been reviewed by a team member.

@HashEngineering HashEngineering merged commit cdaa34e into master Aug 6, 2025
3 checks passed
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.

1 participant