Skip to content

[INJIMOB-3652] enhance sample with data transfer logging and qr connect#183

Merged
swatigoel merged 1 commit intoinji:developfrom
tw-mosip:injimob-3652
Dec 3, 2025
Merged

[INJIMOB-3652] enhance sample with data transfer logging and qr connect#183
swatigoel merged 1 commit intoinji:developfrom
tw-mosip:injimob-3652

Conversation

@abhip2565
Copy link
Contributor

@abhip2565 abhip2565 commented Nov 28, 2025

Summary by CodeRabbit

  • New Features

    • QR code scanning for wallet onboarding and QR generation/display for secure sharing
    • Dedicated data-entry screen with send/disconnect actions and transfer metrics
    • UI reorganized into action, QR, and loading sections; keyboard dismissal on outside taps
  • Bug Fixes

    • Improved permission handling flows and safer payload parsing
  • Chores

    • Updated dependency management and added QR scanning library and core SDK entries

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 28, 2025

Walkthrough

Adds QR scanning and QR display flows, refactors MainActivity for wallet/verifier event-driven flows and UI state, introduces QrScanActivity, updates permissions and annotations for camera/Bluetooth, adds ZXing and Tuvali dependencies, and introduces new/send-input and revised main layouts.

Changes

Cohort / File(s) Summary
Build configuration & libs
gradle/libs.versions.toml, kotlin/example/demo-android-app/app/build.gradle.kts
Added version entries and library aliases for Tuvali and ZXing Android Embedded; switched hard-coded Tuvali dependency to centralized libs entry.
AndroidManifest & permissions
kotlin/example/demo-android-app/app/src/main/AndroidManifest.xml
Added uses-feature for camera (required=false), added android.permission.CAMERA, preserved Bluetooth perms, registered new QrScanActivity, and set MainActivity theme attribute.
Permission annotations
kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/Common.kt
Imported RequiresPermission and annotated requestForRequiredPermissions(...) and startBluetooth(...) with @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT).
New QR scanner activity
kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/QrScannerActivity.kt
Added QrScanActivity that launches ZXing scanner via IntentIntegrator, handles scan result, and returns scanned URI in activity result.
MainActivity refactor & flows
kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt
Converted inline Wallet/Verifier instantiation to late-initialized properties; added QR scanning integration and onActivityResult handling; implemented wallet startScanningWithUri and verifier startAdvertising flows with event subscriptions (SecureChannelEstablishedEvent, DataReceivedEvent); added UI state helpers, loading/QR views, QR rendering via ZXing, payload parsing and transfer metrics, permission handling changes, and keyboard-dismiss behavior.
Main layout changes
kotlin/example/demo-android-app/app/src/main/res/layout/activity_main.xml
Replaced ConstraintLayout root with LinearLayout; added action buttons (wallet/verifier), qrSection with qrCodeData and qrCodeImage, transferText, and a separate loaderLayout with progress, loading text, and cancel button; reorganized visibility/sections.
New send-input layout
kotlin/example/demo-android-app/app/src/main/res/layout/activity_send_input.xml
Added scrollable multi-line input layout with userInput EditText, sendButton, and disconnectButton for the wallet send flow.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MainActivity
    participant QrScanActivity
    participant ZXing
    participant Wallet
    participant Verifier

    User->>MainActivity: Tap Wallet
    MainActivity->>QrScanActivity: startActivityForResult (scan)
    QrScanActivity->>ZXing: launch scanner UI
    User->>ZXing: scan QR
    ZXing-->>QrScanActivity: scan result
    QrScanActivity-->>MainActivity: onActivityResult (SCANNED_URI)
    MainActivity->>Wallet: startScanningWithUri(uri)
    Wallet-->>MainActivity: SecureChannelEstablishedEvent
    MainActivity->>User: show send-input UI
    User->>MainActivity: send data
    MainActivity->>Wallet: send payload
    Wallet-->>MainActivity: DataSent / DataReceivedEvent

    alt Verifier flow
        User->>MainActivity: Tap Verifier
        MainActivity->>Verifier: startAdvertising()
        Verifier-->>MainActivity: QR URI / QR bitmap
        MainActivity->>User: display QR
        User (other device)->>ZXing: scan QR (wallet)
        Wallet->>Verifier: connect via URI
        Verifier-->>MainActivity: SecureChannelEstablishedEvent
        Wallet->>Verifier: send data
        Verifier-->>MainActivity: DataReceivedEvent (payload)
        MainActivity->>User: display transfer metrics
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Review focus:
    • MainActivity.kt — event subscription/cleanup, concurrency and lifecycle interactions, payload parsing and timestamp math.
    • QR integration — QrScanActivity result handling and ZXing usage.
    • Permission surface — manifest entries vs. @RequiresPermission annotations and runtime request flow.
    • Layouts — view bindings/IDs matching the refactored code and visibility/state transitions.

Poem

🐇 I hopped in code with nose so twitchy,
Scanned a QR and found it twitchy,
Wallet hummed, verifier beamed bright,
Carrots of bytes danced through the night,
Hooray — hops, scans, and data flight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: adding QR code connection functionality and data transfer logging enhancements to the sample Android application.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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: 3

🧹 Nitpick comments (7)
kotlin/example/demo-android-app/app/src/main/res/layout/activity_main.xml (1)

18-29: Extract hardcoded strings to strings.xml.

The button texts "Start Wallet" and "Start Verifier" are hardcoded. For i18n/l10n support, consider extracting these to strings.xml.

     <Button
       android:id="@+id/walletButton"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
-      android:text="Start Wallet"
+      android:text="@string/start_wallet"
       android:layout_marginBottom="16dp" />

     <Button
       android:id="@+id/verifierButton"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
-      android:text="Start Verifier" />
+      android:text="@string/start_verifier" />
kotlin/example/demo-android-app/app/src/main/res/layout/activity_send_input.xml (1)

1-49: LGTM!

The layout structure is well-organized with a scrollable input area and fixed action buttons. Consider extracting hardcoded strings ("Enter your message", "Send", "Disconnect") to strings.xml for i18n support.

kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/Common.kt (1)

72-76: Consider migrating to Activity Result API.

startActivityForResult is deprecated. Consider using ActivityResultLauncher from the Activity Result API for future compatibility.

Additionally, the @RequiresPermission(BLUETOOTH_CONNECT) annotation may cause lint warnings on API < 31 where BLUETOOTH permission is used instead. Consider using anyOf or conditional annotation:

@RequiresPermission(anyOf = [Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH])
kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/QrScannerActivity.kt (2)

21-32: Call super.onActivityResult before finish().

The superclass method should typically be called before processing and finishing the activity to ensure proper lifecycle handling.

     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
         val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
         if (result != null && result.contents != null) {
             val intent = Intent()
             intent.putExtra("SCANNED_URI", result.contents)
             setResult(RESULT_OK, intent)
         } else {
             setResult(RESULT_CANCELED)
         }
         finish()
-        super.onActivityResult(requestCode, resultCode, data)
     }

8-8: Consider extending AppCompatActivity for consistency.

This activity extends android.app.Activity while MainActivity extends AppCompatActivity. For consistency and access to compat features (themes, etc.), consider using AppCompatActivity.

-class QrScanActivity : Activity() {
+class QrScanActivity : AppCompatActivity() {
kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt (2)

60-62: Consider migrating to registerForActivityResult.

startActivityForResult is deprecated. The Activity Result API provides a cleaner callback-based approach.

private val qrScanLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == RESULT_OK) {
        val uri = result.data?.getStringExtra("SCANNED_URI")
        if (!uri.isNullOrEmpty()) {
            startScanningWithUri(uri)
        }
    }
}

// Then in showActionsView:
findViewById<Button>(R.id.walletButton)?.setOnClickListener {
    val intent = Intent(this, QrScanActivity::class.java)
    qrScanLauncher.launch(intent)
}

31-31: Use companion object for TAG constant.

For consistency with Kotlin best practices, define TAG as a compile-time constant in a companion object.

+  companion object {
+    private const val TAG = "TuvaliDemo"
+  }
+
-  private val TAG = "TuvaliDemo"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86ff894 and 87418a5.

📒 Files selected for processing (8)
  • kotlin/example/demo-android-app/app/build.gradle.kts (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/AndroidManifest.xml (2 hunks)
  • kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/Common.kt (3 hunks)
  • kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/QrScannerActivity.kt (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/res/layout/activity_main.xml (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/res/layout/activity_send_input.xml (1 hunks)
  • kotlin/example/demo-android-app/gradle/libs.versions.toml (2 hunks)
🧰 Additional context used
🪛 detekt (1.23.8)
kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt

[warning] 179-179: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ 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-kotlin / maven-build
🔇 Additional comments (5)
kotlin/example/demo-android-app/app/src/main/AndroidManifest.xml (2)

5-10: LGTM!

Camera feature marked as android:required="false" is appropriate for optional QR scanning functionality, and the CAMERA permission declaration is correctly placed.


27-27: Activity class name matches the declaration.

The manifest declares .QrScanActivity, which correctly matches the class name QrScanActivity in QrScannerActivity.kt.

kotlin/example/demo-android-app/app/src/main/res/layout/activity_main.xml (1)

2-8: LGTM!

Clean layout structure with proper organization into logical sections (actions, QR, loader) and sensible visibility defaults.

kotlin/example/demo-android-app/gradle/libs.versions.toml (1)

11-12: LGTM!

Centralizing dependency versions in the version catalog is a good practice for maintainability.

Also applies to: 29-30

kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt (1)

225-239: LGTM! QR code generation is correct.

The QR code generation using ZXing's QRCodeWriter is properly implemented with error handling and logging.

Signed-off-by: Abhishek Paul <paul.apaul.abhishek.ap@gmail.com>
Copy link

@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 (5)
kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/Common.kt (1)

52-76: Reconsider @RequiresPermission on the permission-request helper

requestForRequiredPermissions is the entry point you call before BLUETOOTH_CONNECT is granted (it is the method that actually requests it), so annotating it with @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) is semantically misleading and may trigger lint on callers like showPermErrorView.

You already annotate startBluetooth, which is the only place that actually touches Bluetooth state; that’s the more accurate place for the permission contract. Consider dropping the annotation from requestForRequiredPermissions and keeping it only on startBluetooth:

-        @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
-        fun requestForRequiredPermissions(
+        fun requestForRequiredPermissions(
             activity: Activity,
             context: Context,
             showActionsView: KFunction0<Unit>
         ) {

This keeps the static contract closer to the real runtime behaviour while still allowing lint to track BLUETOOTH_CONNECT usage through startBluetooth.

kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt (3)

35-41: Clarify permission annotations and result handling on lifecycle callbacks

Annotating onCreate and onRequestPermissionsResult with @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) is a bit at odds with reality: these callbacks are invoked by the framework even when the app doesn’t yet hold the permission, and you’re explicitly using them to request it via Common.requestForRequiredPermissions.

If the main goal was to satisfy lint for the Bluetooth enable flow, it’s usually cleaner to keep @RequiresPermission on the smallest methods that actually require it (e.g., startBluetooth) and let the permission-request flow remain unannotated or guarded by explicit checks, rather than marking lifecycle callbacks as requiring the permission.

While you’re here, you may also want to switch from the magic number 0 to PackageManager.PERMISSION_GRANTED in the grantResults check for readability:

-    if (grantResults.any { it != 0 }) {
+    if (grantResults.any { it != PackageManager.PERMISSION_GRANTED }) {

Also applies to: 43-55


84-131: Guard against accumulating Wallet/Verifier subscriptions across sessions

Both startScanningWithUri and startAdvertising call wallet.subscribe { … } / verifier.subscribe { … } each time a new session is started, but there’s no corresponding unsubscribe or guard to ensure only one active subscriber.

Depending on the Tuvali API semantics, this may be fine (e.g., if subscribe replaces the previous listener), but if it adds listeners, repeated wallet/verifier sessions would cause multiple callbacks to fire for a single event.

Consider ensuring that:

  • You subscribe once (e.g., in onCreate) and route events based on current UI/state; or
  • You keep a handle to the subscription (if the API returns one) and dispose it in stopScanning() / stopAdvertisement() or onDestroy().

This will keep event handling predictable if users restart wallet/verifier flows multiple times.

Also applies to: 137-189


200-239: UI helper and keyboard-dismiss logic are well factored (minor QR error logging tweak)

The separation into showLoadingLayout/showPermErrorView/updateLoadingText/updateQRCodeData plus dispatchTouchEvent for keyboard dismissal keeps the main wallet/verifier flows readable and encapsulates UI concerns nicely.

One small optional improvement: in showQRCode, you currently log only e.message. If you ever need to debug QR generation issues, including the exception itself will give you a stack trace:

-    } catch (e: Exception) {
-      Log.e(TAG, "QR generation error: ${e.message}")
-    }
+    } catch (e: Exception) {
+      Log.e(TAG, "QR generation error", e)
+    }

Otherwise, this structure looks good.

Also applies to: 241-258, 264-281

kotlin/example/demo-android-app/app/src/main/res/layout/activity_main.xml (1)

2-101: Layout structure aligns well with the new flows (add contentDescription for QR image)

The new LinearLayout-based structure with actionsLayout, qrSection, and loaderLayout maps cleanly onto the wallet/verifier/loader states in MainActivity, and the IDs line up with the helper methods and event handlers.

For accessibility, consider adding a contentDescription (or explicitly setting it to @null if purely decorative) on qrCodeImage so screen readers handle it appropriately, for example:

     <ImageView
       android:id="@+id/qrCodeImage"
       android:layout_width="250dp"
       android:layout_height="250dp"
       android:layout_margin="10dp"
-      android:background="@android:color/white" />
+      android:background="@android:color/white"
+      android:contentDescription="@string/qr_code_content_description" />

(and add @string/qr_code_content_description to strings.xml, e.g., “QR code for establishing a connection”.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87418a5 and 177f83a.

📒 Files selected for processing (8)
  • kotlin/example/demo-android-app/app/build.gradle.kts (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/AndroidManifest.xml (2 hunks)
  • kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/Common.kt (3 hunks)
  • kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/QrScannerActivity.kt (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/res/layout/activity_main.xml (1 hunks)
  • kotlin/example/demo-android-app/app/src/main/res/layout/activity_send_input.xml (1 hunks)
  • kotlin/example/demo-android-app/gradle/libs.versions.toml (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • kotlin/example/demo-android-app/app/build.gradle.kts
  • kotlin/example/demo-android-app/app/src/main/res/layout/activity_send_input.xml
  • kotlin/example/demo-android-app/app/src/main/AndroidManifest.xml
  • kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/QrScannerActivity.kt
🔇 Additional comments (2)
kotlin/example/demo-android-app/gradle/libs.versions.toml (1)

29-30: Library entries correctly reference versioned dependencies.

The library entries properly use version.ref to reference the versions defined earlier, following Gradle version catalog best practices. This ensures consistent dependency versions across the project.

kotlin/example/demo-android-app/app/src/main/java/com/example/myapplication/MainActivity.kt (1)

160-185: DataReceived parsing, logging, and UI updates look solid

The DataReceivedEvent handler now cleanly:

  • Decodes the payload,
  • Parses the timestamp and message with a safe split(limit = 2),
  • Computes transferTime,
  • Logs both message and transfer time, and
  • Falls back to a plain “Received (no timestamp)” view on parse errors while logging the full exception.

Also, the earlier duplicated “Transfer Time” in the UI has been removed. No further changes needed here.

@swatigoel swatigoel merged commit 1b4c5b6 into inji:develop Dec 3, 2025
4 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.

2 participants