Как работает функция "параллельных приложений" на устройствах 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, каждый из которых назначен другому номеру телефона:

enter image description here

Дело в том, что это может нарушить работу Intents с одним экземпляром приложения. Теперь Intent не знает, для какого приложения нужно идти. В Launcher теперь отображаются 2 значка для WhatsApp:

enter image description here

Если вы решите запустить WhatsApp через обычный значок запуска (левый), он отобразит это диалоговое окно:

enter image description here

Прекрасно работает, но если вы решите использовать намерение выбора, вы все равно получите этот диалог, но когда вы выберете элемент, из диалогового окна он не позволит вам ничего с ним делать (открывает и закрывает приложение), в то время как показ тоста "Формат файла не поддерживается".

Что я пробовал

Поскольку у меня нет устройства, я пытался прочитать об этом через Интернет, но я нашел только информацию, связанную с пользователем:

Я решил попытаться исследовать его дальше, отправив 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"), который требует, чтобы вы выбрали контакт, когда эта функция включена. Оказывается, он не может справиться с этим хорошо. Он спрашивает, какое приложение должно создать виджет для: оригинала или клона. Если вы выберете оригинал, все в порядке. Если вы выберете клон, он добавит виджет всем хорошо и хорошо, но при нажатии на него он переходит в главное окно приложения вместо того, чтобы идти к человеку.

Вопросы

  1. Как я могу различать основной экземпляр и "клонированный"? Я имею в виду, как можно настроить Intent на один экземпляр (основной) целевого приложения? Я спрашиваю об обоих намерениях, которые я представил (запуск и сборщик).

  2. Как эта функция работает? Где теперь сохраняются личные данные каждого экземпляра? У каждого из них есть процесс с другим именем?

  3. У других устройств других OEM-производителей есть эта функция? Он работает там так же, как здесь?

  4. Почему мы видим сообщение с тостом, если пользователь выбрал целевое приложение? Возможно, это багги, которая будет работать только для запуска?

  5. Есть ли хотя бы способ узнать, что данное приложение (с учетом его имени) поддерживает эту функцию?

Ответы