找回密码
 立即注册
首页 业界区 安全 Launcher 桌面源码笔记一(3D车模桌面)

Launcher 桌面源码笔记一(3D车模桌面)

乳杂丫 昨天 17:30
3D车模通过TaskView显示在Launcher,首先需要知道,为什么要用TaskView,而不是Activity,然后在说加载流程
1、surface比activity等效率更高,特别是针对车模跟地图等重量级场景
2、切换桌面等场景时,可以更精确的控制暂停恢复
3、进程隔离,更精细的生命周期管理跟控制
4、taskView中SurfaceControl的跨进程绑定机制(reparent操作)效率比binder通讯效率更高
android.app.ActivityView(标记为弃用‌)

跟TaskView一样都是Android系统中用于管理多窗口和任务嵌入的组件
1.gif
2.gif
  1. public class ActivityView extends ViewGroup {
  2.     private static final String DISPLAY_NAME = "ActivityViewVirtualDisplay";
  3.     private static final String TAG = "ActivityView";
  4.     private final SurfaceView mSurfaceView;
  5.     private Surface mSurface;
  6.     private final SurfaceCallback mSurfaceCallback;
  7.     private StateCallback mActivityViewCallback;
  8.     private IActivityManager mActivityManager;
  9.     private TaskStackListener mTaskStackListener;
  10.     public ActivityView(Context context) {
  11.         this(context, (AttributeSet)null);
  12.     }
  13.     public ActivityView(Context context, AttributeSet attrs) {
  14.         this(context, attrs, 0);
  15.     }
  16.     public ActivityView(Context context, AttributeSet attrs, int defStyle) {
  17.         super(context, attrs, defStyle);
  18.         this.mActivityManager = ActivityManager.getService();
  19.         this.mSurfaceView = new SurfaceView(context);
  20.         this.mSurfaceCallback = new SurfaceCallback();
  21.         this.mSurfaceView.getHolder().addCallback(this.mSurfaceCallback);
  22.         this.addView(this.mSurfaceView);
  23.     }
复制代码
View Code从源码中看到,继承自ViewGroup,基于SurfaceView实现,在初始化时将 SurfaceView addView到ViewGroup中
ActivityView‌ 直接依赖传统的窗口管理机制,通过 WindowManager 控制嵌入的 Activity 生命周期。
com.android.wm.shell.TaskView

3.gif
4.gif
  1. public class TaskView extends SurfaceView implements SurfaceHolder.Callback, ShellTaskOrganizer.TaskListener, ViewTreeObserver.OnComputeInternalInsetsListener {
  2.     private final ShellTaskOrganizer mTaskOrganizer;
  3.     private ActivityManager.RunningTaskInfo mTaskInfo;
  4.     private WindowContainerToken mTaskToken;
  5.     private SurfaceControl mTaskLeash;
  6.     private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
  7.     public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
  8.         super(context, (AttributeSet)null, 0, 0, true);
  9.         this.getHolder().addCallback(this);
  10.     }
复制代码
View Code
5.gif
6.gif
  1. public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
  2.         this.mTaskInfo = taskInfo;
  3.         this.mTaskToken = taskInfo.token;
  4.         this.mTaskLeash = leash;
  5.         if (this.mSurfaceCreated) {
  6.             this.mTransaction.reparent(this.mTaskLeash, this.getSurfaceControl()).show(this.mTaskLeash).apply();
  7.         } else {
  8.             this.updateTaskVisibility();
  9.         }
  10.         this.mTaskOrganizer.setInterceptBackPressedOnTaskRoot(this.mTaskToken, true);
  11.         this.onLocationChanged();
  12.         if (taskInfo.taskDescription != null) {
  13.             int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
  14.             this.mSyncQueue.runInSync((t) -> this.setResizeBackgroundColor(t, backgroundColor));
  15.         }
  16.         if (this.mListener != null) {
  17.             int taskId = taskInfo.taskId;
  18.             ComponentName baseActivity = taskInfo.baseActivity;
  19.             this.mListenerExecutor.execute(() -> this.mListener.onTaskCreated(taskId, baseActivity));
  20.         }
  21.     }
复制代码
View CodeAndroid 12 引入的替代方案,专为 Android Automotive OS (AAOS) 优化,并扩展了 ShellTaskOrganizer.TaskListener 接口。直接继承自SurfaceView,并且在onTaskAppeared回调中使用了reparent操作,支持动态调整嵌入任务的位置和大小‌。
车模加载流程

车模属于unity交互,在android中提供一个容器用于显示,这里直接使用Activity
7.gif
8.gif
  1. [/code]View Code新建一个CarLauncherActivity用于显示这个UnityPlayerFragment
  2. 3DActivity有了,需要将它显示到launcher桌面(MainActivity),自定义TaskView,用于3D桌面
  3. [align=center] 9.gif [/align][align=center] 10.gif [/align][code]
复制代码
View Code在MainActivity启动时通过 surfaceCreated 回调,对桌面进行初始化操作
11.gif
12.gif
  1. public void surfaceCreated(SurfaceHolder holder) {
  2.         this.mSurfaceCreated = true;
  3.         if (this.mListener != null && !this.mIsInitialized) {
  4.             this.mIsInitialized = true;
  5.             this.mListenerExecutor.execute(() -> this.mListener.onInitialized());
  6.         }
  7.         this.mShellExecutor.execute(() -> {
  8.             if (this.mTaskToken != null) {
  9.                 this.mTransaction.reparent(this.mTaskLeash, this.getSurfaceControl()).show(this.mTaskLeash).apply();
  10.                 this.updateTaskVisibility();
  11.             }
  12.         });
  13.     }
复制代码
View Code接下来需要实现嵌入操作,可以看到在首次进入时,会调用 TaskView. Listener的 onInitialized 方法,在回调中需要执行taskview的startActivity进行内嵌
13.gif
14.gif
  1. public void startActivity(PendingIntent pendingIntent, Intent fillInIntent, ActivityOptions options, Rect launchBounds) {
  2.         this.prepareActivityOptions(options, launchBounds);
  3.         try {
  4.             pendingIntent.send(this.mContext, 0, fillInIntent, (PendingIntent.OnFinished)null, (Handler)null, (String)null, options.toBundle());
  5.         } catch (Exception e) {
  6.             throw new RuntimeException(e);
  7.         }
  8.     }
复制代码
View CodestartActivity后会触发状态变更,系统层处理后,在Systemui层通过TaskOrganizer监听状态
android.window.TaskOrganizer

当需要嵌入显示另一个应用的 Activity 时,TaskOrganizer 会提供该任务窗口的 SurfaceControl(称为 "leash"),但是TaskOrganizer位与SystemUI模块,所以需要通过AIDL进行跨进程通讯
首先在launcher模块定义好接口
15.gif
16.gif
  1. package com.android.wm.shell;
  2. import android.view.SurfaceControl;
  3. import android.graphics.Rect;
  4. import android.window.WindowContainerToken;
  5. import com.android.wm.shell.RunningTaskInfo;
  6. /**
  7. * Interface for ActivityTaskManager/WindowManager to delegate control of tasks.
  8. * {@hide}
  9. */
  10. oneway interface ITaskView {
  11.     /**
  12.      * Called when a Task is starting and the system would like to show a UI to indicate that an
  13.      * application is starting. The client is responsible to add/remove the starting window if it
  14.      * has create a starting window for the Task.
  15.      *
  16.      * @param info The information about the Task that's available
  17.      * @param appToken Token of the application being started.
  18.      */
  19.     /**
  20.      * Called when the Task want to remove the starting window.
  21.      * @param removalInfo The information used to remove the starting window.
  22.      */
  23.     /**
  24.      * Called when the Task want to copy the splash screen.
  25.      */
  26.     void copySplashScreenView(int taskId);
  27.     /**
  28.      * Called when the Task removed the splash screen.
  29.      */
  30.     void onAppSplashScreenViewRemoved(int taskId);
  31.     /**
  32.      * A callback when the Task is available for the registered organizer. The client is responsible
  33.      * for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially
  34.      * be hidden so it is up to the organizer to show this task.
  35.      *
  36.      * @param taskInfo The information about the Task that's available
  37.      * @param leash A persistent leash for this Task.
  38.      */
  39.     void onTaskAppeared(in RunningTaskInfo taskInfo, in SurfaceControl leash);
  40.     void onTaskVanished(in RunningTaskInfo taskInfo);
  41.     /**
  42.      * Will fire when core attributes of a Task's info change. Relevant properties include the
  43.      * {@link WindowConfiguration.ActivityType} and whether it is resizable.
  44.      *
  45.      * This is used, for example, during split-screen. The flow for starting is: Something sends an
  46.      * Intent with windowingmode. Then WM finds a matching root task and launches the new task into
  47.      * it. This causes the root task's info to change because now it has a task when it didn't
  48.      * before. The default Divider implementation interprets this as a request to enter
  49.      * split-screen mode and will move all other Tasks into the secondary root task. When WM
  50.      * applies this change, it triggers an info change in the secondary root task because it now
  51.      * has children. The Divider impl looks at the info and can see that the secondary root task
  52.      * has adopted an ActivityType of HOME and proceeds to show the minimized dock UX.
  53.      */
  54.     void onTaskInfoChanged(in RunningTaskInfo taskInfo);
  55.     /**
  56.      * Called when the task organizer has requested
  57.      * {@link ITaskOrganizerController.setInterceptBackPressedOnTaskRoot} to get notified when the
  58.      * user has pressed back on the root activity of a task controlled by the task organizer.
  59.      */
  60.     void onBackPressedOnTaskRoot(in RunningTaskInfo taskInfo);
  61.     /**
  62.      * Called when the IME has drawn on the organized task.
  63.      */
  64.     void onImeDrawnOnTask(int taskId);
  65. }
复制代码
View Code并且提供Service
17.gif
18.gif
  1.         <service
  2.             android:name="com.test.SharedTaskViewService"
  3.             android:enabled="true"
  4.             android:exported="true" />
  5.     </application>
  6. package com.test.taskview;
  7. import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
  8. import android.app.Service;
  9. import android.content.Intent;
  10. import android.os.Handler;
  11. import android.os.IBinder;
  12. import android.os.Looper;
  13. import android.util.Log;
  14. import android.view.SurfaceControl;
  15. import com.android.wm.shell.ITaskView;
  16. import com.android.wm.shell.ShellTaskOrganizer;
  17. import com.android.wm.shell.RunningTaskInfo;
  18. import com.android.wm.shell.common.HandlerExecutor;
  19. import java.util.concurrent.ConcurrentHashMap;
  20. public class SharedTaskViewService extends Service {
  21.     private static final String TAG = "SharedTaskViewService";
  22.     private ConcurrentHashMap<Integer, Integer> taskIdDisplayMap = new ConcurrentHashMap<>();
  23.     private final HandlerExecutor mExecutor = new HandlerExecutor(new Handler(Looper.getMainLooper()));
  24.     private int getRealDisplayId(int taskId) {
  25.         if (taskIdDisplayMap.containsKey(taskId)) {
  26.             Integer val = taskIdDisplayMap.get(taskId);
  27.             if (val == null) {
  28.                 return -1;
  29.             }
  30.             return val.intValue();
  31.         }
  32.         return -2;
  33.     }
  34.     ITaskView taskView = new ITaskView.Stub() {
  35.         public void copySplashScreenView(int taskId) {
  36.         }
  37.         public void onAppSplashScreenViewRemoved(int taskId) {
  38.         }
  39.         public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
  40.             mExecutor.execute(() -> SharedTaskViewService.this.onTaskAppeared(taskInfo, leash));
  41.         }
  42.         public void onTaskVanished(RunningTaskInfo taskInfo) {
  43.             mExecutor.execute(() -> SharedTaskViewService.this.onTaskVanished(taskInfo));
  44.         }
  45.         public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
  46.             mExecutor.execute(() -> SharedTaskViewService.this.onTaskInfoChanged(taskInfo));
  47.         }
  48.         public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
  49.         }
  50.         /**
  51.          * Called when the IME has drawn on the organized task.
  52.          */
  53.         public void onImeDrawnOnTask(int taskId) {
  54.         }
  55.     };
  56.     public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
  57.         if (SharedTaskManager.getInstance().getListeners() != null) {
  58.             if (taskInfo != null && taskInfo.info != null && taskInfo.info.baseIntent != null && taskInfo.info.baseIntent.getComponent() != null) {
  59.                 String pkgName = taskInfo.info.baseIntent.getComponent().getPackageName();
  60.                 String clsName = taskInfo.info.baseIntent.getComponent().getClassName();
  61.                 Log.w(TAG, "onTaskAppeared clsName:" + clsName);
  62.                 boolean hasKey = SharedTaskManager.getInstance().getListeners().containsKey(clsName);
  63.                 boolean hasTaskLeash = SharedTaskManager.getInstance().hasTaskLeash(clsName);
  64.                 if (hasTaskLeash) {
  65.                     SharedTaskManager.getInstance().addOrUpdateTaskLeash(clsName, new SharedTaskManager.SharedTaskInfo(leash, taskInfo.info.token));
  66.                 }
  67.                 if (hasKey) {
  68.                     ShellTaskOrganizer.TaskListener taskListener = SharedTaskManager.getInstance().getListeners().get(clsName);
  69.                     if (taskListener != null) {
  70.                         Logger.w(TAG, "onTaskAppeared:" + pkgName + "displayId:" + taskInfo.info.displayId);
  71.                         if (taskInfo.info.displayId == 0 && taskInfo.info.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW && taskInfo.info.numActivities > 0) {
  72.                             taskIdDisplayMap.put(taskInfo.info.taskId, 0);
  73.                             taskListener.onTaskAppeared(taskInfo.info, leash);
  74.                         }
  75.                     }
  76.                 } else {
  77.                     Log.w(TAG, "onTaskAppeared,no suiteable key in TaskManager.getInstance().getListeners:" + pkgName);
  78.                 }
  79.             }
  80.         }
  81.     }
  82.     public void onTaskVanished(RunningTaskInfo taskInfo) {
  83.         if (SharedTaskManager.getInstance().getListeners() != null) {
  84.             if (taskInfo != null && taskInfo.info != null && taskInfo.info.realActivity != null) {
  85.                 String pkgName = taskInfo.info.baseIntent.getComponent().getPackageName();
  86.                 String clsName = taskInfo.info.baseIntent.getComponent().getClassName();
  87.                 boolean hasKey = SharedTaskManager.getInstance().getListeners().containsKey(clsName);
  88.                 if (hasKey) {
  89.                     ShellTaskOrganizer.TaskListener taskListener = SharedTaskManager.getInstance().getListeners().get(clsName);
  90.                     if (taskListener != null) {
  91.                         int realDisplayId = getRealDisplayId(taskInfo.info.taskId);
  92.                         Logger.w(TAG, "onTaskVanished:" + pkgName + ",displayId:" + taskInfo.info.displayId + ",realDisplayId:" + realDisplayId);
  93.                         if (realDisplayId == 0) {
  94.                             taskListener.onTaskVanished(taskInfo.info);
  95.                         }
  96.                     }
  97.                 } else {
  98.                     Log.w(TAG, "onTaskVanished,no suiteable key in TaskManager.getInstance().getListeners:" + clsName);
  99.                 }
  100.             }
  101.         }
  102.     }
  103.     public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
  104.         if (SharedTaskManager.getInstance().getListeners() != null) {
  105.             if (taskInfo != null && taskInfo.info != null && taskInfo.info.realActivity != null) {
  106.                 String pkgName = taskInfo.info.baseIntent.getComponent().getPackageName();
  107.                 String clsName = taskInfo.info.baseIntent.getComponent().getClassName();
  108.                 boolean hasKey = SharedTaskManager.getInstance().getListeners().containsKey(clsName);
  109.                 if (hasKey) {
  110.                     ShellTaskOrganizer.TaskListener taskListener = SharedTaskManager.getInstance().getListeners().get(clsName);
  111.                     if (taskListener != null) {
  112.                         Logger.w(TAG, "onTaskInfoChanged:" + taskInfo.info);
  113.                         taskListener.onTaskInfoChanged(taskInfo.info);
  114.                     }
  115.                 } else {
  116.                     Log.w(TAG, "onTaskInfoChanged,no suiteable key in TaskManager.getInstance().getListeners:" + clsName);
  117.                 }
  118.             }
  119.         }
  120.     }
  121.     @Override
  122.     public IBinder onBind(Intent intent) {
  123.         Logger.w(TAG, "onBind: ");
  124.         return (IBinder) taskaskView;
  125.     }
  126.     @Override
  127.     public void onCreate() {
  128.         super.onCreate();
  129.         Logger.w(TAG, "onCreate: ");
  130.     }
  131.     @Override
  132.     public void onDestroy() {
  133.         super.onDestroy();
  134.         Logger.w(TAG, "onDestroy: ");
  135.     }
  136. }
复制代码
View Code 
SystemUI模块需要继承TaskOrganizer,在TaskOrganizer的onTaskAppeared中去同步到Launcher模块
19.gif
20.gif
  1. private void bind(){
  2.         Intent intent = new Intent();
  3.         intent.setComponent(new ComponentName(PACKAGE_LAUNCHER, SERVICE_LAUNCHER_TASK_VIEW));
  4.         mContext.bindService(intent,connection,Context.BIND_AUTO_CREATE);
  5.         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "bind SharedTaskViewService");
  6.     }
  7. private final ServiceConnection connection = new ServiceConnection() {
  8.         @Override
  9.         public void onServiceConnected(ComponentName name, IBinder service) {
  10.             synchronized (mLock) {
  11.                 StringBuilder tempInfoString = new StringBuilder();
  12.                 for (TaskAppearedInfo taskAppearedInfo : tempInfoList) {
  13.                     tempInfoString.append(getPackageName(taskAppearedInfo.getTaskInfo())).append(",");
  14.                 }
  15.                 Log.d(TAG, "onServiceConnected name = " + name + ", tempInfoList = " + tempInfoString);
  16.                 ProtoLog.v(WM_SHELL_TASK_ORG, "onServiceConnected name = " + name);
  17.                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
  18.                 iTaskView = ITaskView.Stub.asInterface(service);
  19.                 isConnecting = false;
  20.                 if(tempInfoList.size()>0){
  21.                     try {
  22.                         Log.d(TAG, "tempInfo is not null, notify launcher");
  23.                         for (TaskAppearedInfo taskAppearedInfo : tempInfoList) {
  24.                             Log.d(TAG, "tempInfo = " + getPackageName(taskAppearedInfo.getTaskInfo()));
  25.                             RunningTaskInfo runningTaskInfo = taskAppearedInfo.getTaskInfo();
  26.                             if (runningTaskInfo == null || runningTaskInfo.numActivities == 0) {
  27.                                 Log.d(TAG, "ignore empty taskInfo " + runningTaskInfo);
  28.                             } else {
  29.                                 iTaskView.onTaskAppeared(new RunningTaskInfo(taskAppearedInfo.getTaskInfo()), taskAppearedInfo.getLeash());
  30.                             }
  31.                         }
  32.                         tempInfoList.clear();
  33.                     } catch (RemoteException e) {
  34.                         e.printStackTrace();
  35.                     }
  36.                 }
  37.             }
  38.         }
  39.         @Override
  40.         public void onServiceDisconnected(ComponentName name) {
  41.             Log.d(TAG,"onServiceDisconnected componentName="+name);
  42.             iTaskView = null;
  43.             bind();
  44.         }
  45.         @Override
  46.         public void onBindingDied(ComponentName name) {
  47.             Log.d(TAG,"onBindingDied componentName="+name);
  48.             iTaskView = null;
  49.             bind();
  50.         }
  51.     };
  52. @Override
  53.     public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
  54.         Log.d(TAG, "task appeared " + getPackageName(taskInfo));
  55.         synchronized (mLock) {
  56.             iTaskView.onTaskAppeared(new RunningTaskInfo(info.getTaskInfo()), info.getLeash());
  57.         }
  58.     }
复制代码
View Code初始化时通过bind()方法绑定服务,在onTaskAppeared回调中调用服务方法
Launcher在收到AIDL方法onTaskAppeared时会执行 mTransaction.reparent(mTaskLeash, getSurfaceControl()) 显示当前车模桌面
 
 
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册