В своей программе я использую вызов настроек телефона.

Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
startActivity(intent);

Это просьба открыть нужное место в системных настройках телефона

Торопыги могут сказать, что я забыл этот вызов взять в try-catch и будут не правы.

Ошибка была не в том, что службы печати нет на телефоне, а гораздо дальше.

Телефон открывает список всех установленных служб как и положено.

А вот после клика по имени любой службы, даже по службе от самого андроида получаем креш.

Скрытый текст

Под спойлером скриншоты

Но такая глобальная ошибка не могла остаться в релизе. Если мы попробуем попасть в настройки штатным путем (свап по экрану сверху - шестеренка - искать печать - и далее)

Скрытый текст

То все работает. Но что это такое ? Внешний вид разный !

Получается вендор не стал полностью переделывать кастомизировать штатные настройки так как есть альтернатива от него.

Первая мысль надо искать кастомный интент (для торопыг Ошибка там использован штатный)

Начинаю гуглить про Realme UI и какие нибудь кастомные интенты с печатью - пусто. Про Color OS - пусто.

Полное не понимание, а куда дальше копать.

Решаю посмотреть пакеты.

> adb shell pm list packages | grep setting

package:com.android.settings.intelligence
package:com.oplus.wirelesssettings
package:com.android.settings
package:com.android.providers.settings

>adb shell pm list packages | grep print  
package:com.google.android.printservice.recommendation
package:com.android.systemui.overlay.fingerprint.anim.lgsy
package:com.android.systemui.overlay.fingerprint.anim.jslz
package:com.android.systemui.overlay.fingerprint.anim.jhsy
package:com.android.printspooler
package:com.android.systemui.overlay.fingerprint.anim.xklc
package:com.android.systemui.overlay.fingerprint.anim.ccyh
package:com.android.systemui.overlay.fingerprint.anim.nlgs
package:com.smart.printer.print.photos.documents.printing.pictures
package:com.android.systemui.overlay.fingerprint.anim.tyjw

Идей не добавляется. Но ВОТ есть же гдето-код, который работает. Но ничего похожего на настройки от самого реалми не видно.

!!! Так как узнать какое приложение на топе стека активити ? гуглим

adb shell dumpsys window | grep Focused.
  mTopFocusedDisplayId=0
  mFocusedApp=ActivityRecord{37325a8 u0 ru.a402d.printservice/.ui.MainActivity t4128}
      mLastFocusedRootTask=Task{a1d27a7 #4128 type=standard A=10319:ru.a402d.printservice}
    mFocusedWindow=Window{22f4af7 u0 Application Error: com.android.settings}
    DO DUMP StrategyManager : { StrategyIgnoreSleepKey StrategyShutdown StrategyLaunchIntercomCustom StrategyCustomizeIgnoreVirtualKey StrategyUnusedPhoneDetection StrategyBlackscreenshot StrategyPowerKeyEndCall StrategyDisableBottomKey StrategyVolumeKeyLaunchCamera StrategyIngoreKeyInFocusedWindow}
    mQueueingInterceptorsCopy : [120:com.android.server.policy.KeyLongPressBaseStrategy$1@64b501c, 130:com.android.server.policy.StrategyCustomizeIgnoreVirtualKey$1@9f7c21a, 170:com.android.server.policy.StrategyDisableBottomKey$2@221d425, 190:com.android.server.policy.StrategyIngoreKeyInFocusedWindow$1@a8c9fa]
    mDispatchingInterceptorsCopy : [120:com.android.server.policy.KeyLongPressBaseStrategy$1@64b501c, 130:com.android.server.policy.StrategyCustomizeIgnoreVirtualKey$1@9f7c21a, 170:com.android.server.policy.StrategyDisableBottomKey$2@221d425, 190:com.android.server.policy.StrategyIngoreKeyInFocusedWindow$1@a8c9fa]
  mFocusedApp=ActivityRecord{461677f u0 com.android.printspooler/.ui.settings.SettingsEntranceActivity t4127}
      mLastFocusedRootTask=Task{9e02046 #4127 type=standard A=1000:com.android.settings.root}
    mFocusedWindow=Window{f727c67 u0 com.android.printspooler/com.android.printspooler.ui.settings.SettingsEntranceActivity}
  mTopFocusedDisplayId=0

Оказывается из настроек мы незаметно для себя попадаем в спулер печати com.android.printspooler

А что же тогда в его манифесте ? Будем смотреть

adb shell pm path com.android.printspooler            
package:/system_ext/app/PrintSpooler/PrintSpooler.apk

Узнали где лежит, теперь скачаем к себе

adb pull /system_ext/app/PrintSpooler/PrintSpooler.apk     

По быстрому заглянуть в манифест приложения, можно просто открыв apk в android studio.

        <activity
            android:theme="@ref/0x7f11002a"
            android:label="@ref/0x7f100135"
            android:name="com.android.printspooler.ui.settings.SettingsEntranceActivity"
            android:exported="true"
            android:screenOrientation="-1"
            android:configChanges="0x40003dfe"
            android:windowSoftInputMode="0x22">

.....

            <intent-filter
                android:priority="1">

                <action
                    android:name="android.settings.ACTION_PRINT_SETTINGS" />

                <category
                    android:name="android.intent.category.DEFAULT" />
            </intent-filter>

   ...
          
        </activity>

Оказывается нет ни какого специального интента.

Часть вторая. Как вызвать нужный компонент

И начнем мы ее решать с модификации своего манифеста приложения

    <queries>
      ...
        <intent>
            <action
                android:name="android.settings.ACTION_PRINT_SETTINGS" />
            <category
                android:name="android.intent.category.DEFAULT" />
        </intent>
      ...
    </queries>

https://developer.android.com/guide/topics/manifest/queries-element

Начиная с апи 30 (11-й андроид) для секьюрности урезали доступ к списку установленных приложений и ресолвить доступные для выполнения намерения теперь можно только упомянув в манифесте.

По дефолту через startActivity запускается самая подходящая программа. Есть способ предложить пользователю выбрать из нескольких

PackageManager packageManager = requireActivity().getPackageManager();
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, getString(R.string.open));
if (intent.resolveActivity(packageManager) != null) {
    startActivity(chooser);
}

Этот способ показывает выбор, но только, если пользователь в предыдущий раз не поставил [v] использовать всегда.

В моем случае диалог не был показан, а все также открывался экран приводящий к крешу.

Хорошо, проверяем, что ресолвятся несколько компонентов

PackageManager packageManager = requireActivity().getPackageManager();
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);

List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent,
                    PackageManager.GET_RESOLVED_FILTER);

if (resolveInfos.size() > 1) {
   // ок их тут много
}  

Уточнить нужный для запуска можно так

String packageName = resolveInfo.activityInfo.applicationInfo.packageName;
intent.setPackage(packageName);

Время костылить

Из-за вот таких чудес производителей телефонов, да и просто из-за отличий в версиях андроида реальный код обрастает подпорками.

Делаю следующие предположения. Если для события больше одного обработчика, то производитель ввел его намеренно и нужно использовать его. На случай , если их три или больше, то буду брать первый отличный от штатного.

Окончательный код принимает такой вид.

PackageManager packageManager = requireActivity().getPackageManager();
Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);

List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent,
                    PackageManager.GET_RESOLVED_FILTER);

 try {
    if (resolveInfos.size() > 1) {
       for (ResolveInfo resolveInfo : resolveInfos) {
          if (resolveInfo.activityInfo != null) {
                String packageName = resolveInfo.activityInfo.applicationInfo.packageName;
                 if (!"com.android.settings".equals(packageName)) {
                      intent.setPackage(packageName);
                       break;
                 }
          }
       }
   }
} catch (Exception ignored) {}

try {
   startActivity(intent);
} catch (Exception e) {
   Toast.makeText(requireActivity(), R.string.Oopppsss, Toast.LENGTH_SHORT).show();
}

Комментарии (0)