Skip to content

Instantly share code, notes, and snippets.

@fchevitarese
Created November 4, 2025 14:26
Show Gist options
  • Select an option

  • Save fchevitarese/d17680f2ebd8691e139f586e95f607d0 to your computer and use it in GitHub Desktop.

Select an option

Save fchevitarese/d17680f2ebd8691e139f586e95f607d0 to your computer and use it in GitHub Desktop.
// 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