Merge "Enabled multibiometric lockout for aidl hals"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 5195f28..61d7470 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -31,6 +31,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.expresslog.Counter;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -88,6 +89,8 @@
// will never be unsatisfied (our time base can not go backwards).
final long nowElapsedMillis = sElapsedRealtimeClock.millis();
if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) {
+ // We're intentionally excluding jobs whose deadlines have passed
+ // (mostly like deadlines of 0) when the job was scheduled.
return;
} else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job,
nowElapsedMillis)) {
@@ -158,6 +161,8 @@
// Scheduler.
mStateChangedListener.onRunJobNow(job);
}
+ Counter.logIncrement(
+ "job_scheduler.value_job_scheduler_job_deadline_expired_counter");
} else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
// This job's deadline is earlier than the current set alarm. Update the alarm.
setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(),
@@ -228,6 +233,8 @@
// Scheduler.
mStateChangedListener.onRunJobNow(job);
}
+ Counter.logIncrement(
+ "job_scheduler.value_job_scheduler_job_deadline_expired_counter");
it.remove();
} else { // Sorted by expiry time, so take the next one and stop.
if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 6e0e26f..c824106 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -53403,7 +53403,7 @@
method @NonNull public android.view.inputmethod.CursorAnchorInfo.Builder setTextAppearanceInfo(@Nullable android.view.inputmethod.TextAppearanceInfo);
}
- public final class DeleteGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class DeleteGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.RectF getDeletionArea();
method public int getGranularity();
@@ -53419,7 +53419,7 @@
method @NonNull public android.view.inputmethod.DeleteGesture.Builder setGranularity(int);
}
- public final class DeleteRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class DeleteRangeGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.RectF getDeletionEndArea();
method @NonNull public android.graphics.RectF getDeletionStartArea();
@@ -53461,11 +53461,13 @@
method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextBeforeCursor(@IntRange(from=0) int, int);
method public int getInitialToolType();
+ method @NonNull public java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>> getSupportedHandwritingGesturePreviews();
method @NonNull public java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>> getSupportedHandwritingGestures();
method public final void makeCompatible(int);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
method public void setInitialToolType(int);
+ method public void setSupportedHandwritingGesturePreviews(@NonNull java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>>);
method public void setSupportedHandwritingGestures(@NonNull java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>>);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
@@ -53640,6 +53642,7 @@
method public default void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public default boolean performSpellCheck();
+ method public default boolean previewHandwritingGesture(@NonNull android.view.inputmethod.PreviewableHandwritingGesture, @Nullable android.os.CancellationSignal);
method public default boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
@@ -53900,6 +53903,9 @@
method @NonNull public android.view.inputmethod.JoinOrSplitGesture.Builder setJoinOrSplitPoint(@NonNull android.graphics.PointF);
}
+ public abstract class PreviewableHandwritingGesture extends android.view.inputmethod.HandwritingGesture {
+ }
+
public final class RemoveSpaceGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.PointF getEndPoint();
@@ -53915,7 +53921,7 @@
method @NonNull public android.view.inputmethod.RemoveSpaceGesture.Builder setPoints(@NonNull android.graphics.PointF, @NonNull android.graphics.PointF);
}
- public final class SelectGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class SelectGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method public int getGranularity();
method @NonNull public android.graphics.RectF getSelectionArea();
@@ -53931,7 +53937,7 @@
method @NonNull public android.view.inputmethod.SelectGesture.Builder setSelectionArea(@NonNull android.graphics.RectF);
}
- public final class SelectRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class SelectRangeGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method public int getGranularity();
method @NonNull public android.graphics.RectF getSelectionEndArea();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 70b89b8..f0cf471 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3617,6 +3617,7 @@
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field @Deprecated public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
field @Nullable public final String backgroundPermission;
+ field @NonNull public java.util.Set<java.lang.String> knownCerts;
field @StringRes public int requestRes;
}
@@ -5555,6 +5556,28 @@
method @NonNull public android.location.CorrelationVector.Builder setSamplingWidthMeters(@FloatRange(from=0.0f, fromInclusive=false) double);
}
+ public final class Country implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getCountryIso();
+ method public int getSource();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int COUNTRY_SOURCE_LOCALE = 3; // 0x3
+ field public static final int COUNTRY_SOURCE_LOCATION = 1; // 0x1
+ field public static final int COUNTRY_SOURCE_NETWORK = 0; // 0x0
+ field public static final int COUNTRY_SOURCE_SIM = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.Country> CREATOR;
+ }
+
+ public class CountryDetector {
+ method public void addCountryListener(@NonNull android.location.CountryListener, @Nullable android.os.Looper);
+ method @Nullable public android.location.Country detectCountry();
+ method public void removeCountryListener(@NonNull android.location.CountryListener);
+ }
+
+ public interface CountryListener {
+ method public void onCountryDetected(@NonNull android.location.Country);
+ }
+
public final class GnssCapabilities implements android.os.Parcelable {
method @Deprecated public boolean hasMeasurementCorrectionsReflectingPane();
method @Deprecated public boolean hasNavMessages();
@@ -13125,6 +13148,7 @@
field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
+ field public static final int PRECISE_CALL_STATE_INCOMING_SETUP = 9; // 0x9
field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
}
@@ -15380,6 +15404,16 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
}
+ public final class SrvccCall implements android.os.Parcelable {
+ ctor public SrvccCall(@NonNull String, int, @NonNull android.telephony.ims.ImsCallProfile);
+ method public int describeContents();
+ method @NonNull public String getCallId();
+ method @NonNull public android.telephony.ims.ImsCallProfile getImsCallProfile();
+ method public int getPreciseCallState();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SrvccCall> CREATOR;
+ }
+
}
package android.telephony.ims.feature {
@@ -15440,6 +15474,10 @@
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
method public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
method public final void notifyRejectedCall(@NonNull android.telephony.ims.ImsCallProfile, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void notifySrvccCanceled();
+ method public void notifySrvccCompleted();
+ method public void notifySrvccFailed();
+ method public void notifySrvccStarted(@NonNull java.util.function.Consumer<java.util.List<android.telephony.ims.SrvccCall>>);
method public final void notifyVoiceMessageCountUpdate(int);
method public void onFeatureReady();
method public void onFeatureRemoved();
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 025e862..47588d9 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -3,6 +3,10 @@
Method should return Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]`
+ExecutorRegistration: android.location.CountryDetector#addCountryListener(android.location.CountryListener, android.os.Looper):
+ Registration methods should have overload that accepts delivery Executor: `addCountryListener`
+
+
GenericException: android.app.prediction.AppPredictor#finalize():
Methods must not throw generic exceptions (`java.lang.Throwable`)
GenericException: android.hardware.location.ContextHubClient#finalize():
@@ -127,6 +131,8 @@
SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.content.pm.ResolveInfo#dump(android.util.Printer, String):
SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ResolveInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.CountryDetector#addCountryListener(android.location.CountryListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.location.CountryDetector.addCountryListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.location.Location#dump(android.util.Printer, String):
SAM-compatible parameters (such as parameter 1, "pw", in android.location.Location.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e9f9136..f076395 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1252,6 +1252,7 @@
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
+ field public static final int SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY = 3; // 0x3
field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
}
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 3f1844e..96d874e 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -157,13 +157,18 @@
}
/**
- * Sets the source bounds hint. These bounds are only used when an activity first enters
- * picture-in-picture, and describe the bounds in window coordinates of activity entering
- * picture-in-picture that will be visible following the transition. For the best effect,
- * these bounds should also match the aspect ratio in the arguments.
+ * Sets the window-coordinate bounds of an activity transitioning to picture-in-picture.
+ * The bounds is the area of an activity that will be visible in the transition to
+ * picture-in-picture mode. For the best effect, these bounds should also match the
+ * aspect ratio in the arguments.
+ *
+ * In Android 12+ these bounds are also reused to improve the exit transition from
+ * picture-in-picture mode. See
+ * <a href="{@docRoot}develop/ui/views/picture-in-picture#smoother-exit">Support
+ * smoother animations when exiting out of PiP mode</a> for more details.
*
* @param launchBounds window-coordinate bounds indicating the area of the activity that
- * will still be visible following the transition into picture-in-picture (eg. the video
+ * will still be visible following the transition into picture-in-picture (e.g. the video
* view bounds in a video player)
*
* @return this builder instance.
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index bb88486..7c22c088 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -33,6 +34,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
import java.util.Set;
/**
@@ -486,7 +488,10 @@
*
* @hide
*/
- public @Nullable Set<String> knownCerts;
+ // Already being used as mutable and most other fields in this class are also mutable.
+ @SuppressLint("MutableBareField")
+ @SystemApi
+ public @NonNull Set<String> knownCerts = Collections.emptySet();
/** @hide */
public static int fixProtectionLevel(int level) {
@@ -620,6 +625,8 @@
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
+ // Note that knownCerts wasn't properly copied before Android U.
+ knownCerts = orig.knownCerts;
}
/**
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8311190..9b07d3a 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -439,12 +439,13 @@
SWITCHING_TYPE_NONE,
SWITCHING_TYPE_WITHIN_GROUPS,
SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
+ SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SwitchingType {}
/**
- * No mode switching will happen.
+ * No display mode switching will happen.
* @hide
*/
@TestApi
@@ -467,6 +468,13 @@
public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
/**
+ * Allow render frame rate switches, but not physical modes.
+ * @hide
+ */
+ @TestApi
+ public static final int SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY = 3;
+
+ /**
* @hide
*/
@LongDef(flag = true, prefix = {"EVENT_FLAG_"}, value = {
@@ -1308,6 +1316,7 @@
switch (switchingType) {
case SWITCHING_TYPE_NONE:
return MATCH_CONTENT_FRAMERATE_NEVER;
+ case SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY:
case SWITCHING_TYPE_WITHIN_GROUPS:
return MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY;
case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index 95aecfe..c6a3725 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.view.KeyEvent;
@@ -697,6 +698,27 @@
}
/**
+ * Invokes one of {@link IRemoteInputConnection#previewHandwritingGesture(
+ * InputConnectionCommandHeader, ParcelableHandwritingGesture, CancellationSignal)}
+ */
+ @AnyThread
+ public boolean previewHandwritingGesture(
+ @NonNull ParcelableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return false; // cancelled.
+ }
+
+ // TODO(b/254727073): Implement CancellationSignal
+ try {
+ mConnection.previewHandwritingGesture(createHeader(), gesture, null);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes {@link IRemoteInputConnection#requestCursorUpdates(InputConnectionCommandHeader, int,
* int, AndroidFuture)}.
*
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 976e71f..f93f9ab 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
@@ -34,6 +35,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.ParcelableHandwritingGesture;
+import android.view.inputmethod.PreviewableHandwritingGesture;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
import android.view.inputmethod.TextBoundsInfoResult;
@@ -427,6 +429,14 @@
}
@AnyThread
+ public boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return mInvoker.previewHandwritingGesture(ParcelableHandwritingGesture.of(gesture),
+ cancellationSignal);
+ }
+
+ @AnyThread
public boolean requestCursorUpdates(int cursorUpdateMode) {
if (mCancellationGroup.isCanceled()) {
return false;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 3c4abab..8e55692 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -911,11 +911,15 @@
final String transactionName = getTransactionName(transactionCode);
final StringBuffer buf = new StringBuffer();
+ // Keep trace name consistent with cpp trace name in:
+ // system/tools/aidl/generate_cpp.cpp
+ buf.append("AIDL::java::");
if (transactionName != null) {
- buf.append(mSimpleDescriptor).append(":").append(transactionName);
+ buf.append(mSimpleDescriptor).append("::").append(transactionName);
} else {
- buf.append(mSimpleDescriptor).append("#").append(transactionCode);
+ buf.append(mSimpleDescriptor).append("::#").append(transactionCode);
}
+ buf.append("::server");
transactionTraceName = buf.toString();
mTransactionTraceNames.setRelease(index, transactionTraceName);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index bf40bdc..b3e8fb6 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1757,7 +1757,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that will be in this event.
@@ -1771,7 +1771,7 @@
* @param buttonState The state of buttons that are pressed.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1807,7 +1807,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that will be in this event.
@@ -1821,7 +1821,7 @@
* @param buttonState The state of buttons that are pressed.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1847,7 +1847,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that will be in this event.
@@ -1861,7 +1861,7 @@
* @param buttonState The state of buttons that are pressed.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1885,7 +1885,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that will be in this event.
@@ -1897,7 +1897,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1932,7 +1932,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param x The X coordinate of this event.
@@ -1949,7 +1949,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -1969,7 +1969,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param x The X coordinate of this event.
@@ -1986,7 +1986,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param source The source of this event.
@@ -2028,7 +2028,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param pointerCount The number of pointers that are active in this event.
@@ -2046,7 +2046,7 @@
* the event was generated.
* @param xPrecision The precision of the X coordinate being reported.
* @param yPrecision The precision of the Y coordinate being reported.
- * @param deviceId The id for the device that this event came from. An id of
+ * @param deviceId The ID for the device that this event came from. An ID of
* zero indicates that the event didn't come from a physical device; other
* numbers are arbitrary and you shouldn't depend on the values.
* @param edgeFlags A bitfield indicating which edges, if any, were touched by this
@@ -2070,7 +2070,7 @@
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
- * @param eventTime The the time (in ms) when this specific event was generated. This
+ * @param eventTime The time (in ms) when this specific event was generated. This
* must be obtained from {@link SystemClock#uptimeMillis()}.
* @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
* @param x The X coordinate of this event.
diff --git a/core/java/android/view/inputmethod/DeleteGesture.java b/core/java/android/view/inputmethod/DeleteGesture.java
index c88158f..b843594 100644
--- a/core/java/android/view/inputmethod/DeleteGesture.java
+++ b/core/java/android/view/inputmethod/DeleteGesture.java
@@ -33,7 +33,7 @@
* <p>Note: This deletes all text <em>within</em> the given area. To delete a range <em>between</em>
* two areas, use {@link DeleteRangeGesture}.</p>
*/
-public final class DeleteGesture extends HandwritingGesture implements Parcelable {
+public final class DeleteGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mArea;
diff --git a/core/java/android/view/inputmethod/DeleteRangeGesture.java b/core/java/android/view/inputmethod/DeleteRangeGesture.java
index 53b4209..0bce15e 100644
--- a/core/java/android/view/inputmethod/DeleteRangeGesture.java
+++ b/core/java/android/view/inputmethod/DeleteRangeGesture.java
@@ -34,7 +34,7 @@
* <p>Note: this deletes text within a range <em>between</em> two given areas. To delete all text
* <em>within</em> a single area, use {@link DeleteGesture}.</p>
*/
-public final class DeleteRangeGesture extends HandwritingGesture implements Parcelable {
+public final class DeleteRangeGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mStartArea;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index febdac2..9ebaa67 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -55,8 +55,10 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* An EditorInfo describes several attributes of a text editing object
@@ -534,6 +536,8 @@
private @HandwritingGesture.GestureTypeFlags int mSupportedHandwritingGestureTypes;
+ private @HandwritingGesture.GestureTypeFlags int mSupportedHandwritingGesturePreviewTypes;
+
/**
* Set the Handwriting gestures supported by the current {@code Editor}.
* For an editor that supports Stylus Handwriting
@@ -541,8 +545,11 @@
* supported gestures.
* <p> If editor doesn't support one of the declared types, IME will not send those Gestures
* to the editor. Instead they will fallback to using normal text input. </p>
+ * <p>Note: A supported gesture may not have preview supported
+ * {@link #getSupportedHandwritingGesturePreviews()}.</p>
* @param gestures List of supported gesture classes including any of {@link SelectGesture},
* {@link InsertGesture}, {@link DeleteGesture}.
+ * @see #setSupportedHandwritingGesturePreviews(Set)
*/
public void setSupportedHandwritingGestures(
@NonNull List<Class<? extends HandwritingGesture>> gestures) {
@@ -584,6 +591,7 @@
* {@link InputMethodManager#startStylusHandwriting}, it also declares supported gestures.
* @return List of supported gesture classes including any of {@link SelectGesture},
* {@link InsertGesture}, {@link DeleteGesture}.
+ * @see #getSupportedHandwritingGesturePreviews()
*/
@NonNull
public List<Class<? extends HandwritingGesture>> getSupportedHandwritingGestures() {
@@ -623,6 +631,86 @@
}
/**
+ * Set the Handwriting gesture previews supported by the current {@code Editor}.
+ * For an editor that supports Stylus Handwriting
+ * {@link InputMethodManager#startStylusHandwriting}, it is also recommended that it declares
+ * supported gesture previews.
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * <p> If editor doesn't support one of the declared types, gesture preview will be ignored.</p>
+ * @param gestures Set of supported gesture classes. One of {@link SelectGesture},
+ * {@link SelectRangeGesture}, {@link DeleteGesture}, {@link DeleteRangeGesture}.
+ * @see #setSupportedHandwritingGestures(List)
+ */
+ public void setSupportedHandwritingGesturePreviews(
+ @NonNull Set<Class<? extends PreviewableHandwritingGesture>> gestures) {
+ Objects.requireNonNull(gestures);
+ if (gestures.isEmpty()) {
+ mSupportedHandwritingGesturePreviewTypes = 0;
+ return;
+ }
+
+ int supportedTypes = 0;
+ for (Class<? extends PreviewableHandwritingGesture> gesture : gestures) {
+ Objects.requireNonNull(gesture);
+ if (gesture.equals(SelectGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT;
+ } else if (gesture.equals(SelectRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT_RANGE;
+ } else if (gesture.equals(DeleteGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE;
+ } else if (gesture.equals(DeleteRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE_RANGE;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported gesture type for preview: " + gesture);
+ }
+ }
+
+ mSupportedHandwritingGesturePreviewTypes = supportedTypes;
+ }
+
+ /**
+ * Returns the combination of Stylus handwriting gesture preview types
+ * supported by the current {@code Editor}.
+ * For an editor that supports Stylus Handwriting.
+ * {@link InputMethodManager#startStylusHandwriting}, it also declares supported gesture
+ * previews.
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * @return Set of supported gesture preview classes. One of {@link SelectGesture},
+ * {@link SelectRangeGesture}, {@link DeleteGesture}, {@link DeleteRangeGesture}.
+ * @see #getSupportedHandwritingGestures()
+ */
+ @NonNull
+ public Set<Class<? extends PreviewableHandwritingGesture>>
+ getSupportedHandwritingGesturePreviews() {
+ Set<Class<? extends PreviewableHandwritingGesture>> set = new HashSet<>();
+ if (mSupportedHandwritingGesturePreviewTypes == 0) {
+ return set;
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes & HandwritingGesture.GESTURE_TYPE_SELECT)
+ == HandwritingGesture.GESTURE_TYPE_SELECT) {
+ set.add(SelectGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes
+ & HandwritingGesture.GESTURE_TYPE_SELECT_RANGE)
+ == HandwritingGesture.GESTURE_TYPE_SELECT_RANGE) {
+ set.add(SelectRangeGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes & HandwritingGesture.GESTURE_TYPE_DELETE)
+ == HandwritingGesture.GESTURE_TYPE_DELETE) {
+ set.add(DeleteGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes
+ & HandwritingGesture.GESTURE_TYPE_DELETE_RANGE)
+ == HandwritingGesture.GESTURE_TYPE_DELETE_RANGE) {
+ set.add(DeleteRangeGesture.class);
+ }
+ return set;
+ }
+
+ /**
* If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
* matter what user ID the calling process has.
*
@@ -1114,6 +1202,9 @@
pw.println(prefix + "supportedHandwritingGestureTypes="
+ InputMethodDebug.handwritingGestureTypeFlagsToString(
mSupportedHandwritingGestureTypes));
+ pw.println(prefix + "supportedHandwritingGesturePreviewTypes="
+ + InputMethodDebug.handwritingGestureTypeFlagsToString(
+ mSupportedHandwritingGesturePreviewTypes));
pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
if (targetInputMethodUser != null) {
pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier());
@@ -1149,6 +1240,8 @@
newEditorInfo.contentMimeTypes = ArrayUtils.cloneOrNull(contentMimeTypes);
newEditorInfo.targetInputMethodUser = targetInputMethodUser;
newEditorInfo.mSupportedHandwritingGestureTypes = mSupportedHandwritingGestureTypes;
+ newEditorInfo.mSupportedHandwritingGesturePreviewTypes =
+ mSupportedHandwritingGesturePreviewTypes;
return newEditorInfo;
}
@@ -1177,6 +1270,7 @@
dest.writeString(fieldName);
dest.writeBundle(extras);
dest.writeInt(mSupportedHandwritingGestureTypes);
+ dest.writeInt(mSupportedHandwritingGesturePreviewTypes);
dest.writeBoolean(mInitialSurroundingText != null);
if (mInitialSurroundingText != null) {
mInitialSurroundingText.writeToParcel(dest, flags);
@@ -1215,6 +1309,7 @@
res.fieldName = source.readString();
res.extras = source.readBundle();
res.mSupportedHandwritingGestureTypes = source.readInt();
+ res.mSupportedHandwritingGesturePreviewTypes = source.readInt();
boolean hasInitialSurroundingText = source.readBoolean();
if (hasInitialSurroundingText) {
res.mInitialSurroundingText =
@@ -1258,6 +1353,8 @@
&& initialCapsMode == that.initialCapsMode
&& fieldId == that.fieldId
&& mSupportedHandwritingGestureTypes == that.mSupportedHandwritingGestureTypes
+ && mSupportedHandwritingGesturePreviewTypes
+ == that.mSupportedHandwritingGesturePreviewTypes
&& Objects.equals(autofillId, that.autofillId)
&& Objects.equals(privateImeOptions, that.privateImeOptions)
&& Objects.equals(packageName, that.packageName)
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 2099a31..c94a372 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -26,6 +26,7 @@
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.text.TextUtils;
import android.view.KeyCharacterMap;
@@ -1031,6 +1032,8 @@
/**
* Perform a handwriting gesture on text.
*
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
* @param gesture the gesture to perform
* @param executor The executor to run the callback on.
* @param consumer if the caller passes a non-null consumer, the editor must invoke this
@@ -1041,6 +1044,7 @@
* completed. Will be invoked on the given {@link Executor}.
* Default implementation provides a callback to {@link IntConsumer} with
* {@link #HANDWRITING_GESTURE_RESULT_UNSUPPORTED}.
+ * @see #previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal)
*/
default void performHandwritingGesture(
@NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
@@ -1051,6 +1055,31 @@
}
/**
+ * Preview a handwriting gesture on text.
+ * Provides a real-time preview for a gesture to user for an ongoing gesture. e.g. as user
+ * begins to draw a circle around text, resulting selection {@link SelectGesture} is previewed
+ * while stylus is moving over applicable text.
+ *
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} might not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * @param gesture the gesture to preview. Preview support for a gesture (regardless of whether
+ * implemented by editor) can be determined if gesture subclasses
+ * {@link PreviewableHandwritingGesture}. Supported previewable gestures include
+ * {@link SelectGesture}, {@link SelectRangeGesture}, {@link DeleteGesture} and
+ * {@link DeleteRangeGesture}.
+ * @param cancellationSignal signal to cancel an ongoing preview.
+ * @return true on successfully sending command to Editor, false if not implemented by editor or
+ * the input connection is no longer valid or preview was cancelled with
+ * {@link CancellationSignal}.
+ * @see #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)
+ */
+ default boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return false;
+ }
+
+ /**
* The editor is requested to call
* {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
* once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 7af96b6..4befd6f 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.view.KeyEvent;
@@ -340,6 +341,17 @@
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return mTarget.previewHandwritingGesture(gesture, cancellationSignal);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return mTarget.requestCursorUpdates(cursorUpdateMode);
}
diff --git a/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java b/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java
new file mode 100644
index 0000000..7683ece
--- /dev/null
+++ b/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.os.CancellationSignal;
+
+import java.util.Set;
+
+/**
+ * A {@link HandwritingGesture} that can be
+ * {@link InputConnection#previewHandwritingGesture(
+ * PreviewableHandwritingGesture, CancellationSignal) previewed}.
+ *
+ * Note: An editor might only implement a subset of gesture previews and declares the supported
+ * ones via {@link EditorInfo#getSupportedHandwritingGesturePreviews}.
+ *
+ * @see EditorInfo#setSupportedHandwritingGesturePreviews(Set)
+ * @see EditorInfo#getSupportedHandwritingGesturePreviews()
+ */
+public abstract class PreviewableHandwritingGesture extends HandwritingGesture {
+ PreviewableHandwritingGesture() {
+ // intentionally empty.
+ }
+}
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index c01c310..7525d72 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -30,7 +30,9 @@
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.Trace;
@@ -1017,6 +1019,34 @@
@Dispatching(cancellable = true)
@Override
+ public void previewHandwritingGesture(
+ InputConnectionCommandHeader header, ParcelableHandwritingGesture gestureContainer,
+ ICancellationSignal transport) {
+
+ // TODO(b/254727073): Implement CancellationSignal receiver
+ final CancellationSignal cancellationSignal = CancellationSignal.fromTransport(transport);
+ // Previews always use PreviewableHandwritingGesture but if incorrectly wrong class is
+ // passed, ClassCastException will be sent back to caller.
+ final PreviewableHandwritingGesture gesture =
+ (PreviewableHandwritingGesture) gestureContainer.get();
+
+ dispatchWithTracing("previewHandwritingGesture", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()
+ || (cancellationSignal != null && cancellationSignal.isCanceled())) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "previewHandwritingGesture on inactive InputConnection");
+ return; // cancelled
+ }
+
+ ic.previewHandwritingGesture(gesture, cancellationSignal);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
int imeDisplayId, AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
diff --git a/core/java/android/view/inputmethod/SelectGesture.java b/core/java/android/view/inputmethod/SelectGesture.java
index 6dc4ed2..ba600df 100644
--- a/core/java/android/view/inputmethod/SelectGesture.java
+++ b/core/java/android/view/inputmethod/SelectGesture.java
@@ -33,7 +33,7 @@
* <p>Note: This selects all text <em>within</em> the given area. To select a range <em>between</em>
* two areas, use {@link SelectRangeGesture}.</p>
*/
-public final class SelectGesture extends HandwritingGesture implements Parcelable {
+public final class SelectGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mArea;
@@ -72,7 +72,6 @@
return mArea;
}
-
/**
* Builder for {@link SelectGesture}. This class is not designed to be thread-safe.
*/
diff --git a/core/java/android/view/inputmethod/SelectRangeGesture.java b/core/java/android/view/inputmethod/SelectRangeGesture.java
index 7cb6002..c31bc27 100644
--- a/core/java/android/view/inputmethod/SelectRangeGesture.java
+++ b/core/java/android/view/inputmethod/SelectRangeGesture.java
@@ -34,7 +34,7 @@
* <p>Note: this selects text within a range <em>between</em> two given areas. To select all text
* <em>within</em> a single area, use {@link SelectGesture}</p>
*/
-public final class SelectRangeGesture extends HandwritingGesture implements Parcelable {
+public final class SelectRangeGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mStartArea;
@@ -87,7 +87,6 @@
return mEndArea;
}
-
/**
* Builder for {@link SelectRangeGesture}. This class is not designed to be thread-safe.
*/
diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
index 81e060d..65016c2 100644
--- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
+++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
@@ -18,6 +18,7 @@
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.ICancellationSignal;
import android.os.ResultReceiver;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
@@ -92,6 +93,9 @@
void performHandwritingGesture(in InputConnectionCommandHeader header,
in ParcelableHandwritingGesture gesture, in ResultReceiver resultReceiver);
+ void previewHandwritingGesture(in InputConnectionCommandHeader header,
+ in ParcelableHandwritingGesture gesture, in ICancellationSignal transport);
+
void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8fd0401..eaec58b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1202,66 +1202,40 @@
return object;
}
-struct RefreshRateRange {
- const float min;
- const float max;
-
- RefreshRateRange(float min, float max) : min(min), max(max) {}
-
- RefreshRateRange(JNIEnv* env, jobject obj)
- : min(env->GetFloatField(obj, gRefreshRateRangeClassInfo.min)),
- max(env->GetFloatField(obj, gRefreshRateRangeClassInfo.max)) {}
-
- jobject toJava(JNIEnv* env) const {
- return env->NewObject(gRefreshRateRangeClassInfo.clazz, gRefreshRateRangeClassInfo.ctor,
- min, max);
- }
-};
-
-struct RefreshRateRanges {
- const RefreshRateRange physical;
- const RefreshRateRange render;
-
- RefreshRateRanges(RefreshRateRange physical, RefreshRateRange render)
- : physical(physical), render(render) {}
-
- RefreshRateRanges(JNIEnv* env, jobject obj)
- : physical(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.physical)),
- render(env, env->GetObjectField(obj, gRefreshRateRangesClassInfo.render)) {}
-
- jobject toJava(JNIEnv* env) const {
- return env->NewObject(gRefreshRateRangesClassInfo.clazz, gRefreshRateRangesClassInfo.ctor,
- physical.toJava(env), render.toJava(env));
- }
-};
-
static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
jobject DesiredDisplayModeSpecs) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return JNI_FALSE;
- ui::DisplayModeId defaultMode = env->GetIntField(DesiredDisplayModeSpecs,
- gDesiredDisplayModeSpecsClassInfo.defaultMode);
- jboolean allowGroupSwitching =
+ const auto makeRanges = [env](jobject obj) {
+ const auto makeRange = [env](jobject obj) {
+ gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange range;
+ range.min = env->GetFloatField(obj, gRefreshRateRangeClassInfo.min);
+ range.max = env->GetFloatField(obj, gRefreshRateRangeClassInfo.max);
+ return range;
+ };
+
+ gui::DisplayModeSpecs::RefreshRateRanges ranges;
+ ranges.physical = makeRange(env->GetObjectField(obj, gRefreshRateRangesClassInfo.physical));
+ ranges.render = makeRange(env->GetObjectField(obj, gRefreshRateRangesClassInfo.render));
+ return ranges;
+ };
+
+ gui::DisplayModeSpecs specs;
+ specs.defaultMode = env->GetIntField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.defaultMode);
+ specs.allowGroupSwitching =
env->GetBooleanField(DesiredDisplayModeSpecs,
gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
- const jobject primaryRangesObject =
- env->GetObjectField(DesiredDisplayModeSpecs,
- gDesiredDisplayModeSpecsClassInfo.primaryRanges);
- const jobject appRequestRangesObject =
- env->GetObjectField(DesiredDisplayModeSpecs,
- gDesiredDisplayModeSpecsClassInfo.appRequestRanges);
- const RefreshRateRanges primaryRanges(env, primaryRangesObject);
- const RefreshRateRanges appRequestRanges(env, appRequestRangesObject);
+ specs.primaryRanges =
+ makeRanges(env->GetObjectField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.primaryRanges));
+ specs.appRequestRanges =
+ makeRanges(env->GetObjectField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.appRequestRanges));
- size_t result =
- SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
- allowGroupSwitching,
- primaryRanges.physical.min,
- primaryRanges.physical.max,
- appRequestRanges.physical.min,
- appRequestRanges.physical.max);
+ size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, specs);
return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
@@ -1269,33 +1243,26 @@
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return nullptr;
- ui::DisplayModeId defaultMode;
- bool allowGroupSwitching;
- float primaryPhysicalRefreshRateMin;
- float primaryPhysicalRefreshRateMax;
- float appRequestPhysicalRefreshRateMin;
- float appRequestPhysicalRefreshRateMax;
- if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching,
- &primaryPhysicalRefreshRateMin,
- &primaryPhysicalRefreshRateMax,
- &appRequestPhysicalRefreshRateMin,
- &appRequestPhysicalRefreshRateMax) !=
- NO_ERROR) {
+ const auto rangesToJava = [env](const gui::DisplayModeSpecs::RefreshRateRanges& ranges) {
+ const auto rangeToJava =
+ [env](const gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange& range) {
+ return env->NewObject(gRefreshRateRangeClassInfo.clazz,
+ gRefreshRateRangeClassInfo.ctor, range.min, range.max);
+ };
+
+ return env->NewObject(gRefreshRateRangesClassInfo.clazz, gRefreshRateRangesClassInfo.ctor,
+ rangeToJava(ranges.physical), rangeToJava(ranges.render));
+ };
+
+ gui::DisplayModeSpecs specs;
+ if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &specs) != NO_ERROR) {
return nullptr;
}
- const RefreshRateRange primaryPhysicalRange(primaryPhysicalRefreshRateMin,
- primaryPhysicalRefreshRateMax);
- const RefreshRateRange appRequestPhysicalRange(appRequestPhysicalRefreshRateMin,
- appRequestPhysicalRefreshRateMax);
-
- // TODO(b/241460058): populate the render ranges
- const RefreshRateRanges primaryRanges(primaryPhysicalRange, primaryPhysicalRange);
- const RefreshRateRanges appRequestRanges(appRequestPhysicalRange, appRequestPhysicalRange);
-
return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz,
- gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching,
- primaryRanges.toJava(env), appRequestRanges.toJava(env));
+ gDesiredDisplayModeSpecsClassInfo.ctor, specs.defaultMode,
+ specs.allowGroupSwitching, rangesToJava(specs.primaryRanges),
+ rangesToJava(specs.appRequestRanges));
}
static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) {
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index dff7751..9e735d0 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -166,12 +166,12 @@
</string-array>
<array name="sim_colors">
- <item>@color/Teal_700</item>
- <item>@color/Blue_700</item>
- <item>@color/Indigo_700</item>
- <item>@color/Purple_700</item>
- <item>@color/Pink_700</item>
- <item>@color/Red_700</item>
+ <item>@color/SIM_color_cyan</item>
+ <item>@color/SIM_color_blue</item>
+ <item>@color/SIM_color_green</item>
+ <item>@color/SIM_color_purple</item>
+ <item>@color/SIM_color_pink</item>
+ <item>@color/SIM_color_orange</item>
</array>
<!-- Used in ResolverTargetActionsDialogFragment -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 77d7c43..8c356b4 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -198,18 +198,18 @@
<color name="instant_app_badge">#ff757575</color><!-- Grey -->
<!-- Multi-sim sim colors -->
- <color name="Teal_700">#ff00796b</color>
- <color name="Teal_800">#ff00695c</color>
- <color name="Blue_700">#ff3367d6</color>
- <color name="Blue_800">#ff2a56c6</color>
- <color name="Indigo_700">#ff303f9f</color>
- <color name="Indigo_800">#ff283593</color>
- <color name="Purple_700">#ff7b1fa2</color>
- <color name="Purple_800">#ff6a1b9a</color>
- <color name="Pink_700">#ffc2185b</color>
- <color name="Pink_800">#ffad1457</color>
- <color name="Red_700">#ffc53929</color>
- <color name="Red_800">#ffb93221</color>
+ <color name="SIM_color_cyan">#ff006D74</color><!-- Material Custom Cyan -->
+ <color name="SIM_dark_mode_color_cyan">#ff4DD0E1</color><!-- Material Cyan 300 -->
+ <color name="SIM_color_blue">#ff185ABC</color><!-- Material Blue 800 -->
+ <color name="SIM_dark_mode_color_blue">#ff8AB4F8</color><!-- Material Blue 300 -->
+ <color name="SIM_color_green">#ff137333</color><!-- Material Green 800 -->
+ <color name="SIM_dark_mode_color_green">#ff81C995</color><!-- Material Green 300 -->
+ <color name="SIM_color_purple">#ff7627bb</color><!-- Material Purple 800 -->
+ <color name="SIM_dark_mode_color_purple">#ffC58AF9</color><!-- Material Purple 300 -->
+ <color name="SIM_color_pink">#ffb80672</color><!-- Material Pink 800 -->
+ <color name="SIM_dark_mode_color_pink">#ffff8bcb</color><!-- Material Pink 300 -->
+ <color name="SIM_color_orange">#ff995400</color><!-- Material Custom Orange -->
+ <color name="SIM_dark_mode_color_orange">#fffcad70</color><!-- Material Orange 300 -->
<color name="resize_shadow_start_color">#2a000000</color>
<color name="resize_shadow_end_color">#00000000</color>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java
new file mode 100644
index 0000000..2fa3f876
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio.tests.unittests;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+
+import android.graphics.Bitmap;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public final class DefaultRadioTunerTest {
+
+ private static final RadioTuner DEFAULT_RADIO_TUNER = new RadioTuner() {
+ @Override
+ public void close() {}
+
+ @Override
+ public int setConfiguration(RadioManager.BandConfig config) {
+ return 0;
+ }
+
+ @Override
+ public int getConfiguration(RadioManager.BandConfig[] config) {
+ return 0;
+ }
+
+ @Override
+ public int setMute(boolean mute) {
+ return 0;
+ }
+
+ @Override
+ public boolean getMute() {
+ return false;
+ }
+
+ @Override
+ public int step(int direction, boolean skipSubChannel) {
+ return 0;
+ }
+
+ @Override
+ public int scan(int direction, boolean skipSubChannel) {
+ return 0;
+ }
+
+ @Override
+ public int tune(int channel, int subChannel) {
+ return 0;
+ }
+
+ @Override
+ public void tune(@NonNull ProgramSelector selector) {}
+
+ @Override
+ public int cancel() {
+ return 0;
+ }
+
+ @Override
+ public void cancelAnnouncement() {}
+
+ @Override
+ public int getProgramInformation(RadioManager.ProgramInfo[] info) {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public Bitmap getMetadataImage(int id) {
+ return null;
+ }
+
+ @Override
+ public boolean startBackgroundScan() {
+ return false;
+ }
+
+ @NonNull
+ @Override
+ public List<RadioManager.ProgramInfo> getProgramList(
+ @Nullable Map<String, String> vendorFilter) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public boolean isAnalogForced() {
+ return false;
+ }
+
+ @Override
+ public void setAnalogForced(boolean isForced) {}
+
+ @Override
+ public boolean isAntennaConnected() {
+ return false;
+ }
+
+ @Override
+ public boolean hasControl() {
+ return false;
+ }
+ };
+
+ @Test
+ public void getDynamicProgramList_forRadioTuner_returnsNull() {
+ assertWithMessage("Dynamic program list obtained from default radio tuner")
+ .that(DEFAULT_RADIO_TUNER.getDynamicProgramList(new ProgramList.Filter())).isNull();
+ }
+
+ @Test
+ public void isConfigFlagSupported_forRadioTuner_throwsException() {
+ assertWithMessage("Dynamic program list obtained from default radio tuner")
+ .that(DEFAULT_RADIO_TUNER.isConfigFlagSupported(/* flag= */ 1)).isFalse();
+ }
+
+ @Test
+ public void isConfigFlagSet_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.isConfigFlagSet(/* flag= */ 1);
+ });
+ }
+
+ @Test
+ public void setConfigFlag_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.setConfigFlag(/* flag= */ 1, /* value= */ false);
+ });
+ }
+
+ @Test
+ public void setParameters_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.setParameters(Map.of("testKey", "testValue"));
+ });
+ }
+
+ @Test
+ public void getParameters_forRadioTuner_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ DEFAULT_RADIO_TUNER.getParameters(List.of("testKey"));
+ });
+ }
+}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
index 2711100..2b9de18 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
@@ -360,18 +360,35 @@
}
@Test
- public void registerListCallback_withExecutor() throws Exception {
+ public void onItemChanged_forListCallbackRegisteredWithExecutor_invokesWhenIdAdded()
+ throws Exception {
createRadioTuner();
mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
ProgramList.ListCallback listCallbackMock = mock(ProgramList.ListCallback.class);
-
mProgramList.registerListCallback(mExecutor, listCallbackMock);
+
mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
verify(listCallbackMock, CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
}
@Test
+ public void onItemRemoved_forListCallbackRegisteredWithExecutor_invokesWhenIdRemoved()
+ throws Exception {
+ ProgramList.Chunk purgeChunk = new ProgramList.Chunk(/* purge= */ true,
+ /* complete= */ true, new ArraySet<>(), new ArraySet<>());
+ createRadioTuner();
+ mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
+ ProgramList.ListCallback listCallbackMock = mock(ProgramList.ListCallback.class);
+ mProgramList.registerListCallback(mExecutor, listCallbackMock);
+ mTunerCallback.onProgramListUpdated(FM_ADD_INCOMPLETE_CHUNK);
+
+ mTunerCallback.onProgramListUpdated(purgeChunk);
+
+ verify(listCallbackMock, CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
+ }
+
+ @Test
public void onProgramListUpdated_withMultipleListCallBacks() throws Exception {
int numCallbacks = 3;
createRadioTuner();
diff --git a/core/tests/coretests/src/android/app/activity/LocalReceiver.java b/core/tests/coretests/src/android/app/activity/LocalReceiver.java
index 5ac84f8..7f81339 100644
--- a/core/tests/coretests/src/android/app/activity/LocalReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/LocalReceiver.java
@@ -36,8 +36,7 @@
if (BroadcastTest.BROADCAST_FAIL_REGISTER.equals(intent.getAction())) {
resultString = "Successfully registered, but expected it to fail";
try {
- context.registerReceiver(this, new IntentFilter("foo.bar"),
- Context.RECEIVER_EXPORTED_UNAUDITED);
+ context.registerReceiver(this, new IntentFilter("foo.bar"));
context.unregisterReceiver(this);
} catch (ReceiverCallNotAllowedException e) {
//resultString = "This is the correct behavior but not yet implemented";
diff --git a/core/tests/coretests/src/android/app/activity/ServiceTest.java b/core/tests/coretests/src/android/app/activity/ServiceTest.java
index 3f3d6a3..c89f37d 100644
--- a/core/tests/coretests/src/android/app/activity/ServiceTest.java
+++ b/core/tests/coretests/src/android/app/activity/ServiceTest.java
@@ -172,7 +172,7 @@
pidResult.complete(intent.getIntExtra(EXTRA_PID, NOT_STARTED));
mContext.unregisterReceiver(this);
}
- }, new IntentFilter(ACTION_SERVICE_STARTED), Context.RECEIVER_EXPORTED_UNAUDITED);
+ }, new IntentFilter(ACTION_SERVICE_STARTED));
serviceTrigger.run();
try {
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 599cf97..6e73b9f 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -52,6 +52,8 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Supplemental tests that cannot be covered by CTS (e.g. due to hidden API dependencies).
@@ -500,6 +502,7 @@
+ "prefix: extras=null\n"
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ + "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: contentMimeTypes=null\n");
}
@@ -516,6 +519,8 @@
info.label = "testLabel";
info.setInitialToolType(MotionEvent.TOOL_TYPE_STYLUS);
info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class));
+ info.setSupportedHandwritingGesturePreviews(
+ Stream.of(SelectGesture.class).collect(Collectors.toSet()));
info.packageName = "android.view.inputmethod";
info.autofillId = new AutofillId(123);
info.fieldId = 456;
@@ -538,6 +543,7 @@
+ "prefix2: extras=Bundle[{testKey=testValue}]\n"
+ "prefix2: hintLocales=[en,es,zh]\n"
+ "prefix2: supportedHandwritingGestureTypes=SELECT\n"
+ + "prefix2: supportedHandwritingGesturePreviewTypes=SELECT\n"
+ "prefix2: contentMimeTypes=[image/png]\n"
+ "prefix2: targetInputMethodUserId=10\n");
}
@@ -558,6 +564,7 @@
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ + "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: contentMimeTypes=null\n");
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index dbd918e..6245598 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -30,6 +30,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.util.Collection;
import java.util.Locale;
@@ -914,6 +915,51 @@
}
/**
+ * @hide
+ */
+ public abstract static class EcCurve {
+ private EcCurve() {}
+
+ /**
+ * @hide
+ */
+ public static int toKeymasterCurve(ECParameterSpec spec) {
+ int keySize = spec.getCurve().getField().getFieldSize();
+ switch (keySize) {
+ case 224:
+ return android.hardware.security.keymint.EcCurve.P_224;
+ case 256:
+ return android.hardware.security.keymint.EcCurve.P_256;
+ case 384:
+ return android.hardware.security.keymint.EcCurve.P_384;
+ case 521:
+ return android.hardware.security.keymint.EcCurve.P_521;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static int fromKeymasterCurve(int ecCurve) {
+ switch (ecCurve) {
+ case android.hardware.security.keymint.EcCurve.P_224:
+ return 224;
+ case android.hardware.security.keymint.EcCurve.P_256:
+ case android.hardware.security.keymint.EcCurve.CURVE_25519:
+ return 256;
+ case android.hardware.security.keymint.EcCurve.P_384:
+ return 384;
+ case android.hardware.security.keymint.EcCurve.P_521:
+ return 521;
+ default:
+ return -1;
+ }
+ }
+ }
+
+ /**
* Namespaces provide system developers and vendors with a way to use keystore without
* requiring an applications uid. Namespaces can be configured using SEPolicy.
* See <a href="https://source.android.com/security/keystore#access-control">
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
index 5216a90..ace2053 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java
@@ -203,6 +203,11 @@
for (Authorization a : key.getAuthorizations()) {
if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) {
keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a);
+ break;
+ } else if (a.keyParameter.tag == KeymasterDefs.KM_TAG_EC_CURVE) {
+ keySizeBits = KeyProperties.EcCurve.fromKeymasterCurve(
+ a.keyParameter.value.getEcCurve());
+ break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 94bf122..7a320ba 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -66,6 +66,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -566,6 +567,22 @@
spec.getMaxUsageCount()
));
}
+ if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
+ if (key instanceof ECKey) {
+ ECKey ecKey = (ECKey) key;
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_EC_CURVE,
+ KeyProperties.EcCurve.toKeymasterCurve(ecKey.getParams())
+ ));
+ }
+ }
+ /* TODO: check for Ed25519(EdDSA) or X25519(XDH) key algorithm and
+ * add import args for KM_TAG_EC_CURVE as EcCurve.CURVE_25519.
+ * Currently conscrypt does not support EdDSA key import and XDH keys are not an
+ * instance of XECKey, hence these conditions are not added, once it is fully
+ * implemented by conscrypt, we can add CURVE_25519 argument for EdDSA and XDH
+ * algorithms.
+ */
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d244295..a400555 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -709,7 +709,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
- mContext.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index f31a27d..f621351 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -83,7 +83,6 @@
import com.android.wm.shell.bubbles.animation.ExpandedAnimationController;
import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationController;
import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerImpl;
-import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerStub;
import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -105,11 +104,6 @@
*/
public class BubbleStackView extends FrameLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
- /**
- * Set to {@code true} to enable home gesture handling in bubbles
- */
- public static final boolean HOME_GESTURE_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
public static final boolean ENABLE_FLING_TO_DISMISS_BUBBLE =
SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_bubble", true);
@@ -898,12 +892,8 @@
mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
onBubbleAnimatedOut, this);
- if (HOME_GESTURE_ENABLED) {
- mExpandedViewAnimationController =
- new ExpandedViewAnimationControllerImpl(context, mPositioner);
- } else {
- mExpandedViewAnimationController = new ExpandedViewAnimationControllerStub();
- }
+ mExpandedViewAnimationController =
+ new ExpandedViewAnimationControllerImpl(context, mPositioner);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
@@ -1964,11 +1954,7 @@
if (wasExpanded) {
stopMonitoringSwipeUpGesture();
- if (HOME_GESTURE_ENABLED) {
- animateCollapse();
- } else {
- animateCollapseWithScale();
- }
+ animateCollapse();
logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
} else {
animateExpansion();
@@ -1976,13 +1962,11 @@
logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
logBubbleEvent(mExpandedBubble,
FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
- if (HOME_GESTURE_ENABLED) {
- mBubbleController.isNotificationPanelExpanded(notifPanelExpanded -> {
- if (!notifPanelExpanded && mIsExpanded) {
- startMonitoringSwipeUpGesture();
- }
- });
- }
+ mBubbleController.isNotificationPanelExpanded(notifPanelExpanded -> {
+ if (!notifPanelExpanded && mIsExpanded) {
+ startMonitoringSwipeUpGesture();
+ }
+ });
}
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
@@ -2298,106 +2282,6 @@
mMainExecutor.executeDelayed(mDelayedAnimation, startDelay);
}
- private void animateCollapseWithScale() {
- cancelDelayedExpandCollapseSwitchAnimations();
-
- if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
- mManageEduView.hide();
- }
- // Hide the menu if it's visible.
- showManageMenu(false);
-
- mIsExpanded = false;
- mIsExpansionAnimating = true;
-
- showScrim(false);
-
- mBubbleContainer.cancelAllAnimations();
-
- // If we were in the middle of swapping, the animating-out surface would have been scaling
- // to zero - finish it off.
- PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
- mAnimatingOutSurfaceContainer.setScaleX(0f);
- mAnimatingOutSurfaceContainer.setScaleY(0f);
-
- // Let the expanded animation controller know that it shouldn't animate child adds/reorders
- // since we're about to animate collapsed.
- mExpandedAnimationController.notifyPreparingToCollapse();
-
- mExpandedAnimationController.collapseBackToStack(
- mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
- /* collapseTo */,
- () -> mBubbleContainer.setActiveController(mStackAnimationController));
-
- int index;
- if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
- index = mBubbleData.getBubbles().size();
- } else {
- index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
- }
- // Value the bubble is animating from (back into the stack).
- final PointF p = mPositioner.getExpandedBubbleXY(index, getState());
- if (mPositioner.showBubblesVertically()) {
- float pivotX;
- float pivotY = p.y + mBubbleSize / 2f;
- if (mStackOnLeftOrWillBe) {
- pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
- } else {
- pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
- }
- mExpandedViewContainerMatrix.setScale(
- 1f, 1f,
- pivotX, pivotY);
- } else {
- mExpandedViewContainerMatrix.setScale(
- 1f, 1f,
- p.x + mBubbleSize / 2f,
- p.y + mBubbleSize + mExpandedViewPadding);
- }
-
- mExpandedViewAlphaAnimator.reverse();
-
- // When the animation completes, we should no longer be showing the content.
- if (mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setContentVisibility(false);
- }
-
- PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
- PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
- .spring(AnimatableScaleMatrix.SCALE_X,
- AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
- 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
- mScaleOutSpringConfig)
- .spring(AnimatableScaleMatrix.SCALE_Y,
- AnimatableScaleMatrix.getAnimatableValueForScaleFactor(
- 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT),
- mScaleOutSpringConfig)
- .addUpdateListener((target, values) -> {
- mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- })
- .withEndActions(() -> {
- final BubbleViewProvider previouslySelected = mExpandedBubble;
- beforeExpandedViewAnimation();
- if (mManageEduView != null) {
- mManageEduView.hide();
- }
-
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "animateCollapse");
- Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
- mExpandedBubble));
- }
- updateOverflowVisibility();
- updateZOrder();
- updateBadges(true /* setBadgeForCollapsedStack */);
- afterExpandedViewAnimation();
- if (previouslySelected != null) {
- previouslySelected.setTaskViewVisibility(false);
- }
- })
- .start();
- }
-
private void animateCollapse() {
cancelDelayedExpandCollapseSwitchAnimations();
@@ -2579,65 +2463,6 @@
* and clip the expanded view.
*/
public void setImeVisible(boolean visible) {
- if (HOME_GESTURE_ENABLED) {
- setImeVisibleInternal(visible);
- } else {
- setImeVisibleWithoutClipping(visible);
- }
- }
-
- private void setImeVisibleWithoutClipping(boolean visible) {
- if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
- // This will update the animation so the bubbles move to position for the IME
- mExpandedAnimationController.expandFromStack(() -> {
- updatePointerPosition(false /* forIme */);
- afterExpandedViewAnimation();
- } /* after */);
- return;
- }
-
- if (!mIsExpanded && getBubbleCount() > 0) {
- final float stackDestinationY =
- mStackAnimationController.animateForImeVisibility(visible);
-
- // How far the stack is animating due to IME, we'll just animate the flyout by that
- // much too.
- final float stackDy =
- stackDestinationY - mStackAnimationController.getStackPosition().y;
-
- // If the flyout is visible, translate it along with the bubble stack.
- if (mFlyout.getVisibility() == VISIBLE) {
- PhysicsAnimator.getInstance(mFlyout)
- .spring(DynamicAnimation.TRANSLATION_Y,
- mFlyout.getTranslationY() + stackDy,
- FLYOUT_IME_ANIMATION_SPRING_CONFIG)
- .start();
- }
- } else if (mPositioner.showBubblesVertically() && mIsExpanded
- && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- float selectedY = mPositioner.getExpandedBubbleXY(getState().selectedIndex,
- getState()).y;
- float newExpandedViewTop = mPositioner.getExpandedViewY(mExpandedBubble, selectedY);
- mExpandedBubble.getExpandedView().setImeVisible(visible);
- if (!mExpandedBubble.getExpandedView().isUsingMaxHeight()) {
- mExpandedViewContainer.animate().translationY(newExpandedViewTop);
- }
-
- List<Animator> animList = new ArrayList();
- for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
- View child = mBubbleContainer.getChildAt(i);
- float transY = mPositioner.getExpandedBubbleXY(i, getState()).y;
- ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY);
- animList.add(anim);
- }
- updatePointerPosition(true /* forIme */);
- AnimatorSet set = new AnimatorSet();
- set.playTogether(animList);
- set.start();
- }
- }
-
- private void setImeVisibleInternal(boolean visible) {
if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
// This will update the animation so the bubbles move to position for the IME
mExpandedAnimationController.expandFromStack(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index b91062f..33629f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -20,7 +20,6 @@
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import static com.android.wm.shell.bubbles.BubbleStackView.ENABLE_FLING_TO_DISMISS_BUBBLE;
-import static com.android.wm.shell.bubbles.BubbleStackView.HOME_GESTURE_ENABLED;
import android.content.res.Resources;
import android.graphics.Path;
@@ -81,11 +80,6 @@
new PhysicsAnimator.SpringConfig(
EXPAND_COLLAPSE_ANIM_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
- private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfigWithoutHomeGesture =
- new PhysicsAnimator.SpringConfig(
- EXPAND_COLLAPSE_ANIM_STIFFNESS_WITHOUT_HOME_GESTURE,
- SpringForce.DAMPING_RATIO_NO_BOUNCY);
-
/** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx;
/** Size of each bubble. */
@@ -307,14 +301,8 @@
(firstBubbleLeads && index == 0)
|| (!firstBubbleLeads && index == mLayout.getChildCount() - 1);
- Interpolator interpolator;
- if (HOME_GESTURE_ENABLED) {
- // When home gesture is enabled, we use a different animation timing for collapse
- interpolator = expanding
- ? Interpolators.EMPHASIZED_ACCELERATE : Interpolators.EMPHASIZED_DECELERATE;
- } else {
- interpolator = Interpolators.LINEAR;
- }
+ Interpolator interpolator = expanding
+ ? Interpolators.EMPHASIZED_ACCELERATE : Interpolators.EMPHASIZED_DECELERATE;
animation
.followAnimatedTargetAlongPath(
@@ -564,16 +552,10 @@
finishRemoval.run();
mOnBubbleAnimatedOutAction.run();
} else {
- PhysicsAnimator.SpringConfig springConfig;
- if (HOME_GESTURE_ENABLED) {
- springConfig = mAnimateOutSpringConfig;
- } else {
- springConfig = mAnimateOutSpringConfigWithoutHomeGesture;
- }
PhysicsAnimator.getInstance(child)
.spring(DynamicAnimation.ALPHA, 0f)
- .spring(DynamicAnimation.SCALE_X, 0f, springConfig)
- .spring(DynamicAnimation.SCALE_Y, 0f, springConfig)
+ .spring(DynamicAnimation.SCALE_X, 0f, mAnimateOutSpringConfig)
+ .spring(DynamicAnimation.SCALE_Y, 0f, mAnimateOutSpringConfig)
.withEndActions(finishRemoval, mOnBubbleAnimatedOutAction)
.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java
deleted file mode 100644
index bb8a3aa..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.bubbles.animation;
-
-import com.android.wm.shell.bubbles.BubbleExpandedView;
-
-/**
- * Stub implementation {@link ExpandedViewAnimationController} that does not animate the
- * {@link BubbleExpandedView}
- */
-public class ExpandedViewAnimationControllerStub implements ExpandedViewAnimationController {
- @Override
- public void setExpandedView(BubbleExpandedView expandedView) {
- }
-
- @Override
- public void updateDrag(float distance) {
- }
-
- @Override
- public void setSwipeVelocity(float velocity) {
- }
-
- @Override
- public boolean shouldCollapse() {
- return false;
- }
-
- @Override
- public void animateCollapse(Runnable startStackCollapse, Runnable after) {
- }
-
- @Override
- public void animateBackToExpanded() {
- }
-
- @Override
- public void animateForImeVisibilityChange(boolean visible) {
- }
-
- @Override
- public void reset() {
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index f6d6c03..5332476 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -140,7 +140,7 @@
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false), anyInt());
}
@@ -152,7 +152,7 @@
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true), anyInt());
}
diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java
index 8c40338..8e1bb1f0 100644
--- a/location/java/android/location/Country.java
+++ b/location/java/android/location/Country.java
@@ -16,6 +16,9 @@
package android.location;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,7 +31,8 @@
*
* @hide
*/
-public class Country implements Parcelable {
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+public final class Country implements Parcelable {
/**
* The country code came from the mobile network
*/
@@ -78,6 +82,8 @@
* <li>{@link #COUNTRY_SOURCE_SIM}</li>
* <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
* </ul>
+ *
+ * @hide
*/
@UnsupportedAppUsage
public Country(final String countryIso, final int source) {
@@ -100,6 +106,7 @@
mTimestamp = timestamp;
}
+ /** @hide */
public Country(Country country) {
mCountryIso = country.mCountryIso;
mSource = country.mSource;
@@ -109,8 +116,8 @@
/**
* @return the ISO 3166-1 two letters country code
*/
- @UnsupportedAppUsage
- public final String getCountryIso() {
+ @NonNull
+ public String getCountryIso() {
return mCountryIso;
}
@@ -124,20 +131,22 @@
* <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
* </ul>
*/
- @UnsupportedAppUsage
- public final int getSource() {
+ public int getSource() {
return mSource;
}
/**
* Returns the time that this object was created (which we assume to be the time that the source
* was consulted).
+ *
+ * @hide
*/
- public final long getTimestamp() {
+ public long getTimestamp() {
return mTimestamp;
}
- public static final @android.annotation.NonNull Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
+ @android.annotation.NonNull
+ public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
public Country createFromParcel(Parcel in) {
return new Country(in.readString(), in.readInt(), in.readLong());
}
@@ -147,11 +156,13 @@
}
};
+ @Override
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel parcel, int flags) {
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeString(mCountryIso);
parcel.writeInt(mSource);
parcel.writeLong(mTimestamp);
@@ -161,9 +172,10 @@
* Returns true if this {@link Country} is equivalent to the given object. This ignores
* the timestamp value and just checks for equivalence of countryIso and source values.
* Returns false otherwise.
+ *
*/
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (object == this) {
return true;
}
@@ -194,12 +206,15 @@
* @param country the country to compare
* @return true if the specified country's countryIso field is equal to this
* country's, false otherwise.
+ *
+ * @hide
*/
public boolean equalsIgnoreSource(Country country) {
return country != null && mCountryIso.equals(country.getCountryIso());
}
@Override
+ @NonNull
public String toString() {
return "Country {ISO=" + mCountryIso + ", source=" + mSource + ", time=" + mTimestamp + "}";
}
diff --git a/location/java/android/location/CountryDetector.java b/location/java/android/location/CountryDetector.java
index e344b82..3a0edfc 100644
--- a/location/java/android/location/CountryDetector.java
+++ b/location/java/android/location/CountryDetector.java
@@ -16,6 +16,9 @@
package android.location;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -47,6 +50,7 @@
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@SystemService(Context.COUNTRY_DETECTOR)
public class CountryDetector {
@@ -101,7 +105,7 @@
* @return the country if it is available immediately, otherwise null will
* be returned.
*/
- @UnsupportedAppUsage
+ @Nullable
public Country detectCountry() {
try {
return mService.detectCountry();
@@ -120,8 +124,7 @@
* implement the callback mechanism. If looper is null then the
* callbacks will be called on the main thread.
*/
- @UnsupportedAppUsage
- public void addCountryListener(CountryListener listener, Looper looper) {
+ public void addCountryListener(@NonNull CountryListener listener, @Nullable Looper looper) {
synchronized (mListeners) {
if (!mListeners.containsKey(listener)) {
ListenerTransport transport = new ListenerTransport(listener, looper);
@@ -138,8 +141,7 @@
/**
* Remove the listener
*/
- @UnsupportedAppUsage
- public void removeCountryListener(CountryListener listener) {
+ public void removeCountryListener(@NonNull CountryListener listener) {
synchronized (mListeners) {
ListenerTransport transport = mListeners.get(listener);
if (transport != null) {
diff --git a/location/java/android/location/CountryListener.java b/location/java/android/location/CountryListener.java
index eb67205..5c06d82 100644
--- a/location/java/android/location/CountryListener.java
+++ b/location/java/android/location/CountryListener.java
@@ -16,7 +16,8 @@
package android.location;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
/**
* The listener for receiving the notification when the country is detected or
@@ -24,10 +25,11 @@
*
* @hide
*/
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public interface CountryListener {
/**
* @param country the changed or detected country.
+ *
*/
- @UnsupportedAppUsage
- void onCountryDetected(Country country);
+ void onCountryDetected(@NonNull Country country);
}
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index 98578b2..4f45602 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -138,6 +138,7 @@
@SubHalMeasurementCorrectionsCapabilityFlags int measurementCorrectionsFlags,
@SubHalPowerCapabilityFlags int powerFlags,
@NonNull List<GnssSignalType> gnssSignalTypes) {
+ Objects.requireNonNull(gnssSignalTypes);
mTopFlags = topFlags;
mMeasurementCorrectionsFlags = measurementCorrectionsFlags;
mPowerFlags = powerFlags;
@@ -190,6 +191,21 @@
}
/**
+ * Returns a new GnssCapabilities object with a list of GnssSignalType.
+ *
+ * @hide
+ */
+ public GnssCapabilities withSignalTypes(@NonNull List<GnssSignalType> gnssSignalTypes) {
+ Objects.requireNonNull(gnssSignalTypes);
+ if (mGnssSignalTypes.equals(gnssSignalTypes)) {
+ return this;
+ } else {
+ return new GnssCapabilities(mTopFlags, mMeasurementCorrectionsFlags, mPowerFlags,
+ new ArrayList<>(gnssSignalTypes));
+ }
+ }
+
+ /**
* Returns {@code true} if GNSS chipset supports scheduling, {@code false} otherwise.
*/
public boolean hasScheduling() {
@@ -452,7 +468,8 @@
GnssCapabilities that = (GnssCapabilities) o;
return mTopFlags == that.mTopFlags
&& mMeasurementCorrectionsFlags == that.mMeasurementCorrectionsFlags
- && mPowerFlags == that.mPowerFlags;
+ && mPowerFlags == that.mPowerFlags
+ && mGnssSignalTypes.equals(that.mGnssSignalTypes);
}
@Override
diff --git a/location/java/android/location/GnssSignalType.java b/location/java/android/location/GnssSignalType.java
index f9c6e72..16c3f2e 100644
--- a/location/java/android/location/GnssSignalType.java
+++ b/location/java/android/location/GnssSignalType.java
@@ -116,7 +116,7 @@
public String toString() {
StringBuilder s = new StringBuilder();
s.append("GnssSignalType[");
- s.append(" Constellation=").append(mConstellationType);
+ s.append("Constellation=").append(mConstellationType);
s.append(", CarrierFrequencyHz=").append(mCarrierFrequencyHz);
s.append(", CodeType=").append(mCodeType);
s.append(']');
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7a4a93f..d46a93c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -115,6 +115,7 @@
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
"com.google.android.material_material",
+ "kotlin-reflect",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
"iconloader_base",
@@ -224,6 +225,7 @@
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
"mockito-target-extended-minus-junit4",
+ "androidx.test.ext.junit",
"testables",
"truth-prebuilt",
"monet",
diff --git a/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml b/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml
new file mode 100644
index 0000000..ae0f4b2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/user_switcher_fullscreen_button_bg.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="20dp"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="20dp"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"
+ />
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 78884ff..fa9d739 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -80,7 +80,7 @@
<TextView
android:id="@+id/add"
- style="@style/Widget.Dialog.Button.BorderButton"
+ android:background="@drawable/user_switcher_fullscreen_button_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/packages/SystemUI/res/raw/biometricprompt_folded_base_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_folded_base_bottomright.json
new file mode 100644
index 0000000..2797996
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_folded_base_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_portrait_base_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"biometricprompt_landscape_base","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[34.67,28.053],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[80.283,32.779],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}]}],"layers":[{"ddd":0,"ind":6,"ty":0,"nm":"biometricprompt_landscape_base","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[170,170,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":340,"h":340,"ip":0,"op":900,"st":0,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
diff --git a/packages/SystemUI/res/raw/biometricprompt_folded_base_default.json b/packages/SystemUI/res/raw/biometricprompt_folded_base_default.json
new file mode 100644
index 0000000..bf65b34
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_folded_base_default.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_landscape_base","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Null_Circle","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle mask 3","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-60,"s":[55]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":110,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":140,"s":[10]},{"t":170,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-60,"s":[92.146,-65.896,0],"to":[1.361,6.667,0],"ti":[-1.361,-6.667,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.167,"y":0.167},"t":0,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":0.2},"o":{"x":0.7,"y":0.7},"t":110,"s":[100.313,-25.896,0],"to":[0,0,0],"ti":[0,0,0]},{"t":170,"s":[100.313,-25.896,0]}],"ix":2,"l":2},"a":{"a":0,"k":[160.315,58.684,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-11.013,2.518],[5.251,5.023],[8.982,-2.829],[-0.264,-5.587]],"o":[[12.768,-2.854],[-14.961,2.071],[-6.004,1.89],[8.052,1.403]],"v":[[5.115,7.499],[19.814,-10.087],[-16.489,-3.588],[-24.801,8.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[34.67,28.053],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.231,-7],[-27.395,-1.197],[-26.792,4.092],[14.179,15.736]],"o":[[-17.931,5.646],[56.062,2.45],[-1.765,-22.396],[-51.819,17.744]],"v":[[-62.102,-8.314],[-39.958,30.079],[80.033,25.905],[54.879,-32.529]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[80.283,32.779],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"circle mask 7","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".grey600","cl":"grey600","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"circle mask","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".grey800","cl":"grey800","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"circle mask 6","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"circle mask 2","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"circle mask 4","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":1,"nm":".grey900","cl":"grey900","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"circle mask 5","parent":2,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":1,"nm":".black","cl":"black","parent":2,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".grey800","cl":"grey800","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[0,18.167,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[0,10.667,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 4","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".grey900","cl":"grey900","parent":1,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"device frame mask","parent":24,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,1.167,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".blue400","cl":"blue400","parent":18,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-115.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-199,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-159,"s":[100.25,-105.667,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[100.25,-100.167,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-0.75,-14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":3,"nm":"device frame mask 5","parent":18,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-165,"op":6.00000000000001,"st":-271,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"device frame mask 9","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":29,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-145,"s":[50]},{"t":-75,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-165,"s":[0,0]},{"t":-75,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":73,"s":[50]},{"t":113,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-181,"op":-62,"st":-181,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"device frame mask 8","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-165,"s":[50]},{"t":-95,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-195,"s":[0,0]},{"t":-105,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[50]},{"t":83,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-211,"op":-92,"st":-211,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"device frame mask 7","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-195,"s":[50]},{"t":-125,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-225,"s":[0,0]},{"t":-135,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":53,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-241,"op":-122,"st":-241,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"device frame mask 6","parent":1,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-29.25,-0.917,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":".blue400","cl":"blue400","parent":23,"tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":-225,"s":[50]},{"t":-155,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-255,"s":[0,0]},{"t":-165,"s":[94,94]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[50]},{"t":23,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-271,"op":-152,"st":-271,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
diff --git a/packages/SystemUI/res/raw/biometricprompt_folded_base_topleft.json b/packages/SystemUI/res/raw/biometricprompt_folded_base_topleft.json
new file mode 100644
index 0000000..7351d7c
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_folded_base_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Portrait_Base_TopLeft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.8,24.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[31.79,75.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 25ae62d..f02f29a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -762,7 +762,7 @@
<dimen name="keyguard_lock_padding">20dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
- <dimen name="lock_icon_margin_bottom">74dp</dimen>
+ <dimen name="lock_icon_margin_bottom">110dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6a2d1f0..cbcfbf8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -403,6 +403,8 @@
<string name="keyguard_face_failed">Can\u2019t recognize face</string>
<!-- Message shown to suggest using fingerprint sensor to authenticate after another biometric failed. [CHAR LIMIT=25] -->
<string name="keyguard_suggest_fingerprint">Use fingerprint instead</string>
+ <!-- Message shown to inform the user that face unlock is not available. [CHAR LIMIT=25] -->
+ <string name="keyguard_face_unlock_unavailable">Face unlock unavailable.</string>
<!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_connected">Bluetooth connected.</string>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index ffaeaaa..f8f4d4a 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -53,6 +53,11 @@
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
"gson",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-ktx",
+ "androidx.recyclerview_recyclerview",
+ "kotlinx_coroutines_android",
+ "kotlinx_coroutines",
"dagger2",
"jsr330",
],
@@ -64,6 +69,7 @@
},
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
+ kotlincflags: ["-Xjvm-default=enable"],
}
java_library {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index f7049cf..933e586 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -89,7 +89,7 @@
*
* It can be changed or overridden in debug builds but not in release builds.
*/
-data class UnreleasedFlag @JvmOverloads constructor(
+data class UnreleasedFlag constructor(
override val id: Int,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -100,7 +100,7 @@
*
* It can be changed or overridden in any build, meaning it can be turned off if needed.
*/
-data class ReleasedFlag @JvmOverloads constructor(
+data class ReleasedFlag constructor(
override val id: Int,
override val teamfood: Boolean = false,
override val overridden: Boolean = false
@@ -111,7 +111,7 @@
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class ResourceBooleanFlag @JvmOverloads constructor(
+data class ResourceBooleanFlag constructor(
override val id: Int,
@BoolRes override val resourceId: Int,
override val teamfood: Boolean = false
@@ -124,7 +124,7 @@
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class DeviceConfigBooleanFlag @JvmOverloads constructor(
+data class DeviceConfigBooleanFlag constructor(
override val id: Int,
override val name: String,
override val namespace: String,
@@ -139,7 +139,7 @@
*
* Prefer [UnreleasedFlag] and [ReleasedFlag].
*/
-data class SysPropBooleanFlag @JvmOverloads constructor(
+data class SysPropBooleanFlag constructor(
override val id: Int,
override val name: String,
override val default: Boolean = false
@@ -148,7 +148,7 @@
override val teamfood: Boolean = false
}
-data class StringFlag @JvmOverloads constructor(
+data class StringFlag constructor(
override val id: Int,
override val default: String = "",
override val teamfood: Boolean = false,
@@ -173,13 +173,13 @@
}
}
-data class ResourceStringFlag @JvmOverloads constructor(
+data class ResourceStringFlag constructor(
override val id: Int,
@StringRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<String>
-data class IntFlag @JvmOverloads constructor(
+data class IntFlag constructor(
override val id: Int,
override val default: Int = 0,
override val teamfood: Boolean = false,
@@ -205,13 +205,13 @@
}
}
-data class ResourceIntFlag @JvmOverloads constructor(
+data class ResourceIntFlag constructor(
override val id: Int,
@IntegerRes override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Int>
-data class LongFlag @JvmOverloads constructor(
+data class LongFlag constructor(
override val id: Int,
override val default: Long = 0,
override val teamfood: Boolean = false,
@@ -237,7 +237,7 @@
}
}
-data class FloatFlag @JvmOverloads constructor(
+data class FloatFlag constructor(
override val id: Int,
override val default: Float = 0f,
override val teamfood: Boolean = false,
@@ -263,13 +263,13 @@
}
}
-data class ResourceFloatFlag @JvmOverloads constructor(
+data class ResourceFloatFlag constructor(
override val id: Int,
override val resourceId: Int,
override val teamfood: Boolean = false
) : ResourceFlag<Int>
-data class DoubleFlag @JvmOverloads constructor(
+data class DoubleFlag constructor(
override val id: Int,
override val default: Double = 0.0,
override val teamfood: Boolean = false,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9d2dcf0..f08b1be 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -782,7 +782,7 @@
mFingerprintCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
- mLogger.d("onFingerprintAuthenticated");
+ mLogger.logFingerprintSuccess(userId, isStrongBiometric);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2701,7 +2701,7 @@
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
// TODO: always disallow when fp is already locked out?
- final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+ final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -2730,7 +2730,6 @@
final boolean faceDisabledForUser = isFaceDisabled(user);
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
- final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -2748,7 +2747,10 @@
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& !faceAuthenticated
&& !mGoingToSleep
- && !fpOrFaceIsLockedOut;
+ // We only care about fp locked out state and not face because we still trigger
+ // face auth even when face is locked out to show the user a message that face
+ // unlock was supposed to run but didn't
+ && !fpLockedOut;
// Aggregate relevant fields for debug logging.
maybeLogListenerModelData(
@@ -2763,7 +2765,7 @@
faceAuthenticated,
faceDisabledForUser,
isFaceLockedOut(),
- fpLockedout,
+ fpLockedOut,
mGoingToSleep,
awakeKeyguard,
mKeyguardGoingAway,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 34a5ef7..0a82968 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -158,10 +158,6 @@
return mLockIconCenter.y - mRadius;
}
- float getLocationBottom() {
- return mLockIconCenter.y + mRadius;
- }
-
/**
* Updates the icon its default state where no visual is shown.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index b259494..8fbbd38 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -282,10 +282,6 @@
return mView.getLocationTop();
}
- public float getBottom() {
- return mView.getLocationBottom();
- }
-
private void updateVisibility() {
if (mCancelDelayedUpdateVisibilityRunnable != null) {
mCancelDelayedUpdateVisibilityRunnable.run();
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 3308f55..6276142d 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -153,6 +153,13 @@
{ "fingerprintRunningState: $int1" })
}
+ fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
+ logBuffer.log(TAG, DEBUG, {
+ int1 = userId
+ bool1 = isStrongBiometric
+ }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
+ }
+
fun logInvalidSubId(subId: Int) {
logBuffer.log(TAG, INFO,
{ int1 = subId },
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
index 8920c92..8aa3040 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
@@ -17,7 +17,7 @@
package com.android.systemui
import android.content.Context
-import com.android.systemui.dagger.DaggerGlobalRootComponent
+import com.android.systemui.dagger.DaggerReferenceGlobalRootComponent
import com.android.systemui.dagger.GlobalRootComponent
/**
@@ -25,6 +25,6 @@
*/
class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) {
override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder {
- return DaggerGlobalRootComponent.builder()
+ return DaggerReferenceGlobalRootComponent.builder()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index b40b356..b2a2a67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -18,6 +18,7 @@
import android.annotation.RawRes
import android.content.Context
+import android.content.res.Configuration
import android.hardware.fingerprint.FingerprintManager
import android.view.DisplayInfo
import android.view.Surface
@@ -33,15 +34,19 @@
import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP
import com.android.systemui.biometrics.AuthBiometricView.STATE_IDLE
import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.unfold.updates.FoldProvider
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
context: Context,
iconView: LottieAnimationView,
protected val iconViewOverlay: LottieAnimationView
-) : AuthIconController(context, iconView) {
+) : AuthIconController(context, iconView), FoldProvider.FoldCallback {
+ private var isDeviceFolded: Boolean = false
private val isSideFps: Boolean
+ private val screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
@@ -74,6 +79,8 @@
if (isSideFps && displayInfo.rotation == Surface.ROTATION_180) {
iconView.rotation = 180f
}
+ screenSizeFoldProvider.registerCallback(this, context.mainExecutor)
+ screenSizeFoldProvider.onConfigurationChange(context.resources.configuration)
}
private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
@@ -124,6 +131,10 @@
LottieColorUtils.applyDynamicColors(context, iconView)
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ screenSizeFoldProvider.onConfigurationChange(newConfig)
+ }
+
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
if (isSideFps) {
updateIconSideFps(lastState, newState)
@@ -191,11 +202,21 @@
@RawRes
private fun getSideFpsAnimationForTransition(rotation: Int): Int = when (rotation) {
- Surface.ROTATION_0 -> R.raw.biometricprompt_landscape_base
- Surface.ROTATION_90 -> R.raw.biometricprompt_portrait_base_topleft
- Surface.ROTATION_180 -> R.raw.biometricprompt_landscape_base
- Surface.ROTATION_270 -> R.raw.biometricprompt_portrait_base_bottomright
- else -> R.raw.biometricprompt_landscape_base
+ Surface.ROTATION_90 -> if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_topleft
+ } else {
+ R.raw.biometricprompt_portrait_base_topleft
+ }
+ Surface.ROTATION_270 -> if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_bottomright
+ } else {
+ R.raw.biometricprompt_portrait_base_bottomright
+ }
+ else -> if (isDeviceFolded) {
+ R.raw.biometricprompt_folded_base_default
+ } else {
+ R.raw.biometricprompt_landscape_base
+ }
}
@RawRes
@@ -273,4 +294,8 @@
}
else -> null
}
+
+ override fun onFoldUpdated(isFolded: Boolean) {
+ isDeviceFolded = isFolded
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
index 15f487b..b3b6fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt
@@ -18,6 +18,7 @@
import android.annotation.DrawableRes
import android.content.Context
+import android.content.res.Configuration
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
@@ -91,4 +92,6 @@
/** Called during [onAnimationEnd] if the controller is not [deactivated]. */
open fun handleAnimationEnd(drawable: Drawable) {}
+
+ open fun onConfigurationChanged(newConfig: Configuration) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 0ac71c4..e12c170 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.Context;
+import android.content.res.Configuration;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.PromptInfo;
@@ -654,6 +655,12 @@
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mIconController.onConfigurationChanged(newConfig);
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 9e33ee1..fe89c9a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -27,7 +27,9 @@
import dagger.Component;
/**
- * Root component for Dagger injection.
+ * Base root component for Dagger injection.
+ *
+ * See {@link ReferenceGlobalRootComponent} for the one actually used by AOSP.
*/
@Singleton
@Component(modules = {GlobalModule.class})
@@ -51,7 +53,7 @@
WMComponent.Builder getWMComponentBuilder();
/**
- * Builder for a {@link SysUIComponent}, which makes it a subcomponent of this class.
+ * Builder for a {@link ReferenceSysUIComponent}, which makes it a subcomponent of this class.
*/
SysUIComponent.Builder getSysUIComponent();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java
new file mode 100644
index 0000000..be93c9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceGlobalRootComponent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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.dagger;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+/**
+ * Root component for Dagger injection used in AOSP.
+ */
+@Singleton
+@Component(modules = {GlobalModule.class})
+public interface ReferenceGlobalRootComponent extends GlobalRootComponent {
+
+ /**
+ * Builder for a ReferenceGlobalRootComponent.
+ */
+ @Component.Builder
+ interface Builder extends GlobalRootComponent.Builder {
+ ReferenceGlobalRootComponent build();
+ }
+
+ /**
+ * Builder for a {@link ReferenceSysUIComponent}, which makes it a subcomponent of this class.
+ */
+ ReferenceSysUIComponent.Builder getSysUIComponent();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
new file mode 100644
index 0000000..7ab36e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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.dagger;
+
+import com.android.systemui.statusbar.QsFrameTranslateModule;
+
+import dagger.Subcomponent;
+
+/**
+ * Dagger Subcomponent for Core SysUI used in AOSP.
+ */
+@SysUISingleton
+@Subcomponent(modules = {
+ DefaultComponentBinder.class,
+ DependencyProvider.class,
+ QsFrameTranslateModule.class,
+ SystemUIBinder.class,
+ SystemUIModule.class,
+ SystemUICoreStartableModule.class,
+ ReferenceSystemUIModule.class})
+public interface ReferenceSysUIComponent extends SysUIComponent {
+
+ /**
+ * Builder for a ReferenceSysUIComponent.
+ */
+ @SysUISingleton
+ @Subcomponent.Builder
+ interface Builder extends SysUIComponent.Builder {
+ ReferenceSysUIComponent build();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index c260b7d..a14b0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -57,7 +57,9 @@
import dagger.Subcomponent;
/**
- * Dagger Subcomponent for Core SysUI.
+ * An example Dagger Subcomponent for Core SysUI.
+ *
+ * See {@link ReferenceSysUIComponent} for the one actually used by AOSP.
*/
@SysUISingleton
@Subcomponent(modules = {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index 2a1e3cd..ad4b87d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -23,7 +23,6 @@
import com.android.systemui.statusbar.commandline.Command;
import java.io.PrintWriter;
-import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
@@ -230,33 +229,22 @@
}
private int flagNameToId(String flagName) {
- List<Field> fields = Flags.getFlagFields();
- for (Field field : fields) {
- if (flagName.equals(field.getName())) {
- return fieldToId(field);
+ Map<String, Flag<?>> flagFields = Flags.getFlagFields();
+ for (String fieldName : flagFields.keySet()) {
+ if (flagName.equals(fieldName)) {
+ return flagFields.get(fieldName).getId();
}
}
return 0;
}
- private int fieldToId(Field field) {
- try {
- Flag<?> flag = (Flag<?>) field.get(null);
- return flag.getId();
- } catch (IllegalAccessException e) {
- // no-op
- }
-
- return 0;
- }
-
private void printKnownFlags(PrintWriter pw) {
- List<Field> fields = Flags.getFlagFields();
+ Map<String, Flag<?>> fields = Flags.getFlagFields();
int longestFieldName = 0;
- for (Field field : fields) {
- longestFieldName = Math.max(longestFieldName, field.getName().length());
+ for (String fieldName : fields.keySet()) {
+ longestFieldName = Math.max(longestFieldName, fieldName.length());
}
pw.println("Known Flags:");
@@ -268,16 +256,15 @@
for (int i = 0; i < longestFieldName; i++) {
pw.print("=");
}
- pw.println(" ==== =====");
- for (Field field : fields) {
- int id = fieldToId(field);
- Flag<?> flag = mAllFlags.get(id);
-
+ pw.println(" ==== ========");
+ for (String fieldName : fields.keySet()) {
+ Flag<?> flag = fields.get(fieldName);
+ int id = flag.getId();
if (id == 0 || !mAllFlags.containsKey(id)) {
continue;
}
- pw.print(field.getName());
- int fieldWidth = field.getName().length();
+ pw.print(fieldName);
+ int fieldWidth = fieldName.length();
for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
pw.print(" ");
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 10bf23b..804eef3 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -17,8 +17,12 @@
import android.provider.DeviceConfig
import com.android.internal.annotations.Keep
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
-import java.lang.reflect.Field
+import kotlin.reflect.KClass
+import kotlin.reflect.full.declaredMembers
+import kotlin.reflect.full.isSubclassOf
+import kotlin.reflect.full.staticProperties
/**
* List of [Flag] objects for use in SystemUI.
@@ -63,6 +67,7 @@
@JvmField val NOTIFICATION_DISMISSAL_FADE = UnreleasedFlag(113, teamfood = true)
val STABILITY_INDEX_FIX = UnreleasedFlag(114, teamfood = true)
val SEMI_STABLE_SORT = UnreleasedFlag(115, teamfood = true)
+
@JvmField val NOTIFICATION_GROUP_CORNER = UnreleasedFlag(116, teamfood = true)
// next id: 117
@@ -152,6 +157,7 @@
// TODO(b/254512321): Tracking Bug
@JvmField val COMBINED_QS_HEADERS = UnreleasedFlag(501, teamfood = true)
val PEOPLE_TILE = ResourceBooleanFlag(502, R.bool.flag_conversations)
+
@JvmField
val QS_USER_DETAIL_SHORTCUT =
ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut)
@@ -251,20 +257,14 @@
@JvmField val ROUNDED_BOX_RIPPLE = ReleasedFlag(1002)
// 1100 - windowing
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_SHELL_TRANSITIONS =
SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false)
- /** b/170163464: animate bubbles expanded view collapse with home gesture */
- @JvmField
- @Keep
- val BUBBLES_HOME_GESTURE =
- SysPropBooleanFlag(1101, "persist.wm.debug.bubbles_home_gesture", true)
-
// TODO(b/254513207): Tracking Bug
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING =
DeviceConfigBooleanFlag(
1102,
@@ -275,30 +275,30 @@
)
// TODO(b/254512674): Tracking Bug
- @JvmField
@Keep
+ @JvmField
val HIDE_NAVBAR_WINDOW = SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false)
- @JvmField
@Keep
+ @JvmField
val WM_DESKTOP_WINDOWING = SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false)
- @JvmField
@Keep
+ @JvmField
val WM_CAPTION_ON_SHELL = SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false)
- @JvmField
@Keep
+ @JvmField
val ENABLE_FLING_TO_DISMISS_BUBBLE =
SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true)
- @JvmField
@Keep
+ @JvmField
val ENABLE_FLING_TO_DISMISS_PIP =
SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true)
- @JvmField
@Keep
+ @JvmField
val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
SysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", false)
@@ -306,18 +306,18 @@
@JvmField @Keep val WM_BUBBLE_BAR = UnreleasedFlag(1111)
// 1200 - predictive back
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PREDICTIVE_BACK =
SysPropBooleanFlag(1200, "persist.wm.debug.predictive_back", true)
- @JvmField
@Keep
+ @JvmField
val WM_ENABLE_PREDICTIVE_BACK_ANIM =
SysPropBooleanFlag(1201, "persist.wm.debug.predictive_back_anim", false)
- @JvmField
@Keep
+ @JvmField
val WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false)
@@ -326,7 +326,7 @@
// 1300 - screenshots
// TODO(b/254512719): Tracking Bug
- @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, true)
+ @JvmField val SCREENSHOT_REQUEST_PROCESSOR = UnreleasedFlag(1300, teamfood = true)
// TODO(b/254513155): Tracking Bug
@JvmField val SCREENSHOT_WORK_PROFILE_POLICY = UnreleasedFlag(1301)
@@ -343,7 +343,7 @@
@JvmField val A11Y_FLOATING_MENU_FLING_SPRING_ANIMATIONS = UnreleasedFlag(1600)
// 1700 - clipboard
- @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, true)
+ @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, teamfood = true)
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)
// 1800 - shade container
@@ -353,7 +353,7 @@
@JvmField val NOTE_TASKS = SysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
// 2000 - device controls
- @Keep val USE_APP_PANELS = UnreleasedFlag(2000, true)
+ @Keep @JvmField val USE_APP_PANELS = UnreleasedFlag(2000, teamfood = true)
// 2100 - Falsing Manager
@JvmField val FALSING_FOR_LONG_TAPS = ReleasedFlag(2100)
@@ -364,24 +364,48 @@
// | . . . . . . . . . . . . . . . . . . . |
@JvmStatic
fun collectFlags(): Map<Int, Flag<*>> {
- return flagFields
- .map { field ->
- // field[null] returns the current value of the field.
- // See java.lang.Field#get
- val flag = field[null] as Flag<*>
- flag.id to flag
- }
- .toMap()
+ return flagFields.mapKeys { field -> field.value.id }
}
// | . . . . . . . . . . . . . . . . . . . |
@JvmStatic
- val flagFields: List<Field>
- get() {
- return Flags::class.java.fields.filter { f ->
- Flag::class.java.isAssignableFrom(f.type)
+ val flagFields: Map<String, Flag<*>>
+ get() = collectFlagsInClass(Flags)
+
+ @VisibleForTesting
+ fun collectFlagsInClass(instance: Any): Map<String, Flag<*>> {
+ val cls = instance::class
+ val javaPropNames = cls.java.fields.map { it.name }
+ val props = cls.declaredMembers
+ val staticProps = cls.staticProperties
+ val staticPropNames = staticProps.map { it.name }
+ return props
+ .mapNotNull { property ->
+ if ((property.returnType.classifier as KClass<*>).isSubclassOf(Flag::class)) {
+ // Fields with @JvmStatic should be accessed via java mechanisms
+ if (javaPropNames.contains(property.name)) {
+ property.name to cls.java.getField(property.name)[null] as Flag<*>
+ // Fields with @Keep but not @JvmField. Don't do this.
+ } else if (staticPropNames.contains(property.name)) {
+ // The below code causes access violation exceptions. I don't know why.
+ // property.name to (property.call() as Flag<*>)
+ // property.name to (staticProps.find { it.name == property.name }!!
+ // .getter.call() as Flag<*>)
+ throw java.lang.RuntimeException(
+ "The {$property.name} flag needs @JvmField"
+ )
+ // Everything else. Skip the `get` prefixed fields that kotlin adds.
+ } else if (property.name.subSequence(0, 3) != "get") {
+ property.name to (property.call(instance) as Flag<*>)
+ } else {
+ null
+ }
+ } else {
+ null
+ }
}
- }
+ .toMap()
+ }
// | |
// \_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/\_/
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index ca25282..c867c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.data.repository
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
@@ -124,11 +123,6 @@
* Sets the relative offset of the lock-screen clock from its natural position on the screen.
*/
fun setClockPosition(x: Int, y: Int)
-
- /**
- * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
- */
- fun isUdfpsSupported(): Boolean
}
/** Encapsulates application state for the keyguard. */
@@ -136,12 +130,11 @@
class KeyguardRepositoryImpl
@Inject
constructor(
- statusBarStateController: StatusBarStateController,
- dozeHost: DozeHost,
- wakefulnessLifecycle: WakefulnessLifecycle,
- biometricUnlockController: BiometricUnlockController,
- private val keyguardStateController: KeyguardStateController,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ statusBarStateController: StatusBarStateController,
+ private val keyguardStateController: KeyguardStateController,
+ dozeHost: DozeHost,
+ wakefulnessLifecycle: WakefulnessLifecycle,
+ biometricUnlockController: BiometricUnlockController,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -318,8 +311,6 @@
_clockPosition.value = Position(x, y)
}
- override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
-
private fun statusBarStateIntToObject(value: Int): StatusBarState {
return when (value) {
0 -> StatusBarState.SHADE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
index d2a7486..ede50b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
@@ -48,9 +48,4 @@
fun setAnimateDozingTransitions(animate: Boolean) {
repository.setAnimateDozingTransitions(animate)
}
-
- /**
- * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
- */
- fun shouldConstrainToTopOfLockIcon(): Boolean = repository.isUdfpsSupported()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 3276b6d..2c99ca5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -27,8 +27,6 @@
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.LockIconViewController
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Expandable
@@ -71,11 +69,6 @@
/** Notifies that device configuration has changed. */
fun onConfigurationChanged()
-
- /**
- * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
- */
- fun shouldConstrainToTopOfLockIcon(): Boolean
}
/** Binds the view to the view-model, continuing to update the former based on the latter. */
@@ -215,9 +208,6 @@
override fun onConfigurationChanged() {
configurationBasedDimensions.value = loadFromResources(view)
}
-
- override fun shouldConstrainToTopOfLockIcon(): Boolean =
- viewModel.shouldConstrainToTopOfLockIcon()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 227796f..b6b2304 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -90,12 +90,6 @@
.distinctUntilChanged()
}
- /**
- * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
- */
- fun shouldConstrainToTopOfLockIcon(): Boolean =
- bottomAreaInteractor.shouldConstrainToTopOfLockIcon()
-
private fun button(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceViewModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 662d059..8bddffc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -45,6 +45,7 @@
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLock
import javax.inject.Inject
/**
@@ -68,6 +69,7 @@
private val mediaTttFlags: MediaTttFlags,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
private val viewUtil: ViewUtil,
+ wakeLockBuilder: WakeLock.Builder,
) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger>(
context,
logger,
@@ -77,6 +79,7 @@
configurationController,
powerManager,
R.layout.media_ttt_chip_receiver,
+ wakeLockBuilder,
) {
@SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
override val windowLayoutParams = commonWindowLayoutParams.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index cd5647e..200288b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -107,6 +107,7 @@
val filter = IntentFilter().apply {
addAction(Intent.ACTION_USER_SWITCHED)
+ addAction(Intent.ACTION_USER_INFO_CHANGED)
// These get called when a managed profile goes in or out of quiet mode.
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
@@ -124,6 +125,7 @@
Intent.ACTION_USER_SWITCHED -> {
handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
}
+ Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_REMOVED,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3c19ee8..59b1ed7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1308,11 +1308,7 @@
}
private void initBottomArea() {
- mKeyguardBottomArea.init(
- mKeyguardBottomAreaViewModel,
- mFalsingManager,
- mLockIconViewController
- );
+ mKeyguardBottomArea.init(mKeyguardBottomAreaViewModel, mFalsingManager);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 073ab8b..f2b8603 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -21,6 +21,7 @@
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricSourceType.FACE;
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -50,7 +51,6 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
-import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -74,12 +74,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.ViewClippingUtil;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -138,6 +138,7 @@
private final KeyguardStateController mKeyguardStateController;
protected final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final AuthController mAuthController;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
private KeyguardIndicationTextView mLockScreenIndicationView;
@@ -188,14 +189,7 @@
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
private boolean mDozing;
- private final ViewClippingUtil.ClippingParameters mClippingParams =
- new ViewClippingUtil.ClippingParameters() {
- @Override
- public boolean shouldFinish(View view) {
- return view == mIndicationArea;
- }
- };
- private ScreenLifecycle mScreenLifecycle;
+ private final ScreenLifecycle mScreenLifecycle;
private final ScreenLifecycle.Observer mScreenObserver =
new ScreenLifecycle.Observer() {
@Override
@@ -209,6 +203,7 @@
}
}
};
+ private boolean mFaceLockedOutThisAuthSession;
/**
* Creates a new KeyguardIndicationController and registers callbacks.
@@ -229,6 +224,7 @@
@Main DelayableExecutor executor,
@Background DelayableExecutor bgExecutor,
FalsingManager falsingManager,
+ AuthController authController,
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
KeyguardBypassController keyguardBypassController,
@@ -248,6 +244,7 @@
mExecutor = executor;
mBackgroundExecutor = bgExecutor;
mLockPatternUtils = lockPatternUtils;
+ mAuthController = authController;
mFalsingManager = falsingManager;
mKeyguardBypassController = keyguardBypassController;
mAccessibilityManager = accessibilityManager;
@@ -619,7 +616,6 @@
if (mFalsingManager.isFalseTap(LOW_PENALTY)) {
return;
}
- int currentUserId = getCurrentUser();
mDevicePolicyManager.logoutUser();
})
.build(),
@@ -676,7 +672,7 @@
hideTransientIndication();
}
updateDeviceEntryIndication(false);
- } else if (!visible) {
+ } else {
// If we unlock and return to keyguard quickly, previous error should not be shown
hideTransientIndication();
}
@@ -764,7 +760,7 @@
* logic.
*/
private void showBiometricMessage(CharSequence biometricMessage,
- CharSequence biometricMessageFollowUp) {
+ @Nullable CharSequence biometricMessageFollowUp) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)) {
return;
}
@@ -1072,17 +1068,12 @@
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
&& msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
- final boolean isUnlockWithFingerprintPossible =
- mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- getCurrentUser());
+ final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
final boolean isCoExFaceAcquisitionMessage =
faceAuthSoftError && isUnlockWithFingerprintPossible;
if (isCoExFaceAcquisitionMessage && !mCoExFaceAcquisitionMsgIdsToShow.contains(msgId)) {
- if (DEBUG) {
- Log.d(TAG, "skip showing msgId=" + msgId + " helpString=" + helpString
- + ", due to co-ex logic");
- }
- return;
+ debugLog("skip showing msgId=" + msgId + " helpString=" + helpString
+ + ", due to co-ex logic");
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
mInitialTextColorState);
@@ -1120,74 +1111,45 @@
}
@Override
- public void onBiometricError(int msgId, String errString,
- BiometricSourceType biometricSourceType) {
- CharSequence deferredFaceMessage = null;
- if (biometricSourceType == FACE) {
- if (msgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
- deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
- if (DEBUG) {
- Log.d(TAG, "showDeferredFaceMessage msgId=" + deferredFaceMessage);
- }
- }
- mFaceAcquiredMessageDeferral.reset();
- }
-
- if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
- if (DEBUG) {
- Log.d(TAG, "suppressingBiometricError msgId=" + msgId
- + " source=" + biometricSourceType);
- }
- } else if (biometricSourceType == FACE && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
- // Co-ex: show deferred message OR nothing
- if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())) {
- // if we're on the lock screen (bouncer isn't showing), show the deferred msg
- if (deferredFaceMessage != null
- && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
- showBiometricMessage(
- deferredFaceMessage,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
- );
- return;
- }
-
- // otherwise, don't show any message
- if (DEBUG) {
- Log.d(TAG, "skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
- }
- return;
- }
-
- // Face-only: The face timeout message is not very actionable, let's ask the user to
- // manually retry.
- if (deferredFaceMessage != null) {
- showBiometricMessage(
- deferredFaceMessage,
- mContext.getString(R.string.keyguard_unlock)
- );
- } else {
- // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
- showActionToUnlock();
- }
- } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
- } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- showBiometricMessage(errString);
- } else {
- mBiometricErrorMessageToShowOnScreenOn = errString;
+ public void onLockedOutStateChanged(BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE && !mKeyguardUpdateMonitor.isFaceLockedOut()) {
+ mFaceLockedOutThisAuthSession = false;
}
}
- private boolean shouldSuppressBiometricError(int msgId,
- BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
- if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
- return shouldSuppressFingerprintError(msgId, updateMonitor);
- }
+ @Override
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) {
if (biometricSourceType == FACE) {
- return shouldSuppressFaceError(msgId, updateMonitor);
+ onFaceAuthError(msgId, errString);
+ } else if (biometricSourceType == FINGERPRINT) {
+ onFingerprintAuthError(msgId, errString);
}
- return false;
+ }
+
+ private void onFaceAuthError(int msgId, String errString) {
+ CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ mFaceAcquiredMessageDeferral.reset();
+ if (shouldSuppressFaceError(msgId, mKeyguardUpdateMonitor)) {
+ debugLog("suppressingFaceError msgId=" + msgId + " errString= " + errString);
+ return;
+ }
+ if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ handleFaceAuthTimeoutError(deferredFaceMessage);
+ } else if (isLockoutError(msgId)) {
+ handleFaceLockoutError(errString);
+ } else {
+ showErrorMessageNowOrLater(errString, null);
+ }
+ }
+
+ private void onFingerprintAuthError(int msgId, String errString) {
+ if (shouldSuppressFingerprintError(msgId, mKeyguardUpdateMonitor)) {
+ debugLog("suppressingFingerprintError msgId=" + msgId
+ + " errString= " + errString);
+ } else {
+ showErrorMessageNowOrLater(errString, null);
+ }
}
private boolean shouldSuppressFingerprintError(int msgId,
@@ -1197,7 +1159,7 @@
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
- && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ && !isLockoutError(msgId))
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|| msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
@@ -1286,7 +1248,82 @@
}
}
- private StatusBarStateController.StateListener mStatusBarStateListener =
+ private void handleFaceLockoutError(String errString) {
+ int followupMsgId = canUnlockWithFingerprint() ? R.string.keyguard_suggest_fingerprint
+ : R.string.keyguard_unlock;
+ String followupMessage = mContext.getString(followupMsgId);
+ // Lockout error can happen multiple times in a session because we trigger face auth
+ // even when it is locked out so that the user is aware that face unlock would have
+ // triggered but didn't because it is locked out.
+
+ // On first lockout we show the error message from FaceManager, which tells the user they
+ // had too many unsuccessful attempts.
+ if (!mFaceLockedOutThisAuthSession) {
+ mFaceLockedOutThisAuthSession = true;
+ showErrorMessageNowOrLater(errString, followupMessage);
+ } else if (!mAuthController.isUdfpsFingerDown()) {
+ // On subsequent lockouts, we show a more generic locked out message.
+ showBiometricMessage(mContext.getString(R.string.keyguard_face_unlock_unavailable),
+ followupMessage);
+ }
+ }
+
+ private static boolean isLockoutError(int msgId) {
+ return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
+ || msgId == FaceManager.FACE_ERROR_LOCKOUT;
+ }
+
+ private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
+ debugLog("showDeferredFaceMessage msgId=" + deferredFaceMessage);
+ if (canUnlockWithFingerprint()) {
+ // Co-ex: show deferred message OR nothing
+ // if we're on the lock screen (bouncer isn't showing), show the deferred msg
+ if (deferredFaceMessage != null
+ && !mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ } else {
+ // otherwise, don't show any message
+ debugLog("skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
+ }
+ } else if (deferredFaceMessage != null) {
+ // Face-only: The face timeout message is not very actionable, let's ask the
+ // user to manually retry.
+ showBiometricMessage(
+ deferredFaceMessage,
+ mContext.getString(R.string.keyguard_unlock)
+ );
+ } else {
+ // Face-only
+ // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
+ showActionToUnlock();
+ }
+ }
+
+ private boolean canUnlockWithFingerprint() {
+ return mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ KeyguardUpdateMonitor.getCurrentUser());
+ }
+
+ private void debugLog(String logMsg) {
+ if (DEBUG) {
+ Log.d(TAG, logMsg);
+ }
+ }
+
+ private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
+ } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
+ showBiometricMessage(errString, followUpMsg);
+ } else {
+ mBiometricErrorMessageToShowOnScreenOn = errString;
+ }
+ }
+
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onStateChanged(int newState) {
@@ -1307,7 +1344,7 @@
}
};
- private KeyguardStateController.Callback mKeyguardStateCallback =
+ private final KeyguardStateController.Callback mKeyguardStateCallback =
new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 408293c..815b86e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -805,7 +805,7 @@
iconState.hidden = isAppearing
|| (view instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow) view).isLowPriority()
- && mShelfIcons.hasMaxNumDot())
+ && mShelfIcons.areIconsOverflowing())
|| (transitionAmount == 0.0f && !iconState.isAnimating(icon))
|| row.isAboveShelf()
|| row.showingPulsing()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index 78b28d2..4897c52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -23,8 +23,6 @@
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.widget.FrameLayout
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.LockIconViewController
import com.android.systemui.R
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
@@ -53,20 +51,13 @@
private var ambientIndicationArea: View? = null
private lateinit var binding: KeyguardBottomAreaViewBinder.Binding
- private lateinit var lockIconViewController: LockIconViewController
/** Initializes the view. */
fun init(
viewModel: KeyguardBottomAreaViewModel,
falsingManager: FalsingManager,
- lockIconViewController: LockIconViewController,
) {
- binding = bind(
- this,
- viewModel,
- falsingManager,
- )
- this.lockIconViewController = lockIconViewController
+ binding = bind(this, viewModel, falsingManager)
}
/**
@@ -123,29 +114,4 @@
}
return insets
}
-
- override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
- super.onLayout(changed, left, top, right, bottom)
- findViewById<View>(R.id.ambient_indication_container)?.let {
- val (ambientLeft, ambientTop) = it.locationOnScreen
- if (binding.shouldConstrainToTopOfLockIcon()) {
- //make top of ambient indication view the bottom of the lock icon
- it.layout(
- ambientLeft,
- lockIconViewController.bottom.toInt(),
- right - ambientLeft,
- ambientTop + it.measuredHeight
- )
- } else {
- //make bottom of ambient indication view the top of the lock icon
- val lockLocationTop = lockIconViewController.top
- it.layout(
- ambientLeft,
- lockLocationTop.toInt() - it.measuredHeight,
- right - ambientLeft,
- lockLocationTop.toInt()
- )
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index c189ace..4ee2de1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -141,7 +141,6 @@
/* Maximum number of icons in short shelf on lockscreen when also showing overflow dot. */
public static final int MAX_ICONS_ON_LOCKSCREEN = 3;
public static final int MAX_STATIC_ICONS = 4;
- private static final int MAX_DOTS = 1;
private boolean mIsStaticLayout = true;
private final HashMap<View, IconState> mIconStates = new HashMap<>();
@@ -166,8 +165,7 @@
private IconState mLastVisibleIconState;
private IconState mFirstVisibleIconState;
private float mVisualOverflowStart;
- // Keep track of overflow in range [0, 3]
- private int mNumDots;
+ private boolean mIsShowingOverflowDot;
private StatusBarIconView mIsolatedIcon;
private Rect mIsolatedIconLocation;
private int[] mAbsolutePosition = new int[2];
@@ -387,8 +385,8 @@
}
}
- public boolean hasMaxNumDot() {
- return mNumDots >= MAX_DOTS;
+ public boolean areIconsOverflowing() {
+ return mIsShowingOverflowDot;
}
private boolean areAnimationsEnabled(StatusBarIconView icon) {
@@ -494,7 +492,7 @@
: 1f;
translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
}
- mNumDots = 0;
+ mIsShowingOverflowDot = false;
if (firstOverflowIndex != -1) {
translationX = mVisualOverflowStart;
for (int i = firstOverflowIndex; i < childCount; i++) {
@@ -502,15 +500,14 @@
IconState iconState = mIconStates.get(view);
int dotWidth = mStaticDotDiameter + mDotPadding;
iconState.setXTranslation(translationX);
- if (mNumDots < MAX_DOTS) {
- if (mNumDots == 0 && iconState.iconAppearAmount < 0.8f) {
+ if (!mIsShowingOverflowDot) {
+ if (iconState.iconAppearAmount < 0.8f) {
iconState.visibleState = StatusBarIconView.STATE_ICON;
} else {
iconState.visibleState = StatusBarIconView.STATE_DOT;
- mNumDots++;
+ mIsShowingOverflowDot = true;
}
- translationX += (mNumDots == MAX_DOTS ? MAX_DOTS * dotWidth : dotWidth)
- * iconState.iconAppearAmount;
+ translationX += dotWidth * iconState.iconAppearAmount;
mLastVisibleIconState = iconState;
} else {
iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
@@ -618,10 +615,6 @@
return Math.min(getWidth(), translation);
}
- private float getMaxOverflowStart() {
- return getLayoutEnd() - mIconSize;
- }
-
public void setChangingViewPositions(boolean changingViewPositions) {
mChangingViewPositions = changingViewPositions;
}
@@ -645,25 +638,6 @@
mSpeedBumpIndex = speedBumpIndex;
}
- public boolean hasOverflow() {
- return mNumDots > 0;
- }
-
- // Give some extra room for btw notifications if we can
- public int getNoOverflowExtraPadding() {
- if (mNumDots != 0) {
- return 0;
- }
-
- int collapsedPadding = mIconSize;
-
- if (collapsedPadding + getFinalTranslationX() > getWidth()) {
- collapsedPadding = getWidth() - getFinalTranslationX();
- }
-
- return collapsedPadding;
- }
-
public int getIconSize() {
return mIconSize;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java
index 9db207a..f3d183c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SafetyController.java
@@ -78,8 +78,7 @@
synchronized (mListeners) {
mListeners.add(listener);
if (mListeners.size() == 1) {
- mContext.registerReceiver(mPermControllerChangeReceiver, PKG_CHANGE_INTENT_FILTER,
- Context.RECEIVER_EXPORTED_UNAUDITED);
+ mContext.registerReceiver(mPermControllerChangeReceiver, PKG_CHANGE_INTENT_FILTER);
mBgHandler.post(() -> {
mSafetyCenterEnabled = mSafetyCenterManager.isSafetyCenterEnabled();
listener.onSafetyCenterEnableChanged(isSafetyCenterEnabled());
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 637fac0..4cb41f3 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -22,7 +22,6 @@
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.os.PowerManager
-import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -35,6 +34,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.wakelock.WakeLock
/**
* A generic controller that can temporarily display a new view in a new window.
@@ -54,6 +54,7 @@
private val configurationController: ConfigurationController,
private val powerManager: PowerManager,
@LayoutRes private val viewLayoutRes: Int,
+ private val wakeLockBuilder: WakeLock.Builder,
) : CoreStartable {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
@@ -64,7 +65,8 @@
height = WindowManager.LayoutParams.WRAP_CONTENT
type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
- WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
format = PixelFormat.TRANSLUCENT
setTrustedOverlay()
}
@@ -84,6 +86,15 @@
private var cancelViewTimeout: Runnable? = null
/**
+ * A wakelock that is acquired when view is displayed and screen off,
+ * then released when view is removed.
+ */
+ private var wakeLock: WakeLock? = null
+
+ /** A string that keeps track of wakelock reason once it is acquired till it gets released */
+ private var wakeReasonAcquired: String? = null
+
+ /**
* Displays the view with the provided [newInfo].
*
* This method handles inflating and attaching the view, then delegates to [updateView] to
@@ -113,11 +124,15 @@
// the view to show over the dream state, so we should only wake up if the screen is
// completely off.)
if (!powerManager.isScreenOn) {
- powerManager.wakeUp(
- SystemClock.uptimeMillis(),
- PowerManager.WAKE_REASON_APPLICATION,
- "com.android.systemui:${newInfo.wakeReason}",
- )
+ wakeLock = wakeLockBuilder
+ .setTag(newInfo.windowTitle)
+ .setLevelsAndFlags(
+ PowerManager.FULL_WAKE_LOCK or
+ PowerManager.ACQUIRE_CAUSES_WAKEUP
+ )
+ .build()
+ wakeLock?.acquire(newInfo.wakeReason)
+ wakeReasonAcquired = newInfo.wakeReason
}
logger.logViewAddition(newInfo.windowTitle)
inflateAndUpdateView(newInfo)
@@ -155,6 +170,7 @@
it.copyFrom(windowLayoutParams)
it.title = newInfo.windowTitle
}
+ newView.keepScreenOn = true
windowManager.addView(newView, paramsWithTitle)
animateViewIn(newView)
}
@@ -183,7 +199,10 @@
val currentDisplayInfo = displayInfo ?: return
val currentView = currentDisplayInfo.view
- animateViewOut(currentView) { windowManager.removeView(currentView) }
+ animateViewOut(currentView) {
+ windowManager.removeView(currentView)
+ wakeLock?.release(wakeReasonAcquired)
+ }
logger.logViewRemoval(removalReason)
configurationController.removeCallback(displayScaleListener)
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 87b6e8d..44e5ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -44,6 +44,7 @@
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLock
import javax.inject.Inject
/**
@@ -75,6 +76,7 @@
private val falsingCollector: FalsingCollector,
private val viewUtil: ViewUtil,
private val vibratorHelper: VibratorHelper,
+ wakeLockBuilder: WakeLock.Builder,
) : TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
logger,
@@ -84,6 +86,7 @@
configurationController,
powerManager,
R.layout.chipbar,
+ wakeLockBuilder,
) {
private lateinit var parent: ChipbarRootView
@@ -92,8 +95,6 @@
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
}
- override fun start() {}
-
override fun updateView(
newInfo: ChipbarInfo,
currentView: ViewGroup
@@ -192,6 +193,8 @@
)
}
+ override fun start() {}
+
override fun getTouchableRegion(view: View, outRect: Rect) {
viewUtil.setRectToViewWindowLocation(view, outRect)
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index f017126..b56c403 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -25,6 +25,8 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -59,6 +61,7 @@
private final ActivityStarter mActivityStarter;
private Dialog mSetupUserDialog;
+ private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
@Inject
public CreateUserActivity(UserCreator userCreator,
@@ -82,6 +85,10 @@
mSetupUserDialog = createDialog();
mSetupUserDialog.show();
+
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mBackCallback);
}
@Override
@@ -125,10 +132,20 @@
@Override
public void onBackPressed() {
- super.onBackPressed();
+ onBackInvoked();
+ }
+
+ private void onBackInvoked() {
if (mSetupUserDialog != null) {
mSetupUserDialog.dismiss();
}
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
+ super.onDestroy();
}
private void addUserNow(String userName, Drawable userIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 6a23260..ffaf524 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -250,6 +250,10 @@
override fun onUserChanged(newUser: Int, userContext: Context) {
send()
}
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ send()
+ }
}
tracker.addCallback(callback, mainDispatcher.asExecutor())
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index 8d77c4a..f320d07 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -38,6 +38,11 @@
long DEFAULT_MAX_TIMEOUT = 20000;
/**
+ * Default wake-lock levels and flags.
+ */
+ int DEFAULT_LEVELS_AND_FLAGS = PowerManager.PARTIAL_WAKE_LOCK;
+
+ /**
* @param why A tag that will be saved for sysui dumps.
* @see android.os.PowerManager.WakeLock#acquire()
**/
@@ -60,13 +65,21 @@
* Creates a {@link WakeLock} that has a default release timeout.
* @see android.os.PowerManager.WakeLock#acquire(long) */
static WakeLock createPartial(Context context, String tag, long maxTimeout) {
- return wrap(createPartialInner(context, tag), maxTimeout);
+ return wrap(createWakeLockInner(context, tag, DEFAULT_LEVELS_AND_FLAGS), maxTimeout);
+ }
+
+ /**
+ * Creates a {@link WakeLock} that has a default release timeout and flags.
+ */
+ static WakeLock createWakeLock(Context context, String tag, int flags, long maxTimeout) {
+ return wrap(createWakeLockInner(context, tag, flags), maxTimeout);
}
@VisibleForTesting
- static PowerManager.WakeLock createPartialInner(Context context, String tag) {
+ static PowerManager.WakeLock createWakeLockInner(
+ Context context, String tag, int levelsAndFlags) {
return context.getSystemService(PowerManager.class)
- .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag);
+ .newWakeLock(levelsAndFlags, tag);
}
static Runnable wrapImpl(WakeLock w, Runnable r) {
@@ -131,6 +144,7 @@
class Builder {
private final Context mContext;
private String mTag;
+ private int mLevelsAndFlags = DEFAULT_LEVELS_AND_FLAGS;
private long mMaxTimeout = DEFAULT_MAX_TIMEOUT;
@Inject
@@ -143,13 +157,18 @@
return this;
}
+ public Builder setLevelsAndFlags(int levelsAndFlags) {
+ this.mLevelsAndFlags = levelsAndFlags;
+ return this;
+ }
+
public Builder setMaxTimeout(long maxTimeout) {
this.mMaxTimeout = maxTimeout;
return this;
}
public WakeLock build() {
- return WakeLock.createPartial(mContext, mTag, mMaxTimeout);
+ return WakeLock.createWakeLock(mContext, mTag, mLevelsAndFlags, mMaxTimeout);
}
}
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index c55ee61..e52225b 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -106,6 +106,12 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true" />
+ <activity android:name=".user.CreateUserActivityTest$CreateUserActivityTestable"
+ android:exported="false"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true" />
+
<provider
android:name="androidx.startup.InitializationProvider"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 5718835..717aa66 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -1620,7 +1620,7 @@
}
@Test
- public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
+ public void testShouldListenForFace_whenFaceIsLockedOut_returnsTrue()
throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
@@ -1637,7 +1637,9 @@
faceAuthLockedOut();
mTestableLooper.processAllMessages();
- assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ // This is needed beccause we want to show face locked out error message whenever face auth
+ // is supposed to run.
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
deleted file mode 100644
index 250cc48..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.flags;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.util.Pair;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Test;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@SmallTest
-public class FlagsTest extends SysuiTestCase {
-
- @Test
- public void testDuplicateFlagIdCheckWorks() {
- List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
- Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
-
- assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size()).isEqualTo(2);
- }
-
- @Test
- public void testNoDuplicateFlagIds() {
- List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
- Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
-
- assertWithMessage(generateAssertionMessage(duplicates))
- .that(duplicates.size()).isEqualTo(0);
- }
-
- private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append("Duplicate flag keys found: {");
- for (int id : duplicates.keySet()) {
- stringBuilder
- .append(" ")
- .append(id)
- .append(": [")
- .append(String.join(", ", duplicates.get(id)))
- .append("]");
- }
- stringBuilder.append(" }");
-
- return stringBuilder.toString();
- }
-
- private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
- List<Pair<String, Flag<?>>> flags = new ArrayList<>();
-
- Field[] fields = clz.getFields();
-
- for (Field field : fields) {
- Class<?> t = field.getType();
- if (Flag.class.isAssignableFrom(t)) {
- try {
- flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
- } catch (IllegalAccessException e) {
- // no-op
- }
- }
- }
-
- return flags;
- }
-
- private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
- Map<Integer, List<String>> grouping = new HashMap<>();
-
- for (Pair<String, Flag<?>> flag : flags) {
- grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
- grouping.get(flag.second.getId()).add(flag.first);
- }
-
- Map<Integer, List<String>> result = new HashMap<>();
- for (Integer id : grouping.keySet()) {
- if (grouping.get(id).size() > 1) {
- result.put(id, grouping.get(id));
- }
- }
-
- return result;
- }
-
- private static class DuplicateFlagContainer {
- public static final BooleanFlag A_FLAG = new UnreleasedFlag(0);
- public static final BooleanFlag B_FLAG = new UnreleasedFlag(0);
- public static final StringFlag C_FLAG = new StringFlag(0);
-
- public static final BooleanFlag D_FLAG = new UnreleasedFlag(1);
-
- public static final DoubleFlag E_FLAG = new DoubleFlag(3);
- public static final DoubleFlag F_FLAG = new DoubleFlag(3);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt
new file mode 100644
index 0000000..2b556f1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.flags
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth
+import java.lang.StringBuilder
+import java.util.ArrayList
+import java.util.HashMap
+import org.junit.Test
+
+@SmallTest
+class FlagsTest : SysuiTestCase() {
+ @Test
+ fun testDuplicateFlagIdCheckWorks() {
+ val flags = Flags.collectFlagsInClass(DuplicateFlagContainer)
+ val duplicates = groupDuplicateFlags(flags)
+ Truth.assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size)
+ .isEqualTo(2)
+ }
+
+ @Test
+ fun testNoDuplicateFlagIds() {
+ val flags = Flags.collectFlagsInClass(Flags)
+ val duplicates = groupDuplicateFlags(flags)
+ Truth.assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size)
+ .isEqualTo(0)
+ }
+
+ private fun generateAssertionMessage(duplicates: Map<Int, List<String>>): String {
+ val stringBuilder = StringBuilder()
+ stringBuilder.append("Duplicate flag keys found: {")
+ for (id in duplicates.keys) {
+ stringBuilder
+ .append(" ")
+ .append(id)
+ .append(": [")
+ .append(java.lang.String.join(", ", duplicates[id]))
+ .append("]")
+ }
+ stringBuilder.append(" }")
+ return stringBuilder.toString()
+ }
+
+ private fun groupDuplicateFlags(flags: Map<String, Flag<*>>): Map<Int, List<String>> {
+ val grouping: MutableMap<Int, MutableList<String>> = HashMap()
+ for (flag in flags) {
+ grouping.putIfAbsent(flag.value.id, ArrayList())
+ grouping[flag.value.id]!!.add(flag.key)
+ }
+ val result: MutableMap<Int, List<String>> = HashMap()
+ for (id in grouping.keys) {
+ if (grouping[id]!!.size > 1) {
+ result[id] = grouping[id]!!
+ }
+ }
+ return result
+ }
+
+ private object DuplicateFlagContainer {
+ val A_FLAG: BooleanFlag = UnreleasedFlag(0)
+ val B_FLAG: BooleanFlag = UnreleasedFlag(0)
+ val C_FLAG = StringFlag(0)
+ val D_FLAG: BooleanFlag = UnreleasedFlag(1)
+ val E_FLAG = DoubleFlag(3)
+ val F_FLAG = DoubleFlag(3)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 7bc39c7..53d9b87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.data.repository
import androidx.test.filters.SmallTest
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
@@ -49,7 +48,6 @@
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
private lateinit var underTest: KeyguardRepositoryImpl
@@ -60,12 +58,11 @@
underTest =
KeyguardRepositoryImpl(
- statusBarStateController,
- dozeHost,
- wakefulnessLifecycle,
- biometricUnlockController,
- keyguardStateController,
- keyguardUpdateMonitor,
+ statusBarStateController,
+ keyguardStateController,
+ dozeHost,
+ wakefulnessLifecycle,
+ biometricUnlockController,
)
}
@@ -226,15 +223,6 @@
}
@Test
- fun isUdfpsSupported() = runBlockingTest {
- whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
- assertThat(underTest.isUdfpsSupported()).isTrue()
-
- whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
- assertThat(underTest.isUdfpsSupported()).isFalse()
- }
-
- @Test
fun isBouncerShowing() = runBlockingTest {
whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 8c3ae3d..68a5f47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -43,6 +43,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -85,6 +86,10 @@
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var uiEventLoggerFake: UiEventLoggerFake
private lateinit var receiverUiEventLogger: MediaTttReceiverUiEventLogger
+ private lateinit var fakeClock: FakeSystemClock
+ private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
@Before
fun setUp() {
@@ -99,15 +104,22 @@
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
+ fakeClock = FakeSystemClock()
+ fakeExecutor = FakeExecutor(fakeClock)
+
uiEventLoggerFake = UiEventLoggerFake()
receiverUiEventLogger = MediaTttReceiverUiEventLogger(uiEventLoggerFake)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
controllerReceiver = MediaTttChipControllerReceiver(
commandQueue,
context,
logger,
windowManager,
- FakeExecutor(FakeSystemClock()),
+ fakeExecutor,
accessibilityManager,
configurationController,
powerManager,
@@ -115,6 +127,7 @@
mediaTttFlags,
receiverUiEventLogger,
viewUtil,
+ fakeWakeLockBuilder,
)
controllerReceiver.start()
@@ -141,6 +154,7 @@
mediaTttFlags,
receiverUiEventLogger,
viewUtil,
+ fakeWakeLockBuilder,
)
controllerReceiver.start()
@@ -200,6 +214,39 @@
}
@Test
+ fun commandQueueCallback_closeThenFar_wakeLockAcquiredThenReleased() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo,
+ null,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
+ fun commandQueueCallback_closeThenFar_wakeLockNeverAcquired() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo,
+ null,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
fun receivesNewStateFromCommandQueue_isLogged() {
commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index ad19bc2..4437394 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -52,6 +52,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -89,6 +90,8 @@
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var vibratorHelper: VibratorHelper
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
private lateinit var chipbarCoordinator: ChipbarCoordinator
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -118,6 +121,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
uiEventLoggerFake = UiEventLoggerFake()
uiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
@@ -134,6 +141,7 @@
falsingCollector,
viewUtil,
vibratorHelper,
+ fakeWakeLockBuilder,
)
chipbarCoordinator.start()
@@ -472,6 +480,36 @@
}
@Test
+ fun commandQueueCallback_almostCloseThenFarFromReceiver_wakeLockAcquiredThenReleased() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ routeInfo,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
+ fun commandQueueCallback_FarFromReceiver_wakeLockNeverReleased() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
fun commandQueueCallback_invalidStateParam_noChipShown() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(100, routeInfo, null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index bd4b94e..52462c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -311,6 +311,37 @@
}
@Test
+ fun testCallbackCalledOnUserInfoChanged() {
+ tracker.initialize(0)
+ val callback = TestCallback()
+ tracker.addCallback(callback, executor)
+ val profileID = tracker.userId + 10
+
+ `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+ val id = invocation.getArgument<Int>(0)
+ val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+ val infoProfile = UserInfo(
+ id + 10,
+ "",
+ "",
+ UserInfo.FLAG_MANAGED_PROFILE,
+ UserManager.USER_TYPE_PROFILE_MANAGED
+ )
+ infoProfile.profileGroupId = id
+ listOf(info, infoProfile)
+ }
+
+ val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+ tracker.onReceive(context, intent)
+
+ assertThat(callback.calledOnUserChanged).isEqualTo(0)
+ assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+ assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
+ }
+
+ @Test
fun testCallbackRemoved() {
tracker.initialize(0)
val newID = 5
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index ec5d089..c658593 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -20,6 +20,7 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
@@ -54,6 +55,7 @@
import static org.mockito.Mockito.reset;
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.app.Instrumentation;
@@ -88,6 +90,7 @@
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -173,6 +176,8 @@
private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private AuthController mAuthController;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@Captor
@@ -263,8 +268,9 @@
mWakeLockBuilder,
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
- mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
- mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager,
+ mUserManager, mExecutor, mExecutor, mFalsingManager,
+ mAuthController, mLockPatternUtils, mScreenLifecycle,
+ mKeyguardBypassController, mAccessibilityManager,
mFaceHelpMessageDeferral);
mController.init();
mController.setIndicationArea(mIndicationArea);
@@ -1374,6 +1380,110 @@
}
+ @Test
+ public void onBiometricError_faceLockedOutFirstTime_showsThePassedInMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "first lockout");
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutFirstTimeAndFpAllowed_showsTheFpFollowupMessage() {
+ createController();
+ fingerprintUnlockIsPossible();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutFirstTimeAndFpNotAllowed_showsDefaultFollowup() {
+ createController();
+ fingerprintUnlockIsNotPossible();
+ onFaceLockoutError("first lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutSecondTimeInSession_showsUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_unlock_unavailable));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutSecondTimeButUdfpsActive_showsNoMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ when(mAuthController.isUdfpsFingerDown()).thenReturn(true);
+ onFaceLockoutError("second lockout");
+
+ verifyNoMoreInteractions(mRotateTextViewController);
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutAgainAndFpAllowed_showsTheFpFollowupMessage() {
+ createController();
+ fingerprintUnlockIsPossible();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
+ public void onBiometricError_faceLockedOutAgainAndFpNotAllowed_showsDefaultFollowup() {
+ createController();
+ fingerprintUnlockIsNotPossible();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void onBiometricError_whenFaceLockoutReset_onLockOutError_showsPassedInMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(false);
+ mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "second lockout");
+ }
+
+ @Test
+ public void onBiometricError_whenFaceIsLocked_onMultipleLockOutErrors_showUnavailableMessage() {
+ createController();
+ onFaceLockoutError("first lockout");
+ clearInvocations(mRotateTextViewController);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+ mKeyguardUpdateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE);
+
+ onFaceLockoutError("second lockout");
+
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_unlock_unavailable));
+ }
private void sendUpdateDisclosureBroadcast() {
mBroadcastReceiver.onReceive(mContext, new Intent());
@@ -1422,4 +1532,33 @@
anyObject(), anyBoolean());
}
}
+
+ private void verifyIndicationShown(int indicationType, String message) {
+ verify(mRotateTextViewController)
+ .updateIndication(eq(indicationType),
+ mKeyguardIndicationCaptor.capture(),
+ eq(true));
+ assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
+ .isEqualTo(message);
+ }
+
+ private void fingerprintUnlockIsNotPossible() {
+ setupFingerprintUnlockPossible(false);
+ }
+
+ private void fingerprintUnlockIsPossible() {
+ setupFingerprintUnlockPossible(true);
+ }
+
+ private void setupFingerprintUnlockPossible(boolean possible) {
+ when(mKeyguardUpdateMonitor
+ .getCachedIsUnlockWithFingerprintPossible(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(possible);
+ }
+
+ private void onFaceLockoutError(String errMsg) {
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_LOCKOUT_PERMANENT,
+ errMsg,
+ BiometricSourceType.FACE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 086e5df..b80b825 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -92,7 +92,7 @@
iconContainer.calculateIconXTranslations()
assertEquals(10f, iconState.xTranslation)
- assertFalse(iconContainer.hasOverflow())
+ assertFalse(iconContainer.areIconsOverflowing())
}
@Test
@@ -121,7 +121,7 @@
assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
assertEquals(40f, iconContainer.getIconState(iconFour).xTranslation)
- assertFalse(iconContainer.hasOverflow())
+ assertFalse(iconContainer.areIconsOverflowing())
}
@Test
@@ -150,7 +150,7 @@
assertEquals(10f, iconContainer.getIconState(iconOne).xTranslation)
assertEquals(20f, iconContainer.getIconState(iconTwo).xTranslation)
assertEquals(30f, iconContainer.getIconState(iconThree).xTranslation)
- assertTrue(iconContainer.hasOverflow())
+ assertTrue(iconContainer.areIconsOverflowing())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 91b5c35..9dea48e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -35,6 +35,8 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.wakelock.WakeLock
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -53,6 +55,9 @@
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
+
@Mock
private lateinit var logger: TemporaryViewLogger
@Mock
@@ -74,6 +79,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
underTest = TestController(
context,
logger,
@@ -82,7 +91,9 @@
accessibilityManager,
configurationController,
powerManager,
+ fakeWakeLockBuilder,
)
+ underTest.start()
}
@Test
@@ -112,25 +123,33 @@
}
@Test
- fun displayView_screenOff_screenWakes() {
- whenever(powerManager.isScreenOn).thenReturn(false)
-
+ fun displayView_screenOff_wakeLockAcquired() {
underTest.displayView(getState())
- verify(powerManager).wakeUp(any(), any(), any())
+ assertThat(fakeWakeLock.isHeld).isTrue()
}
@Test
- fun displayView_screenAlreadyOn_screenNotWoken() {
+ fun displayView_screenAlreadyOn_wakeLockNotAcquired() {
whenever(powerManager.isScreenOn).thenReturn(true)
underTest.displayView(getState())
- verify(powerManager, never()).wakeUp(any(), any(), any())
+ assertThat(fakeWakeLock.isHeld).isFalse()
}
@Test
- fun displayView_twiceWithSameWindowTitle_viewNotAddedTwice() {
+ fun displayView_screenOff_wakeLockCanBeReleasedAfterTimeOut() {
+ underTest.displayView(getState())
+ assertThat(fakeWakeLock.isHeld).isTrue()
+
+ fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+ assertThat(fakeWakeLock.isHeld).isFalse()
+ }
+
+ @Test
+ fun displayView_twice_viewNotAddedTwice() {
underTest.displayView(getState())
reset(windowManager)
@@ -269,6 +288,7 @@
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
+ wakeLockBuilder: WakeLock.Builder,
) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger>(
context,
logger,
@@ -278,13 +298,12 @@
configurationController,
powerManager,
R.layout.chipbar,
+ wakeLockBuilder,
) {
var mostRecentViewInfo: ViewInfo? = null
override val windowLayoutParams = commonWindowLayoutParams
- override fun start() {}
-
override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) {
mostRecentViewInfo = newInfo
}
@@ -292,6 +311,8 @@
override fun getTouchableRegion(view: View, outRect: Rect) {
outRect.setEmpty()
}
+
+ override fun start() {}
}
inner class ViewInfo(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index f643973..8e37aa2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -43,6 +43,7 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLockFake
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -69,6 +70,8 @@
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var vibratorHelper: VibratorHelper
+ private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
+ private lateinit var fakeWakeLock: WakeLockFake
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -81,6 +84,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+ fakeWakeLock = WakeLockFake()
+ fakeWakeLockBuilder = WakeLockFake.Builder(context)
+ fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
+
uiEventLoggerFake = UiEventLoggerFake()
underTest =
@@ -96,6 +103,7 @@
falsingCollector,
viewUtil,
vibratorHelper,
+ fakeWakeLockBuilder,
)
underTest.start()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
index 574f70e..beedf9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLock
/** A fake implementation of [ChipbarCoordinator] for testing. */
class FakeChipbarCoordinator(
@@ -41,6 +42,7 @@
falsingCollector: FalsingCollector,
viewUtil: ViewUtil,
vibratorHelper: VibratorHelper,
+ wakeLockBuilder: WakeLock.Builder,
) :
ChipbarCoordinator(
context,
@@ -54,6 +56,7 @@
falsingCollector,
viewUtil,
vibratorHelper,
+ wakeLockBuilder,
) {
override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
// Just bypass the animation in tests
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
new file mode 100644
index 0000000..51afbcb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
@@ -0,0 +1,55 @@
+package com.android.systemui.user
+
+import android.app.Dialog
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class CreateUserActivityTest : SysuiTestCase() {
+ open class CreateUserActivityTestable :
+ CreateUserActivity(
+ /* userCreator = */ mock(),
+ /* editUserInfoController = */ mock {
+ val dialog: Dialog = mock()
+ whenever(
+ createDialog(
+ /* activity = */ nullable(),
+ /* activityStarter = */ nullable(),
+ /* oldUserIcon = */ nullable(),
+ /* defaultUserName = */ nullable(),
+ /* title = */ nullable(),
+ /* successCallback = */ nullable(),
+ /* cancelCallback = */ nullable()
+ )
+ )
+ .thenReturn(dialog)
+ },
+ /* activityManager = */ mock(),
+ /* activityStarter = */ mock(),
+ )
+
+ @get:Rule val activityRule = ActivityScenarioRule(CreateUserActivityTestable::class.java)
+
+ @Test
+ fun onBackPressed_finishActivity() {
+ activityRule.scenario.onActivity { activity ->
+ assertThat(activity.isFinishing).isFalse()
+
+ activity.onBackPressed()
+
+ assertThat(activity.isFinishing).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
index 525d837..7c7f0e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplRefactoredTest.kt
@@ -145,6 +145,25 @@
assertThat(userInfos).isEqualTo(expectedUsers)
}
+ @Test
+ fun `userTrackerCallback - updates selectedUserInfo`() = runSelfCancelingTest {
+ underTest = create(this)
+ var selectedUserInfo: UserInfo? = null
+ underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 0,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id == 0)
+ setUpUsers(
+ count = 2,
+ selectedIndex = 1,
+ )
+ tracker.onProfileChanged()
+ assertThat(selectedUserInfo?.id == 1)
+ }
+
private fun setUpUsers(
count: Int,
isLastGuestUser: Boolean = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index fe01f84..6e109ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -42,7 +42,9 @@
@Before
public void setUp() {
- mInner = WakeLock.createPartialInner(mContext, WakeLockTest.class.getName());
+ mInner = WakeLock.createWakeLockInner(mContext,
+ WakeLockTest.class.getName(),
+ PowerManager.PARTIAL_WAKE_LOCK);
mWakeLock = WakeLock.wrap(mInner, 20000);
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index c132537..a35427f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -27,9 +27,8 @@
private val listenerFlagIds = mutableMapOf<FlagListenable.Listener, MutableSet<Int>>()
init {
- Flags.flagFields.forEach { field ->
- val flag: Flag<*> = field.get(null) as Flag<*>
- knownFlagNames[flag.id] = field.name
+ Flags.flagFields.forEach { entry: Map.Entry<String, Flag<*>> ->
+ knownFlagNames[entry.value.id] = entry.key
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 6f70f0e..627bd09 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -53,8 +53,6 @@
private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
- private val _isUdfpsSupported = MutableStateFlow(false)
-
private val _isBouncerShowing = MutableStateFlow(false)
override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
@@ -88,8 +86,4 @@
fun setDozeAmount(dozeAmount: Float) {
_dozeAmount.value = dozeAmount
}
-
- override fun isUdfpsSupported(): Boolean {
- return _isUdfpsSupported.value
- }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 9726bf8..a7eadba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -68,4 +68,8 @@
callbacks.forEach { it.onUserChanged(_userId, userContext) }
}
+
+ fun onProfileChanged() {
+ callbacks.forEach { it.onProfilesChanged(_userProfiles) }
+ }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index fcfee5b..6b6351f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -802,12 +802,20 @@
break;
}
case H_CLOUD_MEDIA_PROVIDER_CHANGED: {
- final Object listener = msg.obj;
- if (listener instanceof StorageManagerInternal.CloudProviderChangeListener) {
- notifyCloudMediaProviderChangedAsync(
- (StorageManagerInternal.CloudProviderChangeListener) listener);
+ // We send this message in two cases:
+ // 1. After the cloud provider has been set/updated for a user.
+ // In this case Message's #arg1 is set to UserId, and #obj is set to the
+ // authority of the new cloud provider.
+ // 2. After a new CloudProviderChangeListener is registered.
+ // In this case Message's #obj is set to the CloudProviderChangeListener.
+ if (msg.obj instanceof StorageManagerInternal.CloudProviderChangeListener) {
+ final StorageManagerInternal.CloudProviderChangeListener listener =
+ (StorageManagerInternal.CloudProviderChangeListener) msg.obj;
+ notifyCloudMediaProviderChangedAsync(listener);
} else {
- onCloudMediaProviderChangedAsync(msg.arg1);
+ final int userId = msg.arg1;
+ final String authority = (String) msg.obj;
+ onCloudMediaProviderChangedAsync(userId, authority);
}
break;
}
@@ -1686,17 +1694,15 @@
@NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
synchronized (mCloudMediaProviders) {
for (int i = mCloudMediaProviders.size() - 1; i >= 0; --i) {
- listener.onCloudProviderChanged(
- mCloudMediaProviders.keyAt(i), mCloudMediaProviders.valueAt(i));
+ final int userId = mCloudMediaProviders.keyAt(i);
+ final String authority = mCloudMediaProviders.valueAt(i);
+ listener.onCloudProviderChanged(userId, authority);
}
}
}
- private void onCloudMediaProviderChangedAsync(int userId) {
- final String authority;
- synchronized (mCloudMediaProviders) {
- authority = mCloudMediaProviders.get(userId);
- }
+ private void onCloudMediaProviderChangedAsync(
+ @UserIdInt int userId, @Nullable String authority) {
for (StorageManagerInternal.CloudProviderChangeListener listener :
mStorageManagerInternal.mCloudProviderChangeListeners) {
listener.onCloudProviderChanged(userId, authority);
@@ -4831,7 +4837,7 @@
public void registerCloudProviderChangeListener(
@NonNull StorageManagerInternal.CloudProviderChangeListener listener) {
mCloudProviderChangeListeners.add(listener);
- mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener);
+ mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener).sendToTarget();
}
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 417a0e5..9d96008 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -175,7 +175,7 @@
*/
public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis";
- private static final long DEFAULT_DELAY_CACHED_MILLIS = 0;
+ private static final long DEFAULT_DELAY_CACHED_MILLIS = +30_000;
/**
* For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 33e4070..438a2d43 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -567,6 +567,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@Override
public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
+ super.getCurrentStats_enforcePermission();
+
Parcel current = Parcel.obtain();
synchronized (mLock) {
long now = SystemClock.uptimeMillis();
@@ -623,6 +625,8 @@
public long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
List<ParcelFileDescriptor> committedStats, ProcessStats mergedStats) {
+ super.getCommittedStatsMerged_enforcePermission();
+
long newHighWaterMark = highWaterMarkMs;
mFileLock.lock();
try {
@@ -709,6 +713,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@Override
public ParcelFileDescriptor getStatsOverTime(long minTime) {
+ super.getStatsOverTime_enforcePermission();
+
Parcel current = Parcel.obtain();
long curTime;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 33d3b64..2f147c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -200,6 +200,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void scheduleWatchdog() {
+ super.scheduleWatchdog_enforcePermission();
+
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for scheduling watchdog");
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 70470e9..48367b2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -960,6 +960,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public void scheduleWatchdog() {
+ super.scheduleWatchdog_enforcePermission();
+
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for scheduling watchdog");
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 726326f..a5e5c24 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -526,22 +526,28 @@
ranges, ranges);
}
- if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
- || primarySummary.disableRefreshRateSwitching) {
+ boolean modeSwitchingDisabled =
+ mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
+ || mModeSwitchingType
+ == DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
+
+ if (modeSwitchingDisabled || primarySummary.disableRefreshRateSwitching) {
float fps = baseMode.getRefreshRate();
primarySummary.minPhysicalRefreshRate = primarySummary.maxPhysicalRefreshRate = fps;
- if (mRenderFrameRateIsPhysicalRefreshRate) {
- primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps;
- }
- if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
- primarySummary.minRenderFrameRate = primarySummary.maxRenderFrameRate = fps;
+ if (modeSwitchingDisabled) {
appRequestSummary.minPhysicalRefreshRate =
appRequestSummary.maxPhysicalRefreshRate = fps;
- appRequestSummary.minRenderFrameRate =
- appRequestSummary.maxRenderFrameRate = fps;
}
}
+ if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
+ || mRenderFrameRateIsPhysicalRefreshRate) {
+ primarySummary.minRenderFrameRate = primarySummary.minPhysicalRefreshRate;
+ primarySummary.maxRenderFrameRate = primarySummary.maxPhysicalRefreshRate;
+ appRequestSummary.minRenderFrameRate = appRequestSummary.minPhysicalRefreshRate;
+ appRequestSummary.maxRenderFrameRate = appRequestSummary.maxPhysicalRefreshRate;
+ }
+
boolean allowGroupSwitching =
mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
@@ -842,6 +848,8 @@
return "SWITCHING_TYPE_WITHIN_GROUPS";
case DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS";
+ case DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY:
+ return "SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY";
default:
return "Unknown SwitchingType " + type;
}
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 1fa56bc..2d015a5d 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -25,6 +25,7 @@
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementsEvent;
import android.location.GnssNavigationMessage;
+import android.location.GnssSignalType;
import android.location.GnssStatus;
import android.location.Location;
import android.os.Binder;
@@ -1144,6 +1145,13 @@
onCapabilitiesChanged(oldCapabilities, mCapabilities);
}
+ @NativeEntryPoint
+ void setSignalTypeCapabilities(List<GnssSignalType> signalTypes) {
+ GnssCapabilities oldCapabilities = mCapabilities;
+ mCapabilities = oldCapabilities.withSignalTypes(signalTypes);
+ onCapabilitiesChanged(oldCapabilities, mCapabilities);
+ }
+
private void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
Binder.withCleanCallingIdentity(() -> {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index d1d39af..6ff2bb3 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
@@ -41,7 +40,6 @@
import android.window.BackNavigationInfo;
import android.window.IBackAnimationFinishedCallback;
import android.window.OnBackInvokedCallbackInfo;
-import android.window.ScreenCapture;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -269,10 +267,11 @@
removedWindowContainer,
BackNavigationInfo.typeToString(backType));
- // For now, we only animate when going home and cross task.
+ // For now, we only animate when going home, cross task or cross-activity.
boolean prepareAnimation =
(backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
- || backType == BackNavigationInfo.TYPE_CROSS_TASK)
+ || backType == BackNavigationInfo.TYPE_CROSS_TASK
+ || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY)
&& adapter != null;
// Only prepare animation if no leash has been created (no animation is running).
@@ -336,6 +335,7 @@
RemoteAnimationTarget behindAppTarget = null;
if (needsScreenshot(backType)) {
HardwareBuffer screenshotBuffer = null;
+ Task backTargetTask = prevTask;
switch(backType) {
case BackNavigationInfo.TYPE_CROSS_TASK:
int prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
@@ -343,14 +343,10 @@
screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
break;
case BackNavigationInfo.TYPE_CROSS_ACTIVITY:
- //TODO(207481538) Remove once the infrastructure to support per-activity
- // screenshot is implemented. For now we simply have the mBackScreenshots hash
- // map that dumbly saves the screenshots.
- if (prevActivity != null
- && prevActivity.mActivityComponent != null) {
- screenshotBuffer =
- getActivitySnapshot(currentTask, prevActivity.mActivityComponent);
+ if (prevActivity != null && prevActivity.mActivityComponent != null) {
+ screenshotBuffer = getActivitySnapshot(currentTask, prevActivity);
}
+ backTargetTask = currentTask;
break;
}
@@ -369,8 +365,9 @@
// leash needs to be added before to be in the synchronized block.
startedTransaction.setLayer(topAppTarget.leash, 1);
- behindAppTarget = createRemoteAnimationTargetLocked(
- prevTask, screenshotSurface, MODE_OPENING);
+ behindAppTarget =
+ createRemoteAnimationTargetLocked(
+ backTargetTask, screenshotSurface, MODE_OPENING);
// reset leash after animation finished.
leashes.add(screenshotSurface);
@@ -543,14 +540,8 @@
mShowWallpaper = false;
}
- private HardwareBuffer getActivitySnapshot(@NonNull Task task,
- ComponentName activityComponent) {
- // Check if we have a screenshot of the previous activity, indexed by its
- // component name.
- ScreenCapture.ScreenshotHardwareBuffer backBuffer = task.mBackScreenshots
- .get(activityComponent.flattenToString());
- return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
-
+ private HardwareBuffer getActivitySnapshot(@NonNull Task task, ActivityRecord r) {
+ return task.getSnapshotForActivityRecord(r);
}
private HardwareBuffer getTaskSnapshot(int taskId, int userId) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 063f0db..ae1e3e7 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -79,6 +79,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -1859,7 +1860,6 @@
super.addChild(child, index);
if (isAddingActivity && task != null) {
-
// TODO(b/207481538): temporary per-activity screenshoting
if (r != null && BackNavigationController.isScreenshotEnabled()) {
ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
@@ -2528,6 +2528,19 @@
return !mCreatedByOrganizer || mIsRemovalRequested;
}
+ @Nullable
+ HardwareBuffer getSnapshotForActivityRecord(@Nullable ActivityRecord r) {
+ if (!BackNavigationController.isScreenshotEnabled()) {
+ return null;
+ }
+ if (r != null && r.mActivityComponent != null) {
+ ScreenCapture.ScreenshotHardwareBuffer backBuffer =
+ mBackScreenshots.get(r.mActivityComponent.flattenToString());
+ return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
+ }
+ return null;
+ }
+
@Override
void removeChild(WindowContainer child) {
removeChild(child, true /* removeSelfIfPossible */);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0168d50..369f1ed 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -25,6 +25,7 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.graphics.GraphicsProtos.dumpPointProto;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
+import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -201,6 +202,7 @@
import android.graphics.RectF;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
@@ -5475,8 +5477,10 @@
// If refresh rate switching is disabled there is no point to set the frame rate on the
// surface as the refresh rate will be limited by display manager to a single value
// and SurfaceFlinger wouldn't be able to change it anyways.
- if (mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType()
- != SWITCHING_TYPE_NONE) {
+ @DisplayManager.SwitchingType int refreshRateSwitchingType =
+ mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType();
+ if (refreshRateSwitchingType != SWITCHING_TYPE_NONE
+ && refreshRateSwitchingType != SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY) {
final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this);
if (mAppPreferredFrameRate != refreshRate) {
mAppPreferredFrameRate = refreshRate;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 5fa8dcc..3c78819 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -152,7 +152,7 @@
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 0531ae2..f3ba484f62 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -61,7 +61,7 @@
"libnativehelper",
"libhardware_legacy",
"libutils",
- "android.hardware.gnss-V2-cpp",
+ "android.hardware.gnss-V3-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/gnss/GnssCallback.cpp b/services/core/jni/gnss/GnssCallback.cpp
index b931e91..3c1ac1e 100644
--- a/services/core/jni/gnss/GnssCallback.cpp
+++ b/services/core/jni/gnss/GnssCallback.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "GnssCallbckJni"
+#define LOG_TAG "GnssCallbackJni"
#include "GnssCallback.h"
@@ -31,6 +31,7 @@
using hardware::Void;
using GnssLocationAidl = android::hardware::gnss::GnssLocation;
+using GnssSignalType = android::hardware::gnss::GnssSignalType;
using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
@@ -42,11 +43,18 @@
namespace {
+jclass class_arrayList;
+jclass class_gnssSignalType;
+
+jmethodID method_arrayListAdd;
+jmethodID method_arrayListCtor;
+jmethodID method_gnssSignalTypeCreate;
jmethodID method_reportLocation;
jmethodID method_reportStatus;
jmethodID method_reportSvStatus;
jmethodID method_reportNmea;
jmethodID method_setTopHalCapabilities;
+jmethodID method_setSignalTypeCapabilities;
jmethodID method_setGnssYearOfHardware;
jmethodID method_setGnssHardwareModelName;
jmethodID method_requestLocation;
@@ -88,6 +96,8 @@
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V");
+ method_setSignalTypeCapabilities =
+ env->GetMethodID(clazz, "setSignalTypeCapabilities", "(Ljava/util/List;)V");
method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
method_setGnssHardwareModelName =
env->GetMethodID(clazz, "setGnssHardwareModelName", "(Ljava/lang/String;)V");
@@ -95,16 +105,58 @@
method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+ method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+ method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
+
+ jclass gnssSignalTypeClass = env->FindClass("android/location/GnssSignalType");
+ class_gnssSignalType = (jclass)env->NewGlobalRef(gnssSignalTypeClass);
+ method_gnssSignalTypeCreate =
+ env->GetStaticMethodID(class_gnssSignalType, "create",
+ "(IDLjava/lang/String;)Landroid/location/GnssSignalType;");
}
Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
- ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities);
+ ALOGD("%s: %du\n", __func__, capabilities);
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Status::ok();
}
+namespace {
+
+jobject translateSingleSignalType(JNIEnv* env, const GnssSignalType& signalType) {
+ jstring jstringCodeType = env->NewStringUTF(signalType.codeType.c_str());
+ jobject signalTypeObject =
+ env->CallStaticObjectMethod(class_gnssSignalType, method_gnssSignalTypeCreate,
+ signalType.constellation, signalType.carrierFrequencyHz,
+ jstringCodeType);
+ env->DeleteLocalRef(jstringCodeType);
+ return signalTypeObject;
+}
+
+} // anonymous namespace
+
+Status GnssCallbackAidl::gnssSetSignalTypeCapabilitiesCb(
+ const std::vector<GnssSignalType>& signalTypes) {
+ ALOGD("%s: %d signal types", __func__, (int)signalTypes.size());
+ JNIEnv* env = getJniEnv();
+ jobject arrayList = env->NewObject(class_arrayList, method_arrayListCtor);
+ for (auto& signalType : signalTypes) {
+ jobject signalTypeObject = translateSingleSignalType(env, signalType);
+ env->CallBooleanMethod(arrayList, method_arrayListAdd, signalTypeObject);
+ // Delete Local Refs
+ env->DeleteLocalRef(signalTypeObject);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_setSignalTypeCapabilities, arrayList);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(arrayList);
+ return Status::ok();
+}
+
Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
diff --git a/services/core/jni/gnss/GnssCallback.h b/services/core/jni/gnss/GnssCallback.h
index a7f96fb..33acec8 100644
--- a/services/core/jni/gnss/GnssCallback.h
+++ b/services/core/jni/gnss/GnssCallback.h
@@ -61,6 +61,8 @@
class GnssCallbackAidl : public hardware::gnss::BnGnssCallback {
public:
binder::Status gnssSetCapabilitiesCb(const int capabilities) override;
+ binder::Status gnssSetSignalTypeCapabilitiesCb(
+ const std::vector<android::hardware::gnss::GnssSignalType>& signalTypes) override;
binder::Status gnssStatusCb(const GnssStatusValue status) override;
binder::Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
binder::Status gnssLocationCb(const hardware::gnss::GnssLocation& location) override;
@@ -180,4 +182,4 @@
} // namespace android::gnss
-#endif // _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
\ No newline at end of file
+#endif // _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 593e648..88c0ec6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -318,8 +318,6 @@
"com.android.clockwork.displayoffload.DisplayOffloadService";
private static final String WEAR_DISPLAY_SERVICE_CLASS =
"com.android.clockwork.display.WearDisplayService";
- private static final String WEAR_LEFTY_SERVICE_CLASS =
- "com.google.android.clockwork.lefty.WearLeftyService";
private static final String WEAR_TIME_SERVICE_CLASS =
"com.android.clockwork.time.WearTimeService";
private static final String WEAR_GLOBAL_ACTIONS_SERVICE_CLASS =
@@ -1404,7 +1402,6 @@
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
- boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1");
@@ -2502,12 +2499,6 @@
mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
t.traceEnd();
- if (enableLeftyService) {
- t.traceBegin("StartWearLeftyService");
- mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS);
- t.traceEnd();
- }
-
t.traceBegin("StartWearGlobalActionsService");
mSystemServiceManager.startService(WEAR_GLOBAL_ACTIONS_SERVICE_CLASS);
t.traceEnd();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 1fe267f..9e61cab 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -795,6 +795,71 @@
}
@Test
+ @Parameters({
+ "true",
+ "false"
+ })
+ public void testVotingWithSwitchingTypeRenderFrameRateOnly(boolean frameRateIsRefreshRate) {
+ when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(30, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+
+ director.injectVotesByDisplay(votesByDisplay);
+ assertThat(director.getModeSwitchingType())
+ .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
+ } else {
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ }
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
+ 60);
+ } else {
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
+ }
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(30);
+
+ director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+ assertThat(director.getModeSwitchingType())
+ .isEqualTo(DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+
+ desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
+ } else {
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ }
+ assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
+ if (frameRateIsRefreshRate) {
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(30);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ }
+
+ assertThat(desiredSpecs.baseModeId).isEqualTo(30);
+ }
+
+ @Test
public void testVotingWithSwitchingTypeWithinGroups() {
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index e3b389b..f3f56e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -115,18 +115,14 @@
@Test
public void backTypeCrossActivityWhenBackToPreviousActivity() {
- Task task = createTopTaskWithActivity();
- WindowState window = createAppWindow(task, FIRST_APPLICATION_WINDOW, "window");
- addToWindowMap(window, true);
- IOnBackInvokedCallback callback = createOnBackInvokedCallback();
- window.setOnBackInvokedCallbackInfo(
- new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM));
+ CrossActivityTestCase testCase = createTopTaskWithTwoActivities();
+ IOnBackInvokedCallback callback = withSystemCallback(testCase.task);
+
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
+ assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
- assertWithMessage("Activity callback").that(
- backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
}
@Test
@@ -302,6 +298,34 @@
return task;
}
+ @NonNull
+ private CrossActivityTestCase createTopTaskWithTwoActivities() {
+ Task task = createTask(mDefaultDisplay);
+ ActivityRecord record1 = createActivityRecord(task);
+ ActivityRecord record2 = createActivityRecord(task);
+ // enable OnBackInvokedCallbacks
+ record2.info.applicationInfo.privateFlagsExt |=
+ PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+ WindowState window1 = createWindow(null, FIRST_APPLICATION_WINDOW, record1, "window1");
+ WindowState window2 = createWindow(null, FIRST_APPLICATION_WINDOW, record2, "window2");
+ when(task.mSurfaceControl.isValid()).thenReturn(true);
+ when(record1.mSurfaceControl.isValid()).thenReturn(true);
+ when(record2.mSurfaceControl.isValid()).thenReturn(true);
+ Mockito.doNothing().when(task).reparentSurfaceControl(any(), any());
+ Mockito.doNothing().when(record1).reparentSurfaceControl(any(), any());
+ Mockito.doNothing().when(record2).reparentSurfaceControl(any(), any());
+ mAtm.setFocusedTask(task.mTaskId, record1);
+ mAtm.setFocusedTask(task.mTaskId, record2);
+ addToWindowMap(window1, true);
+ addToWindowMap(window2, true);
+
+ CrossActivityTestCase testCase = new CrossActivityTestCase();
+ testCase.task = task;
+ testCase.recordBack = record1;
+ testCase.recordFront = record2;
+ return testCase;
+ }
+
private void addToWindowMap(WindowState window, boolean focus) {
mWm.mWindowMap.put(window.mClient.asBinder(), window);
if (focus) {
@@ -310,4 +334,10 @@
doReturn(window).when(mWm).getFocusedWindowLocked();
}
}
+
+ private class CrossActivityTestCase {
+ public Task task;
+ public ActivityRecord recordBack;
+ public ActivityRecord recordFront;
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 151ff80..7207e373 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1174,6 +1174,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public void setDisabled(boolean disabled) {
+ super.setDisabled_enforcePermission();
+
synchronized (this) {
if (mTemporarilyDisabled == disabled) {
if (DEBUG) Slog.d(TAG, "setDisabled(): already " + disabled);
@@ -1244,6 +1246,8 @@
public void updateState(
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory) {
+ super.updateState_enforcePermission();
+
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
@@ -1260,6 +1264,8 @@
@Nullable SharedMemory sharedMemory,
IHotwordRecognitionStatusCallback callback,
int detectorType) {
+ super.initAndVerifyDetector_enforcePermission();
+
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
@@ -1711,6 +1717,8 @@
@Nullable String attributionTag,
@Nullable IVoiceInteractionSessionShowCallback showCallback,
@Nullable IBinder activityToken) {
+ super.showSessionForActiveService_enforcePermission();
+
if (DEBUG_USER) Slog.d(TAG, "showSessionForActiveService()");
synchronized (this) {
@@ -1742,6 +1750,8 @@
@Override
public void hideCurrentSession() throws RemoteException {
+ super.hideCurrentSession_enforcePermission();
+
if (mImpl == null) {
return;
}
@@ -1762,6 +1772,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public void launchVoiceAssistFromKeyguard() {
+ super.launchVoiceAssistFromKeyguard_enforcePermission();
+
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "launchVoiceAssistFromKeyguard without running voice interaction"
@@ -1780,6 +1792,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public boolean isSessionRunning() {
+ super.isSessionRunning_enforcePermission();
+
synchronized (this) {
return mImpl != null && mImpl.mActiveSession != null;
}
@@ -1788,6 +1802,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public boolean activeServiceSupportsAssist() {
+ super.activeServiceSupportsAssist_enforcePermission();
+
synchronized (this) {
return mImpl != null && mImpl.mInfo != null && mImpl.mInfo.getSupportsAssist();
}
@@ -1796,6 +1812,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public boolean activeServiceSupportsLaunchFromKeyguard() throws RemoteException {
+ super.activeServiceSupportsLaunchFromKeyguard_enforcePermission();
+
synchronized (this) {
return mImpl != null && mImpl.mInfo != null
&& mImpl.mInfo.getSupportsLaunchFromKeyguard();
@@ -1805,6 +1823,8 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
public void onLockscreenShown() {
+ super.onLockscreenShown_enforcePermission();
+
synchronized (this) {
if (mImpl == null) {
return;
@@ -1828,6 +1848,8 @@
@Override
public void registerVoiceInteractionSessionListener(
IVoiceInteractionSessionListener listener) {
+ super.registerVoiceInteractionSessionListener_enforcePermission();
+
synchronized (this) {
mVoiceInteractionSessionListeners.register(listener);
}
@@ -1837,6 +1859,8 @@
@Override
public void getActiveServiceSupportedActions(List<String> voiceActions,
IVoiceActionCheckCallback callback) {
+ super.getActiveServiceSupportedActions_enforcePermission();
+
synchronized (this) {
if (mImpl == null) {
try {
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 98eeacf..d4b912e 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -66,6 +66,11 @@
public static final int PRECISE_CALL_STATE_DISCONNECTED = 7;
/** Call state: Disconnecting. */
public static final int PRECISE_CALL_STATE_DISCONNECTING = 8;
+ /**
+ * Call state: Incoming in pre-alerting state.
+ * A call will be in this state prior to entering {@link #PRECISE_CALL_STATE_ALERTING}.
+ */
+ public static final int PRECISE_CALL_STATE_INCOMING_SETUP = 9;
private @PreciseCallStates int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
private @PreciseCallStates int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
diff --git a/telephony/java/android/telephony/ims/SrvccCall.aidl b/telephony/java/android/telephony/ims/SrvccCall.aidl
new file mode 100644
index 0000000..0f0a079
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SrvccCall.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable SrvccCall;
diff --git a/telephony/java/android/telephony/ims/SrvccCall.java b/telephony/java/android/telephony/ims/SrvccCall.java
new file mode 100644
index 0000000..cdc271e
--- /dev/null
+++ b/telephony/java/android/telephony/ims/SrvccCall.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.PreciseCallStates;
+
+import java.util.Objects;
+
+/**
+ * A Parcelable object to represent the current state of an IMS call that is being tracked
+ * in the ImsService when an SRVCC begins. This information will be delivered to modem.
+ * @see SrvccStartedCallback
+ *
+ * @hide
+ */
+@SystemApi
+public final class SrvccCall implements Parcelable {
+ private static final String TAG = "SrvccCall";
+
+ /** The IMS call profile */
+ private ImsCallProfile mImsCallProfile;
+
+ /** The IMS call id */
+ private String mCallId;
+
+ /** The call state */
+ private @PreciseCallStates int mCallState;
+
+ private SrvccCall(Parcel in) {
+ readFromParcel(in);
+ }
+
+ /**
+ * Constructs an instance of SrvccCall.
+ *
+ * @param callId the call ID associated with the IMS call
+ * @param callState the state of this IMS call
+ * @param imsCallProfile the profile associated with this IMS call
+ * @throws IllegalArgumentException if the callId or the imsCallProfile is null
+ */
+ public SrvccCall(@NonNull String callId, @PreciseCallStates int callState,
+ @NonNull ImsCallProfile imsCallProfile) {
+ if (callId == null) throw new IllegalArgumentException("callId is null");
+ if (imsCallProfile == null) throw new IllegalArgumentException("imsCallProfile is null");
+
+ mCallId = callId;
+ mCallState = callState;
+ mImsCallProfile = imsCallProfile;
+ }
+
+ /**
+ * @return the {@link ImsCallProfile} associated with this IMS call,
+ * which will be used to get the address, the name, and the audio direction
+ * including the call in pre-alerting state.
+ */
+ @NonNull
+ public ImsCallProfile getImsCallProfile() {
+ return mImsCallProfile;
+ }
+
+ /**
+ * @return the call ID associated with this IMS call.
+ *
+ * @see android.telephony.ims.stub.ImsCallSessionImplBase#getCallId().
+ */
+ @NonNull
+ public String getCallId() {
+ return mCallId;
+ }
+
+ /**
+ * @return the call state of the associated IMS call.
+ */
+ public @PreciseCallStates int getPreciseCallState() {
+ return mCallState;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "{ callId=" + mCallId
+ + ", callState=" + mCallState
+ + ", imsCallProfile=" + mImsCallProfile
+ + " }";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SrvccCall that = (SrvccCall) o;
+ return mImsCallProfile.equals(that.mImsCallProfile)
+ && mCallId.equals(that.mCallId)
+ && mCallState == that.mCallState;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mImsCallProfile, mCallId);
+ result = 31 * result + mCallState;
+ return result;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(mCallId);
+ out.writeInt(mCallState);
+ out.writeParcelable(mImsCallProfile, 0);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mCallId = in.readString();
+ mCallState = in.readInt();
+ mImsCallProfile = in.readParcelable(ImsCallProfile.class.getClassLoader(),
+ android.telephony.ims.ImsCallProfile.class);
+ }
+
+ public static final @android.annotation.NonNull Creator<SrvccCall> CREATOR =
+ new Creator<SrvccCall>() {
+ @Override
+ public SrvccCall createFromParcel(Parcel in) {
+ return new SrvccCall(in);
+ }
+
+ @Override
+ public SrvccCall[] newArray(int size) {
+ return new SrvccCall[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
index 801b81c..8519173 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl
@@ -20,6 +20,7 @@
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.RtpHeaderExtensionType;
@@ -55,6 +56,10 @@
IImsCapabilityCallback c);
oneway void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c);
+ oneway void notifySrvccStarted(in ISrvccStartedCallback cb);
+ oneway void notifySrvccCompleted();
+ oneway void notifySrvccFailed();
+ oneway void notifySrvccCanceled();
// SMS APIs
void setSmsListener(IImsSmsListener l);
oneway void sendSms(in int token, int messageRef, String format, String smsc, boolean retry,
diff --git a/telephony/java/android/telephony/ims/aidl/ISrvccStartedCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISrvccStartedCallback.aidl
new file mode 100644
index 0000000..a173abf
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/ISrvccStartedCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.telephony.ims.SrvccCall;
+
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+oneway interface ISrvccStartedCallback {
+ void onSrvccCallNotified(in List<SrvccCall> profiles);
+}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index c0ff12e..8147759 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -31,10 +31,12 @@
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsService;
import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.SrvccCall;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
@@ -60,6 +62,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -289,6 +292,38 @@
"onSmsReady");
}
+ @Override
+ public void notifySrvccStarted(final ISrvccStartedCallback cb) {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccStarted(
+ (profiles) -> {
+ try {
+ cb.onSrvccCallNotified(profiles);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "onSrvccCallNotified e=" + e);
+ }
+ }),
+ "notifySrvccStarted");
+ }
+
+ @Override
+ public void notifySrvccCompleted() {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccCompleted(), "notifySrvccCompleted");
+ }
+
+ @Override
+ public void notifySrvccFailed() {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccFailed(), "notifySrvccFailed");
+ }
+
+ @Override
+ public void notifySrvccCanceled() {
+ executeMethodAsyncNoException(
+ () -> MmTelFeature.this.notifySrvccCanceled(), "notifySrvccCanceled");
+ }
+
// Call the methods with a clean calling identity on the executor and wait indefinitely for
// the future to return.
private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
@@ -969,6 +1004,75 @@
"Not implemented on device.");
}
+ /**
+ * Notifies the MmTelFeature that the network has initiated an SRVCC (Single radio voice
+ * call continuity) for all IMS calls. When the network initiates an SRVCC, calls from
+ * the LTE domain are handed over to the legacy circuit switched domain. The modem requires
+ * knowledge of ongoing calls in the IMS domain in order to complete the SRVCC operation.
+ * <p>
+ * @param consumer The callback used to notify the framework of the list of IMS calls and their
+ * state at the time of the SRVCC.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccStarted(@NonNull Consumer<List<SrvccCall>> consumer) {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
+ /**
+ * Notifies the MmTelFeature that the SRVCC is completed and the calls have been moved
+ * over to the circuit-switched domain.
+ * {@link android.telephony.CarrierConfigManager.ImsVoice#KEY_SRVCC_TYPE_INT_ARRAY}
+ * specifies the calls can be moved. Other calls will be disconnected.
+ * <p>
+ * The MmTelFeature may now release all resources related to the IMS calls.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccCompleted() {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
+ /**
+ * Notifies the MmTelFeature that the SRVCC has failed.
+ *
+ * The handover can fail by encountering a failure at the radio level
+ * or temporary MSC server internal errors in handover procedure.
+ * Refer to 3GPP TS 23.216 section 8 Handover Failure.
+ * <p>
+ * IMS service will recover and continue calls over IMS.
+ * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
+ * set to "failure to transition to CS domain".
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccFailed() {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
+ /**
+ * Notifies the MmTelFeature that the SRVCC has been canceled.
+ *
+ * Since the state of network can be changed, the network can decide to terminate
+ * the handover procedure before its completion and to return to its state before the handover
+ * procedure was triggered.
+ * Refer to 3GPP TS 23.216 section 8.1.3 Handover Cancellation.
+ *
+ * <p>
+ * IMS service will recover and continue calls over IMS.
+ * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
+ * set to "handover canceled".
+ *
+ * @hide
+ */
+ @SystemApi
+ public void notifySrvccCanceled() {
+ // Base Implementation - Should be overridden by IMS service
+ }
+
private void setSmsListener(IImsSmsListener listener) {
getSmsImplementation().registerSmsListener(listener);
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index df09e47..d0850b8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -69,7 +69,9 @@
StringPiece attr_value = attr->value;
const char* startChar = attr_value.begin();
if (attr_name == "pathPattern") {
- if (*startChar == '/' || *startChar == '.' || *startChar == '*') {
+ // pathPattern starts with '.' or '*' does not need leading slash.
+ // Reference starts with @ does not need leading slash.
+ if (*startChar == '/' || *startChar == '.' || *startChar == '*' || *startChar == '@') {
return true;
} else {
diag->Error(android::DiagMessage(data_el->line_number)
@@ -80,7 +82,8 @@
return false;
}
} else {
- if (*startChar == '/') {
+ // Reference starts with @ does not need leading slash.
+ if (*startChar == '/' || *startChar == '@') {
return true;
} else {
diag->Error(android::DiagMessage(data_el->line_number)
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index cec9a1a..8d1a647 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -1408,5 +1408,24 @@
</application>
</manifest>)";
EXPECT_THAT(Verify(input), NotNull());
+
+ // DeepLink with string reference as a path.
+ input = R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <application>
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="http"
+ android:host="www.example.com"
+ android:path="@string/startup_uri" />
+ </intent-filter>
+ </activity>
+ </application>
+ </manifest>)";
+ EXPECT_THAT(Verify(input), NotNull());
}
} // namespace aapt