fixed handling wallpaper intent without uri
#1052 handle secure review intent
This commit is contained in:
parent
0f1d8ec760
commit
27db528e67
6 changed files with 69 additions and 119 deletions
|
@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
|
|||
### Fixed
|
||||
|
||||
- switching to PiP when changing device orientation on Android >=13
|
||||
- handling wallpaper intent without URI
|
||||
|
||||
## <a id="v1.11.3"></a>[v1.11.3] - 2024-06-17
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:showWhenLocked="true"
|
||||
android:supportsRtl="true"
|
||||
tools:targetApi="tiramisu">
|
||||
<activity
|
||||
|
@ -143,6 +144,7 @@
|
|||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.provider.action.REVIEW" />
|
||||
<action android:name="android.provider.action.REVIEW_SECURE" />
|
||||
<action android:name="com.android.camera.action.REVIEW" />
|
||||
<action android:name="com.android.camera.action.SPLIT_SCREEN_REVIEW" />
|
||||
|
||||
|
@ -163,6 +165,7 @@
|
|||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.provider.action.REVIEW" />
|
||||
<action android:name="android.provider.action.REVIEW_SECURE" />
|
||||
<action android:name="com.android.camera.action.REVIEW" />
|
||||
<action android:name="com.android.camera.action.SPLIT_SCREEN_REVIEW" />
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import deckers.thibault.aves.channel.calls.MetadataEditHandler
|
|||
import deckers.thibault.aves.channel.calls.MetadataFetchHandler
|
||||
import deckers.thibault.aves.channel.calls.SecurityHandler
|
||||
import deckers.thibault.aves.channel.calls.StorageHandler
|
||||
import deckers.thibault.aves.channel.calls.WallpaperHandler
|
||||
import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler
|
||||
import deckers.thibault.aves.channel.calls.window.WindowHandler
|
||||
import deckers.thibault.aves.channel.streams.ActivityResultStreamHandler
|
||||
|
@ -135,6 +136,7 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
MethodChannel(messenger, AccessibilityHandler.CHANNEL).setMethodCallHandler(AccessibilityHandler(this))
|
||||
MethodChannel(messenger, MediaEditHandler.CHANNEL).setMethodCallHandler(MediaEditHandler(this))
|
||||
MethodChannel(messenger, MetadataEditHandler.CHANNEL).setMethodCallHandler(MetadataEditHandler(this))
|
||||
MethodChannel(messenger, WallpaperHandler.CHANNEL).setMethodCallHandler(WallpaperHandler(this))
|
||||
// - need Activity
|
||||
MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(ActivityWindowHandler(this))
|
||||
|
||||
|
@ -301,16 +303,32 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
Intent.ACTION_VIEW,
|
||||
Intent.ACTION_SEND,
|
||||
MediaStore.ACTION_REVIEW,
|
||||
MediaStore.ACTION_REVIEW_SECURE,
|
||||
"com.android.camera.action.REVIEW",
|
||||
"com.android.camera.action.SPLIT_SCREEN_REVIEW" -> {
|
||||
(intent.data ?: intent.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM))?.let { uri ->
|
||||
// MIME type is optional
|
||||
val type = intent.type ?: intent.resolveType(this)
|
||||
return hashMapOf(
|
||||
val fields = hashMapOf<String, Any?>(
|
||||
INTENT_DATA_KEY_ACTION to INTENT_ACTION_VIEW,
|
||||
INTENT_DATA_KEY_MIME_TYPE to type,
|
||||
INTENT_DATA_KEY_URI to uri.toString(),
|
||||
)
|
||||
|
||||
if (action == MediaStore.ACTION_REVIEW_SECURE) {
|
||||
val uris = ArrayList<String>()
|
||||
intent.clipData?.let { clipData ->
|
||||
for (i in 0 until clipData.itemCount) {
|
||||
clipData.getItemAt(i).uri?.let { uris.add(it.toString()) }
|
||||
}
|
||||
}
|
||||
fields[INTENT_DATA_KEY_SECURE_URIS] = uris
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && intent.hasExtra(MediaStore.EXTRA_BRIGHTNESS)) {
|
||||
fields[INTENT_DATA_KEY_BRIGHTNESS] = intent.getFloatExtra(MediaStore.EXTRA_BRIGHTNESS, 0f)
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,7 +408,7 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
return null
|
||||
}
|
||||
|
||||
private fun submitPickedItems(call: MethodCall) {
|
||||
open fun submitPickedItems(call: MethodCall) {
|
||||
val pickedUris = call.argument<List<String>>("uris")
|
||||
if (!pickedUris.isNullOrEmpty()) {
|
||||
val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(this, Uri.parse(uriString)) }
|
||||
|
@ -498,11 +516,13 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
|
||||
const val INTENT_DATA_KEY_ACTION = "action"
|
||||
const val INTENT_DATA_KEY_ALLOW_MULTIPLE = "allowMultiple"
|
||||
const val INTENT_DATA_KEY_BRIGHTNESS = "brightness"
|
||||
const val INTENT_DATA_KEY_FILTERS = "filters"
|
||||
const val INTENT_DATA_KEY_MIME_TYPE = "mimeType"
|
||||
const val INTENT_DATA_KEY_PAGE = "page"
|
||||
const val INTENT_DATA_KEY_QUERY = "query"
|
||||
const val INTENT_DATA_KEY_SAFE_MODE = "safeMode"
|
||||
const val INTENT_DATA_KEY_SECURE_URIS = "secureUris"
|
||||
const val INTENT_DATA_KEY_URI = "uri"
|
||||
const val INTENT_DATA_KEY_WIDGET_ID = "widgetId"
|
||||
|
||||
|
|
|
@ -2,132 +2,53 @@ package deckers.thibault.aves
|
|||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import app.loup.streams_channel.StreamsChannel
|
||||
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
|
||||
import deckers.thibault.aves.channel.calls.AccessibilityHandler
|
||||
import deckers.thibault.aves.channel.calls.DeviceHandler
|
||||
import deckers.thibault.aves.channel.calls.EmbeddedDataHandler
|
||||
import deckers.thibault.aves.channel.calls.MediaFetchBytesHandler
|
||||
import deckers.thibault.aves.channel.calls.MediaFetchObjectHandler
|
||||
import deckers.thibault.aves.channel.calls.MediaSessionHandler
|
||||
import deckers.thibault.aves.channel.calls.MetadataFetchHandler
|
||||
import deckers.thibault.aves.channel.calls.StorageHandler
|
||||
import deckers.thibault.aves.channel.calls.WallpaperHandler
|
||||
import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler
|
||||
import deckers.thibault.aves.channel.calls.window.WindowHandler
|
||||
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
|
||||
import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler
|
||||
import deckers.thibault.aves.channel.calls.AppAdapterHandler
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.getParcelableExtraCompat
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class WallpaperActivity : FlutterFragmentActivity() {
|
||||
private lateinit var intentDataMap: FieldMap
|
||||
private lateinit var mediaSessionHandler: MediaSessionHandler
|
||||
class WallpaperActivity : MainActivity() {
|
||||
private var originalIntent: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
override fun extractIntentData(intent: Intent?): FieldMap {
|
||||
if (intent != null) {
|
||||
when (intent.action) {
|
||||
Intent.ACTION_ATTACH_DATA, Intent.ACTION_SET_WALLPAPER -> {
|
||||
(intent.data ?: intent.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM))?.let { uri ->
|
||||
// MIME type is optional
|
||||
val type = intent.type ?: intent.resolveType(this)
|
||||
return hashMapOf(
|
||||
INTENT_DATA_KEY_ACTION to INTENT_ACTION_SET_WALLPAPER,
|
||||
INTENT_DATA_KEY_MIME_TYPE to type,
|
||||
INTENT_DATA_KEY_URI to uri.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
Log.i(LOG_TAG, "onCreate intent=$intent")
|
||||
intent.extras?.takeUnless { it.isEmpty }?.let {
|
||||
Log.i(LOG_TAG, "onCreate intent extras=$it")
|
||||
}
|
||||
intentDataMap = extractIntentData(intent)
|
||||
}
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
val messenger = flutterEngine.dartExecutor
|
||||
|
||||
// notification: platform -> dart
|
||||
val mediaCommandStreamHandler = MediaCommandStreamHandler().apply {
|
||||
EventChannel(messenger, MediaCommandStreamHandler.CHANNEL).setStreamHandler(this)
|
||||
}
|
||||
|
||||
// dart -> platform -> dart
|
||||
// - need Context
|
||||
mediaSessionHandler = MediaSessionHandler(this, mediaCommandStreamHandler)
|
||||
MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(this))
|
||||
MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(this))
|
||||
MethodChannel(messenger, MediaFetchBytesHandler.CHANNEL, AvesByteSendingMethodCodec.INSTANCE).setMethodCallHandler(MediaFetchBytesHandler(this))
|
||||
MethodChannel(messenger, MediaFetchObjectHandler.CHANNEL).setMethodCallHandler(MediaFetchObjectHandler(this))
|
||||
MethodChannel(messenger, MediaSessionHandler.CHANNEL).setMethodCallHandler(mediaSessionHandler)
|
||||
MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this))
|
||||
MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(this))
|
||||
// - need ContextWrapper
|
||||
MethodChannel(messenger, AccessibilityHandler.CHANNEL).setMethodCallHandler(AccessibilityHandler(this))
|
||||
MethodChannel(messenger, WallpaperHandler.CHANNEL).setMethodCallHandler(WallpaperHandler(this))
|
||||
// - need Activity
|
||||
MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(ActivityWindowHandler(this))
|
||||
|
||||
// result streaming: dart -> platform ->->-> dart
|
||||
// - need Context
|
||||
StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(this, args) }
|
||||
|
||||
// intent handling
|
||||
// detail fetch: dart -> platform
|
||||
MethodChannel(messenger, MainActivity.INTENT_CHANNEL).setMethodCallHandler { call, result -> onMethodCall(call, result) }
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
Log.i(LOG_TAG, "onStart")
|
||||
super.onStart()
|
||||
|
||||
// as of Flutter v3.0.1, the window `viewInsets` and `viewPadding`
|
||||
// are incorrect on startup in some environments (e.g. API 29 emulator),
|
||||
// so we manually request to apply the insets to update the window metrics
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
window.decorView.requestApplyInsets()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
mediaSessionHandler.dispose()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"getIntentData" -> {
|
||||
result.success(intentDataMap)
|
||||
intentDataMap.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractIntentData(intent: Intent?): FieldMap {
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_ATTACH_DATA, Intent.ACTION_SET_WALLPAPER -> {
|
||||
(intent.data ?: intent.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM))?.let { uri ->
|
||||
// MIME type is optional
|
||||
val type = intent.type ?: intent.resolveType(this)
|
||||
return hashMapOf(
|
||||
MainActivity.INTENT_DATA_KEY_ACTION to MainActivity.INTENT_ACTION_SET_WALLPAPER,
|
||||
MainActivity.INTENT_DATA_KEY_MIME_TYPE to type,
|
||||
MainActivity.INTENT_DATA_KEY_URI to uri.toString(),
|
||||
)
|
||||
// if the media URI is not provided we need to pick one first
|
||||
originalIntent = intent.action
|
||||
intent.action = Intent.ACTION_PICK
|
||||
}
|
||||
}
|
||||
Intent.ACTION_RUN -> {
|
||||
// flutter run
|
||||
}
|
||||
else -> {
|
||||
Log.w(LOG_TAG, "unhandled intent action=${intent?.action}")
|
||||
}
|
||||
}
|
||||
return HashMap()
|
||||
|
||||
return super.extractIntentData(intent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<WallpaperActivity>()
|
||||
override fun submitPickedItems(call: MethodCall) {
|
||||
if (originalIntent != null) {
|
||||
val pickedUris = call.argument<List<String>>("uris")
|
||||
if (!pickedUris.isNullOrEmpty()) {
|
||||
val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(this, Uri.parse(uriString)) }
|
||||
onNewIntent(Intent().apply {
|
||||
action = originalIntent
|
||||
data = toUri(pickedUris.first())
|
||||
})
|
||||
} else {
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
} else {
|
||||
super.submitPickedItems(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ class _HomePageState extends State<HomePage> {
|
|||
int? _widgetId;
|
||||
String? _initialRouteName, _initialSearchQuery;
|
||||
Set<CollectionFilter>? _initialFilters;
|
||||
List<String>? _secureUris;
|
||||
|
||||
static const allowedShortcutRoutes = [
|
||||
CollectionPage.routeName,
|
||||
|
@ -91,6 +92,7 @@ class _HomePageState extends State<HomePage> {
|
|||
final safeMode = intentData[IntentDataKeys.safeMode] ?? false;
|
||||
final intentAction = intentData[IntentDataKeys.action];
|
||||
_initialFilters = null;
|
||||
_secureUris = null;
|
||||
|
||||
await androidFileUtils.init();
|
||||
if (!{
|
||||
|
@ -127,6 +129,7 @@ class _HomePageState extends State<HomePage> {
|
|||
uri = intentData[IntentDataKeys.uri];
|
||||
mimeType = intentData[IntentDataKeys.mimeType];
|
||||
}
|
||||
_secureUris = intentData[IntentDataKeys.secureUris];
|
||||
if (uri != null) {
|
||||
_viewerEntry = await _initViewerEntry(
|
||||
uri: uri,
|
||||
|
@ -208,7 +211,7 @@ class _HomePageState extends State<HomePage> {
|
|||
canAnalyze: false,
|
||||
);
|
||||
case AppMode.view:
|
||||
if (_isViewerSourceable(_viewerEntry)) {
|
||||
if (_isViewerSourceable(_viewerEntry) && _secureUris == null) {
|
||||
final directory = _viewerEntry?.directory;
|
||||
if (directory != null) {
|
||||
unawaited(AnalysisService.registerCallback());
|
||||
|
|
|
@ -14,11 +14,13 @@ class IntentActions {
|
|||
class IntentDataKeys {
|
||||
static const action = 'action';
|
||||
static const allowMultiple = 'allowMultiple';
|
||||
static const brightness = 'brightness';
|
||||
static const filters = 'filters';
|
||||
static const mimeType = 'mimeType';
|
||||
static const page = 'page';
|
||||
static const query = 'query';
|
||||
static const safeMode = 'safeMode';
|
||||
static const secureUris = 'secureUris';
|
||||
static const uri = 'uri';
|
||||
static const widgetId = 'widgetId';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue