Merge "Remove placeholder namespace for media_solutions flag" into main
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 4851279..d0d76a4 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -454,12 +454,11 @@
*/
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
- if (mContext != null
+ if (allowsRegisterDefaultOnBackInvokedCallback() && mContext != null
&& WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
// Add onBackPressed as default back behavior.
mDefaultBackCallback = this::onBackPressed;
getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback);
- mDefaultBackCallback = null;
}
}
@@ -470,9 +469,18 @@
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
if (mDefaultBackCallback != null) {
getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback);
+ mDefaultBackCallback = null;
}
}
+ /**
+ * Whether this dialog allows to register the default onBackInvokedCallback.
+ * @hide
+ */
+ protected boolean allowsRegisterDefaultOnBackInvokedCallback() {
+ return true;
+ }
+
private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy";
@@ -697,7 +705,8 @@
if (event.isTracking() && !event.isCanceled()) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
- if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
+ if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)
+ || !allowsRegisterDefaultOnBackInvokedCallback()) {
onBackPressed();
return true;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 892b45e..7d2ef4d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -12819,7 +12819,6 @@
} else {
mBackgroundColor = rawColor;
}
- mProtectionColor = COLOR_INVALID; // filled in at the end
mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
ContrastColorUtil.resolvePrimaryColor(ctx, mBackgroundColor, nightMode),
mBackgroundColor, 4.5);
@@ -12836,7 +12835,6 @@
} else {
int[] attrs = {
R.attr.colorSurface,
- R.attr.colorBackgroundFloating,
R.attr.textColorPrimary,
R.attr.textColorSecondary,
R.attr.colorAccent,
@@ -12848,15 +12846,14 @@
};
try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) {
mBackgroundColor = getColor(ta, 0, nightMode ? Color.BLACK : Color.WHITE);
- mProtectionColor = getColor(ta, 1, COLOR_INVALID);
- mPrimaryTextColor = getColor(ta, 2, COLOR_INVALID);
- mSecondaryTextColor = getColor(ta, 3, COLOR_INVALID);
- mPrimaryAccentColor = getColor(ta, 4, COLOR_INVALID);
- mSecondaryAccentColor = getColor(ta, 5, COLOR_INVALID);
- mTertiaryAccentColor = getColor(ta, 6, COLOR_INVALID);
- mOnAccentTextColor = getColor(ta, 7, COLOR_INVALID);
- mErrorColor = getColor(ta, 8, COLOR_INVALID);
- mRippleAlpha = Color.alpha(getColor(ta, 9, 0x33ffffff));
+ mPrimaryTextColor = getColor(ta, 1, COLOR_INVALID);
+ mSecondaryTextColor = getColor(ta, 2, COLOR_INVALID);
+ mPrimaryAccentColor = getColor(ta, 3, COLOR_INVALID);
+ mSecondaryAccentColor = getColor(ta, 4, COLOR_INVALID);
+ mTertiaryAccentColor = getColor(ta, 5, COLOR_INVALID);
+ mOnAccentTextColor = getColor(ta, 6, COLOR_INVALID);
+ mErrorColor = getColor(ta, 7, COLOR_INVALID);
+ mRippleAlpha = Color.alpha(getColor(ta, 8, 0x33ffffff));
}
mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor,
mBackgroundColor, nightMode);
@@ -12889,9 +12886,7 @@
}
}
// make sure every color has a valid value
- if (mProtectionColor == COLOR_INVALID) {
- mProtectionColor = ColorUtils.blendARGB(mPrimaryTextColor, mBackgroundColor, 0.8f);
- }
+ mProtectionColor = ColorUtils.blendARGB(mPrimaryTextColor, mBackgroundColor, 0.9f);
}
/** calculates the contrast color for the non-colorized notifications */
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index c3df17d..529363f 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -344,15 +344,22 @@
// If there is a label for the launcher intent, then use that as it is typically shorter.
// Otherwise, just use the top-level application name.
Intent launchIntent = pm.getLaunchIntentForPackage(mContext.getPackageName());
+ if (launchIntent == null) {
+ return getDefaultCallingApplicationLabel();
+ }
List<ResolveInfo> infos =
pm.queryIntentActivities(
launchIntent, PackageManager.ResolveInfoFlags.of(MATCH_DEFAULT_ONLY));
if (infos.size() > 0) {
return infos.get(0).loadLabel(pm);
}
+ return getDefaultCallingApplicationLabel();
+ }
+
+ private CharSequence getDefaultCallingApplicationLabel() {
return mContext.getApplicationInfo()
.loadSafeLabel(
- pm,
+ mContext.getPackageManager(),
/* ellipsizeDip= */ 0,
TextUtils.SAFE_STRING_FLAG_SINGLE_LINE
| TextUtils.SAFE_STRING_FLAG_TRIM);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 795eb4a..8f653b3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -576,6 +576,12 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static final long DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE = 148086656L;
+ /**
+ * Enable the logic to allow hiding the IME caption bar ("fake" IME navigation bar).
+ * @hide
+ */
+ public static final boolean ENABLE_HIDE_IME_CAPTION_BAR = true;
+
LayoutInflater mInflater;
TypedArray mThemeAttrs;
@UnsupportedAppUsage
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 78388ef..c01664e 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -16,6 +16,8 @@
package android.inputmethodservice;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
+import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import android.animation.ValueAnimator;
@@ -230,6 +232,16 @@
setIconTintInternal(calculateTargetDarkIntensity(mAppearance,
mDrawLegacyNavigationBarBackground));
+
+ if (ENABLE_HIDE_IME_CAPTION_BAR) {
+ mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> {
+ if (mNavigationBarFrame != null) {
+ boolean visible = insets.isVisible(captionBar());
+ mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ return view.onApplyWindowInsets(insets);
+ });
+ }
}
private void uninstallNavigationBarFrameIfNecessary() {
@@ -240,6 +252,9 @@
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(mNavigationBarFrame);
}
+ if (ENABLE_HIDE_IME_CAPTION_BAR) {
+ mNavigationBarFrame.setOnApplyWindowInsetsListener(null);
+ }
mNavigationBarFrame = null;
}
@@ -414,7 +429,9 @@
decor.bringChildToFront(mNavigationBarFrame);
}
}
- mNavigationBarFrame.setVisibility(View.VISIBLE);
+ if (!ENABLE_HIDE_IME_CAPTION_BAR) {
+ mNavigationBarFrame.setVisibility(View.VISIBLE);
+ }
}
}
@@ -435,6 +452,11 @@
mShouldShowImeSwitcherWhenImeIsShown;
mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
+ if (ENABLE_HIDE_IME_CAPTION_BAR) {
+ mService.mWindow.getWindow().getDecorView().getWindowInsetsController()
+ .setImeCaptionBarInsetsHeight(getImeCaptionBarHeight());
+ }
+
if (imeDrawsImeNavBar) {
installNavigationBarFrameIfNecessary();
if (mNavigationBarFrame == null) {
@@ -528,6 +550,16 @@
return drawLegacyNavigationBarBackground;
}
+ /**
+ * Returns the height of the IME caption bar if this should be shown, or {@code 0} instead.
+ */
+ private int getImeCaptionBarHeight() {
+ return mImeDrawsImeNavBar
+ ? mService.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height)
+ : 0;
+ }
+
@Override
public String toDebugString() {
return "{mImeDrawsImeNavBar=" + mImeDrawsImeNavBar
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 5704dac..e4a09a6 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -79,6 +79,13 @@
@WindowState
private int mWindowState = WindowState.TOKEN_PENDING;
+ @Override
+ protected boolean allowsRegisterDefaultOnBackInvokedCallback() {
+ // Do not register OnBackInvokedCallback from Dialog#onStart, InputMethodService will
+ // register CompatOnBackInvokedCallback for input method window.
+ return false;
+ }
+
/**
* Set {@link IBinder} window token to the window.
*
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index b24b45d..08b32bf 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -525,14 +525,14 @@
public abstract long getDuration();
/**
- * Checks if a given {@link Vibrator} can play this effect as intended.
+ * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
*
- * <p>See @link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information
- * about what counts as supported by a vibrator, and what counts as not.
+ * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more
+ * information about what counts as supported by a vibrator, and what counts as not.
*
* @hide
*/
- public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+ public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo);
/**
* Returns true if this effect could represent a touch haptic feedback.
@@ -813,9 +813,9 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
for (VibrationEffectSegment segment : mSegments) {
- if (!segment.areVibrationFeaturesSupported(vibrator)) {
+ if (!segment.areVibrationFeaturesSupported(vibratorInfo)) {
return false;
}
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 4e852e3..79e0ca8 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -216,9 +216,7 @@
*/
@TestApi
public boolean hasFrequencyControl() {
- // We currently can only control frequency of the vibration using the compose PWLE method.
- return getInfo().hasCapability(
- IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ return getInfo().hasFrequencyControl();
}
/**
@@ -240,7 +238,7 @@
* @hide
*/
public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) {
- return effect.areVibrationFeaturesSupported(this);
+ return getInfo().areVibrationFeaturesSupported(effect);
}
/**
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 02e6856..0b7d7c3 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -242,6 +242,17 @@
}
/**
+ * Check whether the vibrator has frequency control.
+ *
+ * @return True if the hardware can control the frequency of the vibrations, otherwise false.
+ */
+ public boolean hasFrequencyControl() {
+ // We currently can only control frequency of the vibration using the compose PWLE method.
+ return hasCapability(
+ IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ }
+
+ /**
* Returns a default value to be applied to composed PWLE effects for braking.
*
* @return a supported braking value, one of android.hardware.vibrator.Braking.*
@@ -323,6 +334,23 @@
}
/**
+ * Query whether or not the vibrator supports all components of a given {@link VibrationEffect}
+ * (i.e. the vibrator can play the given effect as intended).
+ *
+ * <p>See {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more
+ * information on how the vibrator support is determined.
+ *
+ * @param effect the {@link VibrationEffect} to check if it is supported
+ * @return {@code true} if the vibrator can play the given {@code effect} as intended,
+ * {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) {
+ return effect.areVibrationFeaturesSupported(this);
+ }
+
+ /**
* Query the estimated duration of given primitive.
*
* @param primitiveId Which primitives to query for.
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 42b6c2da..a035092 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -23,6 +23,7 @@
import android.os.Parcelable;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.VibratorInfo;
import java.util.Objects;
@@ -77,8 +78,8 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
- if (vibrator.areAllEffectsSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
+ if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
return true;
}
if (!mFallback) {
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index c52a09c..95d97bf 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -22,7 +22,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
import com.android.internal.util.Preconditions;
@@ -77,8 +77,8 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
- return vibrator.areAllPrimitivesSupported(mPrimitiveId);
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
+ return vibratorInfo.isPrimitiveSupported(mPrimitiveId);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index e997bcd..5f9d102 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -20,7 +20,7 @@
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
import com.android.internal.util.Preconditions;
@@ -96,7 +96,7 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
boolean areFeaturesSupported = true;
// If the start/end frequencies are not the same, require frequency control since we need to
// ramp up/down the frequency.
@@ -104,7 +104,7 @@
// If there is no frequency ramping, make sure that the one frequency used does not
// require frequency control.
|| frequencyRequiresFrequencyControl(mStartFrequencyHz)) {
- areFeaturesSupported &= vibrator.hasFrequencyControl();
+ areFeaturesSupported &= vibratorInfo.hasFrequencyControl();
}
// If the start/end amplitudes are not the same, require amplitude control since we need to
// ramp up/down the amplitude.
@@ -112,7 +112,7 @@
// If there is no amplitude ramping, make sure that the amplitude used does not
// require amplitude control.
|| amplitudeRequiresAmplitudeControl(mStartAmplitude)) {
- areFeaturesSupported &= vibrator.hasAmplitudeControl();
+ areFeaturesSupported &= vibratorInfo.hasAmplitudeControl();
}
return areFeaturesSupported;
}
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index a585aa8..9576a5b 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -21,7 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
import com.android.internal.util.Preconditions;
@@ -82,13 +82,13 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
boolean areFeaturesSupported = true;
if (frequencyRequiresFrequencyControl(mFrequencyHz)) {
- areFeaturesSupported &= vibrator.hasFrequencyControl();
+ areFeaturesSupported &= vibratorInfo.hasFrequencyControl();
}
if (amplitudeRequiresAmplitudeControl(mAmplitude)) {
- areFeaturesSupported &= vibrator.hasAmplitudeControl();
+ areFeaturesSupported &= vibratorInfo.hasAmplitudeControl();
}
return areFeaturesSupported;
}
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 3b286a7..17ac36f 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -21,7 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
/**
* Representation of a single segment of a {@link VibrationEffect}.
@@ -65,7 +65,7 @@
*
* @hide
*/
- public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+ public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo);
/**
* Returns true if this segment could be a haptic feedback effect candidate.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 4ecfc40..c6d8bd1 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,10 +16,12 @@
package android.view;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
import static android.os.Trace.TRACE_TAG_VIEW;
import static android.view.InsetsControllerProto.CONTROL;
import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsSource.ID_IME;
+import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.LAST;
@@ -40,6 +42,7 @@
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.graphics.Insets;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.Handler;
@@ -652,6 +655,7 @@
private int mLastWindowingMode;
private boolean mStartingAnimation;
private int mCaptionInsetsHeight = 0;
+ private int mImeCaptionBarInsetsHeight = 0;
private boolean mAnimationsDisabled;
private boolean mCompatSysUiVisibilityStaled;
@@ -693,6 +697,9 @@
if (!CAPTION_ON_SHELL && source1.getType() == captionBar()) {
return;
}
+ if (source1.getId() == ID_IME_CAPTION_BAR) {
+ return;
+ }
// Don't change the indexes of the sources while traversing. Remove it later.
mPendingRemoveIndexes.add(index1);
@@ -823,6 +830,9 @@
if (mFrame.equals(frame)) {
return;
}
+ if (mImeCaptionBarInsetsHeight != 0) {
+ setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight);
+ }
mHost.notifyInsetsChanged();
mFrame.set(frame);
}
@@ -1007,6 +1017,12 @@
// Ensure to update all existing source consumers
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ if (consumer.getId() == ID_IME_CAPTION_BAR) {
+ // The inset control for the IME caption bar will never be dispatched
+ // by the server.
+ continue;
+ }
+
final InsetsSourceControl control = mTmpControlArray.get(consumer.getId());
if (control != null) {
controllableTypes |= control.getType();
@@ -1499,7 +1515,8 @@
continue;
}
final InsetsSourceControl control = consumer.getControl();
- if (control != null && control.getLeash() != null) {
+ if (control != null
+ && (control.getLeash() != null || control.getId() == ID_IME_CAPTION_BAR)) {
controls.put(control.getId(), new InsetsSourceControl(control));
typesReady |= consumer.getType();
}
@@ -1885,6 +1902,35 @@
}
@Override
+ public void setImeCaptionBarInsetsHeight(int height) {
+ if (!ENABLE_HIDE_IME_CAPTION_BAR) {
+ return;
+ }
+ Rect newFrame = new Rect(mFrame.left, mFrame.bottom - height, mFrame.right, mFrame.bottom);
+ InsetsSource source = mState.peekSource(ID_IME_CAPTION_BAR);
+ if (mImeCaptionBarInsetsHeight != height
+ || (source != null && !newFrame.equals(source.getFrame()))) {
+ mImeCaptionBarInsetsHeight = height;
+ if (mImeCaptionBarInsetsHeight != 0) {
+ mState.getOrCreateSource(ID_IME_CAPTION_BAR, captionBar())
+ .setFrame(newFrame);
+ getSourceConsumer(ID_IME_CAPTION_BAR, captionBar()).setControl(
+ new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(),
+ null /* leash */, false /* initialVisible */,
+ new Point(), Insets.NONE),
+ new int[1], new int[1]);
+ } else {
+ mState.removeSource(ID_IME_CAPTION_BAR);
+ InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR);
+ if (sourceConsumer != null) {
+ sourceConsumer.setControl(null, new int[1], new int[1]);
+ }
+ }
+ mHost.notifyInsetsChanged();
+ }
+ }
+
+ @Override
public void setSystemBarsBehavior(@Behavior int behavior) {
mHost.setSystemBarsBehavior(behavior);
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 6441186..ff009ed 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -20,6 +20,7 @@
import static android.view.InsetsSourceProto.TYPE;
import static android.view.InsetsSourceProto.VISIBLE;
import static android.view.InsetsSourceProto.VISIBLE_FRAME;
+import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.ime;
import android.annotation.IntDef;
@@ -47,6 +48,9 @@
/** The insets source ID of IME */
public static final int ID_IME = createId(null, 0, ime());
+ /** The insets source ID of the IME caption bar ("fake" IME navigation bar). */
+ static final int ID_IME_CAPTION_BAR =
+ InsetsSource.createId(null /* owner */, 1 /* index */, captionBar());
/**
* Controls whether this source suppresses the scrim. If the scrim is ignored, the system won't
@@ -215,8 +219,12 @@
// During drag-move and drag-resizing, the caption insets position may not get updated
// before the app frame get updated. To layout the app content correctly during drag events,
// we always return the insets with the corresponding height covering the top.
+ // However, with the "fake" IME navigation bar treated as a caption bar, we return the
+ // insets with the corresponding height the bottom.
if (getType() == WindowInsets.Type.captionBar()) {
- return Insets.of(0, frame.height(), 0, 0);
+ return getId() == ID_IME_CAPTION_BAR
+ ? Insets.of(0, 0, 0, frame.height())
+ : Insets.of(0, frame.height(), 0, 0);
}
// Checks for whether there is shared edge with insets for 0-width/height window.
final boolean hasIntersection = relativeFrame.isEmpty()
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index e8f62fc..a4cbc52 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -44,6 +44,7 @@
private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
= new ArrayList<>();
private int mCaptionInsetsHeight = 0;
+ private int mImeCaptionBarInsetsHeight = 0;
private WindowInsetsAnimationControlListener mLoggingListener;
private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
@@ -91,6 +92,11 @@
}
@Override
+ public void setImeCaptionBarInsetsHeight(int height) {
+ mImeCaptionBarInsetsHeight = height;
+ }
+
+ @Override
public void setSystemBarsBehavior(int behavior) {
if (mReplayedInsetsController != null) {
mReplayedInsetsController.setSystemBarsBehavior(behavior);
@@ -168,6 +174,9 @@
if (mCaptionInsetsHeight != 0) {
controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
}
+ if (mImeCaptionBarInsetsHeight != 0) {
+ controller.setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight);
+ }
if (mAnimationsDisabled) {
controller.setAnimationsDisabled(true);
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index bc0bab7..cc2cd79 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -250,6 +250,16 @@
void setCaptionInsetsHeight(int height);
/**
+ * Sets the insets height for the IME caption bar, which corresponds to the
+ * "fake" IME navigation bar.
+ *
+ * @param height the insets height of the IME caption bar.
+ * @hide
+ */
+ default void setImeCaptionBarInsetsHeight(int height) {
+ }
+
+ /**
* Controls the behavior of system bars.
*
* @param behavior Determines how the bars behave when being hidden by the application.
diff --git a/core/res/res/drawable/focus_event_rotary_input_background.xml b/core/res/res/drawable/focus_event_rotary_input_background.xml
new file mode 100644
index 0000000..512cd68
--- /dev/null
+++ b/core/res/res/drawable/focus_event_rotary_input_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="focus_event_rotary_input_background"
+ android:shape="rectangle">
+
+ <!-- View background color -->
+ <solid android:color="#80741b47" />
+
+ <!-- View border color and width -->
+ <stroke android:width="1dp" android:color="#ffff00ff" />
+
+ <!-- The radius makes the corners rounded -->
+ <corners android:radius="4dp" />
+
+</shape>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4614dce..a78b040 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5041,6 +5041,21 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ A float array containing a list of ambient brightnesses, in Lux. This array,
+ together with config_displayWhiteBalanceLowLightAmbientBiasesStrong, is used to generate a
+ lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
+ ambient brightness readings to a bias, where the bias is used to linearly interpolate
+ between ambient color temperature and
+ config_displayWhiteBalanceLowLightAmbientColorTemperatureIdle.
+ This table is optional. If used, this array must,
+ 1) Contain at least two entries
+ 2) Be the same length as config_displayWhiteBalanceLowLightAmbientBiasesStrong. -->
+ <array name ="config_displayWhiteBalanceLowLightAmbientBrightnessesStrong">
+ <item>10.0</item>
+ <item>10.0</item>
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
An array containing a list of biases. See
config_displayWhiteBalanceLowLightAmbientBrightnesses for additional details.
This array must be in the range of [0.0, 1.0]. -->
@@ -5050,12 +5065,28 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ An array containing a list of biases. See
+ config_displayWhiteBalanceLowLightAmbientBrightnessesStrong for additional details.
+ This array must be in the range of [0.0, 1.0]. -->
+ <array name ="config_displayWhiteBalanceLowLightAmbientBiasesStrong">
+ <item>0.0</item>
+ <item>1.0</item>
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
The ambient color temperature (in cct) to which we interpolate towards using the
the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnesses
and config_displayWhiteBalanceLowLightAmbientBiases. -->
<item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item>
<!-- See DisplayWhiteBalanceController.
+ The ambient color temperature (in cct) to which we interpolate towards using the
+ the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnessesStrong
+ and config_displayWhiteBalanceLowLightAmbientBiasesStrong. Used when device is in Idle Screen
+ Brightness mode. -->
+ <item name="config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong" format="float" type="dimen">6500.0</item>
+
+ <!-- See DisplayWhiteBalanceController.
A float array containing a list of ambient brightnesses, in Lux. This array,
together with config_displayWhiteBalanceHighLightAmbientBiases, is used to generate a
lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
@@ -5069,6 +5100,19 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ A float array containing a list of ambient brightnesses, in Lux. This array,
+ together with config_displayWhiteBalanceHighLightAmbientBiasesStrong, is used to generate a
+ lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
+ ambient brightness readings to a bias, where the bias is used to linearly interpolate
+ between ambient color temperature and
+ config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong.
+ This table is optional. If used, this array must,
+ 1) Contain at least two entries
+ 2) Be the same length as config_displayWhiteBalanceHighLightAmbientBiasesStrong. -->
+ <array name ="config_displayWhiteBalanceHighLightAmbientBrightnessesStrong">
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
An array containing a list of biases. See
config_displayWhiteBalanceHighLightAmbientBrightnesses for additional details.
This array must be in the range of [0.0, 1.0]. -->
@@ -5076,12 +5120,26 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ An array containing a list of biases. See
+ config_displayWhiteBalanceHighLightAmbientBrightnessesStrong for additional details.
+ This array must be in the range of [0.0, 1.0]. -->
+ <array name ="config_displayWhiteBalanceHighLightAmbientBiasesStrong">
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
The ambient color temperature (in cct) to which we interpolate towards using the
the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnesses
and config_displayWhiteBalanceHighLightAmbientBiases. -->
<item name="config_displayWhiteBalanceHighLightAmbientColorTemperature" format="float" type="dimen">8000.0</item>
<!-- See DisplayWhiteBalanceController.
+ The ambient color temperature (in cct) to which we interpolate towards using the
+ the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnessesStrong
+ and config_displayWhiteBalanceHighLightAmbientBiasesStrong. Used when device is in Idle
+ Screen Brightness mode. -->
+ <item name="config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong" format="float" type="dimen">8000.0</item>
+
+ <!-- See DisplayWhiteBalanceController.
A float array containing a list of ambient color temperatures, in Kelvin. This array,
together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c5aa8b0..0dd6c74 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1895,22 +1895,22 @@
<!-- Content description which should be used for the fingerprint icon. -->
<string name="fingerprint_icon_content_description">Fingerprint icon</string>
+ <!-- Notification name shown when the system requires the user to set up device unlock. [CHAR LIMIT=NONE] -->
+ <string name="device_unlock_notification_name">Device unlock</string>
+ <!-- Notification title shown when the system suggests the user to set up another way to unlock. [CHAR LIMIT=NONE] -->
+ <string name="alternative_unlock_setup_notification_title">Try another way to unlock</string>
+ <!-- Notification content shown when the system suggests the user to enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="alternative_face_setup_notification_content">Use Face Unlock when your fingerprint isn\'t recognized, like when your fingers are wet</string>
+ <!-- Notification content shown when the system suggests the user to enroll their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="alternative_fp_setup_notification_content">Use Fingerprint Unlock when your face isn\'t recognized, like when there\'s not enough light</string>
<!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_name">Face Unlock</string>
<!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_title">Issue with Face Unlock</string>
<!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_content">Tap to delete your face model, then add your face again</string>
- <!-- Title of a notification that directs the user to set up Face Unlock by enrolling their face. [CHAR LIMIT=NONE] -->
- <string name="face_setup_notification_title">Set up Face Unlock</string>
- <!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
- <string name="face_setup_notification_content">Unlock your phone by looking at it</string>
<!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
<string name="face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
- <!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
- <string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
- <!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
- <string name="fingerprint_setup_notification_content">Tap to add a fingerprint</string>
<!-- Notification name shown when the system requires the user to re-calibrate their fingerprint. [CHAR LIMIT=NONE] -->
<string name="fingerprint_recalibrate_notification_name">Fingerprint Unlock</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c4cdd48..eaccd43 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2597,6 +2597,12 @@
<!-- Biometric FRR config -->
<java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" />
+ <!-- Biometric FRR notification messages -->
+ <java-symbol type="string" name="device_unlock_notification_name" />
+ <java-symbol type="string" name="alternative_unlock_setup_notification_title" />
+ <java-symbol type="string" name="alternative_face_setup_notification_content" />
+ <java-symbol type="string" name="alternative_fp_setup_notification_content" />
+
<!-- Device credential strings for BiometricManager -->
<java-symbol type="string" name="screen_lock_app_setting_name" />
<java-symbol type="string" name="screen_lock_dialog_default_subtitle" />
@@ -2637,8 +2643,6 @@
<java-symbol type="string" name="fingerprint_recalibrate_notification_name" />
<java-symbol type="string" name="fingerprint_recalibrate_notification_title" />
<java-symbol type="string" name="fingerprint_recalibrate_notification_content" />
- <java-symbol type="string" name="fingerprint_setup_notification_title" />
- <java-symbol type="string" name="fingerprint_setup_notification_content" />
<java-symbol type="string" name="fingerprint_error_power_pressed" />
<!-- Fingerprint config -->
@@ -2706,7 +2710,6 @@
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
<java-symbol type="string" name="face_authenticated_confirmation_required" />
<java-symbol type="string" name="face_error_security_update_required" />
- <java-symbol type="string" name="face_setup_notification_title" />
<java-symbol type="string" name="config_biometric_prompt_ui_package" />
<java-symbol type="array" name="config_biometric_sensors" />
@@ -4170,11 +4173,17 @@
<java-symbol type="array" name="config_displayWhiteBalanceIncreaseThresholds" />
<java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
<java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnesses" />
+ <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnessesStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiases" />
+ <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiasesStrong" />
<java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnesses" />
+ <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnessesStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiases" />
+ <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiasesStrong" />
<java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperature" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
<java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
<java-symbol type="array" name="config_displayWhiteBalanceStrongAmbientColorTemperatures" />
@@ -5188,6 +5197,7 @@
<java-symbol type="style" name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" />
<java-symbol type="drawable" name="focus_event_pressed_key_background" />
+ <java-symbol type="drawable" name="focus_event_rotary_input_background" />
<java-symbol type="string" name="config_defaultShutdownVibrationFile" />
<java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index c14da29..7f56eb7 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -88,7 +88,7 @@
resource_dirs: ["res"],
resource_zips: [":FrameworksCoreTests_apks_as_resources"],
- java_resources: [":ApkVerityTestCertDer"],
+ java_resources: [":FrameworksCoreTests_unit_test_cert_der"],
data: [
":BinderDeathRecipientHelperApp1",
diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp
index 8d4ecf4..cefdc4d 100644
--- a/core/tests/coretests/certs/Android.bp
+++ b/core/tests/coretests/certs/Android.bp
@@ -13,3 +13,8 @@
name: "FrameworksCoreTests_unit_test_cert",
certificate: "unit_test",
}
+
+filegroup {
+ name: "FrameworksCoreTests_unit_test_cert_der",
+ srcs: ["unit_test.der"],
+}
diff --git a/core/tests/coretests/certs/README b/core/tests/coretests/certs/README
index 00917a1..b5c096e 100644
--- a/core/tests/coretests/certs/README
+++ b/core/tests/coretests/certs/README
@@ -2,3 +2,5 @@
development/tools/make_key unit_test '/CN=unit_test'
development/tools/make_key unit_test_diff '/CN=unit_test_diff'
+
+openssl x509 -in unit_test.x509.pem -out unit_test.der -outform der
diff --git a/core/tests/coretests/certs/unit_test.der b/core/tests/coretests/certs/unit_test.der
new file mode 100644
index 0000000..4dbbc49
--- /dev/null
+++ b/core/tests/coretests/certs/unit_test.der
Binary files differ
diff --git a/core/tests/coretests/res/raw/fsverity_sig b/core/tests/coretests/res/raw/fsverity_sig
index b2f335d..2c28f0b 100644
--- a/core/tests/coretests/res/raw/fsverity_sig
+++ b/core/tests/coretests/res/raw/fsverity_sig
Binary files differ
diff --git a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
index 1513654..a978e3b 100644
--- a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
@@ -229,11 +229,12 @@
@Test
public void testSignatureGeneratedExternally() throws Exception {
var context = InstrumentationRegistry.getInstrumentation().getContext();
- byte[] cert = getClass().getClassLoader().getResourceAsStream("ApkVerityTestCert.der")
+ byte[] cert = getClass().getClassLoader().getResourceAsStream("unit_test.der")
.readAllBytes();
// The signature is generated by:
- // fsverity sign <(echo -n fs-verity) fsverity_sig --key=ApkVerityTestKey.pem \
- // --cert=ApkVerityTestCert.pem
+ // openssl pkcs8 -topk8 -nocrypt -in certs/unit_test.pk8 -out certs/unit_test.key.pem
+ // fsverity sign <(echo -n fs-verity) fsverity_sig --key=certs/unit_test.key.pem \
+ // --cert=certs/unit_test.x509.pem
byte[] sig = context.getResources().openRawResource(R.raw.fsverity_sig).readAllBytes();
// The fs-verity digest is generated by:
// fsverity digest --compact <(echo -n fs-verity)
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 8be489e..e875875 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -41,8 +41,6 @@
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
-import androidx.test.InstrumentationRegistry;
-
import com.android.internal.R;
import org.jetbrains.annotations.NotNull;
@@ -838,31 +836,29 @@
@Test
public void testAreVibrationFeaturesSupported_allSegmentsSupported() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
- .build());
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
assertTrue(VibrationEffect.createWaveform(
/* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(VibrationEffect.createWaveform(
/* timings= */ new long[] {1, 2, 3},
/* amplitudes= */ new int[] {10, 20, 40},
/* repeatIndex= */ 2)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(
VibrationEffect.startComposition()
.addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
.repeatEffectIndefinitely(TEST_ONE_SHOT)
.compose()
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testAreVibrationFeaturesSupported_withUnsupportedSegments() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1).build());
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build();
assertFalse(
VibrationEffect.startComposition()
@@ -872,7 +868,7 @@
/* amplitudes= */ new int[] {10, 20, 40},
/* repeatIndex= */ -1))
.compose()
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
@@ -996,13 +992,4 @@
return context;
}
-
- private Vibrator createVibratorWithCustomInfo(VibratorInfo info) {
- return new SystemVibrator(InstrumentationRegistry.getContext()) {
- @Override
- public VibratorInfo getInfo() {
- return info;
- }
- };
- }
}
diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
index ff917aa..808c4ec 100644
--- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
@@ -57,6 +57,17 @@
}
@Test
+ public void testHasFrequencyControl() {
+ VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ assertFalse(noCapabilities.hasFrequencyControl());
+ VibratorInfo composeAndFrequencyControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setCapabilities(
+ IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
+ .build();
+ assertTrue(composeAndFrequencyControl.hasFrequencyControl());
+ }
+
+ @Test
public void testHasCapabilities() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
@@ -335,4 +346,186 @@
assertEquals(original, restored);
assertEquals(original.hashCode(), restored.hashCode());
}
+
+ @Test
+ public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudeUnsupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build();
+
+ // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT).
+ assertFalse(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30)));
+ assertFalse(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255)));
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudeSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build();
+
+ // All amplitudes are min, max, or default. Requires no amplitude control.
+ assertTrue(info.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_withAmplitudeControl_allWaveformsSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
+
+ // All forms of amplitudes are valid when amplitude control is available.
+ assertTrue(info.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
+ assertTrue(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(7, 255, 0, 0, 60)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_compositionsWithSupportedPrimitivesSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose()));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ /* scale= */ 0.2f,
+ /* delay= */ 200)
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_compositionsWithUnupportedPrimitivesUnsupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .compose()));
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
+ .build();
+
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .compose()));
+
+ info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK)
+ .build();
+
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addEffect(VibrationEffect.createWaveform(
+ // These timings are given either 0 or default amplitudes, which
+ // do not require vibrator's amplitude control.
+ /* timings= */ new long[] {1, 2, 3},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
+ .build();
+
+ // Not supported due to the TICK primitive, which the vibrator has no support for.
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .compose()));
+ // Not supported due to the THUD effect, which the vibrator has no support for.
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD))
+ .compose()));
+
+ info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP)
+ .build();
+
+ // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or
+ // DEFAULT), because the vibrator has no amplitude control.
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .compose()));
+ }
+
+ private static VibrationEffect waveformWithAmplitudes(int...amplitudes) {
+ long[] timings = new long[amplitudes.length];
+ for (int i = 0; i < timings.length; i++) {
+ timings[i] = i * 2; // Arbitrary timings.
+ }
+ return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1);
+ }
}
diff --git a/core/tests/vibrator/src/android/os/VibratorTest.java b/core/tests/vibrator/src/android/os/VibratorTest.java
index c559e34..8141ca4 100644
--- a/core/tests/vibrator/src/android/os/VibratorTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorTest.java
@@ -578,189 +578,6 @@
assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes);
}
- @Test
- public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudes() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedEffects(VibrationEffect.EFFECT_THUD)
- .build());
-
- // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT).
- assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30)));
- assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255)));
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
- }
-
- @Test
- public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudes() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedEffects(VibrationEffect.EFFECT_THUD)
- .build());
-
- // All amplitudes are min, max, or default. Requires no amplitude control.
- assertTrue(vibrator.areVibrationFeaturesSupported(
- waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
- }
-
- @Test
- public void areVibrationFeaturesSupported_withAmplitudeControl() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
- .build());
-
- // All forms of amplitudes are valid when amplitude control is available.
- assertTrue(vibrator.areVibrationFeaturesSupported(
- waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
- assertTrue(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- waveformWithAmplitudes(7, 255, 0, 0, 60)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
- }
-
- @Test
- public void areVibrationFeaturesSupported_primitiveCompositionsWithSupportedPrimitives() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .build());
-
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .compose()));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- /* scale= */ 0.2f,
- /* delay= */ 200)
- .compose()));
- }
-
- @Test
- public void areVibrationFeaturesSupported_primitiveCompositionsWithUnupportedPrimitives() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .build());
-
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
- .compose()));
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
- .compose()));
- }
-
- @Test
- public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
- .build());
-
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
- .compose()));
-
- vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK)
- .build());
-
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
- .addEffect(VibrationEffect.createWaveform(
- // These timings are given either 0 or default amplitudes, which
- // do not require vibrator's amplitude control.
- /* timings= */ new long[] {1, 2, 3},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
- .compose()));
- }
-
- @Test
- public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
- .build());
-
- // Not supported due to the TICK primitive, which the vibrator has no support for.
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .compose()));
- // Not supported due to the THUD effect, which the vibrator has no support for.
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD))
- .compose()));
-
- vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_POP)
- .build());
-
- // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or
- // DEFAULT), because the vibrator has no amplitude control.
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
- .compose()));
- }
-
/**
* Asserts that the frequency profile is empty, and therefore frequency control isn't supported.
*/
@@ -768,21 +585,4 @@
assertTrue(info.getFrequencyProfile().isEmpty());
assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
}
-
- private Vibrator createVibratorWithCustomInfo(VibratorInfo info) {
- return new SystemVibrator(mContextSpy) {
- @Override
- public VibratorInfo getInfo() {
- return info;
- }
- };
- }
-
- private static VibrationEffect waveformWithAmplitudes(int...amplitudes) {
- long[] timings = new long[amplitudes.length];
- for (int i = 0; i < timings.length; i++) {
- timings[i] = i * 2; // Arbitrary timings.
- }
- return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1);
- }
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 8268077..4f5f3c0 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -25,18 +25,14 @@
import static org.testng.Assert.assertThrows;
import android.os.Parcel;
-import android.os.SystemVibrator;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.VibratorInfo;
-import androidx.test.InstrumentationRegistry;
-
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class PrebakedSegmentTest {
@Test
@@ -149,121 +145,121 @@
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_TICK,
VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_DOUBLE_CLICK,
VibrationEffect.EFFECT_HEAVY_CLICK);
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_noVibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_TICK,
VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_DOUBLE_CLICK,
VibrationEffect.EFFECT_HEAVY_CLICK);
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_noVibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_THUD,
VibrationEffect.EFFECT_POP,
VibrationEffect.EFFECT_TEXTURE_TICK);
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_noVibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_THUD,
VibrationEffect.EFFECT_POP,
VibrationEffect.EFFECT_TEXTURE_TICK);
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_noVibSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
@@ -283,14 +279,9 @@
return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
- private static Vibrator createVibratorWithSupportedEffects(int... supportedEffects) {
- return new SystemVibrator(InstrumentationRegistry.getContext()) {
- @Override
- public VibratorInfo getInfo() {
- return new VibratorInfo.Builder(/* id= */ 1)
- .setSupportedEffects(supportedEffects)
- .build();
- }
- };
+ private static VibratorInfo createVibratorInfoWithSupportedEffects(int... supportedEffects) {
+ return new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(supportedEffects)
+ .build();
}
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index 6f5adcd..ec5a084 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -25,18 +25,14 @@
import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
-import android.os.SystemVibrator;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.VibratorInfo;
-import androidx.test.InstrumentationRegistry;
-
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class PrimitiveSegmentTest {
private static final float TOLERANCE = 1e-2f;
@@ -146,15 +142,15 @@
public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() {
assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK)));
assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_THUD)));
assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)));
}
@@ -162,15 +158,15 @@
public void testVibrationFeaturesSupport_primitiveNotSupportedByVibrator() {
assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_THUD)));
assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK)));
assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)));
}
@@ -193,15 +189,10 @@
return new PrimitiveSegment(primitiveId, 0.2f, 10);
}
- private static Vibrator createVibratorWithSupportedPrimitive(int primitiveId) {
- return new SystemVibrator(InstrumentationRegistry.getContext()) {
- @Override
- public VibratorInfo getInfo() {
- return new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(primitiveId, 10)
- .build();
- }
- };
+ private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) {
+ return new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(primitiveId, 10)
+ .build();
}
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 68870e5..5caa86b 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -23,31 +23,21 @@
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class RampSegmentTest {
private static final float TOLERANCE = 1e-2f;
- @Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Mock
- private Vibrator mVibrator;
-
@Test
public void testCreation() {
RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
@@ -147,71 +137,71 @@
@Test
public void testVibrationFeaturesSupport_amplitudeAndFrequencyControls_supported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
// Increasing amplitude
- assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(info));
// Increasing frequency
- assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(info));
// Decreasing amplitude
- assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(info));
// Decreasing frequency
- assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(info));
// Zero duration
- assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noAmplitudeControl_unsupportedForChangingAmplitude() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
// Test with increasing/decreasing amplitudes.
- assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noAmplitudeControl_fractionalAmplitudeUnsupported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_unchangingZeroAmplitude_supported() {
RampSegment amplitudeZeroWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10);
RampSegment amplitudeZeroWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_unchangingOneAmplitude_supported() {
RampSegment amplitudeOneWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10);
RampSegment amplitudeOneWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(info));
}
@Test
@@ -220,52 +210,52 @@
new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.5f, 0.8f, 10);
RampSegment defaultAmplitudeDecreasingFrequency =
new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.8f, 0.5f, 10);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noFrequencyControl_unsupportedForChangingFrequency() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false);
// Test with increasing/decreasing frequencies.
- assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noFrequencyControl_fractionalFrequencyUnsupported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false);
- assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_unchangingZeroFrequency_supported() {
RampSegment frequencyZeroWithIncreasingAmplitude = new RampSegment(0.1f, 1, 0, 0, 10);
RampSegment frequencyZeroWithDecreasingAmplitude = new RampSegment(1, 0.1f, 0, 0, 10);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false);
- assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
- assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(info));
+ assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(info));
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
- assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(info));
+ assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(info));
}
@Test
@@ -274,4 +264,17 @@
// duration checked in VibrationEffect implementations.
assertTrue(new RampSegment(0.5f, 1, 0, 0, 5_000).isHapticFeedbackCandidate());
}
+
+ private static VibratorInfo createVibInfo(
+ boolean hasAmplitudeControl, boolean hasFrequencyControl) {
+ VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1);
+ long capabilities = 0;
+ if (hasAmplitudeControl) {
+ capabilities |= IVibrator.CAP_AMPLITUDE_CONTROL;
+ }
+ if (hasFrequencyControl) {
+ capabilities |= (IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ }
+ return builder.setCapabilities(capabilities).build();
+ }
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 34bb892..44db306 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -21,31 +21,20 @@
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class StepSegmentTest {
private static final float TOLERANCE = 1e-2f;
-
- @Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Mock
- private Vibrator mVibrator;
-
@Test
public void testCreation() {
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f,
@@ -160,26 +149,26 @@
public void testVibrationFeaturesSupport_zeroAmplitude_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 0);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_maxAmplitude_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 0);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
@@ -189,60 +178,60 @@
/* amplitude= */ VibrationEffect.DEFAULT_AMPLITUDE,
/* frequencyHz= */ 0,
/* duration= */ 0);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_fractionalAmplitude_hasAmplitudeCtrl_supported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
assertTrue(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0)
- .areVibrationFeaturesSupported(mVibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_fractionalAmplitude_hasNoAmplitudeCtrl_notSupported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
assertFalse(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0)
- .areVibrationFeaturesSupported(mVibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_zeroFrequency_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0, /* duration= */ 0);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ info = createVibInfoForFrequency(/* hasFrequencyControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_nonZeroFrequency_hasFrequencyCtrl_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_nonZeroFrequency_hasNoFrequencyCtrl_notSupported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ false);
- assertFalse(segment.areVibrationFeaturesSupported(mVibrator));
+ assertFalse(segment.areVibrationFeaturesSupported(info));
}
@Test
@@ -251,4 +240,21 @@
// duration checked in VibrationEffect implementations.
assertTrue(new StepSegment(0, 0, 5_000).isHapticFeedbackCandidate());
}
+
+ private static VibratorInfo createVibInfoForAmplitude(boolean hasAmplitudeControl) {
+ VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1);
+ if (hasAmplitudeControl) {
+ builder.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ }
+ return builder.build();
+ }
+
+ private static VibratorInfo createVibInfoForFrequency(boolean hasFrequencyControl) {
+ VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1);
+ if (hasFrequencyControl) {
+ builder.setCapabilities(
+ IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ }
+ return builder.build();
+ }
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index ef062df..4b10b56 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -23,14 +23,16 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.ContentProvider;
-import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -44,8 +46,8 @@
import android.telephony.TelephonyManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
+import android.util.ArrayMap;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
@@ -77,13 +79,14 @@
@Mock private Context mContext;
@Mock private Resources mResources;
- @Mock private ContentResolver mContentResolver;
@Mock private AudioManager mAudioManager;
@Mock private TelephonyManager mTelephonyManager;
+ @Mock private MockContentResolver mContentResolver;
+ private MockSettingsProvider mSettingsProvider;
+
@Before
public void setUp() {
- clearLongPressPowerValues();
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManager);
when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn(
@@ -91,14 +94,20 @@
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getApplicationContext()).thenReturn(mContext);
when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
- when(mContext.getContentResolver()).thenReturn(getContentResolver());
mSettingsHelper = spy(new SettingsHelper(mContext));
+ mContentResolver = spy(new MockContentResolver());
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mSettingsProvider = new MockSettingsProvider(mContext);
+ mContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
}
@After
public void tearDown() {
- clearLongPressPowerValues();
+ Settings.Global.putString(mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS,
+ null);
+ Settings.Global.putString(mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+ null);
}
@Test
@@ -123,33 +132,30 @@
mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
SETTING_KEY, SETTING_VALUE, /* restoredFromSdkInt */ 0);
- verifyZeroInteractions(mContentResolver);
+ // The only time of interaction happened during setUp()
+ verify(mContentResolver, times(1))
+ .addProvider(Settings.AUTHORITY, mSettingsProvider);
+
+ verifyNoMoreInteractions(mContentResolver);
}
@Test
public void testRestoreValue_lppForAssistantEnabled_updatesValue() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
true);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0);
- assertThat(
- Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
- .isEqualTo(5);
- assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- -1)).isEqualTo(2);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(5);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(2);
}
@Test
public void testRestoreValue_lppForAssistantNotEnabled_updatesValueToDefaultConfig() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
true);
@@ -161,21 +167,17 @@
R.integer.config_keyChordPowerVolumeUp)).thenReturn(
1);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
- assertThat(
- Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
- .isEqualTo(1);
- assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1);
}
@Test
public void testRestoreValue_lppForAssistantNotEnabledDefaultConfig_updatesValue() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
true);
@@ -187,47 +189,39 @@
R.integer.config_keyChordPowerVolumeUp)).thenReturn(
1);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
- assertThat(
- Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
- .isEqualTo(1);
- assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1);
}
@Test
public void testRestoreValue_lppForAssistantNotAvailable_doesNotRestore() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
- when(mResources.getBoolean(
- R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
- false);
+ when(mResources.getBoolean(R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(false);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
- Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0);
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
+ Settings.Global.POWER_BUTTON_LONG_PRESS, "500", 0);
- assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
- -1))).isEqualTo(-1);
+ assertThat((Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1);
}
@Test
public void testRestoreValue_lppForAssistantInvalid_doesNotRestore() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
false);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "trees", 0);
- assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
- -1))).isEqualTo(-1);
+ assertThat((Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1);
}
@Test
@@ -363,9 +357,6 @@
final String newRingtoneValueCanonicalized =
"content://media/internal/audio/media/100?title=Song&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
ContentProvider mockMediaContentProvider =
new MockContentProvider(mContext) {
@Override
@@ -386,25 +377,22 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ resetRingtoneSettingsToDefault();
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(DEFAULT_RINGTONE_VALUE);
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -417,9 +405,6 @@
final String newRingtoneValueCanonicalized =
"content://0@media/external/audio/media/100?title=Song&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
cursor.addRow(new Object[] {100L});
@@ -458,24 +443,21 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -488,9 +470,6 @@
final String newRingtoneValueCanonicalized =
"content://0@media/external/audio/media/200?title=notificationPing&canonicalize=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
cursor.addRow(new Object[] {200L});
@@ -529,17 +508,14 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.NOTIFICATION_SOUND,
@@ -548,7 +524,7 @@
assertThat(
Settings.System.getString(
- mMockContentResolver, Settings.System.NOTIFICATION_SOUND))
+ mContentResolver, Settings.System.NOTIFICATION_SOUND))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -561,9 +537,6 @@
final String newRingtoneValueCanonicalized =
"content://0@media/external/audio/media/300?title=alarmSound&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
cursor.addRow(new Object[] {300L});
@@ -600,26 +573,29 @@
assertThat(selectionArgs).isEqualTo(new String[] {"alarmSound"});
return cursor;
}
+
+ @Override
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType,
+ Bundle opts) {
+ return null;
+ }
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.ALARM_ALERT,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.ALARM_ALERT))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -628,9 +604,6 @@
final String sourceRingtoneValue =
"content://0@media/external/audio/media/1?title=Song&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
// This is to mock the case that there are multiple results by querying title +
// ringtone_type.
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
@@ -651,32 +624,26 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(DEFAULT_RINGTONE_VALUE);
}
@Test
public void testRestoreValue_customRingtone_restoreSilentValue() {
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
ContentProvider mockMediaContentProvider =
new MockContentProvider(mContext) {
@Override
@@ -691,37 +658,46 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
"_silent",
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(null);
}
- public static class MockSettingsProvider extends MockContentProvider {
- ContentResolver mBaseContentResolver;
-
- public MockSettingsProvider(Context context, ContentResolver baseContentResolver) {
+ private static class MockSettingsProvider extends MockContentProvider {
+ private final ArrayMap<String, String> mKeyValueStore = new ArrayMap<>();
+ MockSettingsProvider(Context context) {
super(context);
- this.mBaseContentResolver = baseContentResolver;
}
@Override
public Bundle call(String method, String request, Bundle args) {
- return mBaseContentResolver.call(Settings.AUTHORITY, method, request, args);
+ if (method.startsWith("PUT_")) {
+ mKeyValueStore.put(request, args.getString("value"));
+ return null;
+ } else if (method.startsWith("GET_")) {
+ return Bundle.forPair("value", mKeyValueStore.getOrDefault(request, ""));
+ }
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ String name = values.getAsString("name");
+ String value = values.getAsString("value");
+ mKeyValueStore.put(name, value);
+ return null;
}
}
@@ -752,15 +728,13 @@
}
private int getAutoRotationSettingValue() {
- return Settings.System.getInt(
- getContentResolver(),
+ return Settings.System.getInt(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION,
/* default= */ -1);
}
private void setAutoRotationSettingValue(int value) {
- Settings.System.putInt(
- getContentResolver(),
+ Settings.System.putInt(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION,
value
);
@@ -769,7 +743,7 @@
private void restoreAutoRotationSetting(int newValue) {
mSettingsHelper.restoreValue(
mContext,
- getContentResolver(),
+ mContentResolver,
new ContentValues(),
/* destination= */ Settings.System.CONTENT_URI,
/* name= */ Settings.System.ACCELEROMETER_ROTATION,
@@ -777,31 +751,19 @@
/* restoredFromSdkInt= */ 0);
}
- private ContentResolver getContentResolver() {
- return InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
- }
-
- private void clearLongPressPowerValues() {
- ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
- Settings.Global.putString(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, null);
- Settings.Global.putString(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, null);
- }
-
- private void resetRingtoneSettingsToDefault(ContentResolver contentResolver) {
+ private void resetRingtoneSettingsToDefault() {
Settings.System.putString(
- contentResolver, Settings.System.RINGTONE, DEFAULT_RINGTONE_VALUE);
+ mContentResolver, Settings.System.RINGTONE, DEFAULT_RINGTONE_VALUE);
Settings.System.putString(
- contentResolver, Settings.System.NOTIFICATION_SOUND, DEFAULT_NOTIFICATION_VALUE);
+ mContentResolver, Settings.System.NOTIFICATION_SOUND, DEFAULT_NOTIFICATION_VALUE);
Settings.System.putString(
- contentResolver, Settings.System.ALARM_ALERT, DEFAULT_ALARM_VALUE);
+ mContentResolver, Settings.System.ALARM_ALERT, DEFAULT_ALARM_VALUE);
- assertThat(Settings.System.getString(contentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(DEFAULT_RINGTONE_VALUE);
- assertThat(Settings.System.getString(contentResolver, Settings.System.NOTIFICATION_SOUND))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.NOTIFICATION_SOUND))
.isEqualTo(DEFAULT_NOTIFICATION_VALUE);
- assertThat(Settings.System.getString(contentResolver, Settings.System.ALARM_ALERT))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT))
.isEqualTo(DEFAULT_ALARM_VALUE);
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index e539c95..e7a53e5b 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -62,6 +62,7 @@
private val burmeseLineSpacing =
resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
+ protected var onSecondaryDisplay: Boolean = false
override val events: DefaultClockEvents
override val config = ClockConfig()
@@ -142,6 +143,11 @@
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizePx)
recomputePadding(targetRegion)
}
+
+ override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {
+ this@DefaultClockController.onSecondaryDisplay = onSecondaryDisplay
+ recomputePadding(null)
+ }
}
open fun recomputePadding(targetRegion: Rect?) {}
@@ -182,13 +188,19 @@
override fun recomputePadding(targetRegion: Rect?) {
// We center the view within the targetRegion instead of within the parent
// view by computing the difference and adding that to the padding.
- val parent = view.parent
- val yDiff =
- if (targetRegion != null && parent is View && parent.isLaidOut())
- targetRegion.centerY() - parent.height / 2f
- else 0f
val lp = view.getLayoutParams() as FrameLayout.LayoutParams
- lp.topMargin = (-0.5f * view.bottom + yDiff).toInt()
+ lp.topMargin =
+ if (onSecondaryDisplay) {
+ // On the secondary display we don't want any additional top/bottom margin.
+ 0
+ } else {
+ val parent = view.parent
+ val yDiff =
+ if (targetRegion != null && parent is View && parent.isLaidOut())
+ targetRegion.centerY() - parent.height / 2f
+ else 0f
+ (-0.5f * view.bottom + yDiff).toInt()
+ }
view.setLayoutParams(lp)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index d962732..527f8007 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -177,6 +177,9 @@
* targetRegion is relative to the parent view.
*/
fun onTargetRegionChanged(targetRegion: Rect?)
+
+ /** Called to notify the clock about its display. */
+ fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean)
}
/** Tick rates for clocks */
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
new file mode 100644
index 0000000..593f507f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+**
+** Copyright 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.
+*/
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/presentation"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <com.android.keyguard.KeyguardStatusView
+ android:id="@+id/clock"
+ android:layout_width="410dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/keyguard_clock_container"
+ layout="@layout/keyguard_clock_switch"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </com.android.keyguard.KeyguardStatusView>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 12f13e9..3a15ae4 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -127,6 +127,8 @@
android:gravity="center_vertical"
android:paddingStart="@dimen/shade_header_system_icons_padding_start"
android:paddingEnd="@dimen/shade_header_system_icons_padding_end"
+ android:paddingTop="@dimen/shade_header_system_icons_padding_top"
+ android:paddingBottom="@dimen/shade_header_system_icons_padding_bottom"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/privacy_container"
app:layout_constraintTop_toTopOf="@id/clock">
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index 9af46c5..3796415 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -64,8 +64,7 @@
android:layout_height="wrap_content"
android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
style="@style/TextAppearance.Dialog.Body.Message"
- android:gravity="start"
- android:lineHeight="@dimen/screenrecord_warning_line_height"/>
+ android:gravity="start"/>
<!-- Buttons -->
<LinearLayout
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index d85e012..915dcdb 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -82,6 +82,8 @@
<!-- start padding is smaller to account for status icon margins coming from drawable itself -->
<dimen name="shade_header_system_icons_padding_start">3dp</dimen>
<dimen name="shade_header_system_icons_padding_end">4dp</dimen>
+ <dimen name="shade_header_system_icons_padding_top">2dp</dimen>
+ <dimen name="shade_header_system_icons_padding_bottom">2dp</dimen>
<!-- Lockscreen shade transition values -->
<dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8310b95..2dc1b45 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -496,9 +496,10 @@
<dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
<dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen>
<dimen name="shade_header_system_icons_height">@dimen/large_screen_shade_header_min_height</dimen>
- <dimen name="shade_header_system_icons_height_large_screen">32dp</dimen>
<dimen name="shade_header_system_icons_padding_start">0dp</dimen>
<dimen name="shade_header_system_icons_padding_end">0dp</dimen>
+ <dimen name="shade_header_system_icons_padding_top">0dp</dimen>
+ <dimen name="shade_header_system_icons_padding_bottom">0dp</dimen>
<!-- The top margin of the panel that holds the list of notifications.
On phones it's always 0dp but it's overridden in Car UI
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index 2ec6180..fe61c46 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -56,7 +56,7 @@
<Constraint android:id="@+id/shade_header_system_icons">
<Layout
android:layout_width="wrap_content"
- android:layout_height="@dimen/shade_header_system_icons_height_large_screen"
+ android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/privacy_container"
app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
new file mode 100644
index 0000000..899cad89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.keyguard
+
+import android.app.Presentation
+import android.content.Context
+import android.graphics.Color
+import android.os.Bundle
+import android.view.Display
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import com.android.keyguard.dagger.KeyguardStatusViewComponent
+import com.android.systemui.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** [Presentation] shown in connected displays while on keyguard. */
+class ConnectedDisplayKeyguardPresentation
+@AssistedInject
+constructor(
+ @Assisted display: Display,
+ context: Context,
+ private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+) :
+ Presentation(
+ context,
+ display,
+ R.style.Theme_SystemUI_KeyguardPresentation,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
+ ) {
+
+ private lateinit var keyguardStatusViewController: KeyguardStatusViewController
+ private lateinit var clock: KeyguardStatusView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(
+ LayoutInflater.from(context)
+ .inflate(R.layout.keyguard_clock_presentation, /* root= */ null)
+ )
+ val window = window ?: error("no window available.")
+
+ // Logic to make the lock screen fullscreen
+ window.decorView.systemUiVisibility =
+ (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
+ window.attributes.fitInsetsTypes = 0
+ window.isNavigationBarContrastEnforced = false
+ window.navigationBarColor = Color.TRANSPARENT
+
+ clock = findViewById(R.id.clock)
+ keyguardStatusViewController =
+ keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply {
+ setDisplayedOnSecondaryDisplay()
+ init()
+ }
+ }
+
+ /** [ConnectedDisplayKeyguardPresentation] factory. */
+ @AssistedFactory
+ interface Factory {
+ /** Creates a new [Presentation] for the given [display]. */
+ fun create(
+ display: Display,
+ ): ConnectedDisplayKeyguardPresentation
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 30b8ed0..b589887 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,10 +1,5 @@
package com.android.keyguard;
-import static android.view.View.ALPHA;
-import static android.view.View.SCALE_X;
-import static android.view.View.SCALE_Y;
-import static android.view.View.TRANSLATION_Y;
-
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN;
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN;
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE;
@@ -149,6 +144,13 @@
updateStatusArea(/* animate= */false);
}
+ /** Sets whether the large clock is being shown on a connected display. */
+ public void setLargeClockOnSecondaryDisplay(boolean onSecondaryDisplay) {
+ if (mClock != null) {
+ mClock.getLargeClock().getEvents().onSecondaryDisplayChanged(onSecondaryDisplay);
+ }
+ }
+
/**
* Enable or disable split shade specific positioning
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 7a5ad5e..dd39f1d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -104,6 +104,7 @@
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ private boolean mShownOnSecondaryDisplay = false;
private boolean mOnlyClock = false;
private boolean mIsActiveDreamLockscreenHosted = false;
private FeatureFlags mFeatureFlags;
@@ -185,8 +186,18 @@
}
/**
- * Mostly used for alternate displays, limit the information shown
+ * When set, limits the information shown in an external display.
*/
+ public void setShownOnSecondaryDisplay(boolean shownOnSecondaryDisplay) {
+ mShownOnSecondaryDisplay = shownOnSecondaryDisplay;
+ }
+
+ /**
+ * Mostly used for alternate displays, limit the information shown
+ *
+ * @deprecated use {@link KeyguardClockSwitchController#setShownOnSecondaryDisplay}
+ */
+ @Deprecated
public void setOnlyClock(boolean onlyClock) {
mOnlyClock = onlyClock;
}
@@ -221,6 +232,15 @@
}
}
+ private void hideSliceViewAndNotificationIconContainer() {
+ View ksv = mView.findViewById(R.id.keyguard_slice_view);
+ ksv.setVisibility(View.GONE);
+
+ View nic = mView.findViewById(
+ R.id.left_aligned_notification_icon_container);
+ nic.setVisibility(View.GONE);
+ }
+
@Override
protected void onViewAttached() {
mClockRegistry.registerClockChangeListener(mClockChangedListener);
@@ -234,13 +254,15 @@
mKeyguardDateWeatherViewInvisibility =
mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility);
- if (mOnlyClock) {
- View ksv = mView.findViewById(R.id.keyguard_slice_view);
- ksv.setVisibility(View.GONE);
+ if (mShownOnSecondaryDisplay) {
+ mView.setLargeClockOnSecondaryDisplay(true);
+ displayClock(LARGE, /* animate= */ false);
+ hideSliceViewAndNotificationIconContainer();
+ return;
+ }
- View nic = mView.findViewById(
- R.id.left_aligned_notification_icon_container);
- nic.setVisibility(View.GONE);
+ if (mOnlyClock) {
+ hideSliceViewAndNotificationIconContainer();
return;
}
updateAodIcons();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 9f21a31..1c5a575 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -43,16 +43,19 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import dagger.Lazy;
+
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
@SysUISingleton
public class KeyguardDisplayManager {
@@ -64,6 +67,9 @@
private final DisplayTracker mDisplayTracker;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+ private final ConnectedDisplayKeyguardPresentation.Factory
+ mConnectedDisplayKeyguardPresentationFactory;
+ private final FeatureFlags mFeatureFlags;
private final Context mContext;
private boolean mShowing;
@@ -105,7 +111,10 @@
@Main Executor mainExecutor,
@UiBackground Executor uiBgExecutor,
DeviceStateHelper deviceStateHelper,
- KeyguardStateController keyguardStateController) {
+ KeyguardStateController keyguardStateController,
+ ConnectedDisplayKeyguardPresentation.Factory
+ connectedDisplayKeyguardPresentationFactory,
+ FeatureFlags featureFlags) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
@@ -115,6 +124,8 @@
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
mDeviceStateHelper = deviceStateHelper;
mKeyguardStateController = keyguardStateController;
+ mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
+ mFeatureFlags = featureFlags;
}
private boolean isKeyguardShowable(Display display) {
@@ -185,8 +196,12 @@
return false;
}
- KeyguardPresentation createPresentation(Display display) {
- return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
+ Presentation createPresentation(Display display) {
+ if (mFeatureFlags.isEnabled(Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION)) {
+ return mConnectedDisplayKeyguardPresentationFactory.create(display);
+ } else {
+ return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 757022d..c314586 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -187,6 +187,11 @@
mConfigurationController.removeCallback(mConfigurationListener);
}
+ /** Sets the StatusView as shown on an external display. */
+ public void setDisplayedOnSecondaryDisplay() {
+ mKeyguardClockSwitchController.setShownOnSecondaryDisplay(true);
+ }
+
/**
* Called in notificationPanelViewController to avoid leak
*/
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index cca71e8..f3900ac 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -115,7 +115,7 @@
// TODO(b/292213543): Tracking Bug
@JvmField
val NOTIFICATION_GROUP_EXPANSION_CHANGE =
- unreleasedFlag("notification_group_expansion_change", teamfood = false)
+ unreleasedFlag("notification_group_expansion_change", teamfood = true)
// 200 - keyguard/lockscreen
// ** Flag retired **
@@ -769,6 +769,10 @@
@JvmField
val ONE_WAY_HAPTICS_API_MIGRATION = unreleasedFlag("oneway_haptics_api_migration")
+ /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
+ @JvmField
+ val ENABLE_CLOCK_KEYGUARD_PRESENTATION = unreleasedFlag("enable_clock_keyguard_presentation")
+
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space")
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
new file mode 100644
index 0000000..3f2f67d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import javax.inject.Inject
+
+/**
+ * Sends key events to the appropriate interactors and then acts upon key events that haven't
+ * already been handled but should be handled by SystemUI.
+ */
+@SysUISingleton
+class KeyEventInteractor
+@Inject
+constructor(
+ private val backActionInteractor: BackActionInteractor,
+ private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
+) {
+ fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
+ return true
+ }
+
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_BACK -> {
+ if (event.handleAction()) {
+ backActionInteractor.onBackRequested()
+ }
+ return true
+ }
+ }
+ return false
+ }
+
+ fun interceptMediaKey(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.interceptMediaKey(event)
+ }
+
+ fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.dispatchKeyEventPreIme(event)
+ }
+
+ companion object {
+ // Most actions shouldn't be handled on the down event and instead handled on subsequent
+ // key events like ACTION_UP.
+ fun KeyEvent.handleAction(): Boolean {
+ return action != KeyEvent.ACTION_DOWN
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
new file mode 100644
index 0000000..635961b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.content.Context
+import android.media.AudioManager
+import android.view.KeyEvent
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor.Companion.handleAction
+import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import javax.inject.Inject
+
+/** Handles key events arriving when the keyguard is showing or device is dozing. */
+@SysUISingleton
+class KeyguardKeyEventInteractor
+@Inject
+constructor(
+ private val context: Context,
+ private val statusBarStateController: StatusBarStateController,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ private val shadeController: ShadeController,
+ private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper,
+ private val backActionInteractor: BackActionInteractor,
+) {
+
+ fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (statusBarStateController.isDozing) {
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_VOLUME_DOWN,
+ KeyEvent.KEYCODE_VOLUME_UP -> return dispatchVolumeKeyEvent(event)
+ }
+ }
+
+ if (event.handleAction()) {
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
+ KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
+ }
+ }
+ return false
+ }
+
+ /**
+ * While IME is active and a BACK event is detected, check with {@link
+ * StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event should be
+ * handled before routing to IME, in order to prevent the user from having to hit back twice to
+ * exit bouncer.
+ */
+ fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_BACK ->
+ if (
+ statusBarStateController.state == StatusBarState.KEYGUARD &&
+ statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()
+ ) {
+ return backActionInteractor.onBackRequested()
+ }
+ }
+ return false
+ }
+
+ fun interceptMediaKey(event: KeyEvent): Boolean {
+ return statusBarStateController.state == StatusBarState.KEYGUARD &&
+ statusBarKeyguardViewManager.interceptMediaKey(event)
+ }
+
+ private fun dispatchMenuKeyEvent(): Boolean {
+ val shouldUnlockOnMenuPressed =
+ isDeviceInteractive() &&
+ (statusBarStateController.state != StatusBarState.SHADE) &&
+ statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
+ if (shouldUnlockOnMenuPressed) {
+ shadeController.animateCollapseShadeForced()
+ return true
+ }
+ return false
+ }
+
+ private fun dispatchSpaceEvent(): Boolean {
+ if (isDeviceInteractive() && statusBarStateController.state != StatusBarState.SHADE) {
+ shadeController.animateCollapseShadeForced()
+ return true
+ }
+ return false
+ }
+
+ private fun dispatchVolumeKeyEvent(event: KeyEvent): Boolean {
+ mediaSessionLegacyHelperWrapper
+ .getHelper(context)
+ .sendVolumeKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE, true)
+ return true
+ }
+
+ private fun isDeviceInteractive(): Boolean {
+ return keyguardInteractor.wakefulnessModel.value.isDeviceInteractive()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt
new file mode 100644
index 0000000..9924369
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.media.controls.util
+
+import android.content.Context
+import android.media.session.MediaSessionLegacyHelper
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Injectable wrapper around `MediaSessionLegacyHelper` functions */
+@SysUISingleton
+class MediaSessionLegacyHelperWrapper @Inject constructor() {
+ fun getHelper(context: Context): MediaSessionLegacyHelper {
+ return MediaSessionLegacyHelper.getHelper(context)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index e134f7c..ae0ab84 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -25,6 +25,7 @@
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -1714,10 +1715,12 @@
private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) {
final InsetsFrameProvider navBarProvider =
- new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars())
- .setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] {
- new InsetsFrameProvider.InsetsSizeOverride(
- TYPE_INPUT_METHOD, null)});
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars());
+ if (!ENABLE_HIDE_IME_CAPTION_BAR) {
+ navBarProvider.setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] {
+ new InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, null)
+ });
+ }
if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) {
navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight));
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 132cd61..df7d88f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -163,6 +163,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -355,6 +356,7 @@
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final KeyguardRootView mKeyguardRootView;
private final QuickSettingsController mQsController;
+ private final ShadeInteractor mShadeInteractor;
private final TouchHandler mTouchHandler = new TouchHandler();
private long mDownTime;
@@ -559,7 +561,6 @@
private float mHintDistance;
private float mInitialOffsetOnTouch;
private boolean mCollapsedAndHeadsUpOnDown;
- private float mExpandedFraction = 0;
private float mExpansionDragDownAmountPx = 0;
private boolean mPanelClosedOnDown;
private boolean mHasLayoutedSinceDown;
@@ -709,10 +710,12 @@
VibratorHelper vibratorHelper,
LatencyTracker latencyTracker,
PowerManager powerManager,
- AccessibilityManager accessibilityManager, @DisplayId int displayId,
+ AccessibilityManager accessibilityManager,
+ @DisplayId int displayId,
KeyguardUpdateMonitor keyguardUpdateMonitor,
MetricsLogger metricsLogger,
ShadeLogger shadeLogger,
+ ShadeInteractor shadeInteractor,
ConfigurationController configurationController,
Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
@@ -785,6 +788,7 @@
mView = view;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLockscreenGestureLogger = lockscreenGestureLogger;
+ mShadeInteractor = shadeInteractor;
mShadeExpansionStateManager = shadeExpansionStateManager;
mShadeLog = shadeLogger;
mGutsManager = gutsManager;
@@ -2244,7 +2248,7 @@
if (!isFalseTouch(x, y, interactionType)) {
mShadeLog.logFlingExpands(vel, vectorVel, interactionType,
this.mFlingAnimationUtils.getMinVelocityPxPerSecond(),
- mExpandedFraction > 0.5f, mAllowExpandForSmallExpansion);
+ getExpandedFraction() > 0.5f, mAllowExpandForSmallExpansion);
if (Math.abs(vectorVel) < this.mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
expands = shouldExpandWhenNotFlinging();
} else {
@@ -2419,7 +2423,7 @@
void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScrollLayoutController.updateTopPadding(
mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
- getKeyguardNotificationStaticPadding(), mExpandedFraction), animate);
+ getKeyguardNotificationStaticPadding(), getExpandedFraction()), animate);
if (isKeyguardShowing()
&& mKeyguardBypassController.getBypassEnabled()) {
// update the position of the header
@@ -2501,10 +2505,10 @@
private void onHeightUpdated(float expandedHeight) {
if (expandedHeight <= 0) {
mShadeLog.logExpansionChanged("onHeightUpdated: fully collapsed.",
- mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+ getExpandedFraction(), isExpanded(), mTracking, mExpansionDragDownAmountPx);
} else if (isFullyExpanded()) {
mShadeLog.logExpansionChanged("onHeightUpdated: fully expanded.",
- mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+ getExpandedFraction(), isExpanded(), mTracking, mExpansionDragDownAmountPx);
}
if (!mQsController.getExpanded() || mQsController.isExpandImmediate()
|| mIsExpandingOrCollapsing && mQsController.getExpandedWhenExpandingStarted()) {
@@ -3198,6 +3202,11 @@
}
}
+ @Override
+ public void performHapticFeedback(int constant) {
+ mVibratorHelper.performHapticFeedback(mView, constant);
+ }
+
private class ShadeHeadsUpTrackerImpl implements ShadeHeadsUpTracker {
@Override
public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
@@ -3425,7 +3434,7 @@
ipw.print("mHintDistance="); ipw.println(mHintDistance);
ipw.print("mInitialOffsetOnTouch="); ipw.println(mInitialOffsetOnTouch);
ipw.print("mCollapsedAndHeadsUpOnDown="); ipw.println(mCollapsedAndHeadsUpOnDown);
- ipw.print("mExpandedFraction="); ipw.println(mExpandedFraction);
+ ipw.print("getExpandedFraction()="); ipw.println(getExpandedFraction());
ipw.print("mExpansionDragDownAmountPx="); ipw.println(mExpansionDragDownAmountPx);
ipw.print("mPanelClosedOnDown="); ipw.println(mPanelClosedOnDown);
ipw.print("mHasLayoutedSinceDown="); ipw.println(mHasLayoutedSinceDown);
@@ -3761,7 +3770,7 @@
// don't fling while in keyguard to avoid jump in shade expand animation;
// touch has been intercepted already so flinging here is redundant
- if (mBarState == KEYGUARD && mExpandedFraction >= 1.0) {
+ if (mBarState == KEYGUARD && getExpandedFraction() >= 1.0) {
mShadeLog.d("NPVC endMotionEvent - skipping fling on keyguard");
} else {
fling(vel, expand, isFalseTouch(x, y, interactionType));
@@ -3857,6 +3866,16 @@
@VisibleForTesting
void setExpandedHeight(float height) {
debugLog("setExpandedHeight(%.1f)", height);
+ int maxPanelTransitionDistance = getMaxPanelTransitionDistance();
+ if (maxPanelTransitionDistance == 0) {
+ setExpandedFracAndHeight(0, height);
+ } else {
+ setExpandedFracAndHeight(height / maxPanelTransitionDistance, height);
+ }
+ }
+
+ private void setExpandedFracAndHeight(float frac, float height) {
+ mShadeInteractor.setExpansion(frac);
setExpandedHeightInternal(height);
}
@@ -3912,11 +3931,9 @@
mHeightAnimator.end();
}
}
- mExpandedFraction = Math.min(1f,
- maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
- mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
+ mQsController.setShadeExpansion(mExpandedHeight, getExpandedFraction());
mExpansionDragDownAmountPx = h;
- mAmbientState.setExpansionFraction(mExpandedFraction);
+ mAmbientState.setExpansionFraction(getExpandedFraction());
onHeightUpdated(mExpandedHeight);
updateExpansionAndVisibility();
});
@@ -3944,8 +3961,7 @@
/** Sets the expanded height relative to a number from 0 to 1. */
@VisibleForTesting
void setExpandedFraction(float frac) {
- final int maxDist = getMaxPanelTransitionDistance();
- setExpandedHeight(maxDist * frac);
+ setExpandedFracAndHeight(frac, getMaxPanelTransitionDistance() * frac);
}
float getExpandedHeight() {
@@ -3953,7 +3969,7 @@
}
float getExpandedFraction() {
- return mExpandedFraction;
+ return mShadeInteractor.getExpansion().getValue();
}
@Override
@@ -3975,7 +3991,7 @@
@Override
public boolean isFullyCollapsed() {
- return mExpandedFraction <= 0.0f;
+ return getExpandedFraction() <= 0.0f;
}
@Override
@@ -4134,7 +4150,7 @@
animator.getAnimatedFraction()));
setOverExpansionInternal(expansion, false /* isFromGesture */);
}
- setExpandedHeightInternal((float) animation.getAnimatedValue());
+ setExpandedHeight((float) animation.getAnimatedValue());
});
return animator;
}
@@ -4148,14 +4164,14 @@
@Override
public void updateExpansionAndVisibility() {
mShadeExpansionStateManager.onPanelExpansionChanged(
- mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
+ getExpandedFraction(), isExpanded(), mTracking, mExpansionDragDownAmountPx);
updateVisibility();
}
@Override
public boolean isExpanded() {
- return mExpandedFraction > 0f
+ return getExpandedFraction() > 0f
|| mInstantExpanding
|| isPanelVisibleBecauseOfHeadsUp()
|| mTracking
@@ -4913,7 +4929,7 @@
mMotionAborted = false;
mPanelClosedOnDown = isFullyCollapsed();
mShadeLog.logPanelClosedOnDown("intercept down touch", mPanelClosedOnDown,
- mExpandedFraction);
+ getExpandedFraction());
mCollapsedAndHeadsUpOnDown = false;
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
@@ -5132,7 +5148,7 @@
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
mShadeLog.logPanelClosedOnDown("handle down touch", mPanelClosedOnDown,
- mExpandedFraction);
+ getExpandedFraction());
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
@@ -5191,7 +5207,7 @@
if (isFullyCollapsed()) {
// If panel is fully collapsed, reset haptic effect before adding movement.
mHasVibratedOnOpen = false;
- mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
+ mShadeLog.logHasVibrated(mHasVibratedOnOpen, getExpandedFraction());
}
addMovement(event);
if (!isFullyCollapsed()) {
@@ -5227,7 +5243,7 @@
// otherwise {@link NotificationStackScrollLayout}
// wrongly enables stack height updates at the start of lockscreen swipe-up
mAmbientState.setSwipingUp(h <= 0);
- setExpandedHeightInternal(newHeight);
+ setExpandedHeight(newHeight);
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index bea12de..6564118 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -263,9 +263,11 @@
resources.getDimensionPixelSize(
R.dimen.shade_header_system_icons_padding_start
),
- systemIcons.paddingTop,
+ resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_top),
resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_end),
- systemIcons.paddingBottom
+ resources.getDimensionPixelSize(
+ R.dimen.shade_header_system_icons_padding_bottom
+ )
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index d5b5c87..182a676 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -248,6 +248,16 @@
/** Starts tracking a shade expansion gesture that originated from the status bar. */
fun startTrackingExpansionFromStatusBar()
+ /**
+ * Performs haptic feedback from a view with a haptic feedback constant.
+ *
+ * The implementation of this method should use the [android.view.View.performHapticFeedback]
+ * method with the provided constant.
+ *
+ * @param[constant] One of [android.view.HapticFeedbackConstants]
+ */
+ fun performHapticFeedback(constant: Int)
+
// ******* End Keyguard Section *********
/** Returns the ShadeHeadsUpTracker. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 287ac52..09b74b2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -86,6 +86,8 @@
return false
}
override fun startTrackingExpansionFromStatusBar() {}
+ override fun performHapticFeedback(constant: Int) {}
+
override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index ebb9935..76dca4c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -34,13 +34,34 @@
/** ShadeModel information regarding shade expansion events */
val shadeModel: Flow<ShadeModel>
- /** Amount qs has expanded. Quick Settings can be expanded without the full shade expansion. */
+ /**
+ * Value from `0` to `1` representing the amount the shade has expanded where `1` is fully
+ * expanded and `0` is fully collapsed. Quick Settings can be expanded without a fully expanded
+ * shade.
+ */
val qsExpansion: StateFlow<Float>
+ /**
+ * Value from `0` to `1` representing the amount the shade has expanded where `1` is fully
+ * expanded and `0` is fully collapsed.
+ */
+ val expansion: StateFlow<Float>
+
/** Amount shade has expanded with regard to the UDFPS location */
val udfpsTransitionToFullShadeProgress: StateFlow<Float>
+ /**
+ * Set shade expansion to a value from `0` to `1` representing the amount the shade has expanded
+ * where `1` is fully expanded and `0` is fully collapsed.
+ */
+ fun setExpansion(expansion: Float)
+
+ /**
+ * Set quick settings expansion to a value from `0` to `1` representing the amount quick
+ * settings has expanded where `1` is fully expanded and `0` is fully collapsed.
+ */
fun setQsExpansion(qsExpansion: Float)
+
fun setUdfpsTransitionToFullShadeProgress(progress: Float)
}
@@ -78,9 +99,17 @@
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
+ private val _expansion = MutableStateFlow(0f)
+ override val expansion: StateFlow<Float> = _expansion.asStateFlow()
+
private var _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress: StateFlow<Float> =
_udfpsTransitionToFullShadeProgress.asStateFlow()
+
+ override fun setExpansion(expansion: Float) {
+ _expansion.value = expansion
+ }
+
override fun setQsExpansion(qsExpansion: Float) {
_qsExpansion.value = qsExpansion
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 6fde84a..efe3eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -19,6 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -38,18 +39,29 @@
@Inject
constructor(
@Application scope: CoroutineScope,
+ private val shadeRepository: ShadeRepository,
disableFlagsRepository: DisableFlagsRepository,
keyguardRepository: KeyguardRepository,
userSetupRepository: UserSetupRepository,
deviceProvisionedController: DeviceProvisionedController,
userInteractor: UserInteractor,
) {
+ /**
+ * Value from `0` to `1` representing the amount the shade has expanded where `1` is fully
+ * expanded and `0` is fully collapsed.
+ */
+ val expansion = shadeRepository.expansion
+
/** Emits true if the shade is currently allowed and false otherwise. */
val isShadeEnabled: StateFlow<Boolean> =
disableFlagsRepository.disableFlags
.map { it.isShadeEnabled() }
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+ fun setExpansion(expansion: Float) {
+ shadeRepository.setExpansion(expansion)
+ }
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 6431ef9..2bc7b99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -33,12 +34,15 @@
import android.os.Vibrator;
import android.util.Log;
import android.util.Slog;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.LetterboxDetails;
@@ -49,6 +53,7 @@
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
@@ -107,6 +112,7 @@
private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final QuickSettingsController mQsController;
private final QSHost mQSHost;
+ private final FeatureFlags mFeatureFlags;
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -144,7 +150,8 @@
Lazy<CameraLauncher> cameraLauncherLazy,
UserTracker userTracker,
QSHost qsHost,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter,
+ FeatureFlags featureFlags) {
mCentralSurfaces = centralSurfaces;
mQsController = quickSettingsController;
mContext = context;
@@ -171,6 +178,7 @@
mCameraLauncherLazy = cameraLauncherLazy;
mUserTracker = userTracker;
mQSHost = qsHost;
+ mFeatureFlags = featureFlags;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
@@ -314,7 +322,7 @@
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
if (mShadeViewController.isFullyCollapsed()) {
if (mVibrateOnOpening) {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ vibrateOnNavigationKeyDown();
}
mShadeViewController.expand(true /* animate */);
mNotificationStackScrollLayoutController.setWillExpand(true);
@@ -587,4 +595,15 @@
}
return VibrationEffect.createWaveform(timings, /* repeat= */ -1);
}
+
+ @VisibleForTesting
+ void vibrateOnNavigationKeyDown() {
+ if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+ mShadeViewController.performHapticFeedback(
+ HapticFeedbackConstants.GESTURE_START
+ );
+ } else {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
index 4a31b86..eaae0f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
@@ -33,6 +33,7 @@
import com.android.systemui.Dependency
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.ShadeLogger
import com.android.systemui.util.ViewController
import com.android.systemui.util.time.SystemClock
import java.text.FieldPosition
@@ -83,6 +84,7 @@
private val systemClock: SystemClock,
private val broadcastDispatcher: BroadcastDispatcher,
private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeLogger: ShadeLogger,
private val timeTickHandler: Handler,
view: VariableDateView
) : ViewController<VariableDateView>(view) {
@@ -111,24 +113,29 @@
private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.action
+ if (
+ Intent.ACTION_LOCALE_CHANGED == action ||
+ Intent.ACTION_TIMEZONE_CHANGED == action
+ ) {
+ // need to get a fresh date format
+ dateFormat = null
+ shadeLogger.d("VariableDateViewController received intent to refresh date format")
+ }
+
+ val handler = mView.handler
+
// If the handler is null, it means we received a broadcast while the view has not
// finished being attached or in the process of being detached.
// In that case, do not post anything.
- val handler = mView.handler ?: return
- val action = intent.action
- if (
+ if (handler == null) {
+ shadeLogger.d("VariableDateViewController received intent but handler was null")
+ } else if (
Intent.ACTION_TIME_TICK == action ||
Intent.ACTION_TIME_CHANGED == action ||
Intent.ACTION_TIMEZONE_CHANGED == action ||
Intent.ACTION_LOCALE_CHANGED == action
) {
- if (
- Intent.ACTION_LOCALE_CHANGED == action ||
- Intent.ACTION_TIMEZONE_CHANGED == action
- ) {
- // need to get a fresh date format
- handler.post { dateFormat = null }
- }
handler.post(::updateClock)
}
}
@@ -231,6 +238,7 @@
private val systemClock: SystemClock,
private val broadcastDispatcher: BroadcastDispatcher,
private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeLogger: ShadeLogger,
@Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler
) {
fun create(view: VariableDateView): VariableDateViewController {
@@ -238,6 +246,7 @@
systemClock,
broadcastDispatcher,
shadeExpansionStateManager,
+ shadeLogger,
handler,
view
)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index b349696..438f0f4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -18,6 +18,8 @@
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static com.android.systemui.flags.Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -37,6 +39,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -59,8 +62,13 @@
@Mock
private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@Mock
+ private ConnectedDisplayKeyguardPresentation.Factory
+ mConnectedDisplayKeyguardPresentationFactory;
+ @Mock
private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
@Mock
+ private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation;
+ @Mock
private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
@Mock
private KeyguardStateController mKeyguardStateController;
@@ -69,7 +77,7 @@
private Executor mBackgroundExecutor = Runnable::run;
private KeyguardDisplayManager mManager;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
+ private FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
// The default and secondary displays are both in the default group
private Display mDefaultDisplay;
private Display mSecondaryDisplay;
@@ -80,10 +88,14 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, false);
mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
- mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController));
+ mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController,
+ mConnectedDisplayKeyguardPresentationFactory, mFakeFeatureFlags));
doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
+ doReturn(mConnectedDisplayKeyguardPresentation).when(
+ mConnectedDisplayKeyguardPresentationFactory).create(any());
mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -138,4 +150,15 @@
when(mKeyguardStateController.isOccluded()).thenReturn(true);
verify(mManager, never()).createPresentation(eq(mSecondaryDisplay));
}
+
+ @Test
+ public void testShow_withClockPresentationFlagEnabled_presentationCreated() {
+ when(mManager.createPresentation(any())).thenCallRealMethod();
+ mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, true);
+ mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
+
+ mManager.show();
+
+ verify(mConnectedDisplayKeyguardPresentationFactory).create(eq(mSecondaryDisplay));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
new file mode 100644
index 0000000..632d149
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyEventInteractorTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var keyguardInteractorWithDependencies:
+ KeyguardInteractorFactory.WithDependencies
+ @Mock private lateinit var keyguardKeyEventInteractor: KeyguardKeyEventInteractor
+ @Mock private lateinit var backActionInteractor: BackActionInteractor
+
+ private lateinit var underTest: KeyEventInteractor
+
+ @Before
+ fun setup() {
+ keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ underTest =
+ KeyEventInteractor(
+ backActionInteractor,
+ keyguardKeyEventInteractor,
+ )
+ }
+
+ @Test
+ fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() {
+ val backKeyEventActionDown = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)
+ val backKeyEventActionUp = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+
+ // GIVEN back key ACTION_DOWN and ACTION_UP aren't handled by the keyguardKeyEventInteractor
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionDown))
+ .thenReturn(false)
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionUp))
+ .thenReturn(false)
+
+ // WHEN back key event ACTION_DOWN, the event is handled even though back isn't requested
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionDown)).isTrue()
+ // THEN back event isn't handled on ACTION_DOWN
+ verify(backActionInteractor, never()).onBackRequested()
+
+ // WHEN back key event ACTION_UP
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionUp)).isTrue()
+ // THEN back event is handled on ACTION_UP
+ verify(backActionInteractor).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEvent_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEvent_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun interceptMediaKey_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun interceptMediaKey_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
new file mode 100644
index 0000000..a3f7fc5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -0,0 +1,255 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.media.AudioManager
+import android.media.session.MediaSessionLegacyHelper
+import android.view.KeyEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardKeyEventInteractorTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private val actionDownVolumeDownKeyEvent =
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN)
+ private val actionDownVolumeUpKeyEvent =
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
+ private val backKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+ private val awakeWakefulnessMode =
+ WakefulnessModel(WakefulnessState.AWAKE, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+ private val asleepWakefulnessMode =
+ WakefulnessModel(WakefulnessState.ASLEEP, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+
+ private lateinit var keyguardInteractorWithDependencies:
+ KeyguardInteractorFactory.WithDependencies
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock private lateinit var shadeController: ShadeController
+ @Mock private lateinit var mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper
+ @Mock private lateinit var mediaSessionLegacyHelper: MediaSessionLegacyHelper
+ @Mock private lateinit var backActionInteractor: BackActionInteractor
+
+ private lateinit var underTest: KeyguardKeyEventInteractor
+
+ @Before
+ fun setup() {
+ whenever(mediaSessionLegacyHelperWrapper.getHelper(any()))
+ .thenReturn(mediaSessionLegacyHelper)
+ keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ underTest =
+ KeyguardKeyEventInteractor(
+ context,
+ statusBarStateController,
+ keyguardInteractorWithDependencies.keyguardInteractor,
+ statusBarKeyguardViewManager,
+ shadeController,
+ mediaSessionLegacyHelperWrapper,
+ backActionInteractor,
+ )
+ }
+
+ @Test
+ fun dispatchKeyEvent_volumeKey_dozing_handlesEvents() {
+ whenever(statusBarStateController.isDozing).thenReturn(true)
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeDownKeyEvent)).isTrue()
+ verify(mediaSessionLegacyHelper)
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeDownKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isTrue()
+ verify(mediaSessionLegacyHelper)
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeUpKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+ }
+
+ @Test
+ fun dispatchKeyEvent_volumeKey_notDozing_doesNotHandleEvents() {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeDownKeyEvent)).isFalse()
+ verify(mediaSessionLegacyHelper, never())
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeDownKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isFalse()
+ verify(mediaSessionLegacyHelper, never())
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeUpKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+ }
+
+ @Test
+ fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
+
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
+ }
+
+ @Test
+ fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
+
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
+ }
+
+ @Test
+ fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(asleepWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
+
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+ }
+
+ @Test
+ fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_back_keyguard_onBackRequested() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(true)
+
+ whenever(backActionInteractor.onBackRequested()).thenReturn(false)
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse()
+ verify(backActionInteractor).onBackRequested()
+ clearInvocations(backActionInteractor)
+
+ whenever(backActionInteractor.onBackRequested()).thenReturn(true)
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isTrue()
+ verify(backActionInteractor).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_back_keyguard_SBKVMdoesNotHandle_neverOnBackRequested() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(false)
+ whenever(backActionInteractor.onBackRequested()).thenReturn(true)
+
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse()
+ verify(backActionInteractor, never()).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_back_shade_neverOnBackRequested() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(true)
+ whenever(backActionInteractor.onBackRequested()).thenReturn(true)
+
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse()
+ verify(backActionInteractor, never()).onBackRequested()
+ }
+
+ @Test
+ fun interceptMediaKey_keyguard_SBKVMdoesNotHandle_doesNotHandleMediaKey() {
+ val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.interceptMediaKey(eq(keyEvent))).thenReturn(false)
+
+ assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
+ verify(statusBarKeyguardViewManager).interceptMediaKey(eq(keyEvent))
+ }
+
+ @Test
+ fun interceptMediaKey_keyguard_handleMediaKey() {
+ val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.interceptMediaKey(eq(keyEvent))).thenReturn(true)
+
+ assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
+ verify(statusBarKeyguardViewManager).interceptMediaKey(eq(keyEvent))
+ }
+
+ @Test
+ fun interceptMediaKey_shade_doesNotHandleMediaKey() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+
+ assertThat(
+ underTest.interceptMediaKey(
+ KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
+ )
+ )
+ .isFalse()
+ verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 981e44b..5cad9f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -73,6 +73,7 @@
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.LockIconViewController;
+import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -119,6 +120,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
@@ -134,6 +136,7 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -164,14 +167,17 @@
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.domain.interactor.UserInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
@@ -358,6 +364,16 @@
mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository);
mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
+ mShadeRepository = new FakeShadeRepository();
+ mShadeInteractor = new ShadeInteractor(
+ TestScopeProvider.getTestScope(),
+ mShadeRepository,
+ new FakeDisableFlagsRepository(),
+ new FakeKeyguardRepository(),
+ new FakeUserSetupRepository(),
+ mock(DeviceProvisionedController.class),
+ mock(UserInteractor.class)
+ );
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
@@ -584,19 +600,33 @@
mMainHandler,
mLayoutInflater,
mFeatureFlags,
- coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
- mFalsingManager, new FalsingCollectorFake(),
+ coordinator,
+ expansionHandler,
+ mDynamicPrivacyController,
+ mKeyguardBypassController,
+ mFalsingManager,
+ new FalsingCollectorFake(),
mKeyguardStateController,
mStatusBarStateController,
mStatusBarWindowStateController,
mNotificationShadeWindowController,
- mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
- mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
+ mDozeLog,
+ mDozeParameters,
+ mCommandQueue,
+ mVibratorHelper,
+ mLatencyTracker,
+ mPowerManager,
+ mAccessibilityManager,
+ 0,
+ mUpdateMonitor,
mMetricsLogger,
mShadeLog,
+ mShadeInteractor,
mConfigurationController,
- () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
- mConversationNotificationManager, mMediaHierarchyManager,
+ () -> flingAnimationUtilsBuilder,
+ mStatusBarTouchableRegionManager,
+ mConversationNotificationManager,
+ mMediaHierarchyManager,
mStatusBarKeyguardViewManager,
mGutsManager,
mNotificationsQSContainerController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index a2c2912..35a54d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -49,6 +49,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
@@ -170,6 +171,7 @@
mShadeInteractor =
new ShadeInteractor(
mTestScope.getBackgroundScope(),
+ new FakeShadeRepository(),
mDisableFlagsRepository,
mKeyguardRepository,
new FakeUserSetupRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index cdcd1a2..9e6c12f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -67,6 +67,7 @@
private val featureFlags = FakeFeatureFlags()
private val userSetupRepository = FakeUserSetupRepository()
private val userRepository = FakeUserRepository()
+ private val shadeRepository = FakeShadeRepository()
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
@@ -138,6 +139,7 @@
underTest =
ShadeInteractor(
testScope.backgroundScope,
+ shadeRepository,
disableFlagsRepository,
keyguardRepository,
userSetupRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 4a2518a..ec4367c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -102,8 +102,10 @@
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
+ private val shadeRepository = FakeShadeRepository()
private val shadeInteractor = ShadeInteractor(
testScope.backgroundScope,
+ shadeRepository,
disableFlagsRepository,
keyguardRepository,
userSetupRepository = FakeUserSetupRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 8545b89..3ad3c15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -18,6 +18,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -29,8 +31,10 @@
import android.app.StatusBarManager;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
+import android.view.HapticFeedbackConstants;
import android.view.WindowInsets;
import androidx.test.filters.SmallTest;
@@ -42,6 +46,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
@@ -98,6 +103,7 @@
@Mock private UserTracker mUserTracker;
@Mock private QSHost mQSHost;
@Mock private ActivityStarter mActivityStarter;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -134,7 +140,8 @@
mCameraLauncherLazy,
mUserTracker,
mQSHost,
- mActivityStarter);
+ mActivityStarter,
+ mFeatureFlags);
when(mUserTracker.getUserHandle()).thenReturn(
UserHandle.of(ActivityManager.getCurrentUser()));
@@ -241,4 +248,24 @@
verifyZeroInteractions(mSystemBarAttributesListener);
}
+
+ @Test
+ public void vibrateOnNavigationKeyDown_oneWayHapticsDisabled_usesVibrate() {
+ mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
+
+ mSbcqCallbacks.vibrateOnNavigationKeyDown();
+
+ verify(mVibratorHelper).vibrate(VibrationEffect.EFFECT_TICK);
+ }
+
+ @Test
+ public void vibrateOnNavigationKeyDown_oneWayHapticsEnabled_usesPerformHapticFeedback() {
+ mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+
+ mSbcqCallbacks.vibrateOnNavigationKeyDown();
+
+ verify(mShadeViewController).performHapticFeedback(
+ HapticFeedbackConstants.GESTURE_START
+ );
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
index b698e70..b78e839 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -105,6 +106,7 @@
systemClock,
broadcastDispatcher,
shadeExpansionStateManager,
+ mock(),
testableHandler,
view
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 492e542..35a3fd0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -30,6 +30,9 @@
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion = _qsExpansion
+ private val _expansion = MutableStateFlow(0f)
+ override val expansion = _expansion
+
private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress
@@ -41,6 +44,10 @@
_qsExpansion.value = qsExpansion
}
+ override fun setExpansion(expansion: Float) {
+ _expansion.value = expansion
+ }
+
override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
_udfpsTransitionToFullShadeProgress.value = progress
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 0380756..97e5c6f 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -18,10 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -53,9 +57,21 @@
@NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap;
- @NonNull private AuthenticationStatsPersister mAuthenticationStatsPersister;
+ // TODO(b/295582896): Find a way to make this NonNull
+ @Nullable private AuthenticationStatsPersister mAuthenticationStatsPersister;
@NonNull private BiometricNotification mBiometricNotification;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId != UserHandle.USER_NULL
+ && intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
+ onUserRemoved(userId);
+ }
+ }
+ };
+
public AuthenticationStatsCollector(@NonNull Context context, int modality,
@NonNull BiometricNotification biometricNotification) {
mContext = context;
@@ -64,6 +80,8 @@
mUserAuthenticationStatsMap = new HashMap<>();
mModality = modality;
mBiometricNotification = biometricNotification;
+
+ context.registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
}
private void initializeUserAuthenticationStatsMap() {
@@ -75,8 +93,9 @@
/** Update total authentication and rejected attempts. */
public void authenticate(int userId, boolean authenticated) {
- // Initialize mUserAuthenticationStatsMap when authenticate to ensure SharedPreferences
- // are ready for application use and avoid ramdump issue.
+ // SharedPreference is not ready when starting system server, initialize
+ // mUserAuthenticationStatsMap in authentication to ensure SharedPreference
+ // is ready for application use.
if (mUserAuthenticationStatsMap.isEmpty()) {
initializeUserAuthenticationStatsMap();
}
@@ -146,6 +165,14 @@
}
}
+ private void onUserRemoved(final int userId) {
+ if (mAuthenticationStatsPersister == null) {
+ initializeUserAuthenticationStatsMap();
+ }
+ mUserAuthenticationStatsMap.remove(userId);
+ mAuthenticationStatsPersister.removeFrrStats(userId);
+ }
+
/**
* Only being used in tests. Callers should not make any changes to the returned
* authentication stats.
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
index 96150a6..21e93a8 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
@@ -94,12 +94,35 @@
}
/**
+ * Remove frr data for a specific user.
+ */
+ public void removeFrrStats(int userId) {
+ try {
+ // Copy into a new HashSet to allow modification.
+ Set<String> frrStatsSet = new HashSet<>(readFrrStats());
+
+ // Remove the old authentication stat for the user if it exists.
+ for (Iterator<String> iterator = frrStatsSet.iterator(); iterator.hasNext();) {
+ String frrStats = iterator.next();
+ JSONObject frrStatJson = new JSONObject(frrStats);
+ if (getValue(frrStatJson, USER_ID).equals(String.valueOf(userId))) {
+ iterator.remove();
+ break;
+ }
+ }
+
+ mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply();
+ } catch (JSONException ignored) {
+ }
+ }
+
+ /**
* Persist frr data for a specific user.
*/
public void persistFrrStats(int userId, int totalAttempts, int rejectedAttempts,
int enrollmentNotifications, int modality) {
try {
- // Copy into a new HashSet to avoid iterator exception.
+ // Copy into a new HashSet to allow modification.
Set<String> frrStatsSet = new HashSet<>(readFrrStats());
// Remove the old authentication stat for the user if it exists.
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index 230ba63..2ff695d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -87,11 +87,11 @@
public static void showFaceEnrollNotification(@NonNull Context context) {
final String name =
- context.getString(R.string.face_recalibrate_notification_name);
+ context.getString(R.string.device_unlock_notification_name);
final String title =
- context.getString(R.string.fingerprint_setup_notification_title);
+ context.getString(R.string.alternative_unlock_setup_notification_title);
final String content =
- context.getString(R.string.face_setup_notification_title);
+ context.getString(R.string.alternative_face_setup_notification_content);
final Intent intent = new Intent(FACE_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
@@ -112,11 +112,11 @@
public static void showFingerprintEnrollNotification(@NonNull Context context) {
final String name =
- context.getString(R.string.fingerprint_recalibrate_notification_name);
+ context.getString(R.string.device_unlock_notification_name);
final String title =
- context.getString(R.string.fingerprint_setup_notification_title);
+ context.getString(R.string.alternative_unlock_setup_notification_title);
final String content =
- context.getString(R.string.fingerprint_setup_notification_content);
+ context.getString(R.string.alternative_fp_setup_notification_content);
final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 4ad26c4..7ea576d 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -63,10 +63,15 @@
// high errors. This default is introduced to provide a fixed display color
// temperature when sensor readings become unreliable.
private final float mLowLightAmbientColorTemperature;
+ // As above, but used when in strong mode (idle screen brightness mode).
+ private final float mLowLightAmbientColorTemperatureStrong;
+
// In high brightness conditions certain color temperatures can cause peak display
// brightness to drop. This fixed color temperature can be used to compensate for
// this effect.
private final float mHighLightAmbientColorTemperature;
+ // As above, but used when in strong mode (idle screen brightness mode).
+ private final float mHighLightAmbientColorTemperatureStrong;
private final boolean mLightModeAllowed;
@@ -97,9 +102,11 @@
// ambient color temperature to the defaults. A piecewise linear relationship
// between low light brightness and low light bias.
private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
+ private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSplineStrong;
// A piecewise linear relationship between high light brightness and high light bias.
private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
+ private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSplineStrong;
private float mLatestAmbientColorTemperature;
private float mLatestAmbientBrightness;
@@ -134,17 +141,29 @@
* @param lowLightAmbientBrightnesses
* The ambient brightness used to map the ambient brightnesses to the biases used to
* interpolate to lowLightAmbientColorTemperature.
+ * @param lowLightAmbientBrightnessesStrong
+ * The ambient brightness used to map the ambient brightnesses to the biases used to
+ * interpolate to lowLightAmbientColorTemperature.
* @param lowLightAmbientBiases
* The biases used to map the ambient brightnesses to the biases used to interpolate to
* lowLightAmbientColorTemperature.
+ * @param lowLightAmbientBiasesStrong
+ * The biases used to map the ambient brightnesses to the biases used to interpolate to
+ * lowLightAmbientColorTemperature.
* @param lowLightAmbientColorTemperature
* The ambient color temperature to which we interpolate to based on the low light curve.
* @param highLightAmbientBrightnesses
* The ambient brightness used to map the ambient brightnesses to the biases used to
* interpolate to highLightAmbientColorTemperature.
+ * @param highLightAmbientBrightnessesStrong
+ * The ambient brightness used to map the ambient brightnesses to the biases used to
+ * interpolate to highLightAmbientColorTemperature.
* @param highLightAmbientBiases
* The biases used to map the ambient brightnesses to the biases used to interpolate to
* highLightAmbientColorTemperature.
+ * @param highLightAmbientBiasesStrong
+ * The biases used to map the ambient brightnesses to the biases used to interpolate to
+ * highLightAmbientColorTemperature.
* @param highLightAmbientColorTemperature
* The ambient color temperature to which we interpolate to based on the high light curve.
* @param ambientColorTemperatures
@@ -170,11 +189,17 @@
@NonNull AmbientFilter colorTemperatureFilter,
@NonNull DisplayWhiteBalanceThrottler throttler,
float[] lowLightAmbientBrightnesses,
+ float[] lowLightAmbientBrightnessesStrong,
float[] lowLightAmbientBiases,
+ float[] lowLightAmbientBiasesStrong,
float lowLightAmbientColorTemperature,
+ float lowLightAmbientColorTemperatureStrong,
float[] highLightAmbientBrightnesses,
+ float[] highLightAmbientBrightnessesStrong,
float[] highLightAmbientBiases,
+ float[] highLightAmbientBiasesStrong,
float highLightAmbientColorTemperature,
+ float highLightAmbientColorTemperatureStrong,
float[] ambientColorTemperatures,
float[] displayColorTemperatures,
float[] strongAmbientColorTemperatures,
@@ -188,7 +213,9 @@
mColorTemperatureFilter = colorTemperatureFilter;
mThrottler = throttler;
mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
+ mLowLightAmbientColorTemperatureStrong = lowLightAmbientColorTemperatureStrong;
mHighLightAmbientColorTemperature = highLightAmbientColorTemperature;
+ mHighLightAmbientColorTemperatureStrong = highLightAmbientColorTemperatureStrong;
mAmbientColorTemperature = -1.0f;
mPendingAmbientColorTemperature = -1.0f;
mLastAmbientColorTemperature = -1.0f;
@@ -214,6 +241,23 @@
}
try {
+ mLowLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline(
+ lowLightAmbientBrightnessesStrong, lowLightAmbientBiasesStrong);
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to create strong low light ambient brightness to bias spline.", e);
+ mLowLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ if (mLowLightAmbientBrightnessToBiasSplineStrong != null) {
+ if (mLowLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f
+ || mLowLightAmbientBrightnessToBiasSplineStrong.interpolate(
+ Float.POSITIVE_INFINITY) != 1.0f) {
+ Slog.d(TAG, "invalid strong low light ambient brightness to bias spline, "
+ + "bias must begin at 0.0 and end at 1.0.");
+ mLowLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ }
+
+ try {
mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
highLightAmbientBrightnesses, highLightAmbientBiases);
} catch (Exception e) {
@@ -230,6 +274,23 @@
}
}
+ try {
+ mHighLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline(
+ highLightAmbientBrightnessesStrong, highLightAmbientBiasesStrong);
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to create strong high light ambient brightness to bias spline.", e);
+ mHighLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ if (mHighLightAmbientBrightnessToBiasSplineStrong != null) {
+ if (mHighLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f
+ || mHighLightAmbientBrightnessToBiasSplineStrong.interpolate(
+ Float.POSITIVE_INFINITY) != 1.0f) {
+ Slog.d(TAG, "invalid strong high light ambient brightness to bias spline, "
+ + "bias must begin at 0.0 and end at 1.0.");
+ mHighLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ }
+
if (mLowLightAmbientBrightnessToBiasSpline != null &&
mHighLightAmbientBrightnessToBiasSpline != null) {
if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] >
@@ -241,6 +302,18 @@
}
}
+ if (mLowLightAmbientBrightnessToBiasSplineStrong != null
+ && mHighLightAmbientBrightnessToBiasSplineStrong != null) {
+ if (lowLightAmbientBrightnessesStrong[lowLightAmbientBrightnessesStrong.length - 1]
+ > highLightAmbientBrightnessesStrong[0]) {
+ Slog.d(TAG,
+ "invalid strong low light and high light ambient brightness to bias "
+ + "spline combination, defined domains must not intersect.");
+ mLowLightAmbientBrightnessToBiasSplineStrong = null;
+ mHighLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ }
+
try {
mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
ambientColorTemperatures, displayColorTemperatures);
@@ -365,7 +438,11 @@
mColorTemperatureFilter.dump(writer);
mThrottler.dump(writer);
writer.println(" mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
+ writer.println(" mLowLightAmbientColorTemperatureStrong="
+ + mLowLightAmbientColorTemperatureStrong);
writer.println(" mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
+ writer.println(" mHighLightAmbientColorTemperatureStrong="
+ + mHighLightAmbientColorTemperatureStrong);
writer.println(" mAmbientColorTemperature=" + mAmbientColorTemperature);
writer.println(" mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
@@ -377,8 +454,12 @@
+ mStrongAmbientToDisplayColorTemperatureSpline);
writer.println(" mLowLightAmbientBrightnessToBiasSpline="
+ mLowLightAmbientBrightnessToBiasSpline);
+ writer.println(" mLowLightAmbientBrightnessToBiasSplineStrong="
+ + mLowLightAmbientBrightnessToBiasSplineStrong);
writer.println(" mHighLightAmbientBrightnessToBiasSpline="
+ mHighLightAmbientBrightnessToBiasSpline);
+ writer.println(" mHighLightAmbientBrightnessToBiasSplineStrong="
+ + mHighLightAmbientBrightnessToBiasSplineStrong);
}
@Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -400,6 +481,17 @@
*/
public void updateAmbientColorTemperature() {
final long time = System.currentTimeMillis();
+ final float lowLightAmbientColorTemperature = mStrongModeEnabled
+ ? mLowLightAmbientColorTemperatureStrong : mLowLightAmbientColorTemperature;
+ final float highLightAmbientColorTemperature = mStrongModeEnabled
+ ? mHighLightAmbientColorTemperatureStrong : mHighLightAmbientColorTemperature;
+ final Spline.LinearSpline lowLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
+ ? mLowLightAmbientBrightnessToBiasSplineStrong
+ : mLowLightAmbientBrightnessToBiasSpline;
+ final Spline.LinearSpline highLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
+ ? mHighLightAmbientBrightnessToBiasSplineStrong
+ : mHighLightAmbientBrightnessToBiasSpline;
+
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
mLatestAmbientColorTemperature = ambientColorTemperature;
@@ -423,19 +515,19 @@
mLatestAmbientBrightness = ambientBrightness;
if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
- && mLowLightAmbientBrightnessToBiasSpline != null) {
- float bias = mLowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
+ && lowLightAmbientBrightnessToBiasSpline != null) {
+ float bias = lowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
ambientColorTemperature =
bias * ambientColorTemperature + (1.0f - bias)
- * mLowLightAmbientColorTemperature;
+ * lowLightAmbientColorTemperature;
mLatestLowLightBias = bias;
}
if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
- && mHighLightAmbientBrightnessToBiasSpline != null) {
- float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
+ && highLightAmbientBrightnessToBiasSpline != null) {
+ float bias = highLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
ambientColorTemperature =
(1.0f - bias) * ambientColorTemperature + bias
- * mHighLightAmbientColorTemperature;
+ * highLightAmbientColorTemperature;
mLatestHighLightBias = bias;
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index 62f813f..39e6b3f 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -70,21 +70,39 @@
final float[] displayWhiteBalanceLowLightAmbientBrightnesses = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceLowLightAmbientBrightnesses);
+ final float[] displayWhiteBalanceLowLightAmbientBrightnessesStrong = getFloatArray(
+ resources, com.android.internal.R.array
+ .config_displayWhiteBalanceLowLightAmbientBrightnessesStrong);
final float[] displayWhiteBalanceLowLightAmbientBiases = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceLowLightAmbientBiases);
+ final float[] displayWhiteBalanceLowLightAmbientBiasesStrong = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceLowLightAmbientBiasesStrong);
final float lowLightAmbientColorTemperature = getFloat(resources,
com.android.internal.R.dimen
.config_displayWhiteBalanceLowLightAmbientColorTemperature);
+ final float lowLightAmbientColorTemperatureStrong = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong);
final float[] displayWhiteBalanceHighLightAmbientBrightnesses = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceHighLightAmbientBrightnesses);
+ final float[] displayWhiteBalanceHighLightAmbientBrightnessesStrong = getFloatArray(
+ resources, com.android.internal.R.array
+ .config_displayWhiteBalanceHighLightAmbientBrightnessesStrong);
final float[] displayWhiteBalanceHighLightAmbientBiases = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceHighLightAmbientBiases);
+ final float[] displayWhiteBalanceHighLightAmbientBiasesStrong = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceHighLightAmbientBiasesStrong);
final float highLightAmbientColorTemperature = getFloat(resources,
com.android.internal.R.dimen
.config_displayWhiteBalanceHighLightAmbientColorTemperature);
+ final float highLightAmbientColorTemperatureStrong = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong);
final float[] ambientColorTemperatures = getFloatArray(resources,
com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
final float[] displayColorTemperatures = getFloatArray(resources,
@@ -100,9 +118,15 @@
final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
- displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
+ displayWhiteBalanceLowLightAmbientBrightnessesStrong,
+ displayWhiteBalanceLowLightAmbientBiases,
+ displayWhiteBalanceLowLightAmbientBiasesStrong, lowLightAmbientColorTemperature,
+ lowLightAmbientColorTemperatureStrong,
displayWhiteBalanceHighLightAmbientBrightnesses,
- displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
+ displayWhiteBalanceHighLightAmbientBrightnessesStrong,
+ displayWhiteBalanceHighLightAmbientBiases,
+ displayWhiteBalanceHighLightAmbientBiasesStrong, highLightAmbientColorTemperature,
+ highLightAmbientColorTemperatureStrong,
ambientColorTemperatures, displayColorTemperatures, strongAmbientColorTemperatures,
strongDisplayColorTemperatures, lightModeAllowed);
brightnessSensor.setCallbacks(controller);
diff --git a/services/core/java/com/android/server/input/FocusEventDebugView.java b/services/core/java/com/android/server/input/FocusEventDebugView.java
index 02e0eb0..39519ef 100644
--- a/services/core/java/com/android/server/input/FocusEventDebugView.java
+++ b/services/core/java/com/android/server/input/FocusEventDebugView.java
@@ -28,7 +28,7 @@
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Typeface;
-import android.util.Log;
+import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.Slog;
import android.util.TypedValue;
@@ -38,22 +38,26 @@
import android.view.MotionEvent;
import android.view.RoundedCorner;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.animation.AccelerateInterpolator;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Supplier;
/**
* Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
* the screen.
*/
-class FocusEventDebugView extends LinearLayout {
+class FocusEventDebugView extends RelativeLayout {
private static final String TAG = FocusEventDebugView.class.getSimpleName();
@@ -80,18 +84,24 @@
private PressedKeyContainer mPressedKeyContainer;
@Nullable
private PressedKeyContainer mPressedModifierContainer;
+ private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
+ @Nullable
+ private RotaryInputValueView mRotaryInputValueView;
- FocusEventDebugView(Context c, InputManagerService service) {
+ @VisibleForTesting
+ FocusEventDebugView(Context c, InputManagerService service,
+ Supplier<RotaryInputValueView> rotaryInputValueViewFactory) {
super(c);
setFocusableInTouchMode(true);
mService = service;
+ mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
final var dm = mContext.getResources().getDisplayMetrics();
mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, dm);
+ }
- setOrientation(HORIZONTAL);
- setLayoutDirection(LAYOUT_DIRECTION_RTL);
- setGravity(Gravity.START | Gravity.BOTTOM);
+ FocusEventDebugView(Context c, InputManagerService service) {
+ this(c, service, () -> new RotaryInputValueView(c));
}
@Override
@@ -100,13 +110,13 @@
final RoundedCorner bottomLeft =
insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- if (bottomLeft != null) {
+ if (bottomLeft != null && !insets.isRound()) {
paddingBottom = bottomLeft.getRadius();
}
final RoundedCorner bottomRight =
insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
- if (bottomRight != null) {
+ if (bottomRight != null && !insets.isRound()) {
paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
}
@@ -151,7 +161,7 @@
}
mPressedKeyContainer = new PressedKeyContainer(mContext);
- mPressedKeyContainer.setOrientation(HORIZONTAL);
+ mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
final var scroller = new HorizontalScrollView(mContext);
@@ -160,15 +170,23 @@
scroller.addOnLayoutChangeListener(
(view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
scroller.setHorizontalFadingEdgeEnabled(true);
- addView(scroller, new LayoutParams(0, WRAP_CONTENT, 1));
+ LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
+ addView(scroller, scrollerLayoutParams);
mPressedModifierContainer = new PressedKeyContainer(mContext);
- mPressedModifierContainer.setOrientation(VERTICAL);
+ mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
- addView(mPressedModifierContainer, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
+ modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
+ addView(mPressedModifierContainer, modifierLayoutParams);
}
- private void handleUpdateShowRotaryInput(boolean enabled) {
+ @VisibleForTesting
+ void handleUpdateShowRotaryInput(boolean enabled) {
if (enabled == showRotaryInput()) {
return;
}
@@ -176,10 +194,18 @@
if (!enabled) {
mFocusEventDebugGlobalMonitor.dispose();
mFocusEventDebugGlobalMonitor = null;
+ removeView(mRotaryInputValueView);
+ mRotaryInputValueView = null;
return;
}
mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
+
+ mRotaryInputValueView = mRotaryInputValueViewFactory.get();
+ LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ valueLayoutParams.addRule(CENTER_HORIZONTAL);
+ valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ addView(mRotaryInputValueView, valueLayoutParams);
}
/** Report a key event to the debug view. */
@@ -242,14 +268,14 @@
keyEvent.recycle();
}
- private void handleRotaryInput(MotionEvent motionEvent) {
+ @VisibleForTesting
+ void handleRotaryInput(MotionEvent motionEvent) {
if (!showRotaryInput()) {
return;
}
float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
- // TODO(b/286086154): replace log with visualization.
- Log.d(TAG, "ROTARY INPUT: " + String.valueOf(scrollAxisValue));
+ mRotaryInputValueView.updateValue(scrollAxisValue);
motionEvent.recycle();
}
@@ -308,7 +334,14 @@
/** Determine whether to show rotary input by checking one of the rotary-related objects. */
private boolean showRotaryInput() {
- return mFocusEventDebugGlobalMonitor != null;
+ return mRotaryInputValueView != null;
+ }
+
+ /**
+ * Converts a dimension in scaled pixel units to integer display pixels.
+ */
+ private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
+ return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
}
private static class PressedKeyView extends TextView {
@@ -419,4 +452,66 @@
invalidate();
}
}
+
+ /** Draws the most recent rotary input value and indicates whether the source is active. */
+ @VisibleForTesting
+ static class RotaryInputValueView extends TextView {
+
+ private static final int INACTIVE_TEXT_COLOR = 0xffff00ff;
+ private static final int ACTIVE_TEXT_COLOR = 0xff420f28;
+ private static final int TEXT_SIZE_SP = 8;
+ private static final int SIDE_PADDING_SP = 4;
+ /** Determines how long the active status lasts. */
+ private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */;
+ private static final ColorFilter ACTIVE_BACKGROUND_FILTER =
+ new ColorMatrixColorFilter(new float[]{
+ 0, 0, 0, 0, 255, // red
+ 0, 0, 0, 0, 0, // green
+ 0, 0, 0, 0, 255, // blue
+ 0, 0, 0, 0, 200 // alpha
+ });
+
+ private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false);
+ private final float mScaledVerticalScrollFactor;
+
+ @VisibleForTesting
+ RotaryInputValueView(Context c) {
+ super(c);
+
+ DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+ mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
+
+ setText(getFormattedValue(0));
+ setTextColor(INACTIVE_TEXT_COLOR);
+ setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm));
+ setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0,
+ applyDimensionSp(SIDE_PADDING_SP, dm), 0);
+ setTypeface(null, Typeface.BOLD);
+ setBackgroundResource(R.drawable.focus_event_rotary_input_background);
+ }
+
+ void updateValue(float value) {
+ removeCallbacks(mUpdateActivityStatusCallback);
+
+ setText(getFormattedValue(value * mScaledVerticalScrollFactor));
+
+ updateActivityStatus(true);
+ postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION);
+ }
+
+ @VisibleForTesting
+ void updateActivityStatus(boolean active) {
+ if (active) {
+ setTextColor(ACTIVE_TEXT_COLOR);
+ getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER);
+ } else {
+ setTextColor(INACTIVE_TEXT_COLOR);
+ getBackground().clearColorFilter();
+ }
+ }
+
+ private static String getFormattedValue(float value) {
+ return String.format("%s%.1f", value < 0 ? "-" : "+", Math.abs(value));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2fc4829..8e7baf2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5959,6 +5959,8 @@
mVisibilityStateComputer.dump(pw);
p.println(" mInFullscreenMode=" + mInFullscreenMode);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
+ p.println(" ENABLE_HIDE_IME_CAPTION_BAR="
+ + InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR);
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mStylusIds=" + (mStylusIds != null
? Arrays.toString(mStylusIds.toArray()) : ""));
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index b2d3fca..100c638 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,8 +25,6 @@
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -48,6 +46,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.provider.Settings;
@@ -98,8 +97,7 @@
// the period after which a notification is updated where it can make sound
private static final int MAX_SOUND_DELAY_MS = 2000;
private final StatusBarNotification sbn;
- IActivityManager mAm;
- UriGrantsManagerInternal mUgmInternal;
+ private final UriGrantsManagerInternal mUgmInternal;
final int mTargetSdkVersion;
final int mOriginalFlags;
private final Context mContext;
@@ -223,7 +221,6 @@
this.sbn = sbn;
mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
.getPackageTargetSdkVersion(sbn.getPackageName());
- mAm = ActivityManager.getService();
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
@@ -1387,18 +1384,27 @@
* Collect all {@link Uri} that should have permission granted to whoever
* will be rendering it.
*/
- protected void calculateGrantableUris() {
- final Notification notification = getNotification();
- notification.visitUris((uri) -> {
- visitGrantableUri(uri, false, false);
- });
+ private void calculateGrantableUris() {
+ Trace.beginSection("NotificationRecord.calculateGrantableUris");
+ try {
+ // We can't grant URI permissions from system.
+ final int sourceUid = getSbn().getUid();
+ if (sourceUid == android.os.Process.SYSTEM_UID) return;
- if (notification.getChannelId() != null) {
- NotificationChannel channel = getChannel();
- if (channel != null) {
- visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
- & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
+ final Notification notification = getNotification();
+ notification.visitUris((uri) -> {
+ visitGrantableUri(uri, false, false);
+ });
+
+ if (notification.getChannelId() != null) {
+ NotificationChannel channel = getChannel();
+ if (channel != null) {
+ visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
+ & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
+ }
}
+ } finally {
+ Trace.endSection();
}
}
@@ -1413,13 +1419,14 @@
private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) {
if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
- // We can't grant Uri permissions from system
- final int sourceUid = getSbn().getUid();
- if (sourceUid == android.os.Process.SYSTEM_UID) return;
+ if (mGrantableUris != null && mGrantableUris.contains(uri)) {
+ return; // already verified this URI
+ }
+ final int sourceUid = getSbn().getUid();
final long ident = Binder.clearCallingIdentity();
try {
- // This will throw SecurityException if caller can't grant
+ // This will throw a SecurityException if the caller can't grant.
mUgmInternal.checkGrantUriPermission(sourceUid, null,
ContentProvider.getUriWithoutUserId(uri),
Intent.FLAG_GRANT_READ_URI_PERMISSION,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 385dfcb8..f2797eb 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4907,6 +4907,11 @@
USER_OPERATION_ERROR_UNKNOWN);
}
}
+ if (isMainUser && getMainUserIdUnchecked() != UserHandle.USER_NULL) {
+ throwCheckedUserOperationException(
+ "Cannot add user with FLAG_MAIN as main user already exists.",
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
+ }
if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
throwCheckedUserOperationException(
"Cannot add more users of type " + userType
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3125518..cba215a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -224,6 +224,9 @@
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
+import static com.android.server.wm.StartingData.AFTER_TRANSACTION_COPY_TO_CLIENT;
+import static com.android.server.wm.StartingData.AFTER_TRANSACTION_IDLE;
+import static com.android.server.wm.StartingData.AFTER_TRANSACTION_REMOVE_DIRECTLY;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -2690,6 +2693,11 @@
if (isTransferringSplashScreen()) {
return true;
}
+ // Only do transfer after transaction has done when starting window exist.
+ if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
+ mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
+ return true;
+ }
requestCopySplashScreen();
return isTransferringSplashScreen();
}
@@ -2850,11 +2858,14 @@
if (mStartingData == null) {
return;
}
- mStartingData.mWaitForSyncTransactionCommit = false;
- if (mStartingData.mRemoveAfterTransaction) {
- mStartingData.mRemoveAfterTransaction = false;
- removeStartingWindowAnimation(mStartingData.mPrepareRemoveAnimation);
+ final StartingData lastData = mStartingData;
+ lastData.mWaitForSyncTransactionCommit = false;
+ if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_REMOVE_DIRECTLY) {
+ removeStartingWindowAnimation(lastData.mPrepareRemoveAnimation);
+ } else if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_COPY_TO_CLIENT) {
+ removeStartingWindow();
}
+ lastData.mRemoveAfterTransaction = AFTER_TRANSACTION_IDLE;
}
void removeStartingWindowAnimation(boolean prepareAnimation) {
@@ -2881,7 +2892,7 @@
if (mStartingData != null) {
if (mStartingData.mWaitForSyncTransactionCommit
|| mTransitionController.inCollectingTransition(startingWindow)) {
- mStartingData.mRemoveAfterTransaction = true;
+ mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
mStartingData.mPrepareRemoveAnimation = prepareAnimation;
return;
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 34806bd..a23547e 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import android.annotation.IntDef;
+
import com.android.server.wm.StartingSurfaceController.StartingSurface;
/**
@@ -23,6 +25,20 @@
*/
public abstract class StartingData {
+ /** Nothing need to do after transaction */
+ static final int AFTER_TRANSACTION_IDLE = 0;
+ /** Remove the starting window directly after transaction done. */
+ static final int AFTER_TRANSACTION_REMOVE_DIRECTLY = 1;
+ /** Do copy splash screen to client after transaction done. */
+ static final int AFTER_TRANSACTION_COPY_TO_CLIENT = 2;
+
+ @IntDef(prefix = { "AFTER_TRANSACTION" }, value = {
+ AFTER_TRANSACTION_IDLE,
+ AFTER_TRANSACTION_REMOVE_DIRECTLY,
+ AFTER_TRANSACTION_COPY_TO_CLIENT,
+ })
+ @interface AfterTransaction {}
+
protected final WindowManagerService mService;
protected final int mTypeParams;
@@ -60,7 +76,7 @@
* This starting window should be removed after applying the start transaction of transition,
* which ensures the app window has shown.
*/
- boolean mRemoveAfterTransaction;
+ @AfterTransaction int mRemoveAfterTransaction;
/** Whether to prepare the removal animation. */
boolean mPrepareRemoveAnimation;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index f975b6f..183a84d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -64,7 +64,9 @@
private static final int AMBIENT_COLOR_TYPE = 20705;
private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
+ private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG = 5555.5f;
private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE = 3456.7f;
+ private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG = 3333.3f;
private Handler mHandler = new Handler(Looper.getMainLooper());
private Sensor mLightSensor;
@@ -78,6 +80,10 @@
@Mock private TypedArray mBiases;
@Mock private TypedArray mHighLightBrightnesses;
@Mock private TypedArray mHighLightBiases;
+ @Mock private TypedArray mBrightnessesStrong;
+ @Mock private TypedArray mBiasesStrong;
+ @Mock private TypedArray mHighLightBrightnessesStrong;
+ @Mock private TypedArray mHighLightBiasesStrong;
@Mock private TypedArray mAmbientColorTemperatures;
@Mock private TypedArray mDisplayColorTemperatures;
@Mock private TypedArray mStrongAmbientColorTemperatures;
@@ -108,6 +114,10 @@
LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperature,
HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+ mockResourcesFloat(R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong,
+ LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG);
+ mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong,
+ HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceAmbientColorTemperatures))
.thenReturn(mAmbientColorTemperatures);
@@ -133,6 +143,18 @@
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceHighLightAmbientBiases))
.thenReturn(mHighLightBiases);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceLowLightAmbientBrightnessesStrong))
+ .thenReturn(mBrightnessesStrong);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceLowLightAmbientBiasesStrong))
+ .thenReturn(mBiasesStrong);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceHighLightAmbientBrightnessesStrong))
+ .thenReturn(mHighLightBrightnessesStrong);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceHighLightAmbientBiasesStrong))
+ .thenReturn(mHighLightBiasesStrong);
mockThrottler();
LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -388,8 +410,8 @@
public void testStrongMode() {
final float lowerBrightness = 10.0f;
final float upperBrightness = 50.0f;
- setBrightnesses(lowerBrightness, upperBrightness);
- setBiases(0.0f, 1.0f);
+ setBrightnessesStrong(lowerBrightness, upperBrightness);
+ setBiasesStrong(0.0f, 1.0f);
final int ambientColorTempLow = 6000;
final int ambientColorTempHigh = 8000;
final int displayColorTempLow = 6400;
@@ -413,7 +435,7 @@
setEstimatedBrightnessAndUpdate(controller,
mix(lowerBrightness, upperBrightness, brightnessFraction));
assertEquals(controller.mPendingAmbientColorTemperature,
- mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE,
+ mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG,
mix(displayColorTempLow, displayColorTempHigh, ambientTempFraction),
brightnessFraction),
ALLOWED_ERROR_DELTA);
@@ -458,7 +480,7 @@
assertEquals(-1.0f, controller.mPendingAmbientColorTemperature, 0);
}
- void mockThrottler() {
+ private void mockThrottler() {
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0);
when(mResourcesSpy.getInteger(
@@ -513,10 +535,18 @@
setFloatArrayResource(mBrightnesses, vals);
}
+ private void setBrightnessesStrong(float... vals) {
+ setFloatArrayResource(mBrightnessesStrong, vals);
+ }
+
private void setBiases(float... vals) {
setFloatArrayResource(mBiases, vals);
}
+ private void setBiasesStrong(float... vals) {
+ setFloatArrayResource(mBiasesStrong, vals);
+ }
+
private void setHighLightBrightnesses(float... vals) {
setFloatArrayResource(mHighLightBrightnesses, vals);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 78e5a42..bdbf4ec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -63,6 +63,8 @@
import org.junit.Test;
import org.mockito.Mock;
+import java.io.File;
+
/**
* Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest}
*/
@@ -107,6 +109,8 @@
.getTargetContext();
private final SparseArray<UserData> mUsers = new SparseArray<>();
+ private File mTestDir;
+
private Context mSpiedContext;
private @Mock PackageManagerService mMockPms;
@@ -144,17 +148,23 @@
doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
// Must construct UserManagerService in the UiThread
+ mTestDir = new File(mRealContext.getDataDir(), "umstest");
+ mTestDir.mkdirs();
mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
- mPackagesLock, mRealContext.getDataDir(), mUsers);
+ mPackagesLock, mTestDir, mUsers);
mUmi = LocalServices.getService(UserManagerInternal.class);
assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi)
.isNotNull();
}
@After
- public void resetUserManagerInternal() {
+ public void tearDown() {
// LocalServices follows the "Highlander rule" - There can be only one!
LocalServices.removeServiceForTest(UserManagerInternal.class);
+
+ // Clean up test dir to remove persisted user files.
+ assertThat(deleteRecursive(mTestDir)).isTrue();
+ mUsers.clear();
}
@Test
@@ -496,6 +506,14 @@
@Test
public void testMainUser_hasNoCallsOrSMSRestrictionsByDefault() {
+ // Remove the main user so we can add another one
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserData userData = mUsers.valueAt(i);
+ if (userData.info.isMain()) {
+ mUsers.delete(i);
+ break;
+ }
+ }
UserInfo mainUser = mUms.createUserWithThrow("main user", USER_TYPE_FULL_SECONDARY,
UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN);
@@ -621,6 +639,18 @@
userData.mLastEnteredForegroundTimeMillis = timeMillis;
}
+ public boolean deleteRecursive(File file) {
+ if (file.isDirectory()) {
+ for (File item : file.listFiles()) {
+ boolean success = deleteRecursive(item);
+ if (!success) {
+ return false;
+ }
+ }
+ }
+ return file.delete();
+ }
+
private static final class TestUserData extends UserData {
@SuppressWarnings("deprecation")
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index 285a20c..746fb53 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -36,6 +36,9 @@
import android.content.res.Resources;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.server.biometrics.sensors.BiometricNotification;
@@ -47,6 +50,8 @@
import java.io.File;
+@Presubmit
+@SmallTest
public class AuthenticationStatsCollectorTest {
private AuthenticationStatsCollector mAuthenticationStatsCollector;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
index 455625c..dde2a3c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
@@ -29,6 +29,9 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import org.json.JSONException;
import org.json.JSONObject;
@@ -45,6 +48,8 @@
import java.util.List;
import java.util.Set;
+@Presubmit
+@SmallTest
public class AuthenticationStatsPersisterTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@@ -211,6 +216,20 @@
assertThat(mStringSetArgumentCaptor.getValue()).contains(expectedFrrStats);
}
+ @Test
+ public void removeFrrStats_existingUser_shouldUpdateRecord() throws JSONException {
+ AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1,
+ 300 /* totalAttempts */, 10 /* rejectedAttempts */,
+ 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE);
+ when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn(
+ Set.of(buildFrrStats(authenticationStats)));
+
+ mAuthenticationStatsPersister.removeFrrStats(USER_ID_1);
+
+ verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture());
+ assertThat(mStringSetArgumentCaptor.getValue()).doesNotContain(authenticationStats);
+ }
+
private String buildFrrStats(AuthenticationStats authenticationStats)
throws JSONException {
if (authenticationStats.getModality() == BiometricsProtoEnums.MODALITY_FACE) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index cb659b6..ecd35a5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -1565,6 +1565,25 @@
assertThat(userInfo.name).isEqualTo(newName);
}
+ @Test
+ public void testCannotCreateAdditionalMainUser() {
+ UserHandle mainUser = mUserManager.getMainUser();
+ assumeTrue("There is no main user", mainUser != null);
+
+ // Users with FLAG_MAIN can't be removed, so no point using the local createUser method.
+ UserInfo newMainUser = mUserManager.createUser("test", UserInfo.FLAG_MAIN);
+ assertThat(newMainUser).isNull();
+
+ List<UserInfo> users = mUserManager.getUsers();
+ int mainUserCount = 0;
+ for (UserInfo user : users) {
+ if (user.isMain()) {
+ mainUserCount++;
+ }
+ }
+ assertThat(mainUserCount).isEqualTo(1);
+ }
+
private boolean isPackageInstalledForUser(String packageName, int userId) {
try {
return mPackageManager.getPackageInfoAsUser(packageName, 0, userId) != null;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index fae92d9..f83a1df 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -36,7 +36,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -44,7 +44,6 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
@@ -76,6 +75,7 @@
import com.android.internal.R;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.LocalServices;
import com.android.server.UiServiceTestCase;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -850,84 +850,78 @@
@Test
public void testCalculateGrantableUris_PappProvided() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.setSound(null, null);
Notification n = new Notification.Builder(mContext, channel.getId())
.setSmallIcon(Icon.createWithContentUri(Uri.parse("content://something")))
.build();
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.mUgmInternal = ugm;
- try {
- record.calculateGrantableUris();
- fail("App provided uri for p targeting app should throw exception");
- } catch (SecurityException e) {
- // expected
- }
+ assertThrows("App provided uri for p targeting app should throw exception",
+ SecurityException.class,
+ () -> new NotificationRecord(mMockContext, sbn, channel));
}
@Test
public void testCalculateGrantableUris_PappProvided_invalidSound() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.setSound(Uri.parse("content://something"), mock(AudioAttributes.class));
Notification n = mock(Notification.class);
when(n.getChannelId()).thenReturn(channel.getId());
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.mUgmInternal = ugm;
- record.calculateGrantableUris();
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound());
}
@Test
public void testCalculateGrantableUris_PuserOverridden() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
Notification n = mock(Notification.class);
when(n.getChannelId()).thenReturn(channel.getId());
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.calculateGrantableUris();
+ new NotificationRecord(mMockContext, sbn, channel); // should not throw
}
@Test
public void testCalculateGrantableUris_prePappProvided() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
Notification n = mock(Notification.class);
when(n.getChannelId()).thenReturn(channel.getId());
StatusBarNotification sbn =
new StatusBarNotification(PKG_O, PKG_O, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.calculateGrantableUris();
- // should not throw
+ new NotificationRecord(mMockContext, sbn, channel); // should not throw
}
@Test
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 943d8d6..4a541da 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3388,7 +3388,11 @@
public void onAbort() {}
/**
- * Notifies this Connection of a request to hold.
+ * Notifies this Connection of a request to hold. {@link Connection#setOnHold} should be within
+ * the onHold() body in order to transition the call state to {@link Connection#STATE_HOLDING}.
+ * <p>
+ * Note: If the Connection does not transition to {@link Connection#STATE_HOLDING} within 2
+ * seconds, the call will be disconnected.
*/
public void onHold() {}
diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
new file mode 100644
index 0000000..d8113fc
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 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.input;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest FocusEventDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FocusEventDebugViewTest {
+
+ private FocusEventDebugView mFocusEventDebugView;
+ private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView;
+ private float mScaledVerticalScrollFactor;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ mScaledVerticalScrollFactor =
+ ViewConfiguration.get(context).getScaledVerticalScrollFactor();
+ InputManagerService mockService = mock(InputManagerService.class);
+ when(mockService.monitorInput(anyString(), anyInt()))
+ .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
+
+ mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context);
+ mFocusEventDebugView = new FocusEventDebugView(context, mockService,
+ () -> mRotaryInputValueView);
+ }
+
+ @Test
+ public void startsRotaryInputValueViewWithDefaultValue() {
+ assertEquals("+0.0", mRotaryInputValueView.getText());
+ }
+
+ @Test
+ public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(true);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f));
+
+ assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor),
+ mRotaryInputValueView.getText());
+ }
+
+ @Test
+ public void updateActivityStatus_setsAndRemovesColorFilter() {
+ // It should not be active initially.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(true);
+ // It should be active after rotary input.
+ assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(false);
+ // It should not be active after waiting for mUpdateActivityStatusCallback.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+ }
+
+ private MotionEvent createRotaryMotionEvent(float scrollAxisValue) {
+ PointerCoords pointerCoords = new PointerCoords();
+ pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
+ PointerProperties pointerProperties = new PointerProperties();
+
+ return MotionEvent.obtain(
+ /* downTime */ 0,
+ /* eventTime */ 0,
+ /* action */ MotionEvent.ACTION_SCROLL,
+ /* pointerCount */ 1,
+ /* pointerProperties */ new PointerProperties[] {pointerProperties},
+ /* pointerCoords */ new PointerCoords[] {pointerCoords},
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 0,
+ /* yPrecision */ 0,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
+ /* flags */ 0
+ );
+ }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
index 31ea832..c92d768 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
@@ -16,10 +16,6 @@
package com.android.test.silkfx.hdr
-import android.animation.AnimatorSet
-import android.animation.ObjectAnimator
-import android.animation.ValueAnimator
-import android.animation.ValueAnimator.AnimatorUpdateListener
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
@@ -46,7 +42,7 @@
private var selectedImage = -1
private var outputMode = R.id.output_hdr
private var bitmap: Bitmap? = null
- private var gainmap: Gainmap? = null
+ private var originalGainmap: Gainmap? = null
private var gainmapVisualizer: Bitmap? = null
private lateinit var imageView: SubsamplingScaleImageView
private lateinit var gainmapMetadataEditor: GainmapMetadataEditor
@@ -70,7 +66,6 @@
it.check(outputMode)
it.setOnCheckedChangeListener { _, checkedId ->
outputMode = checkedId
- // Intentionally don't do anything fancy so that mode A/B comparisons are easy
updateDisplay()
}
}
@@ -101,41 +96,10 @@
imageView.apply {
isClickable = true
- // Example of animating between SDR and HDR using gainmap params; animates HDR->SDR->HDR
- // with a brief pause on SDR. The key thing here is that the gainmap's
- // minDisplayRatioForHdrTransition is animated between its original value (for full HDR)
- // and displayRatioForFullHdr (for full SDR). The view must also be invalidated during
- // the animation for the updates to take effect.
setOnClickListener {
- if (gainmap != null && (outputMode == R.id.output_hdr ||
- outputMode == R.id.output_hdr_test)) {
- val animationLengthMs: Long = 500
- val updateListener = object : AnimatorUpdateListener {
- override fun onAnimationUpdate(animation: ValueAnimator) {
- imageView.invalidate()
- }
- }
- val hdrToSdr = ObjectAnimator.ofFloat(
- gainmap, "minDisplayRatioForHdrTransition",
- gainmap!!.minDisplayRatioForHdrTransition,
- gainmap!!.displayRatioForFullHdr).apply {
- duration = animationLengthMs
- addUpdateListener(updateListener)
- }
- val sdrToHdr = ObjectAnimator.ofFloat(
- gainmap, "minDisplayRatioForHdrTransition",
- gainmap!!.displayRatioForFullHdr,
- gainmap!!.minDisplayRatioForHdrTransition).apply {
- duration = animationLengthMs
- addUpdateListener(updateListener)
- }
-
- AnimatorSet().apply {
- play(hdrToSdr)
- play(sdrToHdr).after(animationLengthMs)
- start()
- }
- }
+ animate().alpha(.5f).withEndAction {
+ animate().alpha(1f).start()
+ }.start()
}
}
}
@@ -149,7 +113,7 @@
}
private fun doDecode(source: ImageDecoder.Source) {
- gainmap = null
+ originalGainmap = null
bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, source ->
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
}
@@ -167,9 +131,10 @@
findViewById<TextView>(R.id.error_msg)!!.visibility = View.GONE
findViewById<RadioGroup>(R.id.output_mode)!!.visibility = View.VISIBLE
- gainmap = bitmap!!.gainmap
- gainmapMetadataEditor.setGainmap(gainmap)
- val map = gainmap!!.gainmapContents
+ val gainmap = bitmap!!.gainmap!!
+ originalGainmap = gainmap
+ gainmapMetadataEditor.setGainmap(Gainmap(gainmap, gainmap.gainmapContents))
+ val map = gainmap.gainmapContents
if (map.config != Bitmap.Config.ALPHA_8) {
gainmapVisualizer = map
} else {
@@ -198,14 +163,12 @@
imageView.setImage(ImageSource.cachedBitmap(when (outputMode) {
R.id.output_hdr -> {
- gainmapMetadataEditor.useOriginalMetadata()
- bitmap!!.gainmap = gainmap
+ bitmap!!.gainmap = originalGainmap
bitmap!!
}
R.id.output_hdr_test -> {
- gainmapMetadataEditor.useEditMetadata()
- bitmap!!.gainmap = gainmap
+ bitmap!!.gainmap = gainmapMetadataEditor.editedGainmap()
bitmap!!
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
index 8a65304..c4bc600 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
@@ -28,23 +28,27 @@
import com.android.test.silkfx.R
data class GainmapMetadata(
- var ratioMin: Float,
- var ratioMax: Float,
- var capacityMin: Float,
- var capacityMax: Float,
- var gamma: Float,
- var offsetSdr: Float,
- var offsetHdr: Float
+ var ratioMin: Float,
+ var ratioMax: Float,
+ var capacityMin: Float,
+ var capacityMax: Float,
+ var gamma: Float,
+ var offsetSdr: Float,
+ var offsetHdr: Float
)
+/**
+ * Note: This can only handle single-channel gainmaps nicely. It will force all 3-channel
+ * metadata to have the same value single value and is not intended to be a robust demonstration
+ * of gainmap metadata editing
+ */
class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
- private var gainmap: Gainmap? = null
- private var showingEdits = false
+ private lateinit var gainmap: Gainmap
private var metadataPopup: PopupWindow? = null
private var originalMetadata: GainmapMetadata = GainmapMetadata(
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f)
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f)
private var currentMetadata: GainmapMetadata = originalMetadata.copy()
private val maxProgress = 100.0f
@@ -61,23 +65,18 @@
private val maxGamma = 3.0f
// Min and max offsets are 0.0 and 1.0 respectively
- fun setGainmap(newGainmap: Gainmap?) {
+ fun setGainmap(newGainmap: Gainmap) {
gainmap = newGainmap
- originalMetadata = GainmapMetadata(gainmap!!.getRatioMin()[0],
- gainmap!!.getRatioMax()[0], gainmap!!.getMinDisplayRatioForHdrTransition(),
- gainmap!!.getDisplayRatioForFullHdr(), gainmap!!.getGamma()[0],
- gainmap!!.getEpsilonSdr()[0], gainmap!!.getEpsilonHdr()[0])
+ originalMetadata = GainmapMetadata(gainmap.getRatioMin()[0],
+ gainmap.getRatioMax()[0], gainmap.getMinDisplayRatioForHdrTransition(),
+ gainmap.getDisplayRatioForFullHdr(), gainmap.getGamma()[0],
+ gainmap.getEpsilonSdr()[0], gainmap.getEpsilonHdr()[0])
currentMetadata = originalMetadata.copy()
}
- fun useOriginalMetadata() {
- showingEdits = false
- applyMetadata(originalMetadata)
- }
-
- fun useEditMetadata() {
- showingEdits = true
+ fun editedGainmap(): Gainmap {
applyMetadata(currentMetadata)
+ return gainmap
}
fun closeEditor() {
@@ -93,7 +92,7 @@
val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gainmap_metadata, null)
metadataPopup = PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT)
+ ViewGroup.LayoutParams.WRAP_CONTENT)
metadataPopup!!.showAtLocation(view, Gravity.CENTER, 0, 0)
(view.getParent() as ViewGroup).removeView(view)
@@ -117,7 +116,7 @@
val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
- offsetSdrSeek, offsetHdrSeek).forEach {
+ offsetSdrSeek, offsetHdrSeek).forEach {
it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (!fromUser) return
@@ -149,37 +148,37 @@
val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
gainmapMinSeek.setProgress(
- ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
+ ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
gainmapMaxSeek.setProgress(
- ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
+ ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
capacityMinSeek.setProgress(
- ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
+ ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
capacityMaxSeek.setProgress(
- ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
+ ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
gammaSeek.setProgress(
- ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt())
+ ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt())
// Log base 3 via: log_b(x) = log_y(x) / log_y(b)
offsetSdrSeek.setProgress(
- ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0)
- .toFloat() * maxProgress).toInt())
+ ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0)
+ .toFloat() * maxProgress).toInt())
offsetHdrSeek.setProgress(
- ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
- .toFloat() * maxProgress).toInt())
+ ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
+ .toFloat() * maxProgress).toInt())
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
- "%.3f".format(currentMetadata.ratioMin))
+ "%.3f".format(currentMetadata.ratioMin))
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
- "%.3f".format(currentMetadata.ratioMax))
+ "%.3f".format(currentMetadata.ratioMax))
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
- "%.3f".format(currentMetadata.capacityMin))
+ "%.3f".format(currentMetadata.capacityMin))
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
- "%.3f".format(currentMetadata.capacityMax))
+ "%.3f".format(currentMetadata.capacityMax))
parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
- "%.3f".format(currentMetadata.gamma))
+ "%.3f".format(currentMetadata.gamma))
parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
- "%.5f".format(currentMetadata.offsetSdr))
+ "%.5f".format(currentMetadata.offsetSdr))
parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
- "%.5f".format(currentMetadata.offsetHdr))
+ "%.5f".format(currentMetadata.offsetHdr))
}
private fun resetGainmapMetadata() {
@@ -189,69 +188,59 @@
}
private fun applyMetadata(newMetadata: GainmapMetadata) {
- gainmap!!.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin)
- gainmap!!.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax)
- gainmap!!.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin)
- gainmap!!.setDisplayRatioForFullHdr(newMetadata.capacityMax)
- gainmap!!.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma)
- gainmap!!.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr)
- gainmap!!.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr)
+ gainmap.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin)
+ gainmap.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax)
+ gainmap.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin)
+ gainmap.setDisplayRatioForFullHdr(newMetadata.capacityMax)
+ gainmap.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma)
+ gainmap.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr)
+ gainmap.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr)
renderView.invalidate()
}
private fun updateGainmapMin(normalized: Float) {
val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.ratioMin = newValue
- if (showingEdits) {
- gainmap!!.setRatioMin(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setRatioMin(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateGainmapMax(normalized: Float) {
val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.ratioMax = newValue
- if (showingEdits) {
- gainmap!!.setRatioMax(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setRatioMax(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateCapacityMin(normalized: Float) {
val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.capacityMin = newValue
- if (showingEdits) {
- gainmap!!.setMinDisplayRatioForHdrTransition(newValue)
- renderView.invalidate()
- }
+ gainmap.setMinDisplayRatioForHdrTransition(newValue)
+ renderView.invalidate()
}
private fun updateCapacityMax(normalized: Float) {
val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.capacityMax = newValue
- if (showingEdits) {
- gainmap!!.setDisplayRatioForFullHdr(newValue)
- renderView.invalidate()
- }
+ gainmap.setDisplayRatioForFullHdr(newValue)
+ renderView.invalidate()
}
private fun updateGamma(normalized: Float) {
val newValue = minGamma + normalized * (maxGamma - minGamma)
parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.gamma = newValue
- if (showingEdits) {
- gainmap!!.setGamma(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setGamma(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateOffsetSdr(normalized: Float) {
@@ -260,12 +249,10 @@
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
- "%.5f".format(newValue))
+ "%.5f".format(newValue))
currentMetadata.offsetSdr = newValue
- if (showingEdits) {
- gainmap!!.setEpsilonSdr(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setEpsilonSdr(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateOffsetHdr(normalized: Float) {
@@ -274,11 +261,9 @@
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
- "%.5f".format(newValue))
+ "%.5f".format(newValue))
currentMetadata.offsetHdr = newValue
- if (showingEdits) {
- gainmap!!.setEpsilonHdr(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setEpsilonHdr(newValue, newValue, newValue)
+ renderView.invalidate()
}
}