Как работает функция "параллельных приложений" на устройствах OnePlus 3 и как мы можем правильно использовать намерения?
Фон
Приложения используют намерения для открытия других приложений, иногда со специальными намерениями.
Одним из примеров является это намерение, чтобы выбрать контакт из WhatsApp:
val WHATSAPP_PACKAGE_NAME = "com.whatsapp"
val whatsAppPickIntent = Intent(Intent.ACTION_PICK).setPackage(WHATSAPP_PACKAGE_NAME)
Это прекрасно работает в целом. То же самое происходит, если вы хотите запустить приложение:
val launchIntent=packageManager.getLaunchIntentForPackage(WHATSAPP_PACKAGE_NAME)
Эта проблема
Недавно мне сообщили о относительно новой функции, позволяющей пользователю иметь несколько экземпляров одного и того же приложения. Он может быть доступен на других устройствах, но на устройствах OnePlus он называется "параллельными приложениями". Здесь пример из 2 экземпляров WhatsApp, каждый из которых назначен другому номеру телефона:
Дело в том, что это может нарушить работу Intents с одним экземпляром приложения. Теперь Intent не знает, для какого приложения нужно идти. В Launcher теперь отображаются 2 значка для WhatsApp:
Если вы решите запустить WhatsApp через обычный значок запуска (левый), он отобразит это диалоговое окно:
Прекрасно работает, но если вы решите использовать намерение выбора, вы все равно получите этот диалог, но когда вы выберете элемент, из диалогового окна он не позволит вам ничего с ним делать (открывает и закрывает приложение), в то время как показ тоста "Формат файла не поддерживается".
Что я пробовал
Поскольку у меня нет устройства, я пытался прочитать об этом через Интернет, но я нашел только информацию, связанную с пользователем:
Я решил попытаться исследовать его дальше, отправив APK тому, кто рассказал мне об этом, пытаясь понять, будет ли следующий код работать иначе:
val whatsAppPickIntent = Intent(Intent.ACTION_PICK).setPackage(WHATSAPP_PACKAGE_NAME)
val queryIntentActivities: List<ResolveInfo> = packageManager.queryIntentActivities(whatsAppPickIntent, 0)
button2.setOnClickListener {
intent = Intent(Intent.ACTION_PICK)
val resolveInfo = queryIntentActivities[0]
toast("number of possible choices:" + queryIntentActivities.size)
intent.component = ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
startActivity(intent)
}
Тост, который будет показан, говорит, что у меня есть только одна вещь, которая может справиться с намерением, и действительно, когда я использую ее, я получаю тот же диалог для выбора того, какой экземпляр его использовать. И, как в оригинальном намерении, он терпит неудачу с тем же тостом.
EDIT: Позже я попробовал следующее: я попросил показать, какие свойства ResolveInfo до и после включения функции, используя этот код:
val launchIntent = packageManager.getLaunchIntentForPackage(WHATSAPP_PACKAGE_NAME)
val whatsAppPickIntent = Intent(Intent.ACTION_PICK).setPackage(WHATSAPP_PACKAGE_NAME)
var queryIntentActivities: List<ResolveInfo> = packageManager.queryIntentActivities(whatsAppPickIntent, 0)
var sb = StringBuilder()
queryIntentActivities[0].dump(object : Printer {
override fun println(x: String?) {
if (x != null)
sb.append(x)
}
}, "")
val pickResult = "pick result:packageName:\"" + queryIntentActivities[0].activityInfo.packageName + "\" name:\"" + queryIntentActivities[0].activityInfo.name + "\"\n\n" + "extended:" + sb.toString()
sb = StringBuilder()
queryIntentActivities = packageManager.queryIntentActivities(launchIntent, 0)
queryIntentActivities[0].dump(object : Printer {
override fun println(x: String?) {
if (x != null)
sb.append(x)
}
}, "")
val launchResult = "launch result:packageName:\"" + queryIntentActivities[0].activityInfo.packageName + "\" name:\"" + queryIntentActivities[0].activityInfo.name + "\"\n\n" + "extended:" + sb.toString()
val body = pickResult + "\n\n" + launchResult
val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "", null))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "whatsApp investigation")
emailIntent.putExtra(Intent.EXTRA_TEXT, body)
startActivity(Intent.createChooser(emailIntent, "Send email..."))
В результате все те же, что и все в порядке. Здесь результат, когда он включается/выключается (точно такое же):
pick result:packageName:"com.whatsapp" name:"com.whatsapp.ContactPicker"
extended:priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=falseActivityInfo: name=com.whatsapp.ContactPicker packageName=com.whatsapp enabled=true exported=true directBootAware=false taskAffinity=com.whatsapp targetActivity=null persistableMode=PERSIST_ROOT_ONLY launchMode=0 flags=0x3 theme=0x7f110173 screenOrientation=-1 configChanges=0xfb3 softInputMode=0x0 lockTaskLaunchMode=LOCK_TASK_LAUNCH_MODE_DEFAULT resizeMode=RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ApplicationInfo: name=com.whatsapp.AppShell packageName=com.whatsapp labelRes=0x7f100473 nonLocalizedLabel=null icon=0x7f080c15 banner=0x0 className=com.whatsapp.AppShell processName=com.whatsapp taskAffinity=com.whatsapp uid=10099 flags=0x3 privateFlags=0x1010 theme=0x7f110164 requiresSmallestWidthDp=0 compatibleWidthLimitDp=0 largestWidthLimitDp=0 sourceDir=/data/app/com.whatsapp-NaKTLVhiNTh4zEGhFdkxrg==/base.apk seinfo=default:targetSdkVersion=26 seinfoUser=:complete dataDir=/data/user/0/com.whatsapp deviceProtectedDataDir=/data/user_de/0/com.whatsapp credentialProtectedDataDir=/data/user/0/com.whatsapp enabled=true minSdkVersion=15 targetSdkVersion=26 versionCode=452238 targetSandboxVersion=1 supportsRtl=true fullBackupContent=true category=4
launch result:packageName:"com.whatsapp" name:"com.whatsapp.Main"
extended:priority=0 preferredOrder=0 match=0x0 specificIndex=-1 isDefault=falseActivityInfo: name=com.whatsapp.Main packageName=com.whatsapp labelRes=0x7f10044c nonLocalizedLabel=null icon=0x0 banner=0x0 enabled=true exported=true directBootAware=false taskAffinity=com.whatsapp targetActivity=null persistableMode=PERSIST_ROOT_ONLY launchMode=0 flags=0x3 theme=0x0 screenOrientation=-1 configChanges=0xfb3 softInputMode=0x0 lockTaskLaunchMode=LOCK_TASK_LAUNCH_MODE_DEFAULT resizeMode=RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ApplicationInfo: name=com.whatsapp.AppShell packageName=com.whatsapp labelRes=0x7f100473 nonLocalizedLabel=null icon=0x7f080c15 banner=0x0 className=com.whatsapp.AppShell processName=com.whatsapp taskAffinity=com.whatsapp uid=10099 flags=0x3 privateFlags=0x1010 theme=0x7f110164 requiresSmallestWidthDp=0 compatibleWidthLimitDp=0 largestWidthLimitDp=0 sourceDir=/data/app/com.whatsapp-NaKTLVhiNTh4zEGhFdkxrg==/base.apk seinfo=default:targetSdkVersion=26 seinfoUser=:complete dataDir=/data/user/0/com.whatsapp deviceProtectedDataDir=/data/user_de/0/com.whatsapp credentialProtectedDataDir=/data/user/0/com.whatsapp enabled=true minSdkVersion=15 targetSdkVersion=26 versionCode=452238 targetSandboxVersion=1 supportsRtl=true fullBackupContent=true category=4
Поэтому я хотел проверить что-то еще: попробуйте добавить ярлык WhatsApp (называемый "WhatsApp chat"), который требует, чтобы вы выбрали контакт, когда эта функция включена. Оказывается, он не может справиться с этим хорошо. Он спрашивает, какое приложение должно создать виджет для: оригинала или клона. Если вы выберете оригинал, все в порядке. Если вы выберете клон, он добавит виджет всем хорошо и хорошо, но при нажатии на него он переходит в главное окно приложения вместо того, чтобы идти к человеку.
Вопросы
-
Как я могу различать основной экземпляр и "клонированный"? Я имею в виду, как можно настроить Intent на один экземпляр (основной) целевого приложения? Я спрашиваю об обоих намерениях, которые я представил (запуск и сборщик).
-
Как эта функция работает? Где теперь сохраняются личные данные каждого экземпляра? У каждого из них есть процесс с другим именем?
-
У других устройств других OEM-производителей есть эта функция? Он работает там так же, как здесь?
-
Почему мы видим сообщение с тостом, если пользователь выбрал целевое приложение? Возможно, это багги, которая будет работать только для запуска?
-
Есть ли хотя бы способ узнать, что данное приложение (с учетом его имени) поддерживает эту функцию?