Loading libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml +45 −28 Original line number Diff line number Diff line Loading @@ -14,10 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.android.wm.shell.sizecompatui.SizeCompatHintPopup xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:clipToPadding="false" android:padding="@dimen/bubble_elevation"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/background_light" android:elevation="@dimen/bubble_elevation" android:orientation="vertical"> <TextView Loading Loading @@ -46,3 +59,7 @@ android:textStyle="bold"/> </LinearLayout> </FrameLayout> </com.android.wm.shell.sizecompatui.SizeCompatHintPopup> libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.sizecompatui; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.View; import android.widget.Button; import android.widget.FrameLayout; import androidx.annotation.Nullable; import com.android.wm.shell.R; /** Popup to show the hint about the {@link SizeCompatRestartButton}. */ public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener { private SizeCompatUILayout mLayout; public SizeCompatHintPopup(Context context) { super(context); } public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } void inject(SizeCompatUILayout layout) { mLayout = layout; } @Override protected void onFinishInflate() { super.onFinishInflate(); final Button gotItButton = findViewById(R.id.got_it); gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), null /* content */, null /* mask */)); gotItButton.setOnClickListener(this); } @Override public void onClick(View v) { mLayout.dismissHint(); } } libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java +5 −82 Original line number Diff line number Diff line Loading @@ -22,19 +22,13 @@ import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.PopupWindow; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; /** Button to restart the size compat activity. */ Loading @@ -42,10 +36,6 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick View.OnLongClickListener { private SizeCompatUILayout mLayout; private ImageButton mRestartButton; @VisibleForTesting PopupWindow mShowingHint; private WindowManager.LayoutParams mWinParams; public SizeCompatRestartButton(@NonNull Context context) { super(context); Loading @@ -67,24 +57,19 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick void inject(SizeCompatUILayout layout) { mLayout = layout; mWinParams = layout.getWindowLayoutParams(); } void remove() { dismissHint(); } @Override protected void onFinishInflate() { super.onFinishInflate(); mRestartButton = findViewById(R.id.size_compat_restart_button); final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY); final GradientDrawable mask = new GradientDrawable(); mask.setShape(GradientDrawable.OVAL); mask.setColor(color); mRestartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); mRestartButton.setOnClickListener(this); mRestartButton.setOnLongClickListener(this); restartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); restartButton.setOnClickListener(this); restartButton.setOnLongClickListener(this); } @Override Loading @@ -94,69 +79,7 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick @Override public boolean onLongClick(View v) { showHint(); mLayout.onRestartButtonLongClicked(); return true; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mLayout.mShouldShowHint) { mLayout.mShouldShowHint = false; showHint(); } } @Override public void setVisibility(@Visibility int visibility) { if (visibility == View.GONE && mShowingHint != null) { // Also dismiss the popup. dismissHint(); } super.setVisibility(visibility); } @Override public void setLayoutDirection(int layoutDirection) { final int gravity = SizeCompatUILayout.getGravity(layoutDirection); if (mWinParams.gravity != gravity) { mWinParams.gravity = gravity; getContext().getSystemService(WindowManager.class).updateViewLayout(this, mWinParams); } super.setLayoutDirection(layoutDirection); } void showHint() { if (mShowingHint != null) { return; } // TODO: popup is not attached to the button surface. Need to handle this differently for // non-fullscreen task. final View popupView = LayoutInflater.from(getContext()).inflate( R.layout.size_compat_mode_hint, null); final PopupWindow popupWindow = new PopupWindow(popupView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); popupWindow.setWindowLayoutType(mWinParams.type); popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation)); popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod); popupWindow.setClippingEnabled(false); popupWindow.setOnDismissListener(() -> mShowingHint = null); mShowingHint = popupWindow; final Button gotItButton = popupView.findViewById(R.id.got_it); gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), null /* content */, null /* mask */)); gotItButton.setOnClickListener(view -> dismissHint()); popupWindow.showAtLocation(mRestartButton, mWinParams.gravity, mLayout.mPopupOffsetX, mLayout.mPopupOffsetY); } void dismissHint() { if (mShowingHint != null) { mShowingHint.dismiss(); mShowingHint = null; } } } libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +8 −8 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang /** Whether the IME is shown on display id. */ private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1); /** The showing buttons by task id. */ /** The showing UIs by task id. */ private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0); /** Avoid creating display context frequently for non-default display. */ Loading @@ -77,12 +77,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } /** * Called when the Task info changed. Creates and updates the restart button if there is an * activity in size compat, or removes the restart button if there is no size compat activity. * Called when the Task info changed. Creates and updates the size compat UI if there is an * activity in size compat, or removes the UI if there is no size compat activity. * * @param displayId display the task and activity are in. * @param taskId task the activity is in. * @param taskConfig task config to place the restart button with. * @param taskConfig task config to place the size compat UI with. * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the * top activity in this Task is not in size compat. * @param taskListener listener to handle the Task Surface placement. Loading @@ -94,10 +94,10 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang // Null token means the current foreground activity is not in size compatibility mode. removeLayout(taskId); } else if (mActiveLayouts.contains(taskId)) { // Button already exists, update the button layout. // UI already exists, update the UI layout. updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener); } else { // Create a new restart button. // Create a new size compat UI. createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener); } } Loading @@ -106,7 +106,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang public void onDisplayRemoved(int displayId) { mDisplayContextCache.remove(displayId); // Remove all buttons on the removed display. // Remove all size compat UIs on the removed display. final List<Integer> toRemoveTaskIds = new ArrayList<>(); forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId())); for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) { Loading @@ -128,7 +128,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang mDisplaysWithIme.remove(displayId); } // Hide the button when input method is showing. // Hide the size compat UIs when input method is showing. forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing)); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +117 −32 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.view.Gravity; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; Loading @@ -43,7 +42,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; /** * Records and handles layout of size compat UI on a task with size compat activity. Helps to * calculate proper bounds when configuration or button position changes. * calculate proper bounds when configuration or UI position changes. */ class SizeCompatUILayout { private static final String TAG = "SizeCompatUILayout"; Loading @@ -56,12 +55,18 @@ class SizeCompatUILayout { private IBinder mActivityToken; private ShellTaskOrganizer.TaskListener mTaskListener; private DisplayLayout mDisplayLayout; @VisibleForTesting final SizeCompatUIWindowManager mWindowManager; @VisibleForTesting final SizeCompatUIWindowManager mButtonWindowManager; @VisibleForTesting @Nullable SizeCompatUIWindowManager mHintWindowManager; @VisibleForTesting @Nullable SizeCompatRestartButton mButton; @VisibleForTesting @Nullable SizeCompatHintPopup mHint; final int mButtonSize; final int mPopupOffsetX; final int mPopupOffsetY; Loading @@ -79,7 +84,7 @@ class SizeCompatUILayout { mTaskListener = taskListener; mDisplayLayout = displayLayout; mShouldShowHint = !hasShownHint; mWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); mButtonSize = mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size); Loading @@ -87,21 +92,52 @@ class SizeCompatUILayout { mPopupOffsetY = mButtonSize; } /** Creates the button window. */ /** Creates the activity restart button window. */ void createSizeCompatButton(boolean isImeShowing) { if (isImeShowing || mButton != null) { // When ime is showing, wait until ime is dismiss to create UI. return; } mButton = mWindowManager.createSizeCompatUI(); updateSurfacePosition(); mButton = mButtonWindowManager.createSizeCompatButton(); updateButtonSurfacePosition(); if (mShouldShowHint) { // Only show by default for the first time. mShouldShowHint = false; createSizeCompatHint(); } } /** Creates the restart button hint window. */ private void createSizeCompatHint() { if (mHint != null) { // Hint already shown. return; } mHintWindowManager = createHintWindowManager(); mHint = mHintWindowManager.createSizeCompatHint(); updateHintSurfacePosition(); } /** Releases the button window. */ @VisibleForTesting SizeCompatUIWindowManager createHintWindowManager() { return new SizeCompatUIWindowManager(mContext, mTaskConfig, this); } /** Dismisses the hint window. */ void dismissHint() { mHint = null; if (mHintWindowManager != null) { mHintWindowManager.release(); mHintWindowManager = null; } } /** Releases the UI windows. */ void release() { mButton.remove(); dismissHint(); mButton = null; mWindowManager.release(); mButtonWindowManager.release(); } /** Called when size compat info changed. */ Loading @@ -115,7 +151,10 @@ class SizeCompatUILayout { // Update configuration. mContext = mContext.createConfigurationContext(taskConfig); mWindowManager.setConfiguration(taskConfig); mButtonWindowManager.setConfiguration(taskConfig); if (mHintWindowManager != null) { mHintWindowManager.setConfiguration(taskConfig); } if (mButton == null || prevTaskListener != taskListener) { // TaskListener changed, recreate the button for new surface parent. Loading @@ -126,14 +165,19 @@ class SizeCompatUILayout { if (!taskConfig.windowConfiguration.getBounds() .equals(prevTaskConfig.windowConfiguration.getBounds())) { // Reposition the button surface. updateSurfacePosition(); // Reposition the UI surfaces. updateButtonSurfacePosition(); updateHintSurfacePosition(); } if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { // Update layout for RTL. mButton.setLayoutDirection(taskConfig.getLayoutDirection()); updateSurfacePosition(); updateButtonSurfacePosition(); if (mHint != null) { mHint.setLayoutDirection(taskConfig.getLayoutDirection()); updateHintSurfacePosition(); } } } Loading @@ -149,8 +193,9 @@ class SizeCompatUILayout { displayLayout.getStableBounds(curStableBounds); mDisplayLayout = displayLayout; if (!prevStableBounds.equals(curStableBounds)) { // Stable bounds changed, update button surface position. updateSurfacePosition(); // Stable bounds changed, update UI surface positions. updateButtonSurfacePosition(); updateHintSurfacePosition(); } } Loading @@ -162,27 +207,46 @@ class SizeCompatUILayout { return; } // Hide size compat UIs when IME is showing. final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE; if (mButton.getVisibility() != newVisibility) { mButton.setVisibility(newVisibility); } if (mHint != null && mHint.getVisibility() != newVisibility) { mHint.setVisibility(newVisibility); } } /** Gets the layout params for restart button. */ WindowManager.LayoutParams getWindowLayoutParams() { WindowManager.LayoutParams getButtonWindowLayoutParams() { final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( // Cannot be wrap_content as this determines the actual window size mButtonSize, mButtonSize, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); winParams.gravity = getGravity(getLayoutDirection()); winParams.token = new Binder(); winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + mContext.getDisplayId()); winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId()); winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; return winParams; } /** Gets the layout params for hint popup. */ WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) { final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( // Cannot be wrap_content as this determines the actual window size hint.getMeasuredWidth(), hint.getMeasuredHeight(), TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); winParams.token = new Binder(); winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId()); winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; winParams.windowAnimations = android.R.style.Animation_InputMethod; return winParams; } /** Called when it is ready to be placed button surface button. */ /** Called when it is ready to be placed size compat UI surface. */ void attachToParentSurface(SurfaceControl.Builder b) { mTaskListener.attachChildSurfaceToTask(mTaskId, b); } Loading @@ -192,13 +256,17 @@ class SizeCompatUILayout { ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken); } /** Called when the restart button is long clicked. */ void onRestartButtonLongClicked() { createSizeCompatHint(); } @VisibleForTesting void updateSurfacePosition() { if (mButton == null || mWindowManager.getSurfaceControl() == null) { void updateButtonSurfacePosition() { if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) { return; } // The hint popup won't be at the correct position. mButton.dismissHint(); final SurfaceControl leash = mButtonWindowManager.getSurfaceControl(); // Use stable bounds to prevent the button from overlapping with system bars. final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); Loading @@ -212,8 +280,30 @@ class SizeCompatUILayout { : stableBounds.right - taskBounds.left - mButtonSize; final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize; mSyncQueue.runInSync(t -> t.setPosition(mWindowManager.getSurfaceControl(), positionX, positionY)); mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); } void updateHintSurfacePosition() { if (mHint == null || mHintWindowManager == null || mHintWindowManager.getSurfaceControl() == null) { return; } final SurfaceControl leash = mHintWindowManager.getSurfaceControl(); // Use stable bounds to prevent the hint from overlapping with system bars. final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); final Rect stableBounds = new Rect(); mDisplayLayout.getStableBounds(stableBounds); stableBounds.intersect(taskBounds); // Position of the hint in the container coordinate. final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? stableBounds.left - taskBounds.left + mPopupOffsetX : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth(); final int positionY = stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight(); mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); } int getDisplayId() { Loading @@ -227,9 +317,4 @@ class SizeCompatUILayout { private int getLayoutDirection() { return mContext.getResources().getConfiguration().getLayoutDirection(); } static int getGravity(int layoutDirection) { return Gravity.BOTTOM | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END); } } Loading
libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml +45 −28 Original line number Diff line number Diff line Loading @@ -14,10 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.android.wm.shell.sizecompatui.SizeCompatHintPopup xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:clipToPadding="false" android:padding="@dimen/bubble_elevation"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/background_light" android:elevation="@dimen/bubble_elevation" android:orientation="vertical"> <TextView Loading Loading @@ -46,3 +59,7 @@ android:textStyle="bold"/> </LinearLayout> </FrameLayout> </com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.sizecompatui; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.View; import android.widget.Button; import android.widget.FrameLayout; import androidx.annotation.Nullable; import com.android.wm.shell.R; /** Popup to show the hint about the {@link SizeCompatRestartButton}. */ public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener { private SizeCompatUILayout mLayout; public SizeCompatHintPopup(Context context) { super(context); } public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } void inject(SizeCompatUILayout layout) { mLayout = layout; } @Override protected void onFinishInflate() { super.onFinishInflate(); final Button gotItButton = findViewById(R.id.got_it); gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), null /* content */, null /* mask */)); gotItButton.setOnClickListener(this); } @Override public void onClick(View v) { mLayout.dismissHint(); } }
libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java +5 −82 Original line number Diff line number Diff line Loading @@ -22,19 +22,13 @@ import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.PopupWindow; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; /** Button to restart the size compat activity. */ Loading @@ -42,10 +36,6 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick View.OnLongClickListener { private SizeCompatUILayout mLayout; private ImageButton mRestartButton; @VisibleForTesting PopupWindow mShowingHint; private WindowManager.LayoutParams mWinParams; public SizeCompatRestartButton(@NonNull Context context) { super(context); Loading @@ -67,24 +57,19 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick void inject(SizeCompatUILayout layout) { mLayout = layout; mWinParams = layout.getWindowLayoutParams(); } void remove() { dismissHint(); } @Override protected void onFinishInflate() { super.onFinishInflate(); mRestartButton = findViewById(R.id.size_compat_restart_button); final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY); final GradientDrawable mask = new GradientDrawable(); mask.setShape(GradientDrawable.OVAL); mask.setColor(color); mRestartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); mRestartButton.setOnClickListener(this); mRestartButton.setOnLongClickListener(this); restartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); restartButton.setOnClickListener(this); restartButton.setOnLongClickListener(this); } @Override Loading @@ -94,69 +79,7 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick @Override public boolean onLongClick(View v) { showHint(); mLayout.onRestartButtonLongClicked(); return true; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mLayout.mShouldShowHint) { mLayout.mShouldShowHint = false; showHint(); } } @Override public void setVisibility(@Visibility int visibility) { if (visibility == View.GONE && mShowingHint != null) { // Also dismiss the popup. dismissHint(); } super.setVisibility(visibility); } @Override public void setLayoutDirection(int layoutDirection) { final int gravity = SizeCompatUILayout.getGravity(layoutDirection); if (mWinParams.gravity != gravity) { mWinParams.gravity = gravity; getContext().getSystemService(WindowManager.class).updateViewLayout(this, mWinParams); } super.setLayoutDirection(layoutDirection); } void showHint() { if (mShowingHint != null) { return; } // TODO: popup is not attached to the button surface. Need to handle this differently for // non-fullscreen task. final View popupView = LayoutInflater.from(getContext()).inflate( R.layout.size_compat_mode_hint, null); final PopupWindow popupWindow = new PopupWindow(popupView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); popupWindow.setWindowLayoutType(mWinParams.type); popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation)); popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod); popupWindow.setClippingEnabled(false); popupWindow.setOnDismissListener(() -> mShowingHint = null); mShowingHint = popupWindow; final Button gotItButton = popupView.findViewById(R.id.got_it); gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), null /* content */, null /* mask */)); gotItButton.setOnClickListener(view -> dismissHint()); popupWindow.showAtLocation(mRestartButton, mWinParams.gravity, mLayout.mPopupOffsetX, mLayout.mPopupOffsetY); } void dismissHint() { if (mShowingHint != null) { mShowingHint.dismiss(); mShowingHint = null; } } }
libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +8 −8 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang /** Whether the IME is shown on display id. */ private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1); /** The showing buttons by task id. */ /** The showing UIs by task id. */ private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0); /** Avoid creating display context frequently for non-default display. */ Loading @@ -77,12 +77,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } /** * Called when the Task info changed. Creates and updates the restart button if there is an * activity in size compat, or removes the restart button if there is no size compat activity. * Called when the Task info changed. Creates and updates the size compat UI if there is an * activity in size compat, or removes the UI if there is no size compat activity. * * @param displayId display the task and activity are in. * @param taskId task the activity is in. * @param taskConfig task config to place the restart button with. * @param taskConfig task config to place the size compat UI with. * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the * top activity in this Task is not in size compat. * @param taskListener listener to handle the Task Surface placement. Loading @@ -94,10 +94,10 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang // Null token means the current foreground activity is not in size compatibility mode. removeLayout(taskId); } else if (mActiveLayouts.contains(taskId)) { // Button already exists, update the button layout. // UI already exists, update the UI layout. updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener); } else { // Create a new restart button. // Create a new size compat UI. createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener); } } Loading @@ -106,7 +106,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang public void onDisplayRemoved(int displayId) { mDisplayContextCache.remove(displayId); // Remove all buttons on the removed display. // Remove all size compat UIs on the removed display. final List<Integer> toRemoveTaskIds = new ArrayList<>(); forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId())); for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) { Loading @@ -128,7 +128,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang mDisplaysWithIme.remove(displayId); } // Hide the button when input method is showing. // Hide the size compat UIs when input method is showing. forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing)); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +117 −32 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.view.Gravity; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; Loading @@ -43,7 +42,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; /** * Records and handles layout of size compat UI on a task with size compat activity. Helps to * calculate proper bounds when configuration or button position changes. * calculate proper bounds when configuration or UI position changes. */ class SizeCompatUILayout { private static final String TAG = "SizeCompatUILayout"; Loading @@ -56,12 +55,18 @@ class SizeCompatUILayout { private IBinder mActivityToken; private ShellTaskOrganizer.TaskListener mTaskListener; private DisplayLayout mDisplayLayout; @VisibleForTesting final SizeCompatUIWindowManager mWindowManager; @VisibleForTesting final SizeCompatUIWindowManager mButtonWindowManager; @VisibleForTesting @Nullable SizeCompatUIWindowManager mHintWindowManager; @VisibleForTesting @Nullable SizeCompatRestartButton mButton; @VisibleForTesting @Nullable SizeCompatHintPopup mHint; final int mButtonSize; final int mPopupOffsetX; final int mPopupOffsetY; Loading @@ -79,7 +84,7 @@ class SizeCompatUILayout { mTaskListener = taskListener; mDisplayLayout = displayLayout; mShouldShowHint = !hasShownHint; mWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); mButtonSize = mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size); Loading @@ -87,21 +92,52 @@ class SizeCompatUILayout { mPopupOffsetY = mButtonSize; } /** Creates the button window. */ /** Creates the activity restart button window. */ void createSizeCompatButton(boolean isImeShowing) { if (isImeShowing || mButton != null) { // When ime is showing, wait until ime is dismiss to create UI. return; } mButton = mWindowManager.createSizeCompatUI(); updateSurfacePosition(); mButton = mButtonWindowManager.createSizeCompatButton(); updateButtonSurfacePosition(); if (mShouldShowHint) { // Only show by default for the first time. mShouldShowHint = false; createSizeCompatHint(); } } /** Creates the restart button hint window. */ private void createSizeCompatHint() { if (mHint != null) { // Hint already shown. return; } mHintWindowManager = createHintWindowManager(); mHint = mHintWindowManager.createSizeCompatHint(); updateHintSurfacePosition(); } /** Releases the button window. */ @VisibleForTesting SizeCompatUIWindowManager createHintWindowManager() { return new SizeCompatUIWindowManager(mContext, mTaskConfig, this); } /** Dismisses the hint window. */ void dismissHint() { mHint = null; if (mHintWindowManager != null) { mHintWindowManager.release(); mHintWindowManager = null; } } /** Releases the UI windows. */ void release() { mButton.remove(); dismissHint(); mButton = null; mWindowManager.release(); mButtonWindowManager.release(); } /** Called when size compat info changed. */ Loading @@ -115,7 +151,10 @@ class SizeCompatUILayout { // Update configuration. mContext = mContext.createConfigurationContext(taskConfig); mWindowManager.setConfiguration(taskConfig); mButtonWindowManager.setConfiguration(taskConfig); if (mHintWindowManager != null) { mHintWindowManager.setConfiguration(taskConfig); } if (mButton == null || prevTaskListener != taskListener) { // TaskListener changed, recreate the button for new surface parent. Loading @@ -126,14 +165,19 @@ class SizeCompatUILayout { if (!taskConfig.windowConfiguration.getBounds() .equals(prevTaskConfig.windowConfiguration.getBounds())) { // Reposition the button surface. updateSurfacePosition(); // Reposition the UI surfaces. updateButtonSurfacePosition(); updateHintSurfacePosition(); } if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { // Update layout for RTL. mButton.setLayoutDirection(taskConfig.getLayoutDirection()); updateSurfacePosition(); updateButtonSurfacePosition(); if (mHint != null) { mHint.setLayoutDirection(taskConfig.getLayoutDirection()); updateHintSurfacePosition(); } } } Loading @@ -149,8 +193,9 @@ class SizeCompatUILayout { displayLayout.getStableBounds(curStableBounds); mDisplayLayout = displayLayout; if (!prevStableBounds.equals(curStableBounds)) { // Stable bounds changed, update button surface position. updateSurfacePosition(); // Stable bounds changed, update UI surface positions. updateButtonSurfacePosition(); updateHintSurfacePosition(); } } Loading @@ -162,27 +207,46 @@ class SizeCompatUILayout { return; } // Hide size compat UIs when IME is showing. final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE; if (mButton.getVisibility() != newVisibility) { mButton.setVisibility(newVisibility); } if (mHint != null && mHint.getVisibility() != newVisibility) { mHint.setVisibility(newVisibility); } } /** Gets the layout params for restart button. */ WindowManager.LayoutParams getWindowLayoutParams() { WindowManager.LayoutParams getButtonWindowLayoutParams() { final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( // Cannot be wrap_content as this determines the actual window size mButtonSize, mButtonSize, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); winParams.gravity = getGravity(getLayoutDirection()); winParams.token = new Binder(); winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + mContext.getDisplayId()); winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId()); winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; return winParams; } /** Gets the layout params for hint popup. */ WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) { final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( // Cannot be wrap_content as this determines the actual window size hint.getMeasuredWidth(), hint.getMeasuredHeight(), TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); winParams.token = new Binder(); winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId()); winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; winParams.windowAnimations = android.R.style.Animation_InputMethod; return winParams; } /** Called when it is ready to be placed button surface button. */ /** Called when it is ready to be placed size compat UI surface. */ void attachToParentSurface(SurfaceControl.Builder b) { mTaskListener.attachChildSurfaceToTask(mTaskId, b); } Loading @@ -192,13 +256,17 @@ class SizeCompatUILayout { ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken); } /** Called when the restart button is long clicked. */ void onRestartButtonLongClicked() { createSizeCompatHint(); } @VisibleForTesting void updateSurfacePosition() { if (mButton == null || mWindowManager.getSurfaceControl() == null) { void updateButtonSurfacePosition() { if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) { return; } // The hint popup won't be at the correct position. mButton.dismissHint(); final SurfaceControl leash = mButtonWindowManager.getSurfaceControl(); // Use stable bounds to prevent the button from overlapping with system bars. final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); Loading @@ -212,8 +280,30 @@ class SizeCompatUILayout { : stableBounds.right - taskBounds.left - mButtonSize; final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize; mSyncQueue.runInSync(t -> t.setPosition(mWindowManager.getSurfaceControl(), positionX, positionY)); mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); } void updateHintSurfacePosition() { if (mHint == null || mHintWindowManager == null || mHintWindowManager.getSurfaceControl() == null) { return; } final SurfaceControl leash = mHintWindowManager.getSurfaceControl(); // Use stable bounds to prevent the hint from overlapping with system bars. final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); final Rect stableBounds = new Rect(); mDisplayLayout.getStableBounds(stableBounds); stableBounds.intersect(taskBounds); // Position of the hint in the container coordinate. final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? stableBounds.left - taskBounds.left + mPopupOffsetX : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth(); final int positionY = stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight(); mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); } int getDisplayId() { Loading @@ -227,9 +317,4 @@ class SizeCompatUILayout { private int getLayoutDirection() { return mContext.getResources().getConfiguration().getLayoutDirection(); } static int getGravity(int layoutDirection) { return Gravity.BOTTOM | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END); } }