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

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

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

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

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

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

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

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

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

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

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

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

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

Проблема возникла по причине смены дизайна родительской активити для этого фрагмента. В штатном вот этот кусок отрабатывает правильно https://github.com/GZOSP/packages_apps_settings/blob/11.0/src/com/android/settings/print/PrintServiceSettingsFragment.java#L354 , а в переделаном дизайне меню (... в правом верхнем углу) не предусмотрено.

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

Начинаю гуглить про 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)