Merge changes from topic "new-ohm-bg-panel"

* changes:
  (2/n) Implement BackgroundWindowManager to manage tutorial background
  (1/n) Phase out FEATURE_ONE_HANDED_BACKGROUND_PANEL
This commit is contained in:
Jason Chang
2022-01-12 15:33:21 +00:00
committed by Android (Google) Code Review
15 changed files with 383 additions and 489 deletions

View File

@@ -100,14 +100,6 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
*/
public static final int FEATURE_IME_PLACEHOLDER = FEATURE_SYSTEM_FIRST + 7;
/**
* Display area for one handed background layer, which preventing when user
* turning the Dark theme on, they can not clearly identify the screen has entered
* one handed mode.
* @hide
*/
public static final int FEATURE_ONE_HANDED_BACKGROUND_PANEL = FEATURE_SYSTEM_FIRST + 8;
/**
* Display area hosting IME window tokens (@see ImeContainer). By default, IMEs are parented
* to FEATURE_IME_PLACEHOLDER but can be reparented under other RootDisplayArea.
@@ -118,7 +110,7 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
* app on another screen).
* @hide
*/
public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 9;
public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 8;
/**
* The last boundary of display area for system features

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/background_panel_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal | center_vertical"
android:background="@android:color/transparent">
</LinearLayout>

View File

@@ -0,0 +1,246 @@
/*
* Copyright (C) 2022 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.onehanded;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.util.Slog;
import android.view.ContextThemeWrapper;
import android.view.IWindow;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
/**
* Holds view hierarchy of a root surface and helps inflate a themeable view for background.
*/
public final class BackgroundWindowManager extends WindowlessWindowManager {
private static final String TAG = BackgroundWindowManager.class.getSimpleName();
private static final int THEME_COLOR_OFFSET = 10;
private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mTransactionFactory;
private Context mContext;
private Rect mDisplayBounds;
private SurfaceControlViewHost mViewHost;
private SurfaceControl mLeash;
private View mBackgroundView;
private @OneHandedState.State int mCurrentState;
public BackgroundWindowManager(Context context) {
super(context.getResources().getConfiguration(), null /* rootSurface */,
null /* hostInputToken */);
mContext = context;
mTransactionFactory = SurfaceControl.Transaction::new;
}
@Override
public SurfaceControl getSurfaceControl(IWindow window) {
return super.getSurfaceControl(window);
}
@Override
public void setConfiguration(Configuration configuration) {
super.setConfiguration(configuration);
mContext = mContext.createConfigurationContext(configuration);
}
/**
* onConfigurationChanged events for updating background theme color.
*/
public void onConfigurationChanged() {
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
updateThemeOnly();
}
}
/**
* One-handed mode state changed callback
* @param newState of One-handed mode representing by {@link OneHandedState}
*/
public void onStateChanged(int newState) {
mCurrentState = newState;
}
@Override
protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
.setColorLayer()
.setBufferSize(mDisplayBounds.width(), mDisplayBounds.height())
.setFormat(PixelFormat.RGB_888)
.setOpaque(true)
.setName(TAG)
.setCallsite("BackgroundWindowManager#attachToParentSurface");
mLeash = builder.build();
b.setParent(mLeash);
}
/** Inflates background view on to the root surface. */
boolean initView() {
if (mBackgroundView != null || mViewHost != null) {
return false;
}
mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
mBackgroundView = (View) LayoutInflater.from(mContext)
.inflate(R.layout.background_panel, null /* root */);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
mDisplayBounds.width(), mDisplayBounds.height(), 0 /* TYPE NONE */,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
| FLAG_SLIPPERY, PixelFormat.TRANSLUCENT);
lp.token = new Binder();
lp.setTitle("background-panel");
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
mBackgroundView.setBackgroundColor(getThemeColorForBackground());
mViewHost.setView(mBackgroundView, lp);
return true;
}
/**
* Called when onDisplayAdded() or onDisplayRemoved() callback.
* @param displayLayout The latest {@link DisplayLayout} for display bounds.
*/
public void onDisplayChanged(DisplayLayout displayLayout) {
// One-handed mode is only available on portrait.
if (displayLayout.height() > displayLayout.width()) {
mDisplayBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
} else {
mDisplayBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
}
}
private void updateThemeOnly() {
if (mBackgroundView == null || mViewHost == null || mLeash == null) {
Slog.w(TAG, "Background view or SurfaceControl does not exist when trying to "
+ "update theme only!");
return;
}
WindowManager.LayoutParams lp = (WindowManager.LayoutParams)
mBackgroundView.getLayoutParams();
mBackgroundView.setBackgroundColor(getThemeColorForBackground());
mViewHost.setView(mBackgroundView, lp);
}
/**
* Shows the background layer when One-handed mode triggered.
*/
public void showBackgroundLayer() {
if (!initView()) {
updateThemeOnly();
return;
}
if (mLeash == null) {
Slog.w(TAG, "SurfaceControl mLeash is null, can't show One-handed mode "
+ "background panel!");
return;
}
mTransactionFactory.getTransaction()
.setAlpha(mLeash, 1.0f)
.setLayer(mLeash, -1 /* at bottom-most layer */)
.show(mLeash)
.apply();
}
/**
* Remove the leash of background layer after stop One-handed mode.
*/
public void removeBackgroundLayer() {
if (mBackgroundView != null) {
mBackgroundView = null;
}
if (mViewHost != null) {
mViewHost.release();
mViewHost = null;
}
if (mLeash != null) {
mTransactionFactory.getTransaction().remove(mLeash).apply();
mLeash = null;
}
}
/**
* Gets {@link SurfaceControl} of the background layer.
* @return {@code null} if not exist.
*/
@Nullable
SurfaceControl getSurfaceControl() {
return mLeash;
}
private int getThemeColor() {
final Context themedContext = new ContextThemeWrapper(mContext,
com.android.internal.R.style.Theme_DeviceDefault_DayNight);
return themedContext.getColor(R.color.one_handed_tutorial_background_color);
}
int getThemeColorForBackground() {
final int origThemeColor = getThemeColor();
return android.graphics.Color.argb(Color.alpha(origThemeColor),
Color.red(origThemeColor) - THEME_COLOR_OFFSET,
Color.green(origThemeColor) - THEME_COLOR_OFFSET,
Color.blue(origThemeColor) - THEME_COLOR_OFFSET);
}
private float adjustColor(int origColor) {
return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
}
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
pw.print(innerPrefix + "mDisplayBounds=");
pw.println(mDisplayBounds);
pw.print(innerPrefix + "mViewHost=");
pw.println(mViewHost);
pw.print(innerPrefix + "mLeash=");
pw.println(mLeash);
pw.print(innerPrefix + "mBackgroundView=");
pw.println(mBackgroundView);
}
}

View File

@@ -1,272 +0,0 @@
/*
* Copyright (C) 2020 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.onehanded;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.ContextThemeWrapper;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.animation.LinearInterpolator;
import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Manages OneHanded color background layer areas.
* To avoid when turning the Dark theme on, users can not clearly identify
* the screen has entered one handed mode.
*/
public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
implements OneHandedAnimationCallback, OneHandedState.OnStateChangedListener {
private static final String TAG = "OneHandedBackgroundPanelOrganizer";
private static final int THEME_COLOR_OFFSET = 10;
private static final int ALPHA_ANIMATION_DURATION = 200;
private final Context mContext;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mTransactionFactory;
private @OneHandedState.State int mCurrentState;
private ValueAnimator mAlphaAnimator;
private float mTranslationFraction;
private float[] mThemeColor;
/**
* The background to distinguish the boundary of translated windows and empty region when
* one handed mode triggered.
*/
private Rect mBkgBounds;
private Rect mStableInsets;
@Nullable
@VisibleForTesting
SurfaceControl mBackgroundSurface;
@Nullable
private SurfaceControl mParentLeash;
public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
OneHandedSettingsUtil settingsUtil, Executor executor) {
super(executor);
mContext = context;
mTranslationFraction = settingsUtil.getTranslationFraction(context);
mTransactionFactory = SurfaceControl.Transaction::new;
updateThemeColors();
}
@Override
public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
@NonNull SurfaceControl leash) {
mParentLeash = leash;
}
@Override
public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
final List<DisplayAreaAppearedInfo> displayAreaInfos;
displayAreaInfos = super.registerOrganizer(displayAreaFeature);
for (int i = 0; i < displayAreaInfos.size(); i++) {
final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
}
return displayAreaInfos;
}
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
removeBackgroundPanelLayer();
mParentLeash = null;
}
@Override
public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
final int yTopPos = (mStableInsets.top - mBkgBounds.height()) + Math.round(yPos);
tx.setPosition(mBackgroundSurface, 0, yTopPos);
}
@Nullable
@VisibleForTesting
boolean isRegistered() {
return mParentLeash != null;
}
void createBackgroundSurface() {
mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
.setBufferSize(mBkgBounds.width(), mBkgBounds.height())
.setColorLayer()
.setFormat(PixelFormat.RGB_888)
.setOpaque(true)
.setName("one-handed-background-panel")
.setCallsite("OneHandedBackgroundPanelOrganizer")
.build();
// TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
mAlphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
mAlphaAnimator.setInterpolator(new LinearInterpolator());
mAlphaAnimator.setDuration(ALPHA_ANIMATION_DURATION);
mAlphaAnimator.addUpdateListener(
animator -> detachBackgroundFromParent(animator));
}
void detachBackgroundFromParent(ValueAnimator animator) {
if (mBackgroundSurface == null || mParentLeash == null) {
return;
}
// TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
final float currentValue = (float) animator.getAnimatedValue();
final SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
if (currentValue == 0.0f) {
tx.reparent(mBackgroundSurface, null).apply();
} else {
tx.setAlpha(mBackgroundSurface, (float) animator.getAnimatedValue()).apply();
}
}
/**
* Called when onDisplayAdded() or onDisplayRemoved() callback.
*
* @param displayLayout The latest {@link DisplayLayout} representing current displayId
*/
public void onDisplayChanged(DisplayLayout displayLayout) {
mStableInsets = displayLayout.stableInsets();
// Ensure the mBkgBounds is portrait, due to OHM only support on portrait
if (displayLayout.height() > displayLayout.width()) {
mBkgBounds = new Rect(0, 0, displayLayout.width(),
Math.round(displayLayout.height() * mTranslationFraction) + mStableInsets.top);
} else {
mBkgBounds = new Rect(0, 0, displayLayout.height(),
Math.round(displayLayout.width() * mTranslationFraction) + mStableInsets.top);
}
}
@VisibleForTesting
void onStart() {
if (mBackgroundSurface == null) {
createBackgroundSurface();
}
showBackgroundPanelLayer();
}
/**
* Called when transition finished.
*/
public void onStopFinished() {
if (mAlphaAnimator == null) {
return;
}
mAlphaAnimator.start();
}
@VisibleForTesting
void showBackgroundPanelLayer() {
if (mParentLeash == null) {
return;
}
if (mBackgroundSurface == null) {
createBackgroundSurface();
}
// TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
if (mAlphaAnimator.isRunning()) {
mAlphaAnimator.end();
}
mTransactionFactory.getTransaction()
.reparent(mBackgroundSurface, mParentLeash)
.setAlpha(mBackgroundSurface, 1.0f)
.setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
.setColor(mBackgroundSurface, mThemeColor)
.show(mBackgroundSurface)
.apply();
}
@VisibleForTesting
void removeBackgroundPanelLayer() {
if (mBackgroundSurface == null) {
return;
}
mTransactionFactory.getTransaction()
.remove(mBackgroundSurface)
.apply();
mBackgroundSurface = null;
}
/**
* onConfigurationChanged events for updating tutorial text.
*/
public void onConfigurationChanged() {
updateThemeColors();
if (mCurrentState != STATE_ACTIVE) {
return;
}
showBackgroundPanelLayer();
}
private void updateThemeColors() {
final Context themedContext = new ContextThemeWrapper(mContext,
com.android.internal.R.style.Theme_DeviceDefault_DayNight);
final int themeColor = themedContext.getColor(
R.color.one_handed_tutorial_background_color);
mThemeColor = new float[]{
adjustColor(Color.red(themeColor)),
adjustColor(Color.green(themeColor)),
adjustColor(Color.blue(themeColor))};
}
private float adjustColor(int origColor) {
return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
}
@Override
public void onStateChanged(int newState) {
mCurrentState = newState;
}
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
pw.print(innerPrefix + "mBackgroundSurface=");
pw.println(mBackgroundSurface);
pw.print(innerPrefix + "mBkgBounds=");
pw.println(mBkgBounds);
pw.print(innerPrefix + "mThemeColor=");
pw.println(mThemeColor);
pw.print(innerPrefix + "mTranslationFraction=");
pw.println(mTranslationFraction);
}
}

View File

@@ -99,7 +99,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
private OneHandedEventCallback mEventCallback;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
private OneHandedUiEventLogger mOneHandedUiEventLogger;
private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
@@ -163,7 +162,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
public void onStopFinished(Rect bounds) {
mState.setState(STATE_NONE);
notifyShortcutStateChanged(STATE_NONE);
mBackgroundPanelOrganizer.onStopFinished();
}
};
@@ -201,32 +199,28 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
OneHandedState oneHandedState = new OneHandedState();
BackgroundWindowManager backgroundWindowManager = new BackgroundWindowManager(context);
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
settingsUtil, windowManager);
settingsUtil, windowManager, backgroundWindowManager);
OneHandedAnimationController animationController =
new OneHandedAnimationController(context);
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
mainExecutor);
OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
new OneHandedBackgroundPanelOrganizer(context, displayLayout, settingsUtil,
mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayLayout, settingsUtil, animationController, tutorialHandler,
oneHandedBackgroundPanelOrganizer, jankMonitor, mainExecutor);
jankMonitor, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState, jankMonitor,
oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
mainHandler);
return new OneHandedController(context, displayController, organizer, touchHandler,
tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
jankMonitor, oneHandedUiEventsLogger, overlayManager, taskStackListener,
mainExecutor, mainHandler);
}
@VisibleForTesting
OneHandedController(Context context,
DisplayController displayController,
OneHandedBackgroundPanelOrganizer backgroundPanelOrganizer,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
@@ -243,7 +237,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mContext = context;
mOneHandedSettingsUtil = settingsUtil;
mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
mBackgroundPanelOrganizer = backgroundPanelOrganizer;
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
mTouchHandler = touchHandler;
@@ -286,7 +279,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
mState.addSListeners(mBackgroundPanelOrganizer);
mState.addSListeners(mTutorialHandler);
}
@@ -368,7 +360,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
mOneHandedAccessibilityUtil.announcementForScreenReader(
mOneHandedAccessibilityUtil.getOneHandedStartDescription());
mBackgroundPanelOrganizer.onStart();
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
mTimeoutHandler.resetTimer();
mOneHandedUiEventLogger.writeEvent(
@@ -461,7 +452,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
}
mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
mTutorialHandler.onDisplayChanged(newDisplayLayout);
mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
}
private ContentObserver getObserver(Runnable onChangeRunnable) {
@@ -585,7 +575,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
if (!mIsOneHandedEnabled) {
mDisplayAreaOrganizer.unregisterOrganizer();
mBackgroundPanelOrganizer.unregisterOrganizer();
// Do NOT register + unRegister DA in the same call
return;
}
@@ -594,11 +583,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mDisplayAreaOrganizer.registerOrganizer(
OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
}
if (!mBackgroundPanelOrganizer.isRegistered()) {
mBackgroundPanelOrganizer.registerOrganizer(
OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
}
}
@VisibleForTesting
@@ -613,13 +597,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
}
private void onConfigChanged(Configuration newConfig) {
if (mTutorialHandler == null || mBackgroundPanelOrganizer == null) {
if (mTutorialHandler == null) {
return;
}
if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
return;
}
mBackgroundPanelOrganizer.onConfigurationChanged();
mTutorialHandler.onConfigurationChanged();
}
@@ -650,10 +633,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
pw.print(innerPrefix + "mIsSwipeToNotificationEnabled=");
pw.println(mIsSwipeToNotificationEnabled);
if (mBackgroundPanelOrganizer != null) {
mBackgroundPanelOrganizer.dump(pw);
}
if (mDisplayAreaOrganizer != null) {
mDisplayAreaOrganizer.dump(pw);
}

View File

@@ -80,7 +80,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mSurfaceControlTransactionFactory;
private OneHandedTutorialHandler mTutorialHandler;
private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
@VisibleForTesting
OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -135,7 +134,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedSettingsUtil oneHandedSettingsUtil,
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
InteractionJankMonitor jankMonitor,
ShellExecutor mainExecutor) {
super(mainExecutor);
@@ -150,7 +148,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION,
animationDurationConfig);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer;
mTutorialHandler = tutorialHandler;
}
@@ -258,7 +255,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
animator.setTransitionDirection(direction)
.addOneHandedAnimationCallback(mOneHandedAnimationCallback)
.addOneHandedAnimationCallback(mTutorialHandler)
.addOneHandedAnimationCallback(mBackgroundPanelOrganizer)
.setDuration(durationMs)
.start();
}

View File

@@ -32,7 +32,6 @@ import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -65,6 +64,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
private final float mTutorialHeightRatio;
private final WindowManager mWindowManager;
private final BackgroundWindowManager mBackgroundWindowManager;
private @OneHandedState.State int mCurrentState;
private int mTutorialAreaHeight;
@@ -79,9 +79,10 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
private int mAlphaAnimationDurationMs;
public OneHandedTutorialHandler(Context context, OneHandedSettingsUtil settingsUtil,
WindowManager windowManager) {
WindowManager windowManager, BackgroundWindowManager backgroundWindowManager) {
mContext = context;
mWindowManager = windowManager;
mBackgroundWindowManager = backgroundWindowManager;
mTutorialHeightRatio = settingsUtil.getTranslationFraction(context);
mAlphaAnimationDurationMs = settingsUtil.getTransitionDuration(context);
}
@@ -109,9 +110,20 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
mAlphaAnimator.start();
}
@Override
public void onStartFinished(Rect bounds) {
fillBackgroundColor();
}
@Override
public void onStopFinished(Rect bounds) {
removeBackgroundSurface();
}
@Override
public void onStateChanged(int newState) {
mCurrentState = newState;
mBackgroundWindowManager.onStateChanged(newState);
switch (newState) {
case STATE_ENTERING:
createViewAndAttachToWindow(mContext);
@@ -126,7 +138,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
case STATE_NONE:
checkTransitionEnd();
removeTutorialFromWindowManager();
break;
default:
break;
}
@@ -146,6 +157,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
}
mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio);
mAlphaTransitionStart = mTutorialAreaHeight * START_TRANSITION_FRACTION;
mBackgroundWindowManager.onDisplayChanged(displayLayout);
}
@VisibleForTesting
@@ -169,6 +181,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
private void attachTargetToWindow() {
try {
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
mBackgroundWindowManager.showBackgroundLayer();
} catch (IllegalStateException e) {
// This shouldn't happen, but if the target is already added, just update its
// layout params.
@@ -186,6 +199,11 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
mTargetViewContainer = null;
}
@VisibleForTesting
void removeBackgroundSurface() {
mBackgroundWindowManager.removeBackgroundLayer();
}
/**
* Returns layout params for the dismiss target, using the latest display metrics.
*/
@@ -213,9 +231,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
* onConfigurationChanged events for updating tutorial text.
*/
public void onConfigurationChanged() {
mBackgroundWindowManager.onConfigurationChanged();
removeTutorialFromWindowManager();
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
createViewAndAttachToWindow(mContext);
fillBackgroundColor();
updateThemeColor();
checkTransitionEnd();
}
@@ -247,6 +268,14 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
tutorialDesc.setTextColor(themedTextColorSecondary);
}
private void fillBackgroundColor() {
if (mTargetViewContainer == null || mBackgroundWindowManager == null) {
return;
}
mTargetViewContainer.setBackgroundColor(
mBackgroundWindowManager.getThemeColorForBackground());
}
private void setupAlphaTransition(boolean isEntering) {
final float start = isEntering ? 0.0f : 1.0f;
final float end = isEntering ? 1.0f : 0.0f;
@@ -282,5 +311,9 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
pw.println(mAlphaTransitionStart);
pw.print(innerPrefix + "mAlphaAnimationDurationMs=");
pw.println(mAlphaAnimationDurationMs);
if (mBackgroundWindowManager != null) {
mBackgroundWindowManager.dump(pw);
}
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (C) 2022 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.onehanded;
import static com.google.common.truth.Truth.assertThat;
import android.testing.TestableLooper;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/** Tests for {@link BackgroundWindowManager} */
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4.class)
public class BackgroundWindowManagerTest extends ShellTestCase {
private BackgroundWindowManager mBackgroundWindowManager;
@Mock
private DisplayLayout mMockDisplayLayout;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mBackgroundWindowManager = new BackgroundWindowManager(mContext);
mBackgroundWindowManager.onDisplayChanged(mMockDisplayLayout);
}
@Test
@UiThreadTest
public void testInitRelease() {
mBackgroundWindowManager.initView();
assertThat(mBackgroundWindowManager.getSurfaceControl()).isNotNull();
mBackgroundWindowManager.removeBackgroundLayer();
assertThat(mBackgroundWindowManager.getSurfaceControl()).isNull();
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright (C) 2020 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.onehanded;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
private DisplayAreaInfo mDisplayAreaInfo;
private Display mDisplay;
private DisplayLayout mDisplayLayout;
private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
@Mock
IWindowContainerToken mMockRealToken;
@Mock
DisplayController mMockDisplayController;
@Mock
OneHandedSettingsUtil mMockSettingsUtil;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mToken = new WindowContainerToken(mMockRealToken);
mLeash = new SurfaceControl();
mDisplay = mContext.getDisplay();
mDisplayLayout = new DisplayLayout(mContext, mDisplay);
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
FEATURE_ONE_HANDED_BACKGROUND_PANEL);
mSpiedBackgroundPanelOrganizer = spy(
new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, mMockSettingsUtil,
Runnable::run));
mSpiedBackgroundPanelOrganizer.onDisplayChanged(mDisplayLayout);
}
@Test
public void testOnDisplayAreaAppeared() {
mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isTrue();
verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
}
@Test
public void testShowBackgroundLayer() {
mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, null);
mSpiedBackgroundPanelOrganizer.onStart();
verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
}
@Test
public void testRemoveBackgroundLayer() {
mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isNotNull();
reset(mSpiedBackgroundPanelOrganizer);
mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull();
}
@Test
public void testStateNone_onConfigurationChanged() {
mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_NONE);
mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
}
@Test
public void testStateActivate_onConfigurationChanged() {
mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_ACTIVE);
mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
}
}

View File

@@ -73,8 +73,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
DisplayController mMockDisplayController;
@Mock
OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
OneHandedEventCallback mMockEventCallback;
@@ -115,7 +113,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(null);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
mDefaultEnabled);
when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -134,7 +131,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
mMockDisplayController,
mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,

View File

@@ -95,8 +95,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
@Mock
WindowContainerTransaction mMockWindowContainerTransaction;
@Mock
OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
@Mock
ShellExecutor mMockShellMainExecutor;
@Mock
OneHandedSettingsUtil mMockSettingsUitl;
@@ -143,7 +141,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockSettingsUitl,
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
mJankMonitor,
mMockShellMainExecutor));
@@ -431,7 +428,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockSettingsUitl,
mMockAnimationController,
mTutorialHandler,
mMockBackgroundOrganizer,
mJankMonitor,
mMockShellMainExecutor));

View File

@@ -67,8 +67,6 @@ public class OneHandedStateTest extends OneHandedTestCase {
@Mock
DisplayController mMockDisplayController;
@Mock
OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
OneHandedTouchHandler mMockTouchHandler;
@@ -105,7 +103,6 @@ public class OneHandedStateTest extends OneHandedTestCase {
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
mDefaultEnabled);
when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -123,7 +120,6 @@ public class OneHandedStateTest extends OneHandedTestCase {
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
mMockDisplayController,
mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,

View File

@@ -56,6 +56,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
OneHandedSettingsUtil mMockSettingsUtil;
@Mock
WindowManager mMockWindowManager;
@Mock
BackgroundWindowManager mMockBackgroundWindowManager;
@Before
public void setUp() {
@@ -63,10 +65,11 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
when(mMockSettingsUtil.getTutorialShownCounts(any(), anyInt())).thenReturn(0);
mDisplay = mContext.getDisplay();
mDisplayLayout = new DisplayLayout(mContext, mDisplay);
mDisplayLayout = new DisplayLayout(getTestContext().getApplicationContext(), mDisplay);
mSpiedTransitionState = spy(new OneHandedState());
mSpiedTutorialHandler = spy(
new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager));
new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager,
mMockBackgroundWindowManager));
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
}

View File

@@ -24,13 +24,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT;
import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
@@ -134,11 +132,6 @@ public abstract class DisplayAreaPolicy {
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
TYPE_NOTIFICATION_SHADE)
.build())
.addFeature(new Feature.Builder(wmService.mPolicy,
"OneHandedBackgroundPanel",
FEATURE_ONE_HANDED_BACKGROUND_PANEL)
.upTo(TYPE_WALLPAPER)
.build())
.addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
FEATURE_ONE_HANDED)
.all()

View File

@@ -30,7 +30,6 @@ import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER
import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
@@ -196,25 +195,6 @@ public class DisplayAreaPolicyBuilderTest {
}
}
@Test
public void testBuilder_defaultPolicy_hasOneHandedBackgroundFeature() {
final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
resourcesWithProvider(""));
final DisplayAreaPolicyBuilder.Result defaultPolicy =
(DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
mRoot, mImeContainer);
if (mDisplayContent.isDefaultDisplay) {
final List<Feature> features = defaultPolicy.getFeatures();
boolean hasOneHandedBackgroundFeature = false;
for (Feature feature : features) {
hasOneHandedBackgroundFeature |=
feature.getId() == FEATURE_ONE_HANDED_BACKGROUND_PANEL;
}
assertThat(hasOneHandedBackgroundFeature).isTrue();
}
}
@Test
public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() {
final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(