summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityOptions.java3
-rw-r--r--core/java/android/app/assist/AssistStructure.java18
-rw-r--r--core/java/android/inputmethodservice/InkWindow.java6
-rw-r--r--core/java/android/view/WindowLayout.java8
-rw-r--r--core/java/android/widget/AdapterViewFlipper.java41
-rw-r--r--core/java/android/widget/AnalogClock.java33
-rw-r--r--core/java/android/widget/TextClock.java130
-rw-r--r--core/java/android/widget/ViewFlipper.java40
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java29
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java9
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java21
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt132
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt73
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java4
-rw-r--r--services/core/java/com/android/server/display/RampAnimator.java35
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java25
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java22
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java1
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java50
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java28
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java2
37 files changed, 723 insertions, 284 deletions
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2ae721648656..895dde760058 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1507,8 +1507,9 @@ public class ActivityOptions extends ComponentOptions {
}
/** @hide */
- public void setRemoteTransition(@Nullable RemoteTransition remoteTransition) {
+ public ActivityOptions setRemoteTransition(@Nullable RemoteTransition remoteTransition) {
mRemoteTransition = remoteTransition;
+ return this;
}
/** @hide */
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index b7ec7b55d7db..d66fca8945f1 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -22,6 +22,7 @@ import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.FillRequest;
+import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -1557,6 +1558,10 @@ public class AssistStructure implements Parcelable {
/**
* Returns any text associated with the node that is displayed to the user, or null
* if there is none.
+ *
+ * <p> The text will be stripped of any spans that could potentially contain reference to
+ * the activity context, to avoid memory leak. If the text contained a span, a plain
+ * string version of the text will be returned.
*/
@Nullable
public CharSequence getText() {
@@ -1996,14 +2001,16 @@ public class AssistStructure implements Parcelable {
@Override
public void setText(CharSequence text) {
ViewNodeText t = getNodeText();
- t.mText = TextUtils.trimNoCopySpans(text);
+ // Strip spans from the text to avoid memory leak
+ t.mText = TextUtils.trimToParcelableSize(stripAllSpansFromText(text));
t.mTextSelectionStart = t.mTextSelectionEnd = -1;
}
@Override
public void setText(CharSequence text, int selectionStart, int selectionEnd) {
ViewNodeText t = getNodeText();
- t.mText = TextUtils.trimNoCopySpans(text);
+ // Strip spans from the text to avoid memory leak
+ t.mText = stripAllSpansFromText(text);
t.mTextSelectionStart = selectionStart;
t.mTextSelectionEnd = selectionEnd;
}
@@ -2222,6 +2229,13 @@ public class AssistStructure implements Parcelable {
public void setHtmlInfo(@NonNull HtmlInfo htmlInfo) {
mNode.mHtmlInfo = htmlInfo;
}
+
+ private CharSequence stripAllSpansFromText(CharSequence text) {
+ if (text instanceof Spanned) {
+ return text.toString();
+ }
+ return text;
+ }
}
private static final class HtmlInfoNode extends HtmlInfo implements Parcelable {
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index 24d1c9577f82..1b8d925ec1cd 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -104,7 +104,11 @@ final class InkWindow extends PhoneWindow {
*/
void hide(boolean remove) {
if (getDecorView() != null) {
- getDecorView().setVisibility(remove ? View.GONE : View.INVISIBLE);
+ if (remove) {
+ mWindowManager.removeViewImmediate(getDecorView());
+ } else {
+ getDecorView().setVisibility(View.INVISIBLE);
+ }
}
}
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index 3b8298ed3627..dda399357d8c 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -124,16 +124,16 @@ public class WindowLayout {
|| cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
final Insets systemBarsInsets = state.calculateInsets(
displayFrame, systemBars(), requestedVisibleTypes);
- if (systemBarsInsets.left > 0) {
+ if (systemBarsInsets.left >= cutout.getSafeInsetLeft()) {
displayCutoutSafeExceptMaybeBars.left = MIN_X;
}
- if (systemBarsInsets.top > 0) {
+ if (systemBarsInsets.top >= cutout.getSafeInsetTop()) {
displayCutoutSafeExceptMaybeBars.top = MIN_Y;
}
- if (systemBarsInsets.right > 0) {
+ if (systemBarsInsets.right >= cutout.getSafeInsetRight()) {
displayCutoutSafeExceptMaybeBars.right = MAX_X;
}
- if (systemBarsInsets.bottom > 0) {
+ if (systemBarsInsets.bottom >= cutout.getSafeInsetBottom()) {
displayCutoutSafeExceptMaybeBars.bottom = MAX_Y;
}
}
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 065089f53633..53c73c6bf161 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -16,10 +16,7 @@
package android.widget;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Message;
import android.util.AttributeSet;
@@ -48,7 +45,6 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
- private boolean mUserPresent = true;
private boolean mAdvancedByHost = false;
public AdapterViewFlipper(Context context) {
@@ -82,40 +78,10 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
a.recycle();
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mUserPresent = false;
- updateRunning();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- mUserPresent = true;
- updateRunning(false);
- }
- }
- };
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_PRESENT);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views machanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For exmaple, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
-
-
if (mAutoStart) {
// Automatically start when requested
startFlipping();
@@ -126,8 +92,6 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
-
- getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@@ -235,8 +199,7 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
* true.
*/
private void updateRunning(boolean flipNow) {
- boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
- && mAdapter != null;
+ boolean running = !mAdvancedByHost && mVisible && mStarted && mAdapter != null;
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
@@ -248,7 +211,7 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
}
if (LOGD) {
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
- + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+ + ", mRunning=" + mRunning);
}
}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 1f0e95ea305a..e01583322979 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -23,7 +23,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
@@ -37,6 +36,9 @@ import android.view.RemotableViewMethod;
import android.view.View;
import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
+import android.widget.TextClock.ClockEventDelegate;
+
+import com.android.internal.util.Preconditions;
import java.time.Clock;
import java.time.DateTimeException;
@@ -112,6 +114,7 @@ public class AnalogClock extends View {
public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mClockEventDelegate = new ClockEventDelegate(context);
mSecondsHandFps = AppGlobals.getIntCoreSetting(
WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS,
context.getResources()
@@ -584,21 +587,9 @@ public class AnalogClock extends View {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- IntentFilter filter = new IntentFilter();
if (!mReceiverAttached) {
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mIntentReceiver,
- android.os.Process.myUserHandle(), filter, null, getHandler());
+ mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
mReceiverAttached = true;
}
@@ -615,12 +606,23 @@ public class AnalogClock extends View {
@Override
protected void onDetachedFromWindow() {
if (mReceiverAttached) {
- getContext().unregisterReceiver(mIntentReceiver);
+ mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
mReceiverAttached = false;
}
super.onDetachedFromWindow();
}
+ /**
+ * Sets a delegate to handle clock event registration. This must be called before the view is
+ * attached to the window
+ *
+ * @hide
+ */
+ public void setClockEventDelegate(ClockEventDelegate delegate) {
+ Preconditions.checkState(!mReceiverAttached, "Clock events already registered");
+ mClockEventDelegate = delegate;
+ }
+
private void onVisible() {
if (!mVisible) {
mVisible = true;
@@ -797,6 +799,7 @@ public class AnalogClock extends View {
}
};
private boolean mReceiverAttached;
+ private ClockEventDelegate mClockEventDelegate;
private final Runnable mTick = new Runnable() {
@Override
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index e48afb2a3cc8..255bd679dc35 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.os.Process.myUserHandle;
import static android.view.ViewDebug.ExportedProperty;
import static android.widget.RemoteViews.RemoteView;
@@ -24,7 +25,6 @@ import android.annotation.TestApi;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -43,6 +43,7 @@ import android.view.ViewHierarchyEncoder;
import android.view.inspector.InspectableProperty;
import com.android.internal.R;
+import com.android.internal.util.Preconditions;
import java.time.Duration;
import java.time.Instant;
@@ -141,6 +142,8 @@ public class TextClock extends TextView {
private boolean mRegistered;
private boolean mShouldRunTicker;
+ private ClockEventDelegate mClockEventDelegate;
+
private Calendar mTime;
private String mTimeZone;
@@ -178,8 +181,7 @@ public class TextClock extends TextView {
if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
createTime(timeZone);
- } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
- || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
+ } else if (!mShouldRunTicker && Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
return;
}
onTimeChanged();
@@ -282,6 +284,7 @@ public class TextClock extends TextView {
if (mFormat24 == null) {
mFormat24 = getBestDateTimePattern("Hm");
}
+ mClockEventDelegate = new ClockEventDelegate(getContext());
createTime(mTimeZone);
chooseFormat();
@@ -431,6 +434,17 @@ public class TextClock extends TextView {
}
/**
+ * Sets a delegate to handle clock event registration. This must be called before the view is
+ * attached to the window
+ *
+ * @hide
+ */
+ public void setClockEventDelegate(ClockEventDelegate delegate) {
+ Preconditions.checkState(!mRegistered, "Clock events already registered");
+ mClockEventDelegate = delegate;
+ }
+
+ /**
* Update the displayed time if necessary and invalidate the view.
*/
public void refreshTime() {
@@ -557,7 +571,7 @@ public class TextClock extends TextView {
if (!mRegistered) {
mRegistered = true;
- registerReceiver();
+ mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
registerObserver();
createTime(mTimeZone);
@@ -582,7 +596,7 @@ public class TextClock extends TextView {
super.onDetachedFromWindow();
if (mRegistered) {
- unregisterReceiver();
+ mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
unregisterObserver();
mRegistered = false;
@@ -598,56 +612,27 @@ public class TextClock extends TextView {
mStopTicking = true;
}
- private void registerReceiver() {
- final IntentFilter filter = new IntentFilter();
-
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a managed profile to the
- // home screen. Therefore, we register the receiver as the user
- // the app is running as not the one the context is for.
- getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
- }
-
private void registerObserver() {
if (mRegistered) {
if (mFormatChangeObserver == null) {
mFormatChangeObserver = new FormatChangeObserver(getHandler());
}
- final ContentResolver resolver = getContext().getContentResolver();
- Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
- if (mShowCurrentUserTime) {
- resolver.registerContentObserver(uri, true,
- mFormatChangeObserver, UserHandle.USER_ALL);
- } else {
- // UserHandle.myUserId() is needed. This class is supported by the
- // remote views mechanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For example, when adding widgets from a managed profile to the
- // home screen. Therefore, we register the ContentObserver with the user
- // the app is running (e.g. the launcher) and not the user of the
- // context (e.g. the widget's profile).
- resolver.registerContentObserver(uri, true,
- mFormatChangeObserver, UserHandle.myUserId());
- }
+ // UserHandle.myUserId() is needed. This class is supported by the
+ // remote views mechanism and as a part of that the remote views
+ // can be inflated by a context for another user without the app
+ // having interact users permission - just for loading resources.
+ // For example, when adding widgets from a managed profile to the
+ // home screen. Therefore, we register the ContentObserver with the user
+ // the app is running (e.g. the launcher) and not the user of the
+ // context (e.g. the widget's profile).
+ int userHandle = mShowCurrentUserTime ? UserHandle.USER_ALL : UserHandle.myUserId();
+ mClockEventDelegate.registerFormatChangeObserver(mFormatChangeObserver, userHandle);
}
}
- private void unregisterReceiver() {
- getContext().unregisterReceiver(mIntentReceiver);
- }
-
private void unregisterObserver() {
if (mFormatChangeObserver != null) {
- final ContentResolver resolver = getContext().getContentResolver();
- resolver.unregisterContentObserver(mFormatChangeObserver);
+ mClockEventDelegate.unregisterFormatChangeObserver(mFormatChangeObserver);
}
}
@@ -674,4 +659,59 @@ public class TextClock extends TextView {
stream.addProperty("format", mFormat == null ? null : mFormat.toString());
stream.addProperty("hasSeconds", mHasSeconds);
}
+
+ /**
+ * Utility class to delegate some system event handling to allow overring the default behavior
+ *
+ * @hide
+ */
+ public static class ClockEventDelegate {
+
+ private final Context mContext;
+
+ public ClockEventDelegate(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Registers a receiver for actions {@link Intent#ACTION_TIME_CHANGED} and
+ * {@link Intent#ACTION_TIMEZONE_CHANGED}
+ *
+ * OK, this is gross but needed. This class is supported by the remote views mechanism and
+ * as a part of that the remote views can be inflated by a context for another user without
+ * the app having interact users permission - just for loading resources. For example,
+ * when adding widgets from a managed profile to the home screen. Therefore, we register
+ * the receiver as the user the app is running as not the one the context is for.
+ */
+ public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) {
+ final IntentFilter filter = new IntentFilter();
+
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+
+ mContext.registerReceiverAsUser(receiver, myUserHandle(), filter, null, handler);
+ }
+
+ /**
+ * Unregisters a previously registered receiver
+ */
+ public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) {
+ mContext.unregisterReceiver(receiver);
+ }
+
+ /**
+ * Registers an observer for time format changes
+ */
+ public void registerFormatChangeObserver(ContentObserver observer, int userHandle) {
+ Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
+ mContext.getContentResolver().registerContentObserver(uri, true, observer, userHandle);
+ }
+
+ /**
+ * Unregisters a previously registered observer
+ */
+ public void unregisterFormatChangeObserver(ContentObserver observer) {
+ mContext.getContentResolver().unregisterContentObserver(observer);
+ }
+ }
}
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 5abb6e1637e7..eaf037e68976 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -18,10 +18,7 @@ package android.widget;
import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Message;
@@ -51,8 +48,6 @@ public class ViewFlipper extends ViewAnimator {
private boolean mRunning = false;
private boolean mStarted = false;
private boolean mVisible = false;
- @UnsupportedAppUsage
- private boolean mUserPresent = true;
public ViewFlipper(Context context) {
super(context);
@@ -70,39 +65,10 @@ public class ViewFlipper extends ViewAnimator {
a.recycle();
}
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mUserPresent = false;
- updateRunning();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- mUserPresent = true;
- updateRunning(false);
- }
- }
- };
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_PRESENT);
-
- // OK, this is gross but needed. This class is supported by the
- // remote views machanism and as a part of that the remote views
- // can be inflated by a context for another user without the app
- // having interact users permission - just for loading resources.
- // For exmaple, when adding widgets from a user profile to the
- // home screen. Therefore, we register the receiver as the current
- // user not the one the context is for.
- getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
- filter, null, getHandler());
-
if (mAutoStart) {
// Automatically start when requested
startFlipping();
@@ -113,8 +79,6 @@ public class ViewFlipper extends ViewAnimator {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
-
- getContext().unregisterReceiver(mReceiver);
updateRunning();
}
@@ -186,7 +150,7 @@ public class ViewFlipper extends ViewAnimator {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void updateRunning(boolean flipNow) {
- boolean running = mVisible && mStarted && mUserPresent;
+ boolean running = mVisible && mStarted;
if (running != mRunning) {
if (running) {
showOnly(mWhichChild, flipNow);
@@ -198,7 +162,7 @@ public class ViewFlipper extends ViewAnimator {
}
if (LOGD) {
Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
- + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+ + ", mRunning=" + mRunning);
}
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2b39bb4eb7a5..0a726d99723a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3023,28 +3023,31 @@ public class ChooserActivity extends ResolverActivity implements
return shouldShowTabs()
&& (mMultiProfilePagerAdapter.getListAdapterForUserHandle(
UserHandle.of(UserHandle.myUserId())).getCount() > 0
- || shouldShowContentPreviewWhenEmpty())
+ || shouldShowStickyContentPreviewWhenEmpty())
&& shouldShowContentPreview();
}
/**
- * This method could be used to override the default behavior when we hide the preview area
- * when the current tab doesn't have any items.
+ * This method could be used to override the default behavior when we hide the sticky preview
+ * area when the current tab doesn't have any items.
*
- * @return true if we want to show the content preview area even if the tab for the current
- * user is empty
+ * @return {@code true} if we want to show the sticky content preview area even if the tab for
+ * the current user is empty
*/
- protected boolean shouldShowContentPreviewWhenEmpty() {
+ protected boolean shouldShowStickyContentPreviewWhenEmpty() {
return false;
}
- /**
- * @return true if we want to show the content preview area
- */
- protected boolean shouldShowContentPreview() {
+ @Override
+ public boolean shouldShowContentPreview() {
return isSendAction(getTargetIntent());
}
+ @Override
+ public boolean shouldShowServiceTargets() {
+ return shouldShowContentPreview() && !ActivityManager.isLowRamDeviceStatic();
+ }
+
private void updateStickyContentPreview() {
if (shouldShowStickyContentPreviewNoOrientationCheck()) {
// The sticky content preview is only shown when we show the work and personal tabs.
@@ -3406,11 +3409,7 @@ public class ChooserActivity extends ResolverActivity implements
// There can be at most one row in the listview, that is internally
// a ViewGroup with 2 rows
public int getServiceTargetRowCount() {
- if (shouldShowContentPreview()
- && !ActivityManager.isLowRamDeviceStatic()) {
- return 1;
- }
- return 0;
+ return shouldShowServiceTargets() ? 1 : 0;
}
public int getAzLabelRowCount() {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 1eecb413adcb..36038ae15853 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -19,7 +19,6 @@ package com.android.internal.app;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
-import android.app.ActivityManager;
import android.app.prediction.AppPredictor;
import android.content.ComponentName;
import android.content.Context;
@@ -425,11 +424,9 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
public int getServiceTargetCount() {
- if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent())
- && !ActivityManager.isLowRamDeviceStatic()) {
+ if (mChooserListCommunicator.shouldShowServiceTargets()) {
return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets());
}
-
return 0;
}
@@ -771,6 +768,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
void sendListViewUpdateMessage(UserHandle userHandle);
boolean isSendAction(Intent targetIntent);
+
+ boolean shouldShowContentPreview();
+
+ boolean shouldShowServiceTargets();
}
/**
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 52ffc984c41e..a513ca535620 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -21,6 +21,7 @@ import static android.content.res.Resources.ID_NULL;
import android.annotation.IdRes;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -53,10 +54,13 @@ public class ResolverDrawerLayout extends ViewGroup {
private static final String TAG = "ResolverDrawerLayout";
private MetricsLogger mMetricsLogger;
+
+
/**
- * Max width of the whole drawer layout
+ * Max width of the whole drawer layout and its res id
*/
- private final int mMaxWidth;
+ private int mMaxWidthResId;
+ private int mMaxWidth;
/**
* Max total visible height of views not marked always-show when in the closed/initial state
@@ -152,6 +156,7 @@ public class ResolverDrawerLayout extends ViewGroup {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ResolverDrawerLayout,
defStyleAttr, 0);
+ mMaxWidthResId = a.getResourceId(R.styleable.ResolverDrawerLayout_maxWidth, -1);
mMaxWidth = a.getDimensionPixelSize(R.styleable.ResolverDrawerLayout_maxWidth, -1);
mMaxCollapsedHeight = a.getDimensionPixelSize(
R.styleable.ResolverDrawerLayout_maxCollapsedHeight, 0);
@@ -1042,6 +1047,18 @@ public class ResolverDrawerLayout extends ViewGroup {
return mAlwaysShowHeight;
}
+ /**
+ * Max width of the drawer needs to be updated after the configuration is changed.
+ * For example, foldables have different layout width when the device is folded and unfolded.
+ */
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mMaxWidthResId > 0) {
+ mMaxWidth = getResources().getDimensionPixelSize(mMaxWidthResId);
+ }
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = getWidth();
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
index 2eeaf53ef146..1c9f04a299cc 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
@@ -19,8 +19,12 @@ package com.android.internal.app
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
+import android.content.pm.ShortcutInfo
+import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.UserHandle
import android.service.chooser.ChooserTarget
@@ -32,12 +36,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.R
import com.android.internal.app.ChooserListAdapter.LoadDirectShareIconTask
+import com.android.internal.app.chooser.DisplayResolveInfo
import com.android.internal.app.chooser.SelectableTargetInfo
import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
import com.android.internal.app.chooser.TargetInfo
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
@@ -46,22 +53,25 @@ import org.mockito.Mockito.verify
@RunWith(AndroidJUnit4::class)
class ChooserListAdapterTest {
- private val packageManager = mock<PackageManager> {
- whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
- }
+ private val packageManager =
+ mock<PackageManager> { whenever(resolveActivity(any(), anyInt())).thenReturn(mock()) }
private val context = InstrumentationRegistry.getInstrumentation().getContext()
private val resolverListController = mock<ResolverListController>()
- private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
- whenever(maxRankedTargets).thenReturn(0)
- }
- private val selectableTargetInfoCommunicator =
- mock<SelectableTargetInfoCommunicator> {
- whenever(targetIntent).thenReturn(mock())
+ private val chooserListCommunicator =
+ mock<ChooserListAdapter.ChooserListCommunicator> {
+ whenever(maxRankedTargets).thenReturn(0)
}
+ private val selectableTargetInfoCommunicator =
+ mock<SelectableTargetInfoCommunicator> { whenever(targetIntent).thenReturn(mock()) }
private val chooserActivityLogger = mock<ChooserActivityLogger>()
+ @Before
+ fun setUp() {
+ whenever(resolverListController.userHandle).thenReturn(UserHandle.CURRENT)
+ }
+
private fun createChooserListAdapter(
- taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
+ taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask = createTaskProvider()
) =
ChooserListAdapterOverride(
context,
@@ -98,9 +108,8 @@ class ChooserListAdapterTest {
view.tag = viewHolderOne
val targetInfo = createSelectableTargetInfo()
val iconTaskOne = mock<LoadDirectShareIconTask>()
- val testTaskProvider = mock<() -> LoadDirectShareIconTask> {
- whenever(invoke()).thenReturn(iconTaskOne)
- }
+ val testTaskProvider =
+ mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
val testSubject = createChooserListAdapter { testTaskProvider.invoke() }
testSubject.testViewBind(view, targetInfo, 0)
@@ -114,6 +123,65 @@ class ChooserListAdapterTest {
verify(testTaskProvider, times(1)).invoke()
}
+ @Test
+ fun getServiceTargetCount_shouldNotShowServiceTargets_returnsZero() {
+ whenever(chooserListCommunicator.shouldShowServiceTargets()).thenReturn(false)
+ val adapter = createChooserListAdapter()
+ whenever(chooserListCommunicator.maxRankedTargets).thenReturn(10)
+ addServiceTargets(adapter, targetCount = 50)
+
+ assertThat(adapter.serviceTargetCount).isEqualTo(0)
+ }
+
+ private fun createTaskProvider(): (SelectableTargetInfo?) -> LoadDirectShareIconTask {
+ val iconTaskOne = mock<LoadDirectShareIconTask>()
+ val testTaskProvider =
+ mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
+ return { testTaskProvider.invoke() }
+ }
+
+ private fun addServiceTargets(adapter: ChooserListAdapter, targetCount: Int) {
+ val origTarget =
+ DisplayResolveInfo(
+ Intent(),
+ createResolveInfo(),
+ Intent(),
+ ResolverListAdapter.ResolveInfoPresentationGetter(context, 200, createResolveInfo())
+ )
+ val targets = mutableListOf<ChooserTarget>()
+ for (i in 1..targetCount) {
+ val score = 1f
+ val componentName = ComponentName("chooser.list.adapter", "Test$i")
+ val extras = Bundle()
+ val icon: Icon? = null
+ targets += ChooserTarget("Title $i", icon, score, componentName, extras)
+ }
+ val directShareToShortcutInfos = mapOf<ChooserTarget, ShortcutInfo>()
+ adapter.addServiceResults(
+ origTarget,
+ targets,
+ ChooserActivity.TARGET_TYPE_DEFAULT,
+ directShareToShortcutInfos
+ )
+ }
+
+ private fun createResolveInfo(): ResolveInfo {
+ val applicationInfo =
+ ApplicationInfo().apply {
+ packageName = "chooser.list.adapter"
+ name = "ChooserListAdapterTestApplication"
+ }
+ val activityInfo =
+ ActivityInfo().apply {
+ packageName = applicationInfo.packageName
+ name = "ChooserListAdapterTest"
+ }
+ activityInfo.applicationInfo = applicationInfo
+ val resolveInfo = ResolveInfo()
+ resolveInfo.activityInfo = activityInfo
+ return resolveInfo
+ }
+
private fun createSelectableTargetInfo(): SelectableTargetInfo =
SelectableTargetInfo(
context,
@@ -125,13 +193,7 @@ class ChooserListAdapterTest {
)
private fun createChooserTarget(): ChooserTarget =
- ChooserTarget(
- "Title",
- null,
- 1f,
- ComponentName("package", "package.Class"),
- Bundle()
- )
+ ChooserTarget("Title", null, 1f, ComponentName("package", "package.Class"), Bundle())
private fun createView(): View {
val view = FrameLayout(context)
@@ -164,23 +226,23 @@ private class ChooserListAdapterOverride(
chooserActivityLogger: ChooserActivityLogger?,
initialIntentsUserHandle: UserHandle?,
private val taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
-) : ChooserListAdapter(
- context,
- payloadIntents,
- initialIntents,
- rList,
- filterLastUsed,
- resolverListController,
- chooserListCommunicator,
- selectableTargetInfoCommunicator,
- packageManager,
- chooserActivityLogger,
- initialIntentsUserHandle,
-) {
+) :
+ ChooserListAdapter(
+ context,
+ payloadIntents,
+ initialIntents,
+ rList,
+ filterLastUsed,
+ resolverListController,
+ chooserListCommunicator,
+ selectableTargetInfoCommunicator,
+ packageManager,
+ chooserActivityLogger,
+ initialIntentsUserHandle,
+ ) {
override fun createLoadDirectShareIconTask(
info: SelectableTargetInfo?
- ): LoadDirectShareIconTask =
- taskProvider.invoke(info)
+ ): LoadDirectShareIconTask = taskProvider.invoke(info)
fun testViewBind(view: View?, info: TargetInfo?, position: Int) {
onBindView(view, info, position)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index bb65da3deade..50ba8975802b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -738,8 +738,17 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
final boolean wasClosing = closingIdx >= 0;
t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
t.setLayer(target.leash, layer);
- // Hide the animation leash if not already visible, let listener show it
- t.setVisibility(target.leash, !wasClosing);
+ if (wasClosing) {
+ // App was previously visible and is closing
+ t.show(target.leash);
+ t.setAlpha(target.leash, 1f);
+ // Also override the task alpha as it was set earlier when dispatching
+ // the transition and setting up the leash to hide the
+ t.setAlpha(change.getLeash(), 1f);
+ } else {
+ // Hide the animation leash, let the listener show it
+ t.hide(target.leash);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
" opening new leaf taskId=%d wasClosing=%b",
target.taskId, wasClosing);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 5d7a3d432dcb..23f50eaba7e7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -34,6 +34,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.wallet.controller.QuickAccessWalletController
+import com.android.systemui.wallet.util.getPaymentCards
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -60,7 +61,7 @@ constructor(
val callback =
object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
- val hasCards = response?.walletCards?.isNotEmpty() == true
+ val hasCards = getPaymentCards(response.walletCards)?.isNotEmpty() == true
trySendWithFailureLogging(
state(
isFeatureEnabled = isWalletAvailable(),
@@ -135,7 +136,7 @@ constructor(
object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
override fun onWalletCardsRetrieved(response: GetWalletCardsResponse) {
continuation.resumeWith(
- Result.success(response?.walletCards ?: emptyList())
+ Result.success(getPaymentCards(response.walletCards) ?: emptyList())
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 60fd10492628..cf64a838c2bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -274,7 +274,9 @@ class MediaProjectionAppSelectorActivity(
recentsViewController.hasRecentTasks
}
- override fun shouldShowContentPreviewWhenEmpty() = shouldShowContentPreview()
+ override fun shouldShowStickyContentPreviewWhenEmpty() = shouldShowContentPreview()
+
+ override fun shouldShowServiceTargets() = false
private fun hasWorkProfile() = mMultiProfilePagerAdapter.count > 1
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 544e6ad295ff..e382eca17369 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -20,6 +20,7 @@ import static android.graphics.drawable.Icon.TYPE_URI;
import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -210,7 +211,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
Log.i(TAG, "Successfully retrieved wallet cards.");
mIsWalletUpdating = false;
- List<WalletCard> cards = response.getWalletCards();
+ List<WalletCard> cards = getPaymentCards(response.getWalletCards());
if (cards.isEmpty()) {
Log.d(TAG, "No wallet cards exist.");
mCardViewDrawable = null;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index 81d04d4458c0..6fd0a4db5a14 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -16,6 +16,8 @@
package com.android.systemui.wallet.ui;
+import static com.android.systemui.wallet.util.WalletCardUtilsKt.getPaymentCards;
+
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -47,10 +49,10 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/** Controller for the wallet card carousel screen. */
public class WalletScreenController implements
@@ -126,22 +128,11 @@ public class WalletScreenController implements
return;
}
Log.i(TAG, "Successfully retrieved wallet cards.");
- List<WalletCard> walletCards = response.getWalletCards();
-
- boolean allUnknown = true;
- for (WalletCard card : walletCards) {
- if (card.getCardType() != WalletCard.CARD_TYPE_UNKNOWN) {
- allUnknown = false;
- break;
- }
- }
+ List<WalletCard> walletCards = getPaymentCards(response.getWalletCards());
- List<WalletCardViewInfo> paymentCardData = new ArrayList<>();
- for (WalletCard card : walletCards) {
- if (allUnknown || card.getCardType() == WalletCard.CARD_TYPE_PAYMENT) {
- paymentCardData.add(new QAWalletCardViewInfo(mContext, card));
- }
- }
+ List<WalletCardViewInfo> paymentCardData = walletCards.stream().map(
+ card -> new QAWalletCardViewInfo(mContext, card)
+ ).collect(Collectors.toList());
// Get on main thread for UI updates.
mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
new file mode 100644
index 000000000000..077b80bde953
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/util/WalletCardUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+
+/**
+ * Filters wallet cards to only those of [WalletCard.CARD_TYPE_PAYMENT], or returns all cards if
+ * they are all of [WalletCard.CARD_TYPE_UNKNOWN] (maintaining pre-U behavior). Used by the wallet
+ * card carousel, quick settings tile, and lock screen.
+ */
+fun getPaymentCards(walletCards: List<WalletCard>): List<WalletCard> {
+ val atLeastOneKnownCardType = walletCards.any { it.cardType != WalletCard.CARD_TYPE_UNKNOWN }
+
+ return if (atLeastOneKnownCardType) {
+ walletCards.filter { it.cardType == WalletCard.CARD_TYPE_PAYMENT }
+ } else {
+ walletCards
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index d36e77889810..b9c0b7fe35d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard.data.quickaffordance
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsResponse
import android.service.quickaccesswallet.QuickAccessWalletClient
+import android.service.quickaccesswallet.WalletCard
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -38,6 +39,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -92,6 +94,40 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
}
@Test
+ fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
+ runTest(UnconfinedTestDispatcher()) {
+ setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ job.cancel()
+ }
+
+ @Test
+ fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
+ runTest(UnconfinedTestDispatcher()) {
+ setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+
+ val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+ assertThat(visibleModel.icon)
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = ICON,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_wallet_button,
+ ),
+ )
+ )
+ job.cancel()
+ }
+
+ @Test
fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest {
setUpState(isWalletFeatureAvailable = false)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -187,6 +223,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
isWalletServiceAvailable: Boolean = true,
isWalletQuerySuccessful: Boolean = true,
hasSelectedCard: Boolean = true,
+ cardType: Int = WalletCard.CARD_TYPE_UNKNOWN
) {
val walletClient: QuickAccessWalletClient = mock()
whenever(walletClient.tileIcon).thenReturn(ICON)
@@ -202,7 +239,19 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
if (isWalletQuerySuccessful) {
onWalletCardsRetrieved(
if (hasSelectedCard) {
- GetWalletCardsResponse(listOf(mock()), 0)
+ GetWalletCardsResponse(
+ listOf(
+ WalletCard.Builder(
+ /*cardId= */ CARD_ID,
+ /*cardType= */ cardType,
+ /*cardImage= */ mock(),
+ /*contentDescription= */ CARD_DESCRIPTION,
+ /*pendingIntent= */ mock()
+ )
+ .build()
+ ),
+ 0
+ )
} else {
GetWalletCardsResponse(emptyList(), 0)
}
@@ -216,5 +265,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
companion object {
private val ICON: Drawable = mock()
+ private const val CARD_ID: String = "Id"
+ private const val CARD_DESCRIPTION: String = "Description"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index b00ae399af21..dd55baddc2d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -395,7 +395,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
PendingIntent.getActivity(mContext, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
WalletCard walletCard =
new WalletCard.Builder(
- CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
+ CARD_ID, mCardImage, CARD_DESCRIPTION, pendingIntent).build();
GetWalletCardsResponse response =
new GetWalletCardsResponse(Collections.singletonList(walletCard), 0);
@@ -410,6 +410,22 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
}
@Test
+ public void testQueryCards_cardDataPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_PAYMENT);
+
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
+ public void testQueryCards_cardDataNonPayment_updateSideViewDrawable() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ setUpWalletCardWithType(/* hasCard =*/ true, WalletCard.CARD_TYPE_NON_PAYMENT);
+
+ assertNull(mTile.getState().sideViewCustomDrawable);
+ }
+
+ @Test
public void testQueryCards_noCards_notUpdateSideViewDrawable() {
setUpWalletCard(/* hasCard= */ false);
@@ -438,6 +454,29 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
verifyZeroInteractions(mQuickAccessWalletClient);
}
+ private WalletCard createWalletCardWithType(Context context, int cardType) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(CARD_ID, cardType, CARD_IMAGE, CARD_DESCRIPTION,
+ pendingIntent).build();
+ }
+
+ private void setUpWalletCardWithType(boolean hasCard, int cardType) {
+ GetWalletCardsResponse response =
+ new GetWalletCardsResponse(
+ hasCard
+ ? Collections.singletonList(
+ createWalletCardWithType(mContext, cardType))
+ : Collections.EMPTY_LIST, 0);
+
+ mTile.handleSetListening(true);
+
+ verify(mController).queryWalletCards(mCallbackCaptor.capture());
+
+ mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ mTestableLooper.processAllMessages();
+ }
+
private void setUpWalletCard(boolean hasCard) {
GetWalletCardsResponse response =
new GetWalletCardsResponse(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 692af6a9a37b..c1d11aa1eb89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -459,7 +459,7 @@ public class WalletScreenControllerTest extends SysuiTestCase {
WalletCard.CARD_TYPE_UNKNOWN),
createWalletCardWithType(mContext, WalletCard.CARD_TYPE_PAYMENT),
createWalletCardWithType(mContext, WalletCard.CARD_TYPE_NON_PAYMENT)
- );
+ );
GetWalletCardsResponse response = new GetWalletCardsResponse(walletCardList, 0);
mController.onWalletCardsRetrieved(response);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
new file mode 100644
index 000000000000..e46c1f554dd6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wallet.util
+
+import android.service.quickaccesswallet.WalletCard
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Test class for WalletCardUtils */
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class WalletCardUtilsTest : SysuiTestCase() {
+
+ private val paymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_PAYMENT)
+ private val nonPaymentCard = createWalletCardWithType(WalletCard.CARD_TYPE_NON_PAYMENT)
+ private val unknownCard = createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+
+ @Test
+ fun paymentCards_cardTypesAllUnknown_getsAllCards() {
+ val walletCardList =
+ mutableListOf(
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN),
+ createWalletCardWithType(WalletCard.CARD_TYPE_UNKNOWN)
+ )
+
+ assertThat(walletCardList).isEqualTo(getPaymentCards(walletCardList))
+ }
+
+ @Test
+ fun paymentCards_cardTypesDifferent_onlyGetsPayment() {
+ val walletCardList = mutableListOf(paymentCard, nonPaymentCard, unknownCard)
+
+ assertThat(getPaymentCards(walletCardList)).isEqualTo(mutableListOf(paymentCard))
+ }
+
+ private fun createWalletCardWithType(cardType: Int): WalletCard {
+ return WalletCard.Builder(
+ /*cardId= */ CARD_ID,
+ /*cardType= */ cardType,
+ /*cardImage= */ mock(),
+ /*contentDescription= */ CARD_DESCRIPTION,
+ /*pendingIntent= */ mock()
+ )
+ .build()
+ }
+
+ companion object {
+ private const val CARD_ID: String = "ID"
+ private const val CARD_DESCRIPTION: String = "Description"
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 60af280614e2..e08fdd65ad94 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1437,6 +1437,7 @@ public class AudioDeviceInventory {
}
});
new MediaMetrics.Item(mMetricsId + "disconnectA2dp")
+ .set(MediaMetrics.Property.EVENT, "disconnectA2dp")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
@@ -1459,6 +1460,7 @@ public class AudioDeviceInventory {
}
});
new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink")
+ .set(MediaMetrics.Property.EVENT, "disconnectA2dpSink")
.record();
toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress));
}
@@ -1474,6 +1476,7 @@ public class AudioDeviceInventory {
}
});
new MediaMetrics.Item(mMetricsId + "disconnectHearingAid")
+ .set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
@@ -1531,6 +1534,7 @@ public class AudioDeviceInventory {
}
});
new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
+ .set(MediaMetrics.Property.EVENT, "disconnectLeAudio")
.record();
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(device,
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 52b92c4c7ca6..378cdba09520 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -31,7 +31,12 @@ class RampAnimator<T> {
private final FloatProperty<T> mProperty;
private float mCurrentValue;
- private float mTargetValue;
+
+ // target in HLG space
+ private float mTargetHlgValue;
+
+ // target in linear space
+ private float mTargetLinearValue;
private float mRate;
private float mAnimationIncreaseMaxTimeSecs;
private float mAnimationDecreaseMaxTimeSecs;
@@ -78,7 +83,8 @@ class RampAnimator<T> {
if (mFirstTime || target != mCurrentValue) {
mFirstTime = false;
mRate = 0;
- mTargetValue = target;
+ mTargetHlgValue = target;
+ mTargetLinearValue = targetLinear;
mCurrentValue = target;
setPropertyValue(target);
mAnimating = false;
@@ -105,13 +111,14 @@ class RampAnimator<T> {
// Otherwise, continue at the previous rate.
if (!mAnimating
|| rate > mRate
- || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
- || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
+ || (target <= mCurrentValue && mCurrentValue <= mTargetHlgValue)
+ || (mTargetHlgValue <= mCurrentValue && mCurrentValue <= target)) {
mRate = rate;
}
- final boolean changed = (mTargetValue != target);
- mTargetValue = target;
+ final boolean changed = (mTargetHlgValue != target);
+ mTargetHlgValue = target;
+ mTargetLinearValue = targetLinear;
// Start animating.
if (!mAnimating && target != mCurrentValue) {
@@ -135,7 +142,11 @@ class RampAnimator<T> {
* into linear space.
*/
private void setPropertyValue(float val) {
- final float linearVal = BrightnessUtils.convertGammaToLinear(val);
+ // To avoid linearVal inconsistency when converting to HLG and back to linear space
+ // used original target linear value for final animation step
+ float linearVal =
+ val == mTargetHlgValue ? mTargetLinearValue : BrightnessUtils.convertGammaToLinear(
+ val);
mProperty.setValue(mObject, linearVal);
}
@@ -150,13 +161,13 @@ class RampAnimator<T> {
final float scale = ValueAnimator.getDurationScale();
if (scale == 0) {
// Animation off.
- mAnimatedValue = mTargetValue;
+ mAnimatedValue = mTargetHlgValue;
} else {
final float amount = timeDelta * mRate / scale;
- if (mTargetValue > mCurrentValue) {
- mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
+ if (mTargetHlgValue > mCurrentValue) {
+ mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetHlgValue);
} else {
- mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
+ mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetHlgValue);
}
}
final float oldCurrentValue = mCurrentValue;
@@ -164,7 +175,7 @@ class RampAnimator<T> {
if (oldCurrentValue != mCurrentValue) {
setPropertyValue(mCurrentValue);
}
- if (mTargetValue == mCurrentValue) {
+ if (mTargetHlgValue == mCurrentValue) {
mAnimating = false;
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7aea63255dc8..a1d28da5f65e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -119,7 +119,6 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
-import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -127,7 +126,6 @@ import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.WindowManagerGlobal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputBinding;
@@ -3073,9 +3071,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
ConcurrentUtils.waitForFutureNoInterrupt(mImeDrawsImeNavBarResLazyInitFuture,
"Waiting for the lazy init of mImeDrawsImeNavBarRes");
}
+ // Whether the current display has a navigation bar. When this is false (e.g. emulator),
+ // the IME should not draw the IME navigation bar.
+ final boolean hasNavigationBar = mWindowManagerInternal
+ .hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
+ ? mCurTokenDisplayId : DEFAULT_DISPLAY);
final boolean canImeDrawsImeNavBar =
- mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get()
- && hasNavigationBarOnCurrentDisplay();
+ mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar;
final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
@@ -3083,21 +3085,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
}
- /**
- * Whether the current display has a navigation bar. When this is {@code false} (e.g. emulator),
- * the IME should <em>not</em> draw the IME navigation bar.
- */
- @GuardedBy("ImfLock.class")
- private boolean hasNavigationBarOnCurrentDisplay() {
- final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- try {
- return wm.hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
- ? mCurTokenDisplayId : DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
@GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 72c6111cba9d..dfd317c58e47 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -976,9 +976,6 @@ public final class MediaProjectionManagerService extends SystemService
throw new SecurityException("Media projections require a foreground service"
+ " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION");
}
-
- mCallback = callback;
- registerCallback(mCallback);
try {
mToken = callback.asBinder();
mDeathEater = () -> {
@@ -1023,6 +1020,11 @@ public final class MediaProjectionManagerService extends SystemService
}
}
startProjectionLocked(this);
+
+ // Register new callbacks after stop has been dispatched to previous session.
+ mCallback = callback;
+ registerCallback(mCallback);
+
// Mark this token as used when the app gets the MediaProjection instance.
mCountStarts++;
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 01786becda61..3639e1b9cb47 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1126,11 +1126,25 @@ final class LetterboxUiController {
return computeAspectRatio(bounds);
}
+ /**
+ * Whether we should enable users to resize the current app.
+ */
+ boolean shouldEnableUserAspectRatioSettings() {
+ // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+ // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+ // the current app doesn't opt-out so the first part of the predicate is true.
+ return !FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
+ && mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && mActivityRecord.mDisplayContent != null
+ && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
+ }
+
+ /**
+ * Whether we should apply the user aspect ratio override to the min aspect ratio for the
+ * current app.
+ */
boolean shouldApplyUserMinAspectRatioOverride() {
- if (FALSE.equals(mBooleanPropertyAllowUserAspectRatioOverride)
- || !mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
- || mActivityRecord.mDisplayContent == null
- || !mActivityRecord.mDisplayContent.getIgnoreOrientationRequest()) {
+ if (!shouldEnableUserAspectRatioSettings()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index c914fa10687f..c8635335a445 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -148,7 +148,8 @@ public class SafeActivityOptions {
.setPendingIntentBackgroundActivityStartMode(
options.getPendingIntentBackgroundActivityStartMode())
.setPendingIntentCreatorBackgroundActivityStartMode(
- options.getPendingIntentCreatorBackgroundActivityStartMode());
+ options.getPendingIntentCreatorBackgroundActivityStartMode())
+ .setRemoteTransition(options.getRemoteTransition());
}
/**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9f2aff28cb11..60b6a8879ecf 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3476,9 +3476,9 @@ class Task extends TaskFragment {
}
}
// User Aspect Ratio Settings is enabled if the app is not in SCM
- info.topActivityEligibleForUserAspectRatioButton =
- mWmService.mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
- && top != null && !info.topActivityInSizeCompat;
+ info.topActivityEligibleForUserAspectRatioButton = top != null
+ && !info.topActivityInSizeCompat
+ && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings();
info.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 805e7ffe7d76..9f1bccb9a27a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -954,4 +954,11 @@ public abstract class WindowManagerInternal {
/** Returns the SurfaceControl accessibility services should use for accessibility overlays. */
public abstract SurfaceControl getA11yOverlayLayer(int displayId);
+
+ /**
+ * Device has a software navigation bar (separate from the status bar) on specific display.
+ *
+ * @param displayId the id of display to check if there is a software navigation bar.
+ */
+ public abstract boolean hasNavigationBar(int displayId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b20be551c114..e6a341fe37e7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8380,6 +8380,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public boolean hasNavigationBar(int displayId) {
+ return WindowManagerService.this.hasNavigationBar(displayId);
+ }
+
+ @Override
public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
synchronized (mGlobalLock) {
mImeTargetChangeListener = listener;
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bfe055354b9c..74a0bafd3a4c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -1279,6 +1279,7 @@ public class WindowManagerShellCommand extends ShellCommand {
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx();
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier();
mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled();
mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
mLetterboxConfiguration.resetEnabledAutomaticReachabilityInBookMode();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
new file mode 100644
index 000000000000..2820da7c49c1
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/RampAnimatorTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.FloatProperty;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class RampAnimatorTest {
+
+ private RampAnimator<TestObject> mRampAnimator;
+
+ private final TestObject mTestObject = new TestObject();
+
+ private final FloatProperty<TestObject> mTestProperty = new FloatProperty<>("mValue") {
+ @Override
+ public void setValue(TestObject object, float value) {
+ object.mValue = value;
+ }
+
+ @Override
+ public Float get(TestObject object) {
+ return object.mValue;
+ }
+ };
+
+ @Before
+ public void setUp() {
+ mRampAnimator = new RampAnimator<>(mTestObject, mTestProperty);
+ }
+
+ @Test
+ public void testInitialValueUsedInLastAnimationStep() {
+ mRampAnimator.setAnimationTarget(0.67f, 0.1f);
+
+ assertEquals(0.67f, mTestObject.mValue, 0);
+ }
+
+ private static class TestObject {
+ private float mValue;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index ddd1221e4c91..d85768dd7588 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -79,6 +79,9 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for the {@link MediaProjectionManagerService} class.
*
@@ -202,6 +205,29 @@ public class MediaProjectionManagerServiceTest {
}
@Test
+ public void testCreateProjection_priorProjectionGrant() throws
+ NameNotFoundException, InterruptedException {
+ // Create a first projection.
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ FakeIMediaProjectionCallback callback1 = new FakeIMediaProjectionCallback();
+ projection.start(callback1);
+
+ // Create a second projection.
+ MediaProjectionManagerService.MediaProjection secondProjection =
+ startProjectionPreconditions();
+ FakeIMediaProjectionCallback callback2 = new FakeIMediaProjectionCallback();
+ secondProjection.start(callback2);
+
+ // Check that the first projection get stopped, but not the second projection.
+ final int timeout = 5;
+ boolean stoppedCallback1 = callback1.mLatch.await(timeout, TimeUnit.SECONDS);
+ boolean stoppedCallback2 = callback2.mLatch.await(timeout, TimeUnit.SECONDS);
+
+ assertThat(stoppedCallback1).isTrue();
+ assertThat(stoppedCallback2).isFalse();
+ }
+
+ @Test
public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
throws NameNotFoundException {
// Create a first projection.
@@ -785,8 +811,10 @@ public class MediaProjectionManagerServiceTest {
}
private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
+ CountDownLatch mLatch = new CountDownLatch(1);
@Override
public void onStop() throws RemoteException {
+ mLatch.countDown();
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 2ad9fa0e5b13..0566f460c655 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -862,6 +862,39 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
}
@Test
+ public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse()
+ throws Exception {
+ prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue()
+ throws Exception {
+ prepareActivityThatShouldApplyUserMinAspectRatioOverride();
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertTrue(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientaion_returnsFalse()
+ throws Exception {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+ mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
+
+ mController = new LetterboxUiController(mWm, mActivity);
+
+ assertFalse(mController.shouldEnableUserAspectRatioSettings());
+ }
+
+ @Test
public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse()
throws Exception {
prepareActivityThatShouldApplyUserMinAspectRatioOverride();
@@ -898,13 +931,26 @@ public class LetterboxUiControllerTest extends WindowTestsBase {
assertTrue(mController.shouldApplyUserMinAspectRatioOverride());
}
- private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientationreturnsFalse() {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
+
+ assertFalse(mController.shouldApplyUserMinAspectRatioOverride());
+ }
+
+ private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
+ boolean orientationRequest) {
spyOn(mController);
- doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
+ doReturn(orientationRequest).when(
+ mLetterboxConfiguration).isUserAppAspectRatioSettingsEnabled();
mDisplayContent.setIgnoreOrientationRequest(true);
doReturn(USER_MIN_ASPECT_RATIO_3_2).when(mController).getUserMinAspectRatioOverrideCode();
}
+ private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
+ prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ true);
+ }
+
private void prepareActivityThatShouldApplyUserFullscreenOverride() {
spyOn(mController);
doReturn(true).when(mLetterboxConfiguration).isUserAppAspectRatioFullscreenEnabled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index f332b6988da0..09b56f450955 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -571,26 +571,28 @@ public class TaskTests extends WindowTestsBase {
final Task task = rootTask.getBottomMostTask();
final ActivityRecord root = task.getTopNonFinishingActivity();
spyOn(mWm.mLetterboxConfiguration);
-
- // When device config flag is disabled the button is not enabled
- doReturn(false).when(mWm.mLetterboxConfiguration)
- .isUserAppAspectRatioSettingsEnabled();
- doReturn(false).when(mWm.mLetterboxConfiguration)
- .isTranslucentLetterboxingEnabled();
- assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
-
- // The flag is enabled
- doReturn(true).when(mWm.mLetterboxConfiguration)
- .isUserAppAspectRatioSettingsEnabled();
spyOn(root);
- doReturn(task).when(root).getOrganizedTask();
- // When the flag is enabled and the top activity is not in size compat mode.
+ spyOn(root.mLetterboxUiController);
+
+ doReturn(true).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
doReturn(false).when(root).inSizeCompatMode();
+ doReturn(task).when(root).getOrganizedTask();
+
+ // The button should be eligible to be displayed
assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ // When shouldApplyUserMinAspectRatioOverride is disable the button is not enabled
+ doReturn(false).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ doReturn(true).when(root.mLetterboxUiController)
+ .shouldEnableUserAspectRatioSettings();
+
// When in size compat mode the button is not enabled
doReturn(true).when(root).inSizeCompatMode();
assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ doReturn(false).when(root).inSizeCompatMode();
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 60d640c71d3a..fbb2eb99a1ba 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10182,7 +10182,7 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
sDefaults.putBoolean(KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL, false);
- sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, true);
+ sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, false);
sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);