Created
November 4, 2025 14:26
-
-
Save fchevitarese/d17680f2ebd8691e139f586e95f607d0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // MainActivity.kt - Demonstrating Chrome Android share Intent bug | |
| // Bug: Chrome reuses Intent.EXTRA_TEXT across multiple share actions | |
| // | |
| // Expected: Each share sends the current URL in EXTRA_TEXT | |
| // Actual: Chrome sends the FIRST URL for all subsequent shares | |
| // | |
| // Test environment: | |
| // - Android 15 | |
| // - Chrome 142.0.7444.48 | |
| // - Activity launch mode: singleTask + documentLaunchMode="never" | |
| package com.fchevitarese.repertory | |
| import android.content.Intent | |
| import android.os.Bundle | |
| import android.util.Log | |
| import io.flutter.embedding.android.FlutterActivity | |
| class MainActivity: FlutterActivity() { | |
| private var sharedText: String? = null | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| Log.d("MainActivity", "onCreate() called") | |
| // Do NOT process intent here - onResume() will be called next | |
| } | |
| override fun onNewIntent(intent: Intent) { | |
| super.onNewIntent(intent) | |
| Log.d("MainActivity", "onNewIntent() called - NEW share detected") | |
| // CRITICAL: Update the activity's intent reference | |
| // This ensures getIntent() returns the NEW intent in onResume() | |
| setIntent(intent) | |
| } | |
| override fun onResume() { | |
| super.onResume() | |
| Log.d("MainActivity", "onResume() called - processing intent") | |
| // ALWAYS process the current intent in onResume() | |
| // This works for both onCreate() and onNewIntent() flows | |
| handleIntent(intent) | |
| } | |
| private fun handleIntent(intent: Intent?) { | |
| if (intent == null) return | |
| // Check if it's a text share (e.g., URL from Chrome) | |
| if (Intent.ACTION_SEND == intent.action && intent.type == "text/plain") { | |
| val extraText = intent.getStringExtra(Intent.EXTRA_TEXT) | |
| val extraSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT) | |
| Log.d("MainActivity", "=== SHARE DETECTED ===") | |
| Log.d("MainActivity", "EXTRA_TEXT: $extraText") | |
| Log.d("MainActivity", "EXTRA_SUBJECT: $extraSubject") | |
| // CHROME BUG INVESTIGATION: Check ClipData | |
| val clipData = intent.clipData | |
| if (clipData != null && clipData.itemCount > 0) { | |
| val clipText = clipData.getItemAt(0).text?.toString() | |
| val clipUri = clipData.getItemAt(0).uri?.toString() | |
| Log.d("MainActivity", "ClipData.text: $clipText") | |
| Log.d("MainActivity", "ClipData.uri: $clipUri") | |
| // Attempted workaround: prefer ClipData.text if different | |
| if (clipText != null && clipText != extraText) { | |
| if (clipText.startsWith("http://") || clipText.startsWith("https://")) { | |
| Log.d("MainActivity", "Using ClipData.text instead of EXTRA_TEXT") | |
| // This never executes - Chrome always sends null in ClipData.text | |
| } | |
| } | |
| } | |
| // Process the shared text | |
| if (extraText != null && extraText != sharedText) { | |
| sharedText = extraText | |
| Log.d("MainActivity", "NEW shared text: $sharedText") | |
| // TODO: Pass to Flutter or process the URL | |
| // Reset for next share | |
| sharedText = null | |
| } else { | |
| Log.d("MainActivity", "Same text already processed, ignoring") | |
| } | |
| } | |
| } | |
| } | |
| /* | |
| REPRODUCTION STEPS: | |
| 1. Install app on Android device | |
| 2. Open Chrome and navigate to: https://www.example.com/page1 | |
| 3. Tap Share → Select this app | |
| 4. In Chrome, navigate to: https://www.example.com/page2 | |
| 5. Tap Share → Select this app again | |
| EXPECTED LOG OUTPUT: | |
| First share: | |
| EXTRA_TEXT: https://www.example.com/page1 ✓ | |
| EXTRA_SUBJECT: Page 1 ✓ | |
| Second share: | |
| EXTRA_TEXT: https://www.example.com/page2 ✓ | |
| EXTRA_SUBJECT: Page 2 ✓ | |
| ACTUAL LOG OUTPUT: | |
| First share: | |
| EXTRA_TEXT: https://www.example.com/page1 ✓ | |
| EXTRA_SUBJECT: Page 1 ✓ | |
| ClipData.text: null | |
| ClipData.uri: content://.../screenshot.png | |
| Second share: | |
| EXTRA_TEXT: https://www.example.com/page1 ✗ (WRONG - reused from first share) | |
| EXTRA_SUBJECT: Page 2 ✓ (correct) | |
| ClipData.text: null | |
| ClipData.uri: content://.../screenshot.png | |
| NOTES: | |
| - Chrome correctly updates EXTRA_SUBJECT with new page title | |
| - Chrome DOES NOT update EXTRA_TEXT (reuses old URL) | |
| - ClipData.text is ALWAYS null (no workaround possible) | |
| - ClipData.uri only contains screenshot file path | |
| - Firefox and Edge DO NOT have this bug | |
| - Force-closing Chrome resets the cached Intent | |
| AndroidManifest.xml configuration: | |
| <activity | |
| android:name=".MainActivity" | |
| android:launchMode="singleTask" | |
| android:documentLaunchMode="never" | |
| ...> | |
| <intent-filter> | |
| <action android:name="android.intent.action.SEND" /> | |
| <category android:name="android.intent.category.DEFAULT" /> | |
| <data android:mimeType="text/plain" /> | |
| </intent-filter> | |
| </activity> | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment