RK3566 + Android 11
替换默认主界面, 更换为指定第三方Launcher后, 点击导航栏的RECENT键无效. 究其原因在于, 在旧版本SDK上, 删除Launcher3并不会影响RECENT的功能 , 而在新的SDK上, RECENT功能集成于Launcher3目录下, 删除 Launcher3后, 导致SystemUI调用对应的RECENT界面启动的服务失败.
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mNavigationBarView = (NavigationBarView) view;
final Display display = view.getDisplay();
// It may not have display when running unit test.
if (display != null) {
mDisplayId = display.getDisplayId();
mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
}
mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
if (savedInstanceState != null) {
mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
}
mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
prepareNavigationBarView();
}
private void prepareNavigationBarView() {
mNavigationBarView.reorient();
ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
recentsButton.setOnClickListener(this::onRecentsClick);
recentsButton.setOnTouchListener(this::onRecentsTouch);
recentsButton.setLongClickable(true);
recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
}
private void onRecentsClick(View v) {
if (LatencyTracker.isEnabled(getContext())) {
LatencyTracker.getInstance(getContext()).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
mStatusBarLazy.get().awakenDreams();
mCommandQueue.toggleRecentApps();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@Override
public void toggleRecentApps() {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
if (!isUserSetup()) {
return;
}
mImpl.toggleRecentApps();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@Override
public void toggleRecentApps() {
Log.d(TAG, "toggleRecentApps");
// If connected to launcher service, let it handle the toggle logic
IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
if (overviewProxy != null) {
final Runnable toggleRecents = () -> {
try {
if (mOverviewProxyService.getProxy() != null) {
mOverviewProxyService.getProxy().onOverviewToggle();
mOverviewProxyService.notifyToggleRecentApps();
}
} catch (RemoteException e) {
Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
}
};
// Preload only if device for current user is unlocked
if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {
mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {
// Flush trustmanager before checking device locked per user
mTrustManager.reportKeyguardShowingChanged();
mHandler.post(toggleRecents);
}, null, true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
} else {
toggleRecents.run();
}
return;
} else {
// Do nothing
}
}
重点看下overviewProxy
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
NavigationBarController navBarController, NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
PipUI pipUI, Optional<Divider> dividerOptional,
Optional<Lazy<StatusBar>> statusBarOptionalLazy,
BroadcastDispatcher broadcastDispatcher) {
super(broadcastDispatcher);
mContext = context;
mPipUI = pipUI;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mHandler = new Handler();
mNavBarController = navBarController;
mStatusBarWinController = statusBarWinController;
mConnectionBackoffAttempts = 0;
mDividerOptional = dividerOptional;
//frameworks/base/core/res/res/values/config.xml
// com.android.launcher3/com.android.quickstep.RecentsActivity
mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
com.android.internal.R.string.config_recentsComponentName));
mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
}
//绑定服务
private void internalConnectToCurrentUser() {
//..............
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
try {
mBound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
UserHandle.of(getCurrentUserId()));
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
//.....
}
//绑定成功
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//.........
mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
}
//由上面OverviewProxyRecentsImpl 调用
public IOverviewProxy getProxy() {
return mOverviewProxy;
}
近期任务UI的实现有别与旧版本的SDK, 有frameworks/base/packages/SystemUI改到了packages/apps/Launcher3/quickstep
packages/apps/Launcher3/quickstep/AndroidManifest.xml
<service
android:name="com.android.quickstep.TouchInteractionService"
android:permission="android.permission.STATUS_BAR_SERVICE"
android:directBootAware="true" >
<intent-filter>
<action android:name="android.intent.action.QUICKSTEP_SERVICE" />
intent-filter>
service>
为了使用默认第三方Launcher, 需要注释掉quickstep自带的Launcher
packages/apps/Launcher3/quickstep/AndroidManifest-launcher.xml
<activity
android:name="com.android.launcher3.uioverrides.QuickstepLauncher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!--category android:name="android.intent.category.HOME" /-->
<category android:name="android.intent.category.DEFAULT" />
<!--category android:name="android.intent.category.MONKEY"/-->
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
<meta-data
android:name="com.android.launcher3.grid.control"
android:value="${packageName}.grid_control" />
</activity>
2022-07-10 04:01:50.179 1472-1472/com.android.launcher3 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.launcher3, PID: 1472
java.lang.RuntimeException: Unable to create service com.android.quickstep.TouchInteractionService: java.lang.NullPointerException: Attempt to read from field 'android.content.pm.ActivityInfo android.content.pm.ResolveInfo.activityInfo' on a null object reference
at android.app.ActivityThread.handleCreateService(ActivityThread.java:4198)
at android.app.ActivityThread.access$1500(ActivityThread.java:237)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1932)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7664)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.NullPointerException: Attempt to read from field 'android.content.pm.ActivityInfo android.content.pm.ResolveInfo.activityInfo' on a null object reference
at com.android.quickstep.OverviewComponentObserver.(OverviewComponentObserver.java:82)
at com.android.quickstep.TouchInteractionService.onUserUnlocked(TouchInteractionService.java:345)
at com.android.quickstep.-$$Lambda$6M6xH0rMyGjroOocJ2F5KYabvuw.run(Unknown Source:2)
at com.android.quickstep.RecentsAnimationDeviceState.runOnUserUnlocked(RecentsAnimationDeviceState.java:272)
at com.android.quickstep.TouchInteractionService.onCreate(TouchInteractionService.java:301)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:4186)
at android.app.ActivityThread.access$1500(ActivityThread.java:237)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1932)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7664)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
mContext = context;
mDeviceState = deviceState;
mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//异常位置: 由于前面注释掉了自带Launcher导致, 将mContext.getPackageName 替换成 三方Launcher的包名可解决.
mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(/*mContext.getPackageName()*/"com.android.launcherX");
ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
ComponentName myHomeComponent =
new ComponentName(/*mContext.getPackageName()*/"com.android.launcherX", info.activityInfo.name);
mMyHomeIntent.setComponent(myHomeComponent);
mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
mFallbackIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_DEFAULT)
.setComponent(fallbackComponent)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo(
mFallbackIntent.getComponent(), 0 /* flags */);
mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
mContext.registerReceiver(mUserPreferenceChangeReceiver,
new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
updateOverviewTargets();
}
解决奔溃问题后, 点击recent键返回了HOME, 而不是显示RECENT界面:
packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
/**
* Update overview intent and {@link BaseActivityInterface} based off the current launcher home
* component.
*/
private void updateOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
mIsHomeDisabled = mDeviceState.isHomeDisabled();
mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
// Set assistant visibility to 0 from launcher's perspective, ensures any elements that
// launcher made invisible become visible again before the new activity control helper
// becomes active.
if (mActivityInterface != null) {
mActivityInterface.onAssistantVisibilityChanged(0.f);
}
if (SEPARATE_RECENTS_ACTIVITY.get()) {
mIsDefaultHome = false;
if (defaultHome == null) {
defaultHome = mMyHomeIntent.getComponent();
}
}
//不走if分支,
if (false && !mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
mActivityInterface = LauncherActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = true;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
// Remove any update listener as we don't care about other packages.
unregisterOtherHomeAppUpdateReceiver();
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
mActivityInterface = FallbackActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
mCurrentHomeIntent.setComponent(defaultHome);
// User's default home app can change as a result of package updates of this app (such
// as uninstalling the app or removing the "Launcher" feature in an update).
// Listen for package updates of this app (and remove any previously attached
// package listener).
if (defaultHome == null) {
unregisterOtherHomeAppUpdateReceiver();
} else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
unregisterOtherHomeAppUpdateReceiver();
mUpdateRegisteredPackage = defaultHome.getPackageName();
mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
ACTION_PACKAGE_REMOVED));
}
}
mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
}
关键在于使mOverviewIntent = mFallbackIntent;
由SystemUI调用onOverviewToggle:
packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@BinderThread
@Override
public void onOverviewToggle() {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
mOverviewCommandHelper.onOverviewToggle();
}
packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@BinderThread
public void onOverviewToggle() {
// If currently screen pinning, do not enter overview
if (mDeviceState.isScreenPinningActive()) {
return;
}
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
}
MAIN_EXECUTOR 和 LooperExecutor
RecentsActivityCommand
private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
protected final BaseActivityInterface<?, T> mActivityInterface;
//.....
private ActivityInitListener mListener;
public RecentsActivityCommand() {
mActivityInterface = mOverviewComponentObserver.getActivityInterface();
//....
}
@Override
public void run() {
//...
// Otherwise, start overview.
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
new RemoteAnimationProvider() {
@Override
public AnimatorSet createWindowAnimation(
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets) {
return RecentsActivityCommand.this.createWindowAnimation(appTargets,
wallpaperTargets);
}
}, mContext, MAIN_EXECUTOR.getHandler(),
mAnimationProvider.getRecentsLaunchDuration());
}
packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
/**
* Get the current activity control helper for managing interactions to the overview activity.
*
* @return the current activity control helper
*/
public BaseActivityInterface getActivityInterface() {
return mActivityInterface;
}
OverviewComponentObserver.mActivityInterface = FallbackActivityInterface.INSTANCE;
packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
public final class FallbackActivityInterface extends
BaseActivityInterface<RecentsState, RecentsActivity> {
public static final FallbackActivityInterface INSTANCE = new FallbackActivityInterface();
@Override
public ActivityInitListener createActivityInitListener(
Predicate<Boolean> onInitListener) {
return new ActivityInitListener<>((activity, alreadyOnHome) ->
onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
}
packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
/**
* Starts the given intent with the provided animation. Unlike {@link #register(Intent)}, this
* method will not call {@link #init} if the activity already exists, it will only call it when
* we get handleIntent() for the provided intent that we're starting.
*/
public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
Context context, Handler handler, long duration) {
mIsRegistered = true;
Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
context.startActivity(addToIntent(new Intent(intent)), options);
}
最终context.startActivity 其中的Intent是:mOverviewComponentObserver.getOverviewIntent()
packages/apps/Launcher3/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
/**
* Get the current intent for going to the overview activity.
*
* @return the overview intent
*/
public Intent getOverviewIntent() {
/*参见前面的代码:
ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
mFallbackIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_DEFAULT)
.setComponent(fallbackComponent)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
*/
return mOverviewIntent;
}
至此RecentsActivity启动完成.
模块编译:
mmm packages/apps/Launcher3/:Launcher3QuickStep -j4
需要注意的是