Merge "Exit split if got request split when invisble" into tm-qpr-dev
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f227bbd..c0183ad 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3799,44 +3799,7 @@
}
if (mAdded) {
- profileRendering(hasWindowFocus);
- if (hasWindowFocus) {
- if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
- mFullRedrawNeeded = true;
- try {
- final Rect surfaceInsets = mWindowAttributes.surfaceInsets;
- mAttachInfo.mThreadedRenderer.initializeIfNeeded(
- mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
- } catch (OutOfResourcesException e) {
- Log.e(mTag, "OutOfResourcesException locking surface", e);
- try {
- if (!mWindowSession.outOfMemory(mWindow)) {
- Slog.w(mTag, "No processes killed for memory;"
- + " killing self");
- Process.killProcess(Process.myPid());
- }
- } catch (RemoteException ex) {
- }
- // Retry in a bit.
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_WINDOW_FOCUS_CHANGED), 500);
- return;
- }
- }
- }
-
- mAttachInfo.mHasWindowFocus = hasWindowFocus;
- mImeFocusController.updateImeFocusable(mWindowAttributes, true /* force */);
- mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
-
- if (mView != null) {
- mAttachInfo.mKeyDispatchState.reset();
- mView.dispatchWindowFocusChanged(hasWindowFocus);
- mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
- if (mAttachInfo.mTooltipHost != null) {
- mAttachInfo.mTooltipHost.hideTooltip();
- }
- }
+ dispatchFocusEvent(hasWindowFocus);
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
@@ -3872,6 +3835,45 @@
}
}
+ private void dispatchFocusEvent(boolean hasWindowFocus) {
+ profileRendering(hasWindowFocus);
+ if (hasWindowFocus && mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
+ mFullRedrawNeeded = true;
+ try {
+ final Rect surfaceInsets = mWindowAttributes.surfaceInsets;
+ mAttachInfo.mThreadedRenderer.initializeIfNeeded(
+ mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
+ } catch (OutOfResourcesException e) {
+ Log.e(mTag, "OutOfResourcesException locking surface", e);
+ try {
+ if (!mWindowSession.outOfMemory(mWindow)) {
+ Slog.w(mTag, "No processes killed for memory;"
+ + " killing self");
+ Process.killProcess(Process.myPid());
+ }
+ } catch (RemoteException ex) {
+ }
+ // Retry in a bit.
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_WINDOW_FOCUS_CHANGED), 500);
+ return;
+ }
+ }
+
+ mAttachInfo.mHasWindowFocus = hasWindowFocus;
+ mImeFocusController.updateImeFocusable(mWindowAttributes, true /* force */);
+ mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
+
+ if (mView != null) {
+ mAttachInfo.mKeyDispatchState.reset();
+ mView.dispatchWindowFocusChanged(hasWindowFocus);
+ mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
+ if (mAttachInfo.mTooltipHost != null) {
+ mAttachInfo.mTooltipHost.hideTooltip();
+ }
+ }
+ }
+
private void handleWindowTouchModeChanged() {
final boolean inTouchMode;
synchronized (this) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 18c29d1..bbbb79c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5300,6 +5300,10 @@
<!-- Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. -->
<bool name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled">false</bool>
+ <!-- Whether the specific behaviour for translucent activities letterboxing is enabled.
+ TODO(b/255532890) Enable when ignoreOrientationRequest is set -->
+ <bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool>
+
<!-- Whether a camera compat controller is enabled to allow the user to apply or revert
treatment for stretched issues in camera viewfinder. -->
<bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b925921..b0f6ae6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4401,6 +4401,9 @@
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
+ <!-- Set to true to enable letterboxing on translucent activities. -->
+ <java-symbol type="bool" name="config_letterboxIsEnabledForTranslucentActivities" />
+
<java-symbol type="string" name="config_overrideComponentUiPackage" />
<java-symbol type="string" name="notification_channel_network_status" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index e64b586..8497ff0 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -27,6 +27,7 @@
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
+ android:paddingTop="@dimen/keyguard_lock_padding"
android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
from this view when bouncer is shown -->
diff --git a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml b/packages/SystemUI/res/drawable/controls_panel_background.xml
similarity index 82%
rename from packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
rename to packages/SystemUI/res/drawable/controls_panel_background.xml
index 1992c77..9092877 100644
--- a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
+++ b/packages/SystemUI/res/drawable/controls_panel_background.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
@@ -12,9 +13,10 @@
~ 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.
+ ~
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/dream_overlay_aqi_unknown" />
- <corners android:radius="@dimen/dream_aqi_badge_corner_radius" />
+ <solid android:color="#1F1F1F" />
+ <corners android:radius="@dimen/notification_corner_radius" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index 9efad22..ee3adba 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -90,7 +90,7 @@
android:layout_weight="1"
android:layout_marginLeft="@dimen/global_actions_side_margin"
android:layout_marginRight="@dimen/global_actions_side_margin"
- android:background="#ff0000"
+ android:background="@drawable/controls_panel_background"
android:padding="@dimen/global_actions_side_margin"
android:visibility="gone"
/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml b/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml
deleted file mode 100644
index fcebb8d..0000000
--- a/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- ~ 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.
- -->
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/aqi_view"
- style="@style/clock_subtitle"
- android:visibility="gone"
- android:background="@drawable/dream_aqi_badge_bg"
- android:paddingHorizontal="@dimen/dream_aqi_badge_padding_horizontal"
- android:paddingVertical="@dimen/dream_aqi_badge_padding_vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
deleted file mode 100644
index efbdd1a..0000000
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
--->
-<TextClock
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/date_view"
- style="@style/clock_subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:format12Hour="@string/dream_date_complication_date_format"
- android:format24Hour="@string/dream_date_complication_date_format"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
deleted file mode 100644
index f05922f..0000000
--- a/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.
--->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/weather_view"
- style="@style/clock_subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml b/packages/SystemUI/res/values-h411dp/dimens.xml
similarity index 74%
copy from packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
copy to packages/SystemUI/res/values-h411dp/dimens.xml
index 1992c77..6b21353 100644
--- a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
+++ b/packages/SystemUI/res/values-h411dp/dimens.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
@@ -13,8 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/dream_overlay_aqi_unknown" />
- <corners android:radius="@dimen/dream_aqi_badge_corner_radius" />
-</shape>
\ No newline at end of file
+<resources>
+ <dimen name="volume_row_slider_height">137dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/res/values-h700dp/dimens.xml
index 055308f..39777ab 100644
--- a/packages/SystemUI/res/values-h700dp/dimens.xml
+++ b/packages/SystemUI/res/values-h700dp/dimens.xml
@@ -17,4 +17,5 @@
<resources>
<!-- Margin above the ambient indication container -->
<dimen name="ambient_indication_container_margin_top">15dp</dimen>
-</resources>
\ No newline at end of file
+ <dimen name="volume_row_slider_height">177dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml b/packages/SystemUI/res/values-h841dp/dimens.xml
similarity index 74%
copy from packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
copy to packages/SystemUI/res/values-h841dp/dimens.xml
index 1992c77..412da19 100644
--- a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
+++ b/packages/SystemUI/res/values-h841dp/dimens.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
@@ -13,8 +14,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/dream_overlay_aqi_unknown" />
- <corners android:radius="@dimen/dream_aqi_badge_corner_radius" />
-</shape>
\ No newline at end of file
+<resources>
+ <dimen name="volume_row_slider_height">237dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index bc88bee..ca4217f 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -236,15 +236,6 @@
<color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
- <!-- Air Quality -->
- <color name="dream_overlay_aqi_good">#689F38</color>
- <color name="dream_overlay_aqi_moderate">#FBC02D</color>
- <color name="dream_overlay_aqi_unhealthy_sensitive">#F57C00</color>
- <color name="dream_overlay_aqi_unhealthy">#C53929</color>
- <color name="dream_overlay_aqi_very_unhealthy">#AD1457</color>
- <color name="dream_overlay_aqi_hazardous">#880E4F</color>
- <color name="dream_overlay_aqi_unknown">#BDC1C6</color>
-
<!-- Dream overlay text shadows -->
<color name="dream_overlay_clock_key_text_shadow_color">#4D000000</color>
<color name="dream_overlay_clock_ambient_text_shadow_color">#4D000000</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f3d2638..c10e7528 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1560,10 +1560,6 @@
<dimen name="dream_overlay_y_offset">80dp</dimen>
<dimen name="dream_overlay_exit_y_offset">40dp</dimen>
- <dimen name="dream_aqi_badge_corner_radius">28dp</dimen>
- <dimen name="dream_aqi_badge_padding_vertical">6dp</dimen>
- <dimen name="dream_aqi_badge_padding_horizontal">16dp</dimen>
-
<dimen name="status_view_margin_horizontal">0dp</dimen>
<!-- Media output broadcast dialog QR code picture size -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 084a0e0..643f831 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2688,9 +2688,6 @@
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is unknown -->
<string name="bt_le_audio_broadcast_dialog_unknown_name">Unknown</string>
- <!-- Date format for the Dream Date Complication [CHAR LIMIT=NONE] -->
- <string name="dream_date_complication_date_format">EEE, MMM d</string>
-
<!-- Time format for the Dream Time Complication for 12-hour time format [CHAR LIMIT=NONE] -->
<string name="dream_time_complication_12_hr_time_format">h:mm</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 860c8e3..7da27b1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -260,7 +260,8 @@
if (reason != PROMPT_REASON_NONE) {
int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
if (promtReasonStringRes != 0) {
- mMessageAreaController.setMessage(promtReasonStringRes);
+ mMessageAreaController.setMessage(
+ mView.getResources().getString(promtReasonStringRes), false);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 2e9ad58..d1c9a30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -142,8 +142,11 @@
}
public void startAppearAnimation() {
- if (TextUtils.isEmpty(mMessageAreaController.getMessage())) {
- mMessageAreaController.setMessage(getInitialMessageResId());
+ if (TextUtils.isEmpty(mMessageAreaController.getMessage())
+ && getInitialMessageResId() != 0) {
+ mMessageAreaController.setMessage(
+ mView.getResources().getString(getInitialMessageResId()),
+ /* animate= */ false);
}
mView.startAppearAnimation();
}
@@ -163,9 +166,7 @@
}
/** Determines the message to show in the bouncer when it first appears. */
- protected int getInitialMessageResId() {
- return 0;
- }
+ protected abstract int getInitialMessageResId();
/** Factory for a {@link KeyguardInputViewController}. */
public static class Factory {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 5d86ccd..67e3400 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -52,6 +52,7 @@
private int mYTransOffset;
private View mBouncerMessageView;
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
+ public static final long ANIMATION_DURATION = 650;
public KeyguardPINView(Context context) {
this(context, null);
@@ -181,7 +182,7 @@
if (mAppearAnimator.isRunning()) {
mAppearAnimator.cancel();
}
- mAppearAnimator.setDuration(650);
+ mAppearAnimator.setDuration(ANIMATION_DURATION);
mAppearAnimator.addUpdateListener(animation -> animate(animation.getAnimatedFraction()));
mAppearAnimator.start();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index f7423ed..8011efd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -139,4 +139,9 @@
super.startErrorAnimation();
mView.startErrorAnimation();
}
+
+ @Override
+ protected int getInitialMessageResId() {
+ return R.string.keyguard_enter_your_pin;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index f51ac32..35b2db2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -74,9 +74,4 @@
return mView.startDisappearAnimation(
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
}
-
- @Override
- protected int getInitialMessageResId() {
- return R.string.keyguard_enter_your_pin;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 8f3484a..5d7a6f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -36,8 +36,11 @@
import static java.lang.Integer.max;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
@@ -967,11 +970,23 @@
}
mUserSwitcherViewGroup.setAlpha(0f);
- ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
- 1f);
- alphaAnim.setInterpolator(Interpolators.ALPHA_IN);
- alphaAnim.setDuration(500);
- alphaAnim.start();
+ ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+ int yTrans = mView.getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
+ animator.setInterpolator(Interpolators.STANDARD_DECELERATE);
+ animator.setDuration(650);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mUserSwitcherViewGroup.setAlpha(1f);
+ mUserSwitcherViewGroup.setTranslationY(0f);
+ }
+ });
+ animator.addUpdateListener(animation -> {
+ float value = (float) animation.getAnimatedValue();
+ mUserSwitcherViewGroup.setAlpha(value);
+ mUserSwitcherViewGroup.setTranslationY(yTrans - yTrans * value);
+ });
+ animator.start();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index a5c8c78..39b567f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -156,5 +156,10 @@
@Override
public void onStartingToHide() {
}
+
+ @Override
+ protected int getInitialMessageResId() {
+ return 0;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 71d5bf5..ec4b780 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1660,7 +1660,7 @@
@Override
public void onAuthenticationFailed() {
- requestActiveUnlock(
+ requestActiveUnlockDismissKeyguard(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
"fingerprintFailure");
handleFingerprintAuthFailed();
@@ -2591,6 +2591,18 @@
}
/**
+ * Attempts to trigger active unlock from trust agent with a request to dismiss the keyguard.
+ */
+ public void requestActiveUnlockDismissKeyguard(
+ @NonNull ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN requestOrigin,
+ String extraReason
+ ) {
+ requestActiveUnlock(
+ requestOrigin,
+ extraReason + "-dismissKeyguard", true);
+ }
+
+ /**
* Whether the UDFPS bouncer is showing.
*/
public void setUdfpsBouncerShowing(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
index 4aa597e..8d0edf8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
@@ -50,7 +50,12 @@
* Setup an activity to handle enter/exit animations. [view] should be the root of the content.
* Fade and translate together.
*/
- fun observerForAnimations(view: ViewGroup, window: Window, intent: Intent): LifecycleObserver {
+ fun observerForAnimations(
+ view: ViewGroup,
+ window: Window,
+ intent: Intent,
+ animateY: Boolean = true
+ ): LifecycleObserver {
return object : LifecycleObserver {
var showAnimation = intent.getBooleanExtra(ControlsUiController.EXTRA_ANIMATE, false)
@@ -61,8 +66,12 @@
view.transitionAlpha = 0.0f
if (translationY == -1f) {
- translationY = view.context.resources.getDimensionPixelSize(
- R.dimen.global_actions_controls_y_translation).toFloat()
+ if (animateY) {
+ translationY = view.context.resources.getDimensionPixelSize(
+ R.dimen.global_actions_controls_y_translation).toFloat()
+ } else {
+ translationY = 0f
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 5d611c4..d8d8c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -70,7 +70,8 @@
ControlsAnimations.observerForAnimations(
requireViewById<ViewGroup>(R.id.control_detail_root),
window,
- intent
+ intent,
+ !featureFlags.isEnabled(Flags.USE_APP_PANELS)
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index fb678aa..1e3e5cd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -186,7 +186,7 @@
val allStructures = controlsController.get().getFavorites()
val selected = getPreferredSelectedItem(allStructures)
val anyPanels = controlsListingController.get().getCurrentServices()
- .none { it.panelActivity != null }
+ .any { it.panelActivity != null }
return if (controlsController.get().addSeedingFavoritesCallback(onSeedingComplete)) {
ControlsActivity::class.java
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
index 7143be2..f5764c2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
@@ -24,6 +24,10 @@
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RoundRectShape
+import com.android.systemui.R
import com.android.systemui.util.boundsOnScreen
import com.android.wm.shell.TaskView
import java.util.concurrent.Executor
@@ -64,6 +68,16 @@
options.taskAlwaysOnTop = true
taskView.post {
+ val roundedCorner =
+ activityContext.resources.getDimensionPixelSize(
+ R.dimen.notification_corner_radius
+ )
+ val radii = FloatArray(8) { roundedCorner.toFloat() }
+ taskView.background =
+ ShapeDrawable(RoundRectShape(radii, null, null)).apply {
+ setTint(Color.TRANSPARENT)
+ }
+ taskView.clipToOutline = true
taskView.startActivity(
pendingIntent,
fillInIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index ee00512..1065b94 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -136,8 +136,15 @@
final boolean hasFavorites = mControlsComponent.getControlsController()
.map(c -> !c.getFavorites().isEmpty())
.orElse(false);
+ boolean hasPanels = false;
+ for (int i = 0; i < controlsServices.size(); i++) {
+ if (controlsServices.get(i).getPanelActivity() != null) {
+ hasPanels = true;
+ break;
+ }
+ }
final ControlsComponent.Visibility visibility = mControlsComponent.getVisibility();
- return hasFavorites && visibility != UNAVAILABLE;
+ return (hasFavorites || hasPanels) && visibility != UNAVAILABLE;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 2558fab..394426d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -130,6 +130,7 @@
state(
isFeatureEnabled = component.isEnabled(),
hasFavorites = favorites?.isNotEmpty() == true,
+ hasPanels = serviceInfos.any { it.panelActivity != null },
hasServiceInfos = serviceInfos.isNotEmpty(),
iconResourceId = component.getTileImageId(),
visibility = component.getVisibility(),
@@ -148,13 +149,14 @@
private fun state(
isFeatureEnabled: Boolean,
hasFavorites: Boolean,
+ hasPanels: Boolean,
hasServiceInfos: Boolean,
visibility: ControlsComponent.Visibility,
@DrawableRes iconResourceId: Int?,
): KeyguardQuickAffordanceConfig.LockScreenState {
return if (
isFeatureEnabled &&
- hasFavorites &&
+ (hasFavorites || hasPanels) &&
hasServiceInfos &&
iconResourceId != null &&
visibility == ControlsComponent.Visibility.AVAILABLE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index ee3b130..1ed18c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -363,6 +363,9 @@
if (!isChecked && shouldShowMobileDialog()) {
showTurnOffMobileDialog();
} else if (!shouldShowMobileDialog()) {
+ if (mInternetDialogController.isMobileDataEnabled() == isChecked) {
+ return;
+ }
mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
isChecked, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e33248c..bfc810b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2325,7 +2325,7 @@
private boolean handleQsTouch(MotionEvent event) {
- if (mSplitShadeEnabled && touchXOutsideOfQs(event.getX())) {
+ if (isSplitShadeAndTouchXOutsideQs(event.getX())) {
return false;
}
final int action = event.getActionMasked();
@@ -2382,12 +2382,14 @@
return false;
}
- private boolean touchXOutsideOfQs(float touchX) {
- return touchX < mQsFrame.getX() || touchX > mQsFrame.getX() + mQsFrame.getWidth();
+ /** Returns whether split shade is enabled and an x coordinate is outside of the QS frame. */
+ private boolean isSplitShadeAndTouchXOutsideQs(float touchX) {
+ return mSplitShadeEnabled && (touchX < mQsFrame.getX()
+ || touchX > mQsFrame.getX() + mQsFrame.getWidth());
}
private boolean isInQsArea(float x, float y) {
- if (touchXOutsideOfQs(x)) {
+ if (isSplitShadeAndTouchXOutsideQs(x)) {
return false;
}
// Let's reject anything at the very bottom around the home handle in gesture nav
@@ -4720,6 +4722,7 @@
if (!openingWithTouch || !mHasVibratedOnOpen) {
mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
mHasVibratedOnOpen = true;
+ mShadeLog.v("Vibrating on opening, mHasVibratedOnOpen=true");
}
}
}
@@ -5316,7 +5319,7 @@
@Override
public void flingTopOverscroll(float velocity, boolean open) {
// in split shade mode we want to expand/collapse QS only when touch happens within QS
- if (mSplitShadeEnabled && touchXOutsideOfQs(mInitialTouchX)) {
+ if (isSplitShadeAndTouchXOutsideQs(mInitialTouchX)) {
return;
}
mLastOverscroll = 0f;
@@ -6120,6 +6123,7 @@
if (isFullyCollapsed()) {
// If panel is fully collapsed, reset haptic effect before adding movement.
mHasVibratedOnOpen = false;
+ mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
}
addMovement(event);
if (!isFullyCollapsed()) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 0b59af3..5fedbeb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -140,6 +140,15 @@
})
}
+ fun logHasVibrated(hasVibratedOnOpen: Boolean, fraction: Float) {
+ log(LogLevel.VERBOSE, {
+ bool1 = hasVibratedOnOpen
+ double1 = fraction.toDouble()
+ }, {
+ "hasVibratedOnOpen=$bool1, expansionFraction=$double1"
+ })
+ }
+
fun logQsExpansionChanged(
message: String,
qsExpanded: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 5564311..5e98f54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -161,7 +161,6 @@
private KeyguardViewController mKeyguardViewController;
private DozeScrimController mDozeScrimController;
private KeyguardViewMediator mKeyguardViewMediator;
- private ScrimController mScrimController;
private PendingAuthenticated mPendingAuthenticated = null;
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
@@ -261,7 +260,7 @@
@Inject
public BiometricUnlockController(
DozeScrimController dozeScrimController,
- KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
+ KeyguardViewMediator keyguardViewMediator,
ShadeController shadeController,
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController, Handler handler,
@@ -293,7 +292,6 @@
mNotificationShadeWindowController = notificationShadeWindowController;
mDozeScrimController = dozeScrimController;
mKeyguardViewMediator = keyguardViewMediator;
- mScrimController = scrimController;
mKeyguardStateController = keyguardStateController;
mHandler = handler;
mConsecutiveFpFailureThreshold = resources.getInteger(
@@ -375,12 +373,6 @@
Trace.endSection();
}
- private boolean pulsingOrAod() {
- final ScrimState scrimState = mScrimController.getState();
- return scrimState == ScrimState.AOD
- || scrimState == ScrimState.PULSING;
- }
-
@Override
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
@@ -425,7 +417,7 @@
boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
mMode = mode;
mHasScreenTurnedOnSinceAuthenticating = false;
- if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
+ if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
// If we are waking the device up while we are pulsing the clock and the
// notifications would light up first, creating an unpleasant animation.
// Defer changing the screen brightness by forcing doze brightness on our window
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 8bbaf3d..1059543 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -19,6 +19,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -87,6 +88,7 @@
when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
+ when(mAbsKeyInputView.getResources()).thenReturn(getContext().getResources());
mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
@@ -99,6 +101,11 @@
public void onResume(int reason) {
super.onResume(reason);
}
+
+ @Override
+ protected int getInitialMessageResId() {
+ return 0;
+ }
};
mKeyguardAbsKeyInputViewController.init();
reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
@@ -125,4 +132,22 @@
verifyZeroInteractions(mKeyguardSecurityCallback);
verifyZeroInteractions(mKeyguardMessageAreaController);
}
+
+ @Test
+ public void onPromptReasonNone_doesNotSetMessage() {
+ mKeyguardAbsKeyInputViewController.showPromptReason(0);
+ verify(mKeyguardMessageAreaController, never()).setMessage(
+ getContext().getResources().getString(R.string.kg_prompt_reason_restart_password),
+ false);
+ }
+
+ @Test
+ public void onPromptReason_setsMessage() {
+ when(mAbsKeyInputView.getPromptReasonStringRes(1)).thenReturn(
+ R.string.kg_prompt_reason_restart_password);
+ mKeyguardAbsKeyInputViewController.showPromptReason(1);
+ verify(mKeyguardMessageAreaController).setMessage(
+ getContext().getResources().getString(R.string.kg_prompt_reason_restart_password),
+ false);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index d20be56..d912793 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -30,64 +30,54 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class KeyguardPasswordViewControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var keyguardPasswordView: KeyguardPasswordView
- @Mock
- private lateinit var passwordEntry: EditText
- @Mock
- lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock
- lateinit var securityMode: KeyguardSecurityModel.SecurityMode
- @Mock
- lateinit var lockPatternUtils: LockPatternUtils
- @Mock
- lateinit var keyguardSecurityCallback: KeyguardSecurityCallback
- @Mock
- lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory
- @Mock
- lateinit var latencyTracker: LatencyTracker
- @Mock
- lateinit var inputMethodManager: InputMethodManager
- @Mock
- lateinit var emergencyButtonController: EmergencyButtonController
- @Mock
- lateinit var mainExecutor: DelayableExecutor
- @Mock
- lateinit var falsingCollector: FalsingCollector
- @Mock
- lateinit var keyguardViewController: KeyguardViewController
- @Mock
- private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
- @Mock
- private lateinit var mKeyguardMessageAreaController:
- KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
+ @Mock private lateinit var passwordEntry: EditText
+ @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode
+ @Mock lateinit var lockPatternUtils: LockPatternUtils
+ @Mock lateinit var keyguardSecurityCallback: KeyguardSecurityCallback
+ @Mock lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory
+ @Mock lateinit var latencyTracker: LatencyTracker
+ @Mock lateinit var inputMethodManager: InputMethodManager
+ @Mock lateinit var emergencyButtonController: EmergencyButtonController
+ @Mock lateinit var mainExecutor: DelayableExecutor
+ @Mock lateinit var falsingCollector: FalsingCollector
+ @Mock lateinit var keyguardViewController: KeyguardViewController
+ @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
+ @Mock
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
- private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
+ private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- Mockito.`when`(
- keyguardPasswordView
- .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area)
- ).thenReturn(mKeyguardMessageArea)
- Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea))
- .thenReturn(mKeyguardMessageAreaController)
- Mockito.`when`(keyguardPasswordView.passwordTextViewId).thenReturn(R.id.passwordEntry)
- Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry)
- ).thenReturn(passwordEntry)
- keyguardPasswordViewController = KeyguardPasswordViewController(
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ Mockito.`when`(
+ keyguardPasswordView.requireViewById<BouncerKeyguardMessageArea>(
+ R.id.bouncer_message_area))
+ .thenReturn(mKeyguardMessageArea)
+ Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea))
+ .thenReturn(mKeyguardMessageAreaController)
+ Mockito.`when`(keyguardPasswordView.passwordTextViewId).thenReturn(R.id.passwordEntry)
+ Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry))
+ .thenReturn(passwordEntry)
+ `when`(keyguardPasswordView.resources).thenReturn(context.resources)
+ keyguardPasswordViewController =
+ KeyguardPasswordViewController(
keyguardPasswordView,
keyguardUpdateMonitor,
securityMode,
@@ -100,51 +90,48 @@
mainExecutor,
mContext.resources,
falsingCollector,
- keyguardViewController
- )
- }
+ keyguardViewController)
+ }
- @Test
- fun testFocusWhenBouncerIsShown() {
- Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(true)
- Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true)
- keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
- keyguardPasswordView.post {
- verify(keyguardPasswordView).requestFocus()
- verify(keyguardPasswordView).showKeyboard()
- }
+ @Test
+ fun testFocusWhenBouncerIsShown() {
+ Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(true)
+ Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true)
+ keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
+ keyguardPasswordView.post {
+ verify(keyguardPasswordView).requestFocus()
+ verify(keyguardPasswordView).showKeyboard()
}
+ }
- @Test
- fun testDoNotFocusWhenBouncerIsHidden() {
- Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(false)
- Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true)
- keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
- verify(keyguardPasswordView, never()).requestFocus()
- }
+ @Test
+ fun testDoNotFocusWhenBouncerIsHidden() {
+ Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(false)
+ Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true)
+ keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
+ verify(keyguardPasswordView, never()).requestFocus()
+ }
- @Test
- fun testHideKeyboardWhenOnPause() {
- keyguardPasswordViewController.onPause()
- keyguardPasswordView.post {
- verify(keyguardPasswordView).clearFocus()
- verify(keyguardPasswordView).hideKeyboard()
- }
+ @Test
+ fun testHideKeyboardWhenOnPause() {
+ keyguardPasswordViewController.onPause()
+ keyguardPasswordView.post {
+ verify(keyguardPasswordView).clearFocus()
+ verify(keyguardPasswordView).hideKeyboard()
}
+ }
- @Test
- fun startAppearAnimation() {
- keyguardPasswordViewController.startAppearAnimation()
- verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password)
- }
+ @Test
+ fun startAppearAnimation() {
+ keyguardPasswordViewController.startAppearAnimation()
+ verify(mKeyguardMessageAreaController)
+ .setMessage(context.resources.getString(R.string.keyguard_enter_your_password), false)
+ }
- @Test
- fun startAppearAnimation_withExistingMessage() {
- `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
- keyguardPasswordViewController.startAppearAnimation()
- verify(
- mKeyguardMessageAreaController,
- never()
- ).setMessage(R.string.keyguard_enter_your_password)
- }
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ keyguardPasswordViewController.startAppearAnimation()
+ verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index b3d1c8f..85dbdb8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -30,97 +30,93 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.never
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class KeyguardPatternViewControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var mKeyguardPatternView: KeyguardPatternView
+ @Mock private lateinit var mKeyguardPatternView: KeyguardPatternView
- @Mock
- private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock
- private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
+ @Mock private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
- @Mock
- private lateinit var mLockPatternUtils: LockPatternUtils
+ @Mock private lateinit var mLockPatternUtils: LockPatternUtils
- @Mock
- private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+ @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
- @Mock
- private lateinit var mLatencyTracker: LatencyTracker
- private var mFalsingCollector: FalsingCollector = FalsingCollectorFake()
+ @Mock private lateinit var mLatencyTracker: LatencyTracker
+ private var mFalsingCollector: FalsingCollector = FalsingCollectorFake()
- @Mock
- private lateinit var mEmergencyButtonController: EmergencyButtonController
+ @Mock private lateinit var mEmergencyButtonController: EmergencyButtonController
- @Mock
- private lateinit
- var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+ @Mock
+ private lateinit var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
- @Mock
- private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
+ @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
- @Mock
- private lateinit var mKeyguardMessageAreaController:
- KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+ @Mock
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
- @Mock
- private lateinit var mLockPatternView: LockPatternView
+ @Mock private lateinit var mLockPatternView: LockPatternView
- @Mock
- private lateinit var mPostureController: DevicePostureController
+ @Mock private lateinit var mPostureController: DevicePostureController
- private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
+ private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
- `when`(mKeyguardPatternView
- .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area))
- .thenReturn(mKeyguardMessageArea)
- `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
- .thenReturn(mLockPatternView)
- `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
- .thenReturn(mKeyguardMessageAreaController)
- mKeyguardPatternViewController = KeyguardPatternViewController(
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
+ `when`(
+ mKeyguardPatternView.requireViewById<BouncerKeyguardMessageArea>(
+ R.id.bouncer_message_area))
+ .thenReturn(mKeyguardMessageArea)
+ `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
+ .thenReturn(mLockPatternView)
+ `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
+ .thenReturn(mKeyguardMessageAreaController)
+ `when`(mKeyguardPatternView.resources).thenReturn(context.resources)
+ mKeyguardPatternViewController =
+ KeyguardPatternViewController(
mKeyguardPatternView,
- mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
- mLatencyTracker, mFalsingCollector, mEmergencyButtonController,
- mKeyguardMessageAreaControllerFactory, mPostureController
- )
- }
+ mKeyguardUpdateMonitor,
+ mSecurityMode,
+ mLockPatternUtils,
+ mKeyguardSecurityCallback,
+ mLatencyTracker,
+ mFalsingCollector,
+ mEmergencyButtonController,
+ mKeyguardMessageAreaControllerFactory,
+ mPostureController)
+ }
- @Test
- fun onPause_resetsText() {
- mKeyguardPatternViewController.init()
- mKeyguardPatternViewController.onPause()
- verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern)
- }
+ @Test
+ fun onPause_resetsText() {
+ mKeyguardPatternViewController.init()
+ mKeyguardPatternViewController.onPause()
+ verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern)
+ }
+ @Test
+ fun startAppearAnimation() {
+ mKeyguardPatternViewController.startAppearAnimation()
+ verify(mKeyguardMessageAreaController)
+ .setMessage(context.resources.getString(R.string.keyguard_enter_your_pattern), false)
+ }
- @Test
- fun startAppearAnimation() {
- mKeyguardPatternViewController.startAppearAnimation()
- verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern)
- }
-
- @Test
- fun startAppearAnimation_withExistingMessage() {
- `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
- mKeyguardPatternViewController.startAppearAnimation()
- verify(
- mKeyguardMessageAreaController,
- never()
- ).setMessage(R.string.keyguard_enter_your_password)
- }
+ @Test
+ fun startAppearAnimation_withExistingMessage() {
+ `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+ mKeyguardPatternViewController.startAppearAnimation()
+ verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index ce1101f..b742100 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -113,4 +115,9 @@
mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
verify(mPasswordEntry).requestFocus();
}
+
+ @Test
+ public void testGetInitialMessageResId() {
+ assertThat(mKeyguardPinViewController.getInitialMessageResId()).isNotEqualTo(0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 8bcfe6f..cdb7bbb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -31,10 +31,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -79,6 +82,7 @@
keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
)
.thenReturn(keyguardMessageAreaController)
+ `when`(keyguardPinView.resources).thenReturn(context.resources)
pinViewController =
KeyguardPinViewController(
keyguardPinView,
@@ -98,14 +102,14 @@
@Test
fun startAppearAnimation() {
pinViewController.startAppearAnimation()
- verify(keyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pin)
+ verify(keyguardMessageAreaController)
+ .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
}
@Test
fun startAppearAnimation_withExistingMessage() {
Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")
pinViewController.startAppearAnimation()
- verify(keyguardMessageAreaController, Mockito.never())
- .setMessage(R.string.keyguard_enter_your_password)
+ verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 40542d2..849ff08 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2084,6 +2084,96 @@
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
+ @Test
+ public void fingerprintFailure_requestActiveUnlock_dismissKeyguard()
+ throws RemoteException {
+ // GIVEN shouldTriggerActiveUnlock
+ bouncerFullyVisible();
+ when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+ // GIVEN active unlock triggers on biometric failures
+ when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+ .thenReturn(true);
+
+ // WHEN fingerprint fails
+ mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback.onAuthenticationFailed();
+
+ // ALWAYS request unlock with a keyguard dismissal
+ verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq(true));
+ }
+
+ @Test
+ public void faceNonBypassFailure_requestActiveUnlock_doesNotDismissKeyguard()
+ throws RemoteException {
+ // GIVEN shouldTriggerActiveUnlock
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+ keyguardIsVisible();
+ keyguardNotGoingAway();
+ statusBarShadeIsNotLocked();
+ when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+ // GIVEN active unlock triggers on biometric failures
+ when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+ .thenReturn(true);
+
+ // WHEN face fails & bypass is not allowed
+ lockscreenBypassIsNotAllowed();
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
+
+ // THEN request unlock with NO keyguard dismissal
+ verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq(false));
+ }
+
+ @Test
+ public void faceBypassFailure_requestActiveUnlock_dismissKeyguard()
+ throws RemoteException {
+ // GIVEN shouldTriggerActiveUnlock
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+ keyguardIsVisible();
+ keyguardNotGoingAway();
+ statusBarShadeIsNotLocked();
+ when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+ // GIVEN active unlock triggers on biometric failures
+ when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+ .thenReturn(true);
+
+ // WHEN face fails & bypass is not allowed
+ lockscreenBypassIsAllowed();
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
+
+ // THEN request unlock with a keyguard dismissal
+ verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq(true));
+ }
+
+ @Test
+ public void faceNonBypassFailure_requestActiveUnlock_dismissKeyguard()
+ throws RemoteException {
+ // GIVEN shouldTriggerActiveUnlock
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+ lockscreenBypassIsNotAllowed();
+ when(mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())).thenReturn(true);
+
+ // GIVEN active unlock triggers on biometric failures
+ when(mActiveUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+ ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
+ .thenReturn(true);
+
+ // WHEN face fails & on the bouncer
+ bouncerFullyVisible();
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback.onAuthenticationFailed();
+
+ // THEN request unlock with a keyguard dismissal
+ verify(mTrustManager).reportUserRequestedUnlock(eq(KeyguardUpdateMonitor.getCurrentUser()),
+ eq(true));
+ }
+
private void userDeviceLockDown() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
@@ -2101,6 +2191,9 @@
}
private void mockCanBypassLockscreen(boolean canBypass) {
+ // force update the isFaceEnrolled cache:
+ mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(getCurrentUser());
+
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 779788a..d172c9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.management.ControlsProviderSelectorActivity
import com.android.systemui.controls.settings.FakeControlsSettingsRepository
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
@@ -53,6 +54,7 @@
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.android.wm.shell.TaskView
import com.android.wm.shell.TaskViewFactory
@@ -322,6 +324,45 @@
.isFalse()
}
+ @Test
+ fun testResolveActivityWhileSeeding_ControlsActivity() {
+ whenever(controlsController.addSeedingFavoritesCallback(any())).thenReturn(true)
+ assertThat(underTest.resolveActivity()).isEqualTo(ControlsActivity::class.java)
+ }
+
+ @Test
+ fun testResolveActivityNotSeedingNoFavoritesNoPanels_ControlsProviderSelectorActivity() {
+ whenever(controlsController.addSeedingFavoritesCallback(any())).thenReturn(false)
+ whenever(controlsController.getFavorites()).thenReturn(emptyList())
+
+ val selectedItems =
+ listOf(
+ SelectedItem.StructureItem(
+ StructureInfo(ComponentName.unflattenFromString("pkg/.cls1"), "a", ArrayList())
+ ),
+ )
+ sharedPreferences
+ .edit()
+ .putString("controls_component", selectedItems[0].componentName.flattenToString())
+ .putString("controls_structure", selectedItems[0].name.toString())
+ .commit()
+
+ assertThat(underTest.resolveActivity())
+ .isEqualTo(ControlsProviderSelectorActivity::class.java)
+ }
+
+ @Test
+ fun testResolveActivityNotSeedingNoDefaultNoFavoritesPanel_ControlsActivity() {
+ val panel = SelectedItem.PanelItem("App name", ComponentName("pkg", "cls"))
+ val activity = ComponentName("pkg", "activity")
+ val csi = ControlsServiceInfo(panel.componentName, panel.appName, activity)
+ whenever(controlsController.addSeedingFavoritesCallback(any())).thenReturn(true)
+ whenever(controlsController.getFavorites()).thenReturn(emptyList())
+ whenever(controlsListingController.getCurrentServices()).thenReturn(listOf(csi))
+
+ assertThat(underTest.resolveActivity()).isEqualTo(ControlsActivity::class.java)
+ }
+
private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo {
val activity = ComponentName("pkg", "activity")
sharedPreferences
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
index 5cd2ace..de04ef8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
@@ -75,6 +75,7 @@
uiExecutor.execute(it.arguments[0] as Runnable)
true
}
+ whenever(activityContext.resources).thenReturn(context.resources)
uiExecutor = FakeExecutor(FakeSystemClock())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index e6d3a69..89c7280 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.view.View;
@@ -54,6 +55,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -147,6 +149,19 @@
}
@Test
+ public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
+ final DreamHomeControlsComplication.Registrant registrant =
+ new DreamHomeControlsComplication.Registrant(mComplication,
+ mDreamOverlayStateController, mControlsComponent);
+ registrant.start();
+
+ setHaveFavorites(false);
+ setServiceWithPanel();
+
+ verify(mDreamOverlayStateController).addComplication(mComplication);
+ }
+
+ @Test
public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
@@ -232,6 +247,15 @@
triggerControlsListingCallback(serviceInfos);
}
+ private void setServiceWithPanel() {
+ final List<ControlsServiceInfo> serviceInfos = new ArrayList<>();
+ ControlsServiceInfo csi = mock(ControlsServiceInfo.class);
+ serviceInfos.add(csi);
+ when(csi.getPanelActivity()).thenReturn(new ComponentName("a", "b"));
+ when(mControlsListingController.getCurrentServices()).thenReturn(serviceInfos);
+ triggerControlsListingCallback(serviceInfos);
+ }
+
private void setDreamOverlayActive(boolean value) {
when(mDreamOverlayStateController.isOverlayActive()).thenReturn(value);
verify(mDreamOverlayStateController).addCallback(mStateCallbackCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 322014a..f8cb408 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -20,13 +20,14 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import java.util.*
+import java.util.Optional
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -50,20 +51,22 @@
companion object {
@Parameters(
name =
- "feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" +
- " while locked = {3}, visibility is AVAILABLE {4} - expected visible = {5}"
+ "feature enabled = {0}, has favorites = {1}, has panels = {2}, " +
+ "has service infos = {3}, can show while locked = {4}, " +
+ "visibility is AVAILABLE {5} - expected visible = {6}"
)
@JvmStatic
fun data() =
- (0 until 32)
+ (0 until 64)
.map { combination ->
arrayOf(
- /* isFeatureEnabled= */ combination and 0b10000 != 0,
- /* hasFavorites= */ combination and 0b01000 != 0,
- /* hasServiceInfos= */ combination and 0b00100 != 0,
- /* canShowWhileLocked= */ combination and 0b00010 != 0,
- /* visibilityAvailable= */ combination and 0b00001 != 0,
- /* isVisible= */ combination == 0b11111,
+ /* isFeatureEnabled= */ combination and 0b100000 != 0,
+ /* hasFavorites = */ combination and 0b010000 != 0,
+ /* hasPanels = */ combination and 0b001000 != 0,
+ /* hasServiceInfos= */ combination and 0b000100 != 0,
+ /* canShowWhileLocked= */ combination and 0b000010 != 0,
+ /* visibilityAvailable= */ combination and 0b000001 != 0,
+ /* isVisible= */ combination in setOf(0b111111, 0b110111, 0b101111),
)
}
.toList()
@@ -72,6 +75,7 @@
@Mock private lateinit var component: ControlsComponent
@Mock private lateinit var controlsController: ControlsController
@Mock private lateinit var controlsListingController: ControlsListingController
+ @Mock private lateinit var controlsServiceInfo: ControlsServiceInfo
@Captor
private lateinit var callbackCaptor:
ArgumentCaptor<ControlsListingController.ControlsListingCallback>
@@ -80,10 +84,11 @@
@JvmField @Parameter(0) var isFeatureEnabled: Boolean = false
@JvmField @Parameter(1) var hasFavorites: Boolean = false
- @JvmField @Parameter(2) var hasServiceInfos: Boolean = false
- @JvmField @Parameter(3) var canShowWhileLocked: Boolean = false
- @JvmField @Parameter(4) var isVisibilityAvailable: Boolean = false
- @JvmField @Parameter(5) var isVisibleExpected: Boolean = false
+ @JvmField @Parameter(2) var hasPanels: Boolean = false
+ @JvmField @Parameter(3) var hasServiceInfos: Boolean = false
+ @JvmField @Parameter(4) var canShowWhileLocked: Boolean = false
+ @JvmField @Parameter(5) var isVisibilityAvailable: Boolean = false
+ @JvmField @Parameter(6) var isVisibleExpected: Boolean = false
@Before
fun setUp() {
@@ -93,10 +98,13 @@
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController())
.thenReturn(Optional.of(controlsListingController))
+ if (hasPanels) {
+ whenever(controlsServiceInfo.panelActivity).thenReturn(mock())
+ }
whenever(controlsListingController.getCurrentServices())
.thenReturn(
if (hasServiceInfos) {
- listOf(mock(), mock())
+ listOf(controlsServiceInfo, mock())
} else {
emptyList()
}
@@ -134,10 +142,15 @@
val job = underTest.lockScreenState.onEach(values::add).launchIn(this)
if (canShowWhileLocked) {
+ val serviceInfoMock: ControlsServiceInfo = mock {
+ if (hasPanels) {
+ whenever(panelActivity).thenReturn(mock())
+ }
+ }
verify(controlsListingController).addCallback(callbackCaptor.capture())
callbackCaptor.value.onServicesUpdated(
if (hasServiceInfos) {
- listOf(mock())
+ listOf(serviceInfoMock)
} else {
emptyList()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 8c8fdc5..be0ad6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -84,7 +84,8 @@
private View mDialogView;
private View mSubTitle;
private LinearLayout mEthernet;
- private LinearLayout mMobileDataToggle;
+ private LinearLayout mMobileDataLayout;
+ private Switch mMobileToggleSwitch;
private LinearLayout mWifiToggle;
private Switch mWifiToggleSwitch;
private TextView mWifiToggleSummary;
@@ -133,7 +134,8 @@
mDialogView = mInternetDialog.mDialogView;
mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
- mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout);
+ mMobileDataLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+ mMobileToggleSwitch = mDialogView.requireViewById(R.id.mobile_toggle);
mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
mWifiToggleSwitch = mDialogView.requireViewById(R.id.wifi_toggle);
mWifiToggleSummary = mDialogView.requireViewById(R.id.wifi_toggle_summary);
@@ -234,7 +236,7 @@
mInternetDialog.updateDialog(true);
- assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -246,7 +248,7 @@
mInternetDialog.updateDialog(true);
- assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
// Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
@@ -255,7 +257,7 @@
mInternetDialog.updateDialog(true);
- assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -265,7 +267,7 @@
mInternetDialog.updateDialog(true);
- assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -277,7 +279,7 @@
mInternetDialog.updateDialog(true);
- assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -314,6 +316,30 @@
}
@Test
+ public void updateDialog_mobileDataIsEnabled_checkMobileDataSwitch() {
+ doReturn(true).when(mInternetDialogController).hasActiveSubId();
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isMobileDataEnabled()).thenReturn(true);
+ mMobileToggleSwitch.setChecked(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileToggleSwitch.isChecked()).isTrue();
+ }
+
+ @Test
+ public void updateDialog_mobileDataIsNotChanged_checkMobileDataSwitch() {
+ doReturn(true).when(mInternetDialogController).hasActiveSubId();
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isMobileDataEnabled()).thenReturn(false);
+ mMobileToggleSwitch.setChecked(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileToggleSwitch.isChecked()).isFalse();
+ }
+
+ @Test
public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
mInternetDialog.dismissDialog();
doReturn(true).when(mInternetDialogController).hasActiveSubId();
@@ -694,7 +720,7 @@
private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
boolean connectedWifiVisible) {
mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
- mMobileDataToggle.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
+ mMobileDataLayout.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index d1957ac..74f8c61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -89,8 +89,6 @@
@Mock
private KeyguardViewMediator mKeyguardViewMediator;
@Mock
- private ScrimController mScrimController;
- @Mock
private BiometricUnlockController.BiometricModeListener mBiometricModeListener;
@Mock
private ShadeController mShadeController;
@@ -140,7 +138,7 @@
when(mVibratorHelper.hasVibrator()).thenReturn(true);
mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
mBiometricUnlockController = new BiometricUnlockController(mDozeScrimController,
- mKeyguardViewMediator, mScrimController, mShadeController,
+ mKeyguardViewMediator, mShadeController,
mNotificationShadeWindowController, mKeyguardStateController, mHandler,
mUpdateMonitor, res.getResources(), mKeyguardBypassController,
mMetricsLogger, mDumpManager, mPowerManager, mLogger,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b7b3326..1ea0984 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3211,6 +3211,7 @@
return isPackageDeviceAdmin(packageName, UserHandle.USER_ALL);
}
+ // TODO(b/261957226): centralise this logic in DPM
boolean isPackageDeviceAdmin(String packageName, int userId) {
final IDevicePolicyManager dpm = getDevicePolicyManager();
try {
@@ -3237,6 +3238,9 @@
if (dpm.packageHasActiveAdmins(packageName, users[i])) {
return true;
}
+ if (isDeviceManagementRoleHolder(packageName, users[i])) {
+ return true;
+ }
}
}
} catch (RemoteException e) {
@@ -3244,6 +3248,24 @@
return false;
}
+ private boolean isDeviceManagementRoleHolder(String packageName, int userId) {
+ return Objects.equals(packageName, getDevicePolicyManagementRoleHolderPackageName(userId));
+ }
+
+ @Nullable
+ private String getDevicePolicyManagementRoleHolderPackageName(int userId) {
+ return Binder.withCleanCallingIdentity(() -> {
+ RoleManager roleManager = mContext.getSystemService(RoleManager.class);
+ List<String> roleHolders =
+ roleManager.getRoleHoldersAsUser(
+ RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, UserHandle.of(userId));
+ if (roleHolders.isEmpty()) {
+ return null;
+ }
+ return roleHolders.get(0);
+ });
+ }
+
/** Returns the device policy manager interface. */
private IDevicePolicyManager getDevicePolicyManager() {
if (mDevicePolicyManager == null) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index dd870a8..79a4acf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -599,6 +599,13 @@
* for display.
*/
void generateCrop(WallpaperData wallpaper) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("WPMS.generateCrop");
+ generateCropInternal(wallpaper);
+ t.traceEnd();
+ }
+
+ private void generateCropInternal(WallpaperData wallpaper) {
boolean success = false;
// Only generate crop for default display.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index cdc8c01..e639866 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -671,7 +671,7 @@
private boolean mCurrentLaunchCanTurnScreenOn = true;
/** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
- private boolean mLastSurfaceShowing = true;
+ private boolean mLastSurfaceShowing;
/**
* The activity is opaque and fills the entire space of this task.
@@ -1239,8 +1239,8 @@
pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
+ supportsEnterPipOnTaskSwitch);
}
- if (info.getMaxAspectRatio() != 0) {
- pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio());
+ if (getMaxAspectRatio() != 0) {
+ pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
}
final float minAspectRatio = getMinAspectRatio();
if (minAspectRatio != 0) {
@@ -1590,6 +1590,7 @@
newParent.setResumedActivity(this, "onParentChanged");
mImeInsetsFrozenUntilStartInput = false;
}
+ mLetterboxUiController.onActivityParentChanged(newParent);
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -5499,7 +5500,8 @@
// no animation but there will still be a transition set.
// We still need to delay hiding the surface such that it
// can be synchronized with showing the next surface in the transition.
- if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
+ if (!usingShellTransitions && !isVisible() && !delayed
+ && !displayContent.mAppTransition.isTransitionSet()) {
SurfaceControl.openTransaction();
try {
forAllWindows(win -> {
@@ -7438,6 +7440,11 @@
}
@Override
+ boolean showSurfaceOnCreation() {
+ return false;
+ }
+
+ @Override
void prepareSurfaces() {
final boolean show = isVisible() || isAnimating(PARENTS,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
@@ -7676,6 +7683,15 @@
@Configuration.Orientation
@Override
int getRequestedConfigurationOrientation(boolean forDisplay) {
+ if (mLetterboxUiController.hasInheritedOrientation()) {
+ final RootDisplayArea root = getRootDisplayArea();
+ if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
+ return ActivityInfo.reverseOrientation(
+ mLetterboxUiController.getInheritedOrientation());
+ } else {
+ return mLetterboxUiController.getInheritedOrientation();
+ }
+ }
if (mOrientation == SCREEN_ORIENTATION_BEHIND && task != null) {
// We use Task here because we want to be consistent with what happens in
// multi-window mode where other tasks orientations are ignored.
@@ -7803,6 +7819,9 @@
@Nullable
CompatDisplayInsets getCompatDisplayInsets() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedCompatDisplayInsets();
+ }
return mCompatDisplayInsets;
}
@@ -7885,6 +7904,10 @@
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void updateCompatDisplayInsets() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ mCompatDisplayInsets = mLetterboxUiController.getInheritedCompatDisplayInsets();
+ return;
+ }
if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
// The override configuration is set only once in size compatibility mode.
return;
@@ -7946,6 +7969,9 @@
@Override
float getCompatScale() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedSizeCompatScale();
+ }
return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale();
}
@@ -8055,6 +8081,16 @@
}
/**
+ * @return The orientation to use to understand if reachability is enabled.
+ */
+ @ActivityInfo.ScreenOrientation
+ int getOrientationForReachability() {
+ return mLetterboxUiController.hasInheritedLetterboxBehavior()
+ ? mLetterboxUiController.getInheritedOrientation()
+ : getRequestedConfigurationOrientation();
+ }
+
+ /**
* Returns whether activity bounds are letterboxed.
*
* <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
@@ -8094,6 +8130,10 @@
if (!ignoreVisibility && !mVisibleRequested) {
return APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
}
+ // TODO(b/256564921): Investigate if we need new metrics for translucent activities
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedAppCompatState();
+ }
if (mInSizeCompatModeForBounds) {
return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
}
@@ -8564,6 +8604,11 @@
}
private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity
+ // is letterboxed.
+ return false;
+ }
final int appWidth = appBounds.width();
final int appHeight = appBounds.height();
final int containerAppWidth = containerBounds.width();
@@ -8584,10 +8629,11 @@
// The rest of the condition is that only one side is smaller than the container, but it
// still needs to exclude the cases where the size is limited by the fixed aspect ratio.
- if (info.getMaxAspectRatio() > 0) {
+ final float maxAspectRatio = getMaxAspectRatio();
+ if (maxAspectRatio > 0) {
final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
/ Math.min(appWidth, appHeight);
- if (aspectRatio >= info.getMaxAspectRatio()) {
+ if (aspectRatio >= maxAspectRatio) {
// The current size has reached the max aspect ratio.
return false;
}
@@ -8809,7 +8855,7 @@
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
Rect containingBounds, float desiredAspectRatio) {
- final float maxAspectRatio = info.getMaxAspectRatio();
+ final float maxAspectRatio = getMaxAspectRatio();
final Task rootTask = getRootTask();
final float minAspectRatio = getMinAspectRatio();
final TaskFragment organizedTf = getOrganizedTaskFragment();
@@ -8916,6 +8962,9 @@
* Returns the min aspect ratio of this activity.
*/
float getMinAspectRatio() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedMinAspectRatio();
+ }
if (info.applicationInfo == null) {
return info.getMinAspectRatio();
}
@@ -8960,11 +9009,18 @@
&& parent.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
}
+ float getMaxAspectRatio() {
+ if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+ return mLetterboxUiController.getInheritedMaxAspectRatio();
+ }
+ return info.getMaxAspectRatio();
+ }
+
/**
* Returns true if the activity has maximum or minimum aspect ratio.
*/
private boolean hasFixedAspectRatio() {
- return info.getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
+ return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
/**
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index c19353c..127a7bf 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Color;
+import android.provider.DeviceConfig;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -103,6 +104,10 @@
final Context mContext;
+ // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
+ @NonNull
+ private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;
+
// Aspect ratio of letterbox for fixed orientation, values <=
// MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
private float mFixedOrientationLetterboxAspectRatio;
@@ -165,9 +170,12 @@
// Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
- // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
- @NonNull
- private final LetterboxConfigurationPersister mLetterboxConfigurationPersister;
+ // Whether letterboxing strategy is enabled for translucent activities. If {@value false}
+ // all the feature is disabled
+ private boolean mTranslucentLetterboxingEnabled;
+
+ // Allows to enable letterboxing strategy for translucent activities ignoring flags.
+ private boolean mTranslucentLetterboxingOverrideEnabled;
LetterboxConfiguration(Context systemUiContext) {
this(systemUiContext, new LetterboxConfigurationPersister(systemUiContext,
@@ -206,6 +214,8 @@
R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
+ mTranslucentLetterboxingEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsEnabledForTranslucentActivities);
mLetterboxConfigurationPersister = letterboxConfigurationPersister;
mLetterboxConfigurationPersister.start();
}
@@ -817,6 +827,32 @@
R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
}
+ boolean isTranslucentLetterboxingEnabled() {
+ return mTranslucentLetterboxingOverrideEnabled || (mTranslucentLetterboxingEnabled
+ && isTranslucentLetterboxingAllowed());
+ }
+
+ void setTranslucentLetterboxingEnabled(boolean translucentLetterboxingEnabled) {
+ mTranslucentLetterboxingEnabled = translucentLetterboxingEnabled;
+ }
+
+ void setTranslucentLetterboxingOverrideEnabled(
+ boolean translucentLetterboxingOverrideEnabled) {
+ mTranslucentLetterboxingOverrideEnabled = translucentLetterboxingOverrideEnabled;
+ setTranslucentLetterboxingEnabled(translucentLetterboxingOverrideEnabled);
+ }
+
+ /**
+ * Resets whether we use the constraints override strategy for letterboxing when dealing
+ * with translucent activities {@link R.bool.config_letterboxIsEnabledForTranslucentActivities}.
+ */
+ void resetTranslucentLetterboxingEnabled() {
+ final boolean newValue = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsEnabledForTranslucentActivities);
+ setTranslucentLetterboxingEnabled(newValue);
+ setTranslucentLetterboxingOverrideEnabled(false);
+ }
+
/** Calculates a new letterboxPositionForHorizontalReachability value and updates the store */
private void updatePositionForHorizontalReachability(
Function<Integer, Integer> newHorizonalPositionFun) {
@@ -839,4 +875,9 @@
nextVerticalPosition);
}
+ // TODO(b/262378106): Cache runtime flag and implement DeviceConfig.OnPropertiesChangedListener
+ static boolean isTranslucentLetterboxingAllowed() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ "enable_translucent_activity_letterbox", false);
+ }
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index bcea6f4..a53a5fc 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -27,6 +28,7 @@
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT;
@@ -82,13 +84,44 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
+ private static final float UNDEFINED_ASPECT_RATIO = 0f;
+
private final Point mTmpPoint = new Point();
private final LetterboxConfiguration mLetterboxConfiguration;
+
private final ActivityRecord mActivityRecord;
+ /*
+ * WindowContainerListener responsible to make translucent activities inherit
+ * constraints from the first opaque activity beneath them. It's null for not
+ * translucent activities.
+ */
+ @Nullable
+ private WindowContainerListener mLetterboxConfigListener;
+
private boolean mShowWallpaperForLetterboxBackground;
+ // In case of transparent activities we might need to access the aspectRatio of the
+ // first opaque activity beneath.
+ private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
+ private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+
+ @Configuration.Orientation
+ private int mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED;
+
+ // The app compat state for the opaque activity if any
+ private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
+
+ // If true it means that the opaque activity beneath a translucent one is in SizeCompatMode.
+ private boolean mIsInheritedInSizeCompatMode;
+
+ // This is the SizeCompatScale of the opaque activity beneath a translucent one
+ private float mInheritedSizeCompatScale;
+
+ // The CompatDisplayInsets of the opaque activity beneath the translucent one.
+ private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
+
@Nullable
private Letterbox mLetterbox;
@@ -220,7 +253,9 @@
: mActivityRecord.inMultiWindowMode()
? mActivityRecord.getTask().getBounds()
: mActivityRecord.getRootTask().getParent().getBounds();
- mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
+ final Rect innerFrame = hasInheritedLetterboxBehavior()
+ ? mActivityRecord.getWindowConfiguration().getBounds() : w.getFrame();
+ mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
}
@@ -305,7 +340,9 @@
}
private void handleHorizontalDoubleTap(int x) {
- if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) {
+ // TODO(b/260857308): Investigate if enabling reachability for translucent activity
+ if (hasInheritedLetterboxBehavior() || !isHorizontalReachabilityEnabled()
+ || mActivityRecord.isInTransition()) {
return;
}
@@ -341,7 +378,9 @@
}
private void handleVerticalDoubleTap(int y) {
- if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) {
+ // TODO(b/260857308): Investigate if enabling reachability for translucent activity
+ if (hasInheritedLetterboxBehavior() || !isVerticalReachabilityEnabled()
+ || mActivityRecord.isInTransition()) {
return;
}
@@ -390,7 +429,7 @@
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
- && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT);
+ && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT);
}
private boolean isHorizontalReachabilityEnabled() {
@@ -412,7 +451,7 @@
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_PORTRAIT
- && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_LANDSCAPE);
+ && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE);
}
private boolean isVerticalReachabilityEnabled() {
@@ -576,9 +615,7 @@
// Rounded corners should be displayed above the taskbar.
bounds.bottom =
Math.min(bounds.bottom, getTaskbarInsetsSource(mainWindow).getFrame().top);
- if (mActivityRecord.inSizeCompatMode() && mActivityRecord.getCompatScale() < 1.0f) {
- bounds.scale(1.0f / mActivityRecord.getCompatScale());
- }
+ scaleIfNeeded(bounds);
}
private int getInsetsStateCornerRadius(
@@ -788,4 +825,144 @@
w.mAttrs.insetsFlags.appearance
);
}
+
+ /**
+ * Handles translucent activities letterboxing inheriting constraints from the
+ * first opaque activity beneath.
+ * @param parent The parent container.
+ */
+ void onActivityParentChanged(WindowContainer<?> parent) {
+ if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
+ return;
+ }
+ if (mLetterboxConfigListener != null) {
+ mLetterboxConfigListener.onRemoved();
+ clearInheritedConfig();
+ }
+ // In case mActivityRecord.getCompatDisplayInsets() is not null we don't apply the
+ // opaque activity constraints because we're expecting the activity is already letterboxed.
+ if (mActivityRecord.getTask() == null || mActivityRecord.getCompatDisplayInsets() != null
+ || mActivityRecord.fillsParent()) {
+ return;
+ }
+ final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
+ ActivityRecord::fillsParent, mActivityRecord, false /* includeBoundary */,
+ true /* traverseTopToBottom */);
+ if (firstOpaqueActivityBeneath == null
+ || mActivityRecord.launchedFromUid != firstOpaqueActivityBeneath.getUid()) {
+ // We skip letterboxing if the translucent activity doesn't have any opaque
+ // activities beneath of if it's launched from a different user (e.g. notification)
+ return;
+ }
+ inheritConfiguration(firstOpaqueActivityBeneath);
+ mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
+ mActivityRecord, firstOpaqueActivityBeneath,
+ (opaqueConfig, transparentConfig) -> {
+ final Configuration mutatedConfiguration = new Configuration();
+ final Rect parentBounds = parent.getWindowConfiguration().getBounds();
+ final Rect bounds = mutatedConfiguration.windowConfiguration.getBounds();
+ final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds();
+ // We cannot use letterboxBounds directly here because the position relies on
+ // letterboxing. Using letterboxBounds directly, would produce a double offset.
+ bounds.set(parentBounds.left, parentBounds.top,
+ parentBounds.left + letterboxBounds.width(),
+ parentBounds.top + letterboxBounds.height());
+ // We need to initialize appBounds to avoid NPE. The actual value will
+ // be set ahead when resolving the Configuration for the activity.
+ mutatedConfiguration.windowConfiguration.setAppBounds(new Rect());
+ return mutatedConfiguration;
+ });
+ }
+
+ /**
+ * @return {@code true} if the current activity is translucent with an opaque activity
+ * beneath. In this case it will inherit bounds, orientation and aspect ratios from
+ * the first opaque activity beneath.
+ */
+ boolean hasInheritedLetterboxBehavior() {
+ return mLetterboxConfigListener != null && !mActivityRecord.matchParentBounds();
+ }
+
+ /**
+ * @return {@code true} if the current activity is translucent with an opaque activity
+ * beneath and needs to inherit its orientation.
+ */
+ boolean hasInheritedOrientation() {
+ // To force a different orientation, the transparent one needs to have an explicit one
+ // otherwise the existing one is fine and the actual orientation will depend on the
+ // bounds.
+ // To avoid wrong behaviour, we're not forcing orientation for activities with not
+ // fixed orientation (e.g. permission dialogs).
+ return hasInheritedLetterboxBehavior()
+ && mActivityRecord.mOrientation != SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ float getInheritedMinAspectRatio() {
+ return mInheritedMinAspectRatio;
+ }
+
+ float getInheritedMaxAspectRatio() {
+ return mInheritedMaxAspectRatio;
+ }
+
+ int getInheritedAppCompatState() {
+ return mInheritedAppCompatState;
+ }
+
+ float getInheritedSizeCompatScale() {
+ return mInheritedSizeCompatScale;
+ }
+
+ @Configuration.Orientation
+ int getInheritedOrientation() {
+ return mInheritedOrientation;
+ }
+
+ public ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() {
+ return mInheritedCompatDisplayInsets;
+ }
+
+ private void inheritConfiguration(ActivityRecord firstOpaque) {
+ // To avoid wrong behaviour, we're not forcing a specific aspet ratio to activities
+ // which are not already providing one (e.g. permission dialogs) and presumably also
+ // not resizable.
+ if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) {
+ mInheritedMinAspectRatio = firstOpaque.getMinAspectRatio();
+ }
+ if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) {
+ mInheritedMaxAspectRatio = firstOpaque.getMaxAspectRatio();
+ }
+ mInheritedOrientation = firstOpaque.getRequestedConfigurationOrientation();
+ mInheritedAppCompatState = firstOpaque.getAppCompatState();
+ mIsInheritedInSizeCompatMode = firstOpaque.inSizeCompatMode();
+ mInheritedSizeCompatScale = firstOpaque.getCompatScale();
+ mInheritedCompatDisplayInsets = firstOpaque.getCompatDisplayInsets();
+ }
+
+ private void clearInheritedConfig() {
+ mLetterboxConfigListener = null;
+ mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
+ mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+ mInheritedOrientation = Configuration.ORIENTATION_UNDEFINED;
+ mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
+ mIsInheritedInSizeCompatMode = false;
+ mInheritedSizeCompatScale = 1f;
+ mInheritedCompatDisplayInsets = null;
+ }
+
+ private void scaleIfNeeded(Rect bounds) {
+ if (boundsNeedToScale()) {
+ bounds.scale(1.0f / mActivityRecord.getCompatScale());
+ }
+ }
+
+ private boolean boundsNeedToScale() {
+ if (hasInheritedLetterboxBehavior()) {
+ return mIsInheritedInSizeCompatMode
+ && mInheritedSizeCompatScale < 1.0f;
+ } else {
+ return mActivityRecord.inSizeCompatMode()
+ && mActivityRecord.getCompatScale() < 1.0f;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7b16ccc..5c893de 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -635,7 +635,6 @@
if (showSurfaceOnCreation()) {
getSyncTransaction().show(mSurfaceControl);
}
- onSurfaceShown(getSyncTransaction());
updateSurfacePositionNonOrganized();
}
@@ -687,13 +686,6 @@
scheduleAnimation();
}
- /**
- * Called when the surface is shown for the first time.
- */
- void onSurfaceShown(Transaction t) {
- // do nothing
- }
-
// Temp. holders for a chain of containers we are currently processing.
private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList<>();
private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList<>();
@@ -3946,27 +3938,54 @@
unregisterConfigurationChangeListener(listener);
}
+ static void overrideConfigurationPropagation(WindowContainer<?> receiver,
+ WindowContainer<?> supplier) {
+ overrideConfigurationPropagation(receiver, supplier, null /* configurationMerger */);
+ }
+
/**
* Forces the receiver container to always use the configuration of the supplier container as
* its requested override configuration. It allows to propagate configuration without changing
* the relationship between child and parent.
+ *
+ * @param receiver The {@link WindowContainer<?>} which will receive the {@link
+ * Configuration} result of the merging operation.
+ * @param supplier The {@link WindowContainer<?>} which provides the initial {@link
+ * Configuration}.
+ * @param configurationMerger A {@link ConfigurationMerger} which combines the {@link
+ * Configuration} of the receiver and the supplier.
*/
- static void overrideConfigurationPropagation(WindowContainer<?> receiver,
- WindowContainer<?> supplier) {
+ static WindowContainerListener overrideConfigurationPropagation(WindowContainer<?> receiver,
+ WindowContainer<?> supplier, @Nullable ConfigurationMerger configurationMerger) {
final ConfigurationContainerListener listener = new ConfigurationContainerListener() {
@Override
public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
- receiver.onRequestedOverrideConfigurationChanged(supplier.getConfiguration());
+ final Configuration mergedConfiguration =
+ configurationMerger != null
+ ? configurationMerger.merge(mergedOverrideConfig,
+ receiver.getConfiguration())
+ : supplier.getConfiguration();
+ receiver.onRequestedOverrideConfigurationChanged(mergedConfiguration);
}
};
supplier.registerConfigurationChangeListener(listener);
- receiver.registerWindowContainerListener(new WindowContainerListener() {
+ final WindowContainerListener wcListener = new WindowContainerListener() {
@Override
public void onRemoved() {
receiver.unregisterWindowContainerListener(this);
supplier.unregisterConfigurationChangeListener(listener);
}
- });
+ };
+ receiver.registerWindowContainerListener(wcListener);
+ return wcListener;
+ }
+
+ /**
+ * Abstraction for functions merging two {@link Configuration} objects into one.
+ */
+ @FunctionalInterface
+ interface ConfigurationMerger {
+ Configuration merge(Configuration first, Configuration second);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index b8cf0ad..4e692e2d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -963,6 +963,29 @@
return 0;
}
+ private int runSetTranslucentLetterboxingEnabled(PrintWriter pw) {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(enabled);
+ }
+ return 0;
+ }
+
private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -1018,6 +1041,9 @@
case "--isSplitScreenAspectRatioForUnresizableAppsEnabled":
runSetLetterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled(pw);
break;
+ case "--isTranslucentLetterboxingEnabled":
+ runSetTranslucentLetterboxingEnabled(pw);
+ break;
default:
getErrPrintWriter().println(
"Error: Unrecognized letterbox style option: " + arg);
@@ -1081,6 +1107,9 @@
mLetterboxConfiguration
.getIsSplitScreenAspectRatioForUnresizableAppsEnabled();
break;
+ case "isTranslucentLetterboxingEnabled":
+ mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
+ break;
default:
getErrPrintWriter().println(
"Error: Unrecognized letterbox style option: " + arg);
@@ -1181,6 +1210,7 @@
mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
mLetterboxConfiguration.resetIsEducationEnabled();
mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
}
}
@@ -1217,7 +1247,6 @@
pw.println("Is using split screen aspect ratio as aspect ratio for unresizable apps: "
+ mLetterboxConfiguration
.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
-
pw.println("Background type: "
+ LetterboxConfiguration.letterboxBackgroundTypeToString(
mLetterboxConfiguration.getLetterboxBackgroundType()));
@@ -1227,6 +1256,12 @@
+ mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
pw.println(" Wallpaper dark scrim alpha: "
+ mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+
+ if (mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
+ pw.println("Letterboxing for translucent activities: enabled");
+ } else {
+ pw.println("Letterboxing for translucent activities: disabled");
+ }
}
return 0;
}
@@ -1419,12 +1454,16 @@
pw.println(" --isSplitScreenAspectRatioForUnresizableAppsEnabled [true|1|false|0]");
pw.println(" Whether using split screen aspect ratio as a default aspect ratio for");
pw.println(" unresizable apps.");
+ pw.println(" --isTranslucentLetterboxingEnabled [true|1|false|0]");
+ pw.println(" Whether letterboxing for translucent activities is enabled.");
+
pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier");
pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled");
- pw.println(" isEducationEnabled||defaultPositionMultiplierForHorizontalReachability");
- pw.println(" ||defaultPositionMultiplierForVerticalReachability]");
+ pw.println(" |isEducationEnabled||defaultPositionMultiplierForHorizontalReachability");
+ pw.println(" |isTranslucentLetterboxingEnabled");
+ pw.println(" |defaultPositionMultiplierForVerticalReachability]");
pw.println(" Resets overrides to default values for specified properties separated");
pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
pw.println(" If no arguments provided, all values will be reset.");
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index babad4d..94c33f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -164,6 +164,114 @@
}
@Test
+ public void testApplyStrategyToTranslucentActivities() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.info.setMinAspectRatio(1.2f);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(1.1f)
+ .setMaxAspectRatio(3f)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // We check bounds
+ final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
+ final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
+ assertEquals(opaqueBounds, translucentRequestedBounds);
+ // We check orientation
+ final int translucentOrientation =
+ translucentActivity.getRequestedConfigurationOrientation();
+ assertEquals(ORIENTATION_PORTRAIT, translucentOrientation);
+ // We check aspect ratios
+ assertEquals(1.2f, translucentActivity.getMinAspectRatio(), 0.00001f);
+ assertEquals(1.5f, translucentActivity.getMaxAspectRatio(), 0.00001f);
+ }
+
+ @Test
+ public void testNotApplyStrategyToTranslucentActivitiesWithDifferentUid() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.info.setMinAspectRatio(1.2f);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(1.1f)
+ .setMaxAspectRatio(3f)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // We check bounds
+ final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
+ final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
+ assertNotEquals(opaqueBounds, translucentRequestedBounds);
+ }
+
+ @Test
+ public void testApplyStrategyToMultipleTranslucentActivities() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2000, 1000);
+ prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ mActivity.info.setMinAspectRatio(1.2f);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(1.1f)
+ .setMaxAspectRatio(3f)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // We check bounds
+ final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
+ final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
+ assertEquals(opaqueBounds, translucentRequestedBounds);
+ // Launch another translucent activity
+ final ActivityRecord translucentActivity2 = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .build();
+ doReturn(false).when(translucentActivity2).fillsParent();
+ mTask.addChild(translucentActivity2);
+ // We check bounds
+ final Rect translucent2RequestedBounds = translucentActivity2.getRequestedOverrideBounds();
+ assertEquals(opaqueBounds, translucent2RequestedBounds);
+ }
+
+ @Test
+ public void testTranslucentActivitiesDontGoInSizeCompactMode() {
+ mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+ assertTrue(mActivity.inSizeCompatMode());
+ // Rotate back
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
+ assertFalse(mActivity.inSizeCompatMode());
+ // We launch a transparent activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .build();
+ doReturn(true).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+ // It should not be in SCM
+ assertFalse(translucentActivity.inSizeCompatMode());
+ // We rotate again
+ rotateDisplay(translucentActivity.mDisplayContent, ROTATION_90);
+ assertFalse(translucentActivity.inSizeCompatMode());
+ }
+
+ @Test
public void testRestartProcessIfVisible() {
setUpDisplaySizeWithApp(1000, 2500);
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);