analysis chaozhuo multiwindow - HybridOS/Document GitHub Wiki

Android 多窗口分析

我的思路,前期主要了解了tieto项目的jabol分支代码,分析tieto在kitkat 分支上加入的功能解读:


首先看jabol的第一次提交:0001-Initial-commit-for-mw.patch

---
 core/java/android/app/Activity.java                |  41 +-
 core/java/android/app/ActivityManagerNative.java   |  43 +++
 core/java/android/app/IActivityManager.java        |  22 ++
 core/java/android/view/ViewRootImpl.java           |   8 +-
 core/java/android/view/Window.java                 |  37 ++

 core/res/res/drawable/mw_app_header_focused.9.png  | Bin 0 -> 2891 bytes
 .../res/res/drawable/mw_app_header_unfocused.9.png | Bin 0 -> 2883 bytes
 core/res/res/drawable/mw_btn_add_content.xml       |   6 +
 .../res/drawable/mw_btn_add_content_clicked.png    | Bin 0 -> 3221 bytes
 .../res/drawable/mw_btn_add_content_default.png    | Bin 0 -> 3231 bytes
 core/res/res/drawable/mw_btn_close.xml             |   6 +
 core/res/res/drawable/mw_btn_close_clicked.png     | Bin 0 -> 3712 bytes
 core/res/res/drawable/mw_btn_close_default.png     | Bin 0 -> 3362 bytes
 core/res/res/drawable/mw_btn_maximize.xml          |   6 +
 core/res/res/drawable/mw_btn_maximize_clicked.png  | Bin 0 -> 3843 bytes
 core/res/res/drawable/mw_btn_maximize_default.png  | Bin 0 -> 3564 bytes
 core/res/res/drawable/mw_btn_minimize.xml          |   6 +
 core/res/res/drawable/mw_btn_minimize_clicked.png  | Bin 0 -> 3942 bytes
 core/res/res/drawable/mw_btn_minimize_default.png  | Bin 0 -> 3819 bytes
 core/res/res/drawable/mw_btn_resize_left.xml       |   6 +
 .../res/drawable/mw_btn_resize_left_clicked.png    | Bin 0 -> 3840 bytes
 .../res/drawable/mw_btn_resize_left_default.png    | Bin 0 -> 3914 bytes
 core/res/res/drawable/mw_btn_resize_right.xml      |   6 +
 .../res/drawable/mw_btn_resize_right_clicked.png   | Bin 0 -> 3825 bytes
 .../res/drawable/mw_btn_resize_right_default.png   | Bin 0 -> 3905 bytes
 core/res/res/drawable/mw_inner_border.xml          |   5 +
 core/res/res/drawable/mw_outer_border.xml          |   5 +
 core/res/res/drawable/mw_outer_border_wide.xml     |   5 +
 core/res/res/layout/mw_decor.xml                   |  83 +++++
 core/res/res/values/colors.xml                     |   6 +
 core/res/res/values/mw_values.xml                  |  10 +
 core/res/res/values/symbols.xml                    |  28 ++


 .../android/internal/policy/impl/PhoneWindow.java  | 411 +++++++++++++++++++++
 .../android/server/am/ActivityManagerService.java  |  33 +-
 .../java/com/android/server/am/ActivityStack.java  |  14 +-
 .../android/server/am/ActivityStackSupervisor.java |  73 +++-
 services/java/com/android/server/wm/DimLayer.java  |  24 +-
 .../java/com/android/server/wm/DisplayContent.java | 134 ++++++-
 services/java/com/android/server/wm/StackBox.java  |  48 ++-
 .../android/server/wm/WindowManagerService.java    |  37 ++
 .../java/com/android/server/wm/WindowState.java    |  23 +-
 41 files changed, 1076 insertions(+), 50 deletions(-)

 create mode 100644 core/res/res/drawable/mw_app_header_focused.9.png
 create mode 100644 core/res/res/drawable/mw_app_header_unfocused.9.png
 create mode 100644 core/res/res/drawable/mw_btn_add_content.xml
 create mode 100755 core/res/res/drawable/mw_btn_add_content_clicked.png
 create mode 100644 core/res/res/drawable/mw_btn_add_content_default.png
 create mode 100644 core/res/res/drawable/mw_btn_close.xml
 create mode 100755 core/res/res/drawable/mw_btn_close_clicked.png
 create mode 100644 core/res/res/drawable/mw_btn_close_default.png
 create mode 100644 core/res/res/drawable/mw_btn_maximize.xml
create mode 100755 core/res/res/drawable/mw_btn_minimize_clicked.png
 create mode 100755 core/res/res/drawable/mw_btn_minimize_default.png
 create mode 100644 core/res/res/drawable/mw_btn_resize_left.xml
 create mode 100755 core/res/res/drawable/mw_btn_resize_left_clicked.png
 create mode 100644 core/res/res/drawable/mw_btn_resize_left_default.png
 create mode 100644 core/res/res/drawable/mw_btn_resize_right.xml
 create mode 100755 core/res/res/drawable/mw_btn_resize_right_clicked.png
 create mode 100644 core/res/res/drawable/mw_btn_resize_right_default.png
 create mode 100644 core/res/res/drawable/mw_inner_border.xml
 create mode 100644 core/res/res/drawable/mw_outer_border.xml
 create mode 100644 core/res/res/drawable/mw_outer_border_wide.xml
 create mode 100644 core/res/res/layout/mw_decor.xml
 create mode 100644 core/res/res/values/mw_values.xml

对第一个补丁进行分类 ,分为java代码资源代码两类:

  • java 代码部分解释:
core/java/android/app/Activity.java                |  41 +-
 core/java/android/app/ActivityManagerNative.java   |  43 +++
 core/java/android/app/IActivityManager.java        |  22 ++
 core/java/android/view/ViewRootImpl.java           |   8 +-
 core/java/android/view/Window.java                 |  37 ++

分析以上5个文件中的修改内容发现: Activity.java 文件添加获取stackboxID函数,在每个app启动时获取其 sb.stack.taskIds[i]

+    private int getStackBoxId() {
+        try {
+            int taskId = getTaskId();
+            List<StackBoxInfo> list = ActivityManagerNative.getDefault().getStackBoxes();
+            for (StackBoxInfo sb : list) {
+                if ((sb.stackBoxId != 0) && (sb.stack != null) && (sb.stack.taskIds != null)) {
+                    for (int i = 0; i < sb.stack.taskIds.length; i++) {
+                        if (taskId == sb.stack.taskIds[i]) {
+                            return sb.stackBoxId;
+                        }
+                    }
+                }
+            }

mWindow.setStackBoxId(getStackBoxId()); 

**设置窗口的 stackbox id **

ActivityManagerNative.java
IActivityManager.java
ViewRootImpl.java

以上三个java文件的修改,用于获取当前窗口的位置并且调整窗口位置;


  • 窗口绘制部分的java代码分析
.../android/internal/policy/impl/PhoneWindow.java  | 411 +++++++++++++++++++++
 .../android/server/am/ActivityManagerService.java  |  33 +-
 .../java/com/android/server/am/ActivityStack.java  |  14 +-
 .../android/server/am/ActivityStackSupervisor.java |  73 +++-
 services/java/com/android/server/wm/DimLayer.java  |  24 +-
 .../java/com/android/server/wm/DisplayContent.java | 134 ++++++-
 services/java/com/android/server/wm/StackBox.java  |  48 ++-
 .../android/server/wm/WindowManagerService.java    |  37 ++
 .../java/com/android/server/wm/WindowState.java    |  23 +-
 41 files changed, 1076 insertions(+), 50 deletions(-)

<1> PhoneWindow.java 首先导入和stackbox 相关的类; +import android.app.ActivityManager.StackBoxInfo; 绘制相关的类: +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; 输入事件监听相关的类: +import android.view.View.OnTouchListener; +import android.app.ActivityManagerNative; +import android.view.View.OnClickListener; 控件布局相关的类:

+import android.widget.Button; +import android.widget.ImageButton; +import android.widget.LinearLayout;

默认情况下,android原生的DecorView为整个Window界面的最顶层View ,DecorView只有一个子元素为LinearLayout。 代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域。

<2>jabol 代码在这里定义了和decorview类似的 用于多窗口显示的 顶层view如下: + private DecorMWView mDecorMW; 并且在方法setWindowBackground设置了window的背景色为透明Color.TRANSPARENT

紧接着在方法fitSystemWindows(Rect insets)中设置边界偏移量 int of = mDecorMW.getBorderPadding();

这里比较关键的点**DecorMWView** 类的成员的变量的声明:

+    class DecorMWView {
+
+        private LinearLayout mHeader;
+        private ImageButton mCloseBtn;
+        private ImageButton mLaunchBtn;
+        private ImageButton mMaximizeBtn;
+        private View mInnerBorder;
+        private View mOuterBorder;
+        private View mBackground;
+        private View mLeftResize;
+        private View mRightResize;
+
+        private View mDecorView;

+        private int mTopBarHeight;
+        private int mMultiwindowHeight;
+        private int mStatusBarHeight;
+        private int mBorderPadding;
+        private boolean mRemoteConnected = false;
+        private Rect mFullScreen;
+
+        private Rect mOldSize;

//DecorMWView 类的构造函数
+        public DecorMWView() {

+            mDecorView = getLayoutInflater().inflate(com.android.internal.R.layout.mw_decor, null);
+
+            mHeader = (LinearLayout)mDecorView.findViewById(com.android.internal.R.id.mw_decor_header);
+            mTopBarHeight = getContext().getResources().getDimensionPixelSize(com.android.internal.R.dimen.mw_inner_border);
+            //mMultiwindowHeight = getContext().getResources().getDimensionPixelSize(android.R.dimen.mw_app_height);
+            mStatusBarHeight = getContext().getResources().getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+            mBorderPadding = mDecorView.getPaddingLeft() + mTopBarHeight;
+            mCloseBtn = (ImageButton)mDecorView.findViewById(com.android.internal.R.id.mwCloseBtn);
+            mLaunchBtn = (ImageButton)mDecorView.findViewById(com.android.internal.R.id.mwLaunchBtn);
+            mMaximizeBtn = (ImageButton)mDecorView.findViewById(com.android.internal.R.id.mwMaximizeBtn);
.....
.....
+            mInnerBorder = mDecorView.findViewById(com.android.internal.R.id.mwInnerBorder);
+            mOuterBorder = mDecorView.findViewById(com.android.internal.R.id.mwOuterBorder);
+            mBackground = mDecorView.findViewById(com.android.internal.R.id.mwBackground);
+            mLeftResize = mDecorView.findViewById(com.android.internal.R.id.mwResizeLeft);
+            mRightResize = mDecorView.findViewById(com.android.internal.R.id.mwResizeRight);
+
+            DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+            mFullScreen = new Rect(0, mStatusBarHeight + mMultiwindowHeight, metrics.widthPixels, metrics.heightPixels);
+

// 外部输入事件的监听(包括鼠标点击或者触摸时的各种响应)
+            mHeader.setOnTouchListener(new TouchListener(new ResizeWindow() {
+                @Override
+                public Rect resize(Rect frame, int diffX, int diffY) {
                  .....
+                }
+            }, mMaximizeBtn, mFullScreen));
+
+            mLeftResize.setOnTouchListener(new TouchListener(new ResizeWindow() {
+                @Override
+                public Rect resize(Rect frame, int diffX, int diffY) {
                  .......
+                }
+            }, mMaximizeBtn, mLeftResize, mFullScreen));
+
+            mRightResize.setOnTouchListener(new TouchListener(new ResizeWindow() {
+                @Override
+                public Rect resize(Rect frame, int diffX, int diffY) {
.....
+                }
+            }, mMaximizeBtn, mRightResize, mFullScreen));
+
+            mCloseBtn.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
                  .......
                   }
+            mLaunchBtn.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
			.....
+                }
+            });
+
+            mMaximizeBtn.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {

+            });
+        public int getTopBarHeight() {
+            return mTopBarHeight;
+        }
+
//获取边框
+        public int getBorderPadding() {
+            return mBorderPadding;
+        }
+
+        public void startMultiwindowApp(final Intent intent,final int index) {
+            new Thread(new Runnable() {
+                public void run() {
		....
+                }

+        }
+
+        public View getView(){
+            return mDecorView;
+        }
+
//选中模式下视图的背景和边框的设置
+        void setFocus(){
+            int tietoBlue = com.android.internal.R.color.mw_blue_decor;
+            mLeftResize.setBackgroundResource(tietoBlue);
+            mRightResize.setBackgroundResource(tietoBlue);
+            mHeader.setBackgroundResource(tietoBlue);
+            if (mRemoteConnected) {
+                mInnerBorder.setBackgroundResource(0);
+                mInnerBorder.setBackgroundColor(Color.TRANSPARENT);
+                mOuterBorder.setBackgroundResource(com.android.internal.R.drawable.mw_outer_border_wide);
+            } else {
+                mInnerBorder.setBackgroundResource(com.android.internal.R.color.mw_transparent_white);
+                mOuterBorder.setBackgroundResource(com.android.internal.R.drawable.mw_outer_border);
+            }
+            mDecorView.invalidate();
+        }
//  非选中状态下的背景以及边框颜色的设置
+        void unsetFocus() {
+            mLeftResize.setBackgroundResource(0);
+            mRightResize.setBackgroundResource(0);
+            mLeftResize.setBackgroundColor(Color.TRANSPARENT);
+            mRightResize.setBackgroundColor(Color.TRANSPARENT);
+            mHeader.setBackgroundResource(0);
+            mHeader.setBackgroundColor(Color.TRANSPARENT);
+            if (mRemoteConnected) {
+                mInnerBorder.setBackgroundResource(0);
+                mInnerBorder.setBackgroundColor(Color.TRANSPARENT);
+            } else {
+                mInnerBorder.setBackgroundResource(com.android.internal.R.color.mw_transparent_white);
+            }
+            mOuterBorder.setBackgroundResource(com.android.internal.R.drawable.mw_outer_border);
+            mDecorView.invalidate();
+        }

+        public void setBackground(Drawable d) {
+            mBackground.setBackground(d);
+        }
+
+    }