Merge "Add ABS_MT_SLOT config to virtual touch screen." into tm-qpr-dev
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 877e7d3..6f4bb45 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -145,7 +145,10 @@
* that it is preserved through activty destroy and restore.
*/
private ArrayList<String> getPendingExitNames() {
- if (mPendingExitNames == null && mEnterTransitionCoordinator != null) {
+ if (mPendingExitNames == null
+ && mEnterTransitionCoordinator != null
+ && !mEnterTransitionCoordinator.isReturning()
+ ) {
mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames();
}
return mPendingExitNames;
@@ -202,6 +205,7 @@
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
+ getPendingExitNames(); // Set mPendingExitNames before resetting mEnterTransitionCoordinator
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
mEnterActivityOptions.isCrossTask());
@@ -250,6 +254,7 @@
public void onStop(Activity activity) {
restoreExitedViews();
if (mEnterTransitionCoordinator != null) {
+ getPendingExitNames(); // Set mPendingExitNames before clearing
mEnterTransitionCoordinator.stop();
mEnterTransitionCoordinator = null;
}
@@ -275,6 +280,7 @@
restoreReenteringViews();
} else if (mEnterTransitionCoordinator.isReturning()) {
mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
+ getPendingExitNames(); // Set mPendingExitNames before clearing
mEnterTransitionCoordinator = null;
});
}
@@ -374,6 +380,7 @@
}
public void startExitOutTransition(Activity activity, Bundle options) {
+ getPendingExitNames(); // Set mPendingExitNames before clearing mEnterTransitionCoordinator
mEnterTransitionCoordinator = null;
if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) ||
mExitTransitionCoordinators == null) {
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 559313a..41345e0 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -141,7 +141,9 @@
*
* Without this flag, the message passed to {@code grantTrust} is only used for debugging
* purposes. With the flag, it may be displayed to the user as the reason why the device is
- * unlocked.
+ * unlocked. If this flag isn't set OR the message is set to null, the device will display
+ * its own default message for trust granted. If the TrustAgent intentionally doesn't want to
+ * show any message, then it can set this flag AND set the message to an empty string.
*/
public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ba6ba63..b0d4657 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12709,7 +12709,6 @@
if (mViewTranslationCallback != null) {
mViewTranslationCallback.onClearTranslation(this);
}
- clearViewTranslationCallback();
clearViewTranslationResponse();
if (hasTranslationTransientState()) {
setHasTransientState(false);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 2195b83..218ca58 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -285,12 +285,18 @@
int TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
/**
- * Keyguard is being occluded.
+ * Keyguard is being occluded by non-Dream.
* @hide
*/
int TRANSIT_OLD_KEYGUARD_OCCLUDE = 22;
/**
+ * Keyguard is being occluded by Dream.
+ * @hide
+ */
+ int TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM = 33;
+
+ /**
* Keyguard is being unoccluded.
* @hide
*/
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a66b405..cb30021 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -142,6 +142,7 @@
import java.net.URISyntaxException;
import java.text.Collator;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -3708,6 +3709,7 @@
this.mRows = rows;
this.mCellCountPerRow = cellCountPerRow;
this.mCellVisibility = new boolean[rows.size() * cellCountPerRow];
+ Arrays.fill(mCellVisibility, true);
this.mListAdapterSupplier = listAdapterSupplier;
}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index e57b90a..1ec5325 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -43,7 +43,6 @@
import android.widget.TextView;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
@@ -87,7 +86,7 @@
private final ChooserActivityLogger mChooserActivityLogger;
private int mNumShortcutResults = 0;
- private final Map<TargetInfo, AsyncTask> mIconLoaders = new HashMap<>();
+ private Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
private boolean mApplySharingAppLimits;
// Reserve spots for incoming direct share targets by adding placeholders
@@ -105,8 +104,6 @@
private AppPredictor mAppPredictor;
private AppPredictor.Callback mAppPredictorCallback;
- private LoadDirectShareIconTaskProvider mTestLoadDirectShareTaskProvider;
-
// For pinned direct share labels, if the text spans multiple lines, the TextView will consume
// the full width, even if the characters actually take up less than that. Measure the actual
// line widths and constrain the View's width based upon that so that the pin doesn't end up
@@ -243,6 +240,7 @@
mListViewDataChanged = false;
}
+
private void createPlaceHolders() {
mNumShortcutResults = 0;
mServiceTargets.clear();
@@ -267,25 +265,31 @@
return;
}
- if (info instanceof DisplayResolveInfo) {
- DisplayResolveInfo dri = (DisplayResolveInfo) info;
- holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
- startDisplayResolveInfoIconLoading(holder, dri);
- } else {
+ if (!(info instanceof DisplayResolveInfo)) {
holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
+ holder.bindIcon(info);
if (info instanceof SelectableTargetInfo) {
- SelectableTargetInfo selectableInfo = (SelectableTargetInfo) info;
// direct share targets should append the application name for a better readout
- DisplayResolveInfo rInfo = selectableInfo.getDisplayResolveInfo();
+ DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
- CharSequence extendedInfo = selectableInfo.getExtendedInfo();
- String contentDescription = String.join(" ", selectableInfo.getDisplayLabel(),
+ CharSequence extendedInfo = info.getExtendedInfo();
+ String contentDescription = String.join(" ", info.getDisplayLabel(),
extendedInfo != null ? extendedInfo : "", appName);
holder.updateContentDescription(contentDescription);
- startSelectableTargetInfoIconLoading(holder, selectableInfo);
+ }
+ } else {
+ DisplayResolveInfo dri = (DisplayResolveInfo) info;
+ holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
+ LoadIconTask task = mIconLoaders.get(dri);
+ if (task == null) {
+ task = new LoadIconTask(dri, holder);
+ mIconLoaders.put(dri, task);
+ task.execute();
} else {
- holder.bindIcon(info);
+ // The holder was potentially changed as the underlying items were
+ // reshuffled, so reset the target holder
+ task.setViewHolder(holder);
}
}
@@ -326,32 +330,6 @@
}
}
- private void startDisplayResolveInfoIconLoading(ViewHolder holder, DisplayResolveInfo info) {
- LoadIconTask task = (LoadIconTask) mIconLoaders.get(info);
- if (task == null) {
- task = new LoadIconTask(info, holder);
- mIconLoaders.put(info, task);
- task.execute();
- } else {
- // The holder was potentially changed as the underlying items were
- // reshuffled, so reset the target holder
- task.setViewHolder(holder);
- }
- }
-
- private void startSelectableTargetInfoIconLoading(
- ViewHolder holder, SelectableTargetInfo info) {
- LoadDirectShareIconTask task = (LoadDirectShareIconTask) mIconLoaders.get(info);
- if (task == null) {
- task = mTestLoadDirectShareTaskProvider == null
- ? new LoadDirectShareIconTask(info)
- : mTestLoadDirectShareTaskProvider.get();
- mIconLoaders.put(info, task);
- task.loadIcon();
- }
- task.setViewHolder(holder);
- }
-
void updateAlphabeticalList() {
new AsyncTask<Void, Void, List<DisplayResolveInfo>>() {
@Override
@@ -366,7 +344,7 @@
Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
for (DisplayResolveInfo info : allTargets) {
String resolvedTarget = info.getResolvedComponentName().getPackageName()
- + '#' + info.getDisplayLabel();
+ + '#' + info.getDisplayLabel();
DisplayResolveInfo multiDri = consolidated.get(resolvedTarget);
if (multiDri == null) {
consolidated.put(resolvedTarget, info);
@@ -375,7 +353,7 @@
} else {
// create consolidated target from the single DisplayResolveInfo
MultiDisplayResolveInfo multiDisplayResolveInfo =
- new MultiDisplayResolveInfo(resolvedTarget, multiDri);
+ new MultiDisplayResolveInfo(resolvedTarget, multiDri);
multiDisplayResolveInfo.addTarget(info);
consolidated.put(resolvedTarget, multiDisplayResolveInfo);
}
@@ -762,24 +740,10 @@
}
/**
- * An alias for onBindView to use with unit tests.
- */
- @VisibleForTesting
- public void testViewBind(View view, TargetInfo info, int position) {
- onBindView(view, info, position);
- }
-
- @VisibleForTesting
- public void setTestLoadDirectShareTaskProvider(LoadDirectShareIconTaskProvider provider) {
- mTestLoadDirectShareTaskProvider = provider;
- }
-
- /**
* Necessary methods to communicate between {@link ChooserListAdapter}
* and {@link ChooserActivity}.
*/
- @VisibleForTesting
- public interface ChooserListCommunicator extends ResolverListCommunicator {
+ interface ChooserListCommunicator extends ResolverListCommunicator {
int getMaxRankedTargets();
@@ -787,59 +751,4 @@
boolean isSendAction(Intent targetIntent);
}
-
- /**
- * Loads direct share targets icons.
- */
- @VisibleForTesting
- public class LoadDirectShareIconTask extends AsyncTask<Void, Void, Void> {
- private final SelectableTargetInfo mTargetInfo;
- private ViewHolder mViewHolder;
-
- private LoadDirectShareIconTask(SelectableTargetInfo targetInfo) {
- mTargetInfo = targetInfo;
- }
-
- @Override
- protected Void doInBackground(Void... voids) {
- mTargetInfo.loadIcon();
- return null;
- }
-
- @Override
- protected void onPostExecute(Void arg) {
- if (mViewHolder != null) {
- mViewHolder.bindIcon(mTargetInfo);
- notifyDataSetChanged();
- }
- }
-
- /**
- * Specifies a view holder that will be updated when the task is completed.
- */
- public void setViewHolder(ViewHolder viewHolder) {
- mViewHolder = viewHolder;
- mViewHolder.bindIcon(mTargetInfo);
- notifyDataSetChanged();
- }
-
- /**
- * An alias for execute to use with unit tests.
- */
- public void loadIcon() {
- execute();
- }
- }
-
- /**
- * An interface for the unit tests to override icon loading task creation
- */
- @VisibleForTesting
- public interface LoadDirectShareIconTaskProvider {
- /**
- * Provides an instance of the task.
- * @return
- */
- LoadDirectShareIconTask get();
- }
}
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 3a3baa7..66fff5c 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -834,11 +834,7 @@
void onHandlePackagesChanged(ResolverListAdapter listAdapter);
}
- /**
- * A view holder.
- */
- @VisibleForTesting
- public static class ViewHolder {
+ static class ViewHolder {
public View itemView;
public Drawable defaultItemViewBackground;
@@ -846,8 +842,7 @@
public TextView text2;
public ImageView icon;
- @VisibleForTesting
- public ViewHolder(View view) {
+ ViewHolder(View view) {
itemView = view;
defaultItemViewBackground = view.getBackground();
text = (TextView) view.findViewById(com.android.internal.R.id.text1);
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 37eab40..264e4f7 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -37,7 +37,6 @@
import android.text.SpannableStringBuilder;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ChooserActivity;
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
@@ -60,11 +59,8 @@
private final String mDisplayLabel;
private final PackageManager mPm;
private final SelectableTargetInfoCommunicator mSelectableTargetInfoCommunicator;
- @GuardedBy("this")
- private ShortcutInfo mShortcutInfo;
private Drawable mBadgeIcon = null;
private CharSequence mBadgeContentDescription;
- @GuardedBy("this")
private Drawable mDisplayIcon;
private final Intent mFillInIntent;
private final int mFillInFlags;
@@ -82,7 +78,6 @@
mModifiedScore = modifiedScore;
mPm = mContext.getPackageManager();
mSelectableTargetInfoCommunicator = selectableTargetInfoComunicator;
- mShortcutInfo = shortcutInfo;
mIsPinned = shortcutInfo != null && shortcutInfo.isPinned();
if (sourceInfo != null) {
final ResolveInfo ri = sourceInfo.getResolveInfo();
@@ -97,6 +92,8 @@
}
}
}
+ // TODO(b/121287224): do this in the background thread, and only for selected targets
+ mDisplayIcon = getChooserTargetIconDrawable(chooserTarget, shortcutInfo);
if (sourceInfo != null) {
mBackupResolveInfo = null;
@@ -121,10 +118,7 @@
mChooserTarget = other.mChooserTarget;
mBadgeIcon = other.mBadgeIcon;
mBadgeContentDescription = other.mBadgeContentDescription;
- synchronized (other) {
- mShortcutInfo = other.mShortcutInfo;
- mDisplayIcon = other.mDisplayIcon;
- }
+ mDisplayIcon = other.mDisplayIcon;
mFillInIntent = fillInIntent;
mFillInFlags = flags;
mModifiedScore = other.mModifiedScore;
@@ -147,25 +141,6 @@
return mSourceInfo;
}
- /**
- * Load display icon, if needed.
- */
- public void loadIcon() {
- ShortcutInfo shortcutInfo;
- Drawable icon;
- synchronized (this) {
- shortcutInfo = mShortcutInfo;
- icon = mDisplayIcon;
- }
- if (icon == null && shortcutInfo != null) {
- icon = getChooserTargetIconDrawable(mChooserTarget, shortcutInfo);
- synchronized (this) {
- mDisplayIcon = icon;
- mShortcutInfo = null;
- }
- }
- }
-
private Drawable getChooserTargetIconDrawable(ChooserTarget target,
@Nullable ShortcutInfo shortcutInfo) {
Drawable directShareIcon = null;
@@ -295,7 +270,7 @@
}
@Override
- public synchronized Drawable getDisplayIcon(Context context) {
+ public Drawable getDisplayIcon(Context context) {
return mDisplayIcon;
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 4706aff..5f8acff 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -111,6 +111,7 @@
private Icon mLargeIcon;
private View mExpandButtonContainer;
private ViewGroup mExpandButtonAndContentContainer;
+ private ViewGroup mExpandButtonContainerA11yContainer;
private NotificationExpandButton mExpandButton;
private MessagingLinearLayout mImageMessageContainer;
private int mBadgeProtrusion;
@@ -234,6 +235,8 @@
});
mConversationText = findViewById(R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
+ mExpandButtonContainerA11yContainer =
+ findViewById(R.id.expand_button_a11y_container);
mConversationHeader = findViewById(R.id.conversation_header);
mContentContainer = findViewById(R.id.notification_action_list_margin_target);
mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
@@ -1091,7 +1094,7 @@
newContainer = mExpandButtonAndContentContainer;
} else {
buttonGravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
- newContainer = this;
+ newContainer = mExpandButtonContainerA11yContainer;
}
mExpandButton.setExpanded(!mIsCollapsed);
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 42fb4a2..ce8a904 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -89,45 +89,62 @@
<include layout="@layout/notification_material_action_list" />
</com.android.internal.widget.RemeasuringLinearLayout>
- <!--This is dynamically placed between here and at the end of the layout. It starts here since
- only FrameLayout layout params have gravity-->
+ <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded.
+ The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to
+ its sibling view in accessibility focus order.
+ {see android.view.ViewGroup.addChildrenForAccessibility()}
+ expand_button_container will be moved under expand_button_and_content_container when collapsed,
+ this dynamic movement ensures message can flow under expand button when expanded-->
<FrameLayout
- android:id="@+id/expand_button_container"
- android:layout_width="wrap_content"
+ android:id="@+id/expand_button_a11y_container"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end|top"
android:clipChildren="false"
- android:clipToPadding="false">
- <!--This layout makes sure that we can nicely center the expand content in the
- collapsed layout while the parent makes sure that we're never laid out bigger
- than the messaging content.-->
- <LinearLayout
- android:id="@+id/expand_button_touch_container"
+ android:clipToPadding="false"
+ android:layout_marginTop="-1px"
+ android:paddingTop="1px"
+ >
+ <!--expand_button_container is dynamically placed between here and at the end of the
+ layout. It starts here since only FrameLayout layout params have gravity-->
+ <FrameLayout
+ android:id="@+id/expand_button_container"
android:layout_width="wrap_content"
- android:layout_height="@dimen/conversation_expand_button_height"
- android:orientation="horizontal"
+ android:layout_height="match_parent"
android:layout_gravity="end|top"
- android:paddingEnd="0dp"
- android:clipToPadding="false"
android:clipChildren="false"
- >
- <!-- Images -->
- <com.android.internal.widget.MessagingLinearLayout
- android:id="@+id/conversation_image_message_container"
- android:forceHasOverlappingRendering="false"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginStart="@dimen/conversation_image_start_margin"
- android:spacing="0dp"
- android:layout_gravity="center"
+ android:clipToPadding="false">
+ <!--expand_button_touch_container makes sure that we can nicely center the expand
+ content in the collapsed layout while the parent makes sure that we're never laid out
+ bigger than the messaging content.-->
+ <LinearLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/conversation_expand_button_height"
+ android:orientation="horizontal"
+ android:layout_gravity="end|top"
+ android:paddingEnd="0dp"
android:clipToPadding="false"
android:clipChildren="false"
- />
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- />
- </LinearLayout>
+ >
+ <!-- Images -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/conversation_image_message_container"
+ android:forceHasOverlappingRendering="false"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_marginStart="@dimen/conversation_image_start_margin"
+ android:spacing="0dp"
+ android:layout_gravity="center"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ />
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ />
+ </LinearLayout>
+ </FrameLayout>
</FrameLayout>
</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index aad32b1..f0ba5ff 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1804,7 +1804,7 @@
<!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
<string name="face_acquired_too_bright">Too bright. Try gentler lighting.</string>
<!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
- <string name="face_acquired_too_dark">Try brighter lighting</string>
+ <string name="face_acquired_too_dark">Not enough light</string>
<!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
<string name="face_acquired_too_close">Move phone farther away</string>
<!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e773a9c..a6174e1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4279,6 +4279,7 @@
<java-symbol type="id" name="conversation_icon_badge_ring" />
<java-symbol type="id" name="conversation_icon_badge_bg" />
<java-symbol type="id" name="expand_button_container" />
+ <java-symbol type="id" name="expand_button_a11y_container" />
<java-symbol type="id" name="expand_button_touch_container" />
<java-symbol type="id" name="messaging_group_content_container" />
<java-symbol type="id" name="expand_button_and_content_container" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt b/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
deleted file mode 100644
index f027e0b..0000000
--- a/core/tests/coretests/src/com/android/internal/app/ChooserListAdapterTest.kt
+++ /dev/null
@@ -1,155 +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.internal.app
-
-import android.content.ComponentName
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.service.chooser.ChooserTarget
-import android.view.View
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.internal.R
-import com.android.internal.app.chooser.SelectableTargetInfo
-import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
-import com.android.server.testutils.any
-import com.android.server.testutils.mock
-import com.android.server.testutils.whenever
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.doNothing
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-class ChooserListAdapterTest {
- private val packageManager = mock<PackageManager> {
- whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
- }
- private val context = InstrumentationRegistry.getInstrumentation().getContext()
- private val resolverListController = mock<ResolverListController>()
- private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
- whenever(maxRankedTargets).thenReturn(0)
- }
- private val selectableTargetInfoCommunicator =
- mock<SelectableTargetInfoCommunicator> {
- whenever(targetIntent).thenReturn(mock())
- }
- private val chooserActivityLogger = mock<ChooserActivityLogger>()
-
- private val testSubject = ChooserListAdapter(
- context,
- emptyList(),
- emptyArray(),
- emptyList(),
- false,
- resolverListController,
- chooserListCommunicator,
- selectableTargetInfoCommunicator,
- packageManager,
- chooserActivityLogger
- )
-
- @Test
- fun testDirectShareTargetLoadingIconIsStarted() {
- val view = createView()
- val viewHolder = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolder
- val targetInfo = createSelectableTargetInfo()
- val iconTask = mock<ChooserListAdapter.LoadDirectShareIconTask> {
- doNothing().whenever(this).loadIcon()
- }
- testSubject.setTestLoadDirectShareTaskProvider(
- mock {
- whenever(get()).thenReturn(iconTask)
- }
- )
- testSubject.testViewBind(view, targetInfo, 0)
-
- verify(iconTask, times(1)).loadIcon()
- verify(iconTask, times(1)).setViewHolder(viewHolder)
- }
-
- @Test
- fun testOnlyOneTaskPerTarget() {
- val view = createView()
- val viewHolderOne = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderOne
- val targetInfo = createSelectableTargetInfo()
- val iconTaskOne = mock<ChooserListAdapter.LoadDirectShareIconTask> {
- doNothing().whenever(this).loadIcon()
- }
- val testTaskProvider = mock<ChooserListAdapter.LoadDirectShareIconTaskProvider> {
- whenever(get()).thenReturn(iconTaskOne)
- }
- testSubject.setTestLoadDirectShareTaskProvider(
- testTaskProvider
- )
- testSubject.testViewBind(view, targetInfo, 0)
-
- val viewHolderTwo = ResolverListAdapter.ViewHolder(view)
- view.tag = viewHolderTwo
- whenever(testTaskProvider.get()).thenReturn(mock())
-
- testSubject.testViewBind(view, targetInfo, 0)
-
- verify(iconTaskOne, times(1)).loadIcon()
- verify(iconTaskOne, times(1)).setViewHolder(viewHolderOne)
- verify(iconTaskOne, times(1)).setViewHolder(viewHolderTwo)
- verify(testTaskProvider, times(1)).get()
- }
-
- private fun createSelectableTargetInfo(): SelectableTargetInfo =
- SelectableTargetInfo(
- context,
- null,
- createChooserTarget(),
- 1f,
- selectableTargetInfoCommunicator,
- null
- )
-
- private fun createChooserTarget(): ChooserTarget =
- ChooserTarget(
- "Title",
- null,
- 1f,
- ComponentName("package", "package.Class"),
- Bundle()
- )
-
- private fun createView(): View {
- val view = FrameLayout(context)
- TextView(context).apply {
- id = R.id.text1
- view.addView(this)
- }
- TextView(context).apply {
- id = R.id.text2
- view.addView(this)
- }
- ImageView(context).apply {
- id = R.id.icon
- view.addView(this)
- }
- return view
- }
-}
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 6abe34b..9c36fc3 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -214,7 +214,7 @@
* uniform shader myShader;
* vec4 main(vec2 canvas_coordinates) {
* // swap the red and blue color channels when sampling from myShader
- * return myShader.sample(canvas_coordinates).bgra;
+ * return myShader.eval(canvas_coordinates).bgra;
* }</pre>
*
* <p>After creating a {@link RuntimeShader} with that program the shader uniform can
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b5409b7..4367936 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -876,8 +876,12 @@
pkg = info.getTaskInfo().baseActivity.getPackageName();
}
Rect bounds = info.getTaskInfo().getConfiguration().windowConfiguration.getBounds();
+ boolean running = info.getTaskInfo().isRunning;
+ boolean visible = info.getTaskInfo().isVisible;
+ boolean focused = info.getTaskInfo().isFocused;
pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener
- + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds);
+ + " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds
+ + " running=" + running + " visible=" + visible + " focused=" + focused);
}
pw.println();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 99b8885..b5a5754 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -170,7 +170,8 @@
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
- int taskId, @Nullable final String locus, Executor mainExecutor) {
+ int taskId, @Nullable final String locus, Executor mainExecutor,
+ final Bubbles.BubbleMetadataFlagListener listener) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mMetadataShortcutId = shortcutInfo.getId();
@@ -188,11 +189,12 @@
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
mTaskId = taskId;
+ mBubbleMetadataFlagListener = listener;
}
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
- @Nullable final Bubbles.BubbleMetadataFlagListener listener,
+ final Bubbles.BubbleMetadataFlagListener listener,
final Bubbles.PendingIntentCanceledListener intentCancelListener,
Executor mainExecutor) {
mKey = entry.getKey();
@@ -830,6 +832,7 @@
pw.print(" desiredHeight: "); pw.println(getDesiredHeightString());
pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
+ pw.print(" bubbleMetadataFlagListener null: " + (mBubbleMetadataFlagListener == null));
if (mExpandedView != null) {
mExpandedView.dump(pw);
}
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 d63c25d..0dfba34 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
@@ -309,6 +309,7 @@
protected void onInit() {
mBubbleData.setListener(mBubbleDataListener);
mBubbleData.setSuppressionChangedListener(this::onBubbleMetadataFlagChanged);
+ mDataRepository.setSuppressionChangedListener(this::onBubbleMetadataFlagChanged);
mBubbleData.setPendingIntentCancelledListener(bubble -> {
if (bubble.getBubbleIntent() == null) {
@@ -1336,19 +1337,7 @@
}
mSysuiProxy.updateNotificationBubbleButton(bubble.getKey());
}
-
}
- mSysuiProxy.getPendingOrActiveEntry(bubble.getKey(), (entry) -> {
- mMainExecutor.execute(() -> {
- if (entry != null) {
- final String groupKey = entry.getStatusBarNotification().getGroupKey();
- if (getBubblesInGroup(groupKey).isEmpty()) {
- // Time to potentially remove the summary
- mSysuiProxy.notifyMaybeCancelSummary(bubble.getKey());
- }
- }
- });
- });
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index c64133f..af31391 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -158,7 +158,6 @@
@Nullable
private Listener mListener;
- @Nullable
private Bubbles.BubbleMetadataFlagListener mBubbleMetadataFlagListener;
private Bubbles.PendingIntentCanceledListener mCancelledListener;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 97560f4..3a59614 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -25,6 +25,7 @@
import android.content.pm.UserInfo
import android.os.UserHandle
import android.util.Log
+import com.android.wm.shell.bubbles.Bubbles.BubbleMetadataFlagListener
import com.android.wm.shell.bubbles.storage.BubbleEntity
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
@@ -47,6 +48,13 @@
private val ioScope = CoroutineScope(Dispatchers.IO)
private var job: Job? = null
+ // For use in Bubble construction.
+ private lateinit var bubbleMetadataFlagListener: BubbleMetadataFlagListener
+
+ fun setSuppressionChangedListener(listener: BubbleMetadataFlagListener) {
+ bubbleMetadataFlagListener = listener
+ }
+
/**
* Adds the bubble in memory, then persists the snapshot after adding the bubble to disk
* asynchronously.
@@ -197,7 +205,8 @@
entity.title,
entity.taskId,
entity.locus,
- mainExecutor
+ mainExecutor,
+ bubbleMetadataFlagListener
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 453b34e..b3104b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -276,8 +276,6 @@
void notifyInvalidateNotifications(String reason);
- void notifyMaybeCancelSummary(String key);
-
void updateNotificationBubbleButton(String key);
void onStackExpandChanged(boolean shouldExpand);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 47f1e2e..96efeeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -96,7 +96,8 @@
/**
* Different from {@link #equals(Object)}, this method compares the basic geometry properties
- * of two {@link DisplayLayout} objects including width, height, rotation, density and cutout.
+ * of two {@link DisplayLayout} objects including width, height, rotation, density, cutout and
+ * insets.
* @return {@code true} if the given {@link DisplayLayout} is identical geometry wise.
*/
public boolean isSameGeometry(@NonNull DisplayLayout other) {
@@ -104,7 +105,8 @@
&& mHeight == other.mHeight
&& mRotation == other.mRotation
&& mDensityDpi == other.mDensityDpi
- && Objects.equals(mCutout, other.mCutout);
+ && Objects.equals(mCutout, other.mCutout)
+ && Objects.equals(mStableInsets, other.mStableInsets);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index b7656de..18ce364 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -82,7 +82,7 @@
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.SplitscreenPipMixedHandler;
+import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -220,13 +220,14 @@
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
+ Optional<RecentTasksController> recentTasksController,
WindowDecorViewModel<?> windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit
: null;
- return new FreeformTaskListener<>(init, shellTaskOrganizer,
+ return new FreeformTaskListener<>(init, shellTaskOrganizer, recentTasksController,
windowDecorViewModel);
}
@@ -332,6 +333,7 @@
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
+ DisplayInsetsController displayInsetsController,
Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(
@@ -340,7 +342,7 @@
pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- oneHandedController, mainExecutor));
+ displayInsetsController, oneHandedController, mainExecutor));
}
@WMSingleton
@@ -483,13 +485,13 @@
@WMSingleton
@Provides
- static SplitscreenPipMixedHandler provideSplitscreenPipMixedHandler(
+ static DefaultMixedHandler provideDefaultMixedHandler(
ShellInit shellInit,
Optional<SplitScreenController> splitScreenOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
Transitions transitions) {
- return new SplitscreenPipMixedHandler(shellInit, splitScreenOptional,
- pipTouchHandlerOptional, transitions);
+ return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
+ pipTouchHandlerOptional);
}
//
@@ -618,7 +620,7 @@
@ShellCreateTriggerOverride
@Provides
static Object provideIndependentShellComponentsToCreate(
- SplitscreenPipMixedHandler splitscreenPipMixedHandler,
+ DefaultMixedHandler defaultMixedHandler,
Optional<DesktopModeController> desktopModeController) {
return new Object();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
index 64cec2a..8993d54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -50,7 +50,7 @@
Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
return result != 0;
- } catch (Settings.SettingNotFoundException e) {
+ } catch (Exception e) {
ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 4697a01..b59fe18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -258,12 +258,12 @@
break;
case ACTION_DRAG_ENTERED:
pd.dragLayout.show();
- pd.dragLayout.update(event);
break;
case ACTION_DRAG_LOCATION:
pd.dragLayout.update(event);
break;
case ACTION_DROP: {
+ pd.dragLayout.update(event);
return handleDrop(event, pd);
}
case ACTION_DRAG_EXITED: {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 8dcdda1..1baac71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -28,12 +28,15 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* {@link ShellTaskOrganizer.TaskListener} for {@link
@@ -46,6 +49,7 @@
private static final String TAG = "FreeformTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<RecentTasksController> mRecentTasksOptional;
private final WindowDecorViewModel<T> mWindowDecorationViewModel;
private final SparseArray<State<T>> mTasks = new SparseArray<>();
@@ -60,9 +64,11 @@
public FreeformTaskListener(
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
+ Optional<RecentTasksController> recentTasksController,
WindowDecorViewModel<T> windowDecorationViewModel) {
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
+ mRecentTasksOptional = recentTasksController;
if (shellInit != null) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -83,6 +89,12 @@
mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
t.apply();
}
+
+ if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Adding active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ }
}
private State<T> createOrUpdateTaskState(RunningTaskInfo taskInfo, SurfaceControl leash) {
@@ -111,6 +123,12 @@
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
+ if (DesktopMode.IS_SUPPORTED) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Removing active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.removeActiveFreeformTask(taskInfo.taskId));
+ }
+
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
// Save window decorations of closing tasks so that we can hand them over to the
// transition system if this method happens before the transition. In case where the
@@ -131,6 +149,14 @@
if (state.mWindowDecoration != null) {
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
}
+
+ if (DesktopMode.IS_SUPPORTED) {
+ if (taskInfo.isVisible) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "Adding active freeform task: #%d", taskInfo.taskId);
+ mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ }
+ }
}
private State<T> updateTaskInfo(RunningTaskInfo taskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 297c79e..f170e77 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -966,7 +966,7 @@
// Re-set the PIP bounds to none.
mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null);
- mMainExecutor.executeDelayed(() -> mPipMenuController.detach(), 0);
+ mPipMenuController.detach();
mLeash = null;
if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 6c9a6b6..61fafb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -49,6 +49,7 @@
import android.util.Pair;
import android.util.Size;
import android.view.DisplayInfo;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.window.WindowContainerTransaction;
@@ -64,6 +65,7 @@
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
@@ -135,6 +137,7 @@
private PipTransitionController mPipTransitionController;
private TaskStackListenerImpl mTaskStackListener;
private PipParamsChangedForwarder mPipParamsChangedForwarder;
+ private DisplayInsetsController mDisplayInsetsController;
private Optional<OneHandedController> mOneHandedController;
private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
@@ -338,6 +341,7 @@
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
+ DisplayInsetsController displayInsetsController,
Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
@@ -351,7 +355,7 @@
pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- oneHandedController, mainExecutor)
+ displayInsetsController, oneHandedController, mainExecutor)
.mImpl;
}
@@ -374,6 +378,7 @@
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
+ DisplayInsetsController displayInsetsController,
Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor
) {
@@ -407,6 +412,7 @@
mEnterAnimationDuration = mContext.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
mPipParamsChangedForwarder = pipParamsChangedForwarder;
+ mDisplayInsetsController = displayInsetsController;
shellInit.addInitCallback(this::onInit, this);
}
@@ -549,6 +555,16 @@
}
});
+ mDisplayInsetsController.addInsetsChangedListener(mPipBoundsState.getDisplayId(),
+ new DisplayInsetsController.OnInsetsChangedListener() {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ onDisplayChanged(
+ mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
+ false /* saveRestoreSnapFraction */);
+ }
+ });
+
mOneHandedController.ifPresent(controller -> {
controller.registerTransitionCallback(
new OneHandedTransitionCallback() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 7b42350..27bc1a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
@@ -52,6 +53,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -82,6 +84,15 @@
private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
+ * Set of taskId's that have been launched in freeform mode.
+ * This includes tasks that are currently running, visible and in freeform mode. And also
+ * includes tasks that are running in the background, are no longer visible, but at some point
+ * were visible to the user.
+ * This is used to decide which freeform apps belong to the user's desktop.
+ */
+ private final HashSet<Integer> mActiveFreeformTasks = new HashSet<>();
+
+ /**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
* supported.
*/
@@ -206,6 +217,22 @@
notifyRecentTasksChanged();
}
+ /**
+ * Mark a task with given {@code taskId} as active in freeform
+ */
+ public void addActiveFreeformTask(int taskId) {
+ mActiveFreeformTasks.add(taskId);
+ notifyRecentTasksChanged();
+ }
+
+ /**
+ * Remove task with given {@code taskId} from active freeform tasks
+ */
+ public void removeActiveFreeformTask(int taskId) {
+ mActiveFreeformTasks.remove(taskId);
+ notifyRecentTasksChanged();
+ }
+
@VisibleForTesting
void notifyRecentTasksChanged() {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed");
@@ -273,6 +300,9 @@
rawMapping.put(taskInfo.taskId, taskInfo);
}
+ boolean desktopModeActive = DesktopMode.isActive(mContext);
+ ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
+
// Pull out the pairs as we iterate back in the list
ArrayList<GroupedRecentTaskInfo> recentTasks = new ArrayList<>();
for (int i = 0; i < rawList.size(); i++) {
@@ -282,16 +312,31 @@
continue;
}
+ if (desktopModeActive && mActiveFreeformTasks.contains(taskInfo.taskId)) {
+ // Freeform tasks will be added as a separate entry
+ freeformTasks.add(taskInfo);
+ continue;
+ }
+
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
- if (pairedTaskId != INVALID_TASK_ID && rawMapping.contains(pairedTaskId)) {
+ if (!desktopModeActive && pairedTaskId != INVALID_TASK_ID && rawMapping.contains(
+ pairedTaskId)) {
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
rawMapping.remove(pairedTaskId);
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo,
+ recentTasks.add(GroupedRecentTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo,
mTaskSplitBoundsMap.get(pairedTaskId)));
} else {
- recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
+ recentTasks.add(GroupedRecentTaskInfo.forSingleTask(taskInfo));
}
}
+
+ // Add a special entry for freeform tasks
+ if (!freeformTasks.isEmpty()) {
+ // First task is added separately
+ recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks(
+ freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
+ }
+
return recentTasks;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 169e17b..9206afb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -147,7 +147,6 @@
private final DragAndDropController mDragAndDropController;
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
- private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
@@ -186,7 +185,6 @@
mDragAndDropController = dragAndDropController;
mTransitions = transitions;
mTransactionPool = transactionPool;
- mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
@@ -213,14 +211,18 @@
mShellController.addKeyguardChangeListener(this);
if (mStageCoordinator == null) {
// TODO: Multi-display
- mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mTaskOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mIconProvider, mMainExecutor, mRecentTasksOptional);
+ mStageCoordinator = createStageCoordinator();
}
mDragAndDropController.setSplitScreenController(this);
}
+ protected StageCoordinator createStageCoordinator() {
+ return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
+ mTaskOrganizer, mDisplayController, mDisplayImeController,
+ mDisplayInsetsController, mTransitions, mTransactionPool,
+ mIconProvider, mMainExecutor, mRecentTasksOptional);
+ }
+
@Override
public Context getContext() {
return mContext;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index dcfe08d..9c9841f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -69,6 +69,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
@@ -81,6 +82,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
import android.util.Slog;
import android.view.Choreographer;
@@ -244,18 +246,18 @@
}
};
- StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
+ protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger,
+ TransactionPool transactionPool,
IconProvider iconProvider, ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
mTaskOrganizer = taskOrganizer;
- mLogger = logger;
+ mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
@@ -299,7 +301,7 @@
DisplayController displayController, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger, ShellExecutor mainExecutor,
+ ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
@@ -314,7 +316,7 @@
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
this::onTransitionAnimationComplete, this);
- mLogger = logger;
+ mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
mDisplayController.addDisplayWindowListener(this);
@@ -880,6 +882,8 @@
WindowContainerTransaction wct, @ExitReason int exitReason) {
if (!mMainStage.isActive() || mIsExiting) return;
+ onSplitScreenExit();
+
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
@@ -949,6 +953,47 @@
}
/**
+ * Overridden by child classes.
+ */
+ protected void onSplitScreenEnter() {
+ }
+
+ /**
+ * Overridden by child classes.
+ */
+ protected void onSplitScreenExit() {
+ }
+
+ /**
+ * Exits the split screen by finishing one of the tasks.
+ */
+ protected void exitStage(@SplitPosition int stageToClose) {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ StageTaskListener stageToTop = mSideStagePosition == stageToClose
+ ? mMainStage
+ : mSideStage;
+ exitSplitScreen(stageToTop, EXIT_REASON_APP_FINISHED);
+ } else {
+ boolean toEnd = stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ mSplitLayout.flingDividerToDismiss(toEnd, EXIT_REASON_APP_FINISHED);
+ }
+ }
+
+ /**
+ * Grants focus to the main or the side stages.
+ */
+ protected void grantFocusToStage(@SplitPosition int stageToFocus) {
+ IActivityTaskManager activityTaskManagerService = IActivityTaskManager.Stub.asInterface(
+ ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE));
+ try {
+ activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
+ } catch (RemoteException | NullPointerException e) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "%s: Unable to update focus on the chosen stage, %s", TAG, e);
+ }
+ }
+
+ /**
* Returns whether the split pair in the recent tasks list should be broken.
*/
private boolean shouldBreakPairedTaskInRecents(@ExitReason int exitReason) {
@@ -995,6 +1040,7 @@
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
if (mMainStage.isActive()) return;
+ onSplitScreenEnter();
if (taskInfo != null) {
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
@@ -1388,6 +1434,7 @@
}
} else if (isSideStage && hasChildren && !mMainStage.isActive()) {
// TODO (b/238697912) : Add the validation to prevent entering non-recovered status
+ onSplitScreenEnter();
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index e26c259..bcf4fbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -35,10 +35,14 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
+import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
+import java.util.Optional;
/**
* A handler for dealing with transitions involving multiple other handlers. For example: an
@@ -47,8 +51,8 @@
public class DefaultMixedHandler implements Transitions.TransitionHandler {
private final Transitions mPlayer;
- private final PipTransitionController mPipHandler;
- private final StageCoordinator mSplitHandler;
+ private PipTransitionController mPipHandler;
+ private StageCoordinator mSplitHandler;
private static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -77,13 +81,22 @@
mTransition = transition;
}
}
+
private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>();
- public DefaultMixedHandler(@NonNull Transitions player,
- @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
+ public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
+ Optional<SplitScreenController> splitScreenControllerOptional,
+ Optional<PipTouchHandler> pipTouchHandlerOptional) {
mPlayer = player;
- mPipHandler = pipHandler;
- mSplitHandler = splitHandler;
+ if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
+ && splitScreenControllerOptional.isPresent()) {
+ // Add after dependencies because it is higher priority
+ shellInit.addInitCallback(() -> {
+ mPipHandler = pipTouchHandlerOptional.get().getTransitionHandler();
+ mSplitHandler = splitScreenControllerOptional.get().getTransitionHandler();
+ mPlayer.addHandler(this);
+ }, this);
+ }
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java
deleted file mode 100644
index 678e91f..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java
+++ /dev/null
@@ -1,55 +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.transition;
-
-import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.sysui.ShellInit;
-
-import java.util.Optional;
-
-/**
- * Handles transitions between the Splitscreen and PIP components.
- */
-public class SplitscreenPipMixedHandler {
-
- private final Optional<SplitScreenController> mSplitScreenOptional;
- private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
- private final Transitions mTransitions;
-
- public SplitscreenPipMixedHandler(ShellInit shellInit,
- Optional<SplitScreenController> splitScreenControllerOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
- Transitions transitions) {
- mSplitScreenOptional = splitScreenControllerOptional;
- mPipTouchHandlerOptional = pipTouchHandlerOptional;
- mTransitions = transitions;
- if (Transitions.ENABLE_SHELL_TRANSITIONS
- && mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- private void onInit() {
- // Special handling for initializing based on multiple components
- final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions,
- mPipTouchHandlerOptional.get().getTransitionHandler(),
- mSplitScreenOptional.get().getTransitionHandler());
- // Added at end so that it has highest priority.
- mTransitions.addHandler(mixedHandler);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index 2cff171..c045ceb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.util;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.os.Parcel;
@@ -24,40 +25,143 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Simple container for recent tasks. May contain either a single or pair of tasks.
*/
public class GroupedRecentTaskInfo implements Parcelable {
- public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
- public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
- public @Nullable SplitBounds mSplitBounds;
- public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
- this(task1, null, null);
+ public static final int TYPE_SINGLE = 1;
+ public static final int TYPE_SPLIT = 2;
+ public static final int TYPE_FREEFORM = 3;
+
+ @IntDef(prefix = {"TYPE_"}, value = {
+ TYPE_SINGLE,
+ TYPE_SPLIT,
+ TYPE_FREEFORM
+ })
+ public @interface GroupType {}
+
+ @NonNull
+ private final ActivityManager.RecentTaskInfo[] mTasks;
+ @Nullable
+ private final SplitBounds mSplitBounds;
+ @GroupType
+ private final int mType;
+
+ /**
+ * Create new for a single task
+ */
+ public static GroupedRecentTaskInfo forSingleTask(
+ @NonNull ActivityManager.RecentTaskInfo task) {
+ return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
+ TYPE_SINGLE);
}
- public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
- @Nullable ActivityManager.RecentTaskInfo task2,
- @Nullable SplitBounds splitBounds) {
- mTaskInfo1 = task1;
- mTaskInfo2 = task2;
+ /**
+ * Create new for a pair of tasks in split screen
+ */
+ public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
+ @NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
+ return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
+ splitBounds, TYPE_SPLIT);
+ }
+
+ /**
+ * Create new for a group of freeform tasks
+ */
+ public static GroupedRecentTaskInfo forFreeformTasks(
+ @NonNull ActivityManager.RecentTaskInfo... tasks) {
+ return new GroupedRecentTaskInfo(tasks, null, TYPE_FREEFORM);
+ }
+
+ private GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo[] tasks,
+ @Nullable SplitBounds splitBounds, @GroupType int type) {
+ mTasks = tasks;
mSplitBounds = splitBounds;
+ mType = type;
}
GroupedRecentTaskInfo(Parcel parcel) {
- mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
- mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
+ mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
+ mType = parcel.readInt();
+ }
+
+ /**
+ * Get primary {@link ActivityManager.RecentTaskInfo}
+ */
+ @NonNull
+ public ActivityManager.RecentTaskInfo getTaskInfo1() {
+ return mTasks[0];
+ }
+
+ /**
+ * Get secondary {@link ActivityManager.RecentTaskInfo}.
+ *
+ * Used in split screen.
+ */
+ @Nullable
+ public ActivityManager.RecentTaskInfo getTaskInfo2() {
+ if (mTasks.length > 1) {
+ return mTasks[1];
+ }
+ return null;
+ }
+
+ /**
+ * Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
+ */
+ @NonNull
+ public List<ActivityManager.RecentTaskInfo> getTaskInfoList() {
+ return Arrays.asList(mTasks);
+ }
+
+ /**
+ * Return {@link SplitBounds} if this is a split screen entry or {@code null}
+ */
+ @Nullable
+ public SplitBounds getSplitBounds() {
+ return mSplitBounds;
+ }
+
+ /**
+ * Get type of this recents entry. One of {@link GroupType}
+ */
+ @GroupType
+ public int getType() {
+ return mType;
}
@Override
public String toString() {
- String taskString = "Task1: " + getTaskInfo(mTaskInfo1)
- + ", Task2: " + getTaskInfo(mTaskInfo2);
- if (mSplitBounds != null) {
- taskString += ", SplitBounds: " + mSplitBounds.toString();
+ StringBuilder taskString = new StringBuilder();
+ for (int i = 0; i < mTasks.length; i++) {
+ if (i == 0) {
+ taskString.append("Task");
+ } else {
+ taskString.append(", Task");
+ }
+ taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks[i]));
}
- return taskString;
+ if (mSplitBounds != null) {
+ taskString.append(", SplitBounds: ").append(mSplitBounds);
+ }
+ taskString.append(", Type=");
+ switch (mType) {
+ case TYPE_SINGLE:
+ taskString.append("TYPE_SINGLE");
+ break;
+ case TYPE_SPLIT:
+ taskString.append("TYPE_SPLIT");
+ break;
+ case TYPE_FREEFORM:
+ taskString.append("TYPE_FREEFORM");
+ break;
+ }
+ return taskString.toString();
}
private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
@@ -74,9 +178,9 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeTypedObject(mTaskInfo1, flags);
- parcel.writeTypedObject(mTaskInfo2, flags);
+ parcel.writeTypedArray(mTasks, flags);
parcel.writeTypedObject(mSplitBounds, flags);
+ parcel.writeInt(mType);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 1b5091f..a8d3bdc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -99,7 +100,8 @@
@Mock private TaskStackListenerImpl mMockTaskStackListener;
@Mock private ShellExecutor mMockExecutor;
@Mock private Optional<OneHandedController> mMockOneHandedController;
- @Mock private PipParamsChangedForwarder mPipParamsChangedForwarder;
+ @Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder;
+ @Mock private DisplayInsetsController mMockDisplayInsetsController;
@Mock private DisplayLayout mMockDisplayLayout1;
@Mock private DisplayLayout mMockDisplayLayout2;
@@ -120,8 +122,8 @@
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mPipParamsChangedForwarder,
- mMockOneHandedController, mMockExecutor);
+ mMockTaskStackListener, mMockPipParamsChangedForwarder,
+ mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor);
mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -186,8 +188,8 @@
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mPipParamsChangedForwarder,
- mMockOneHandedController, mMockExecutor));
+ mMockTaskStackListener, mMockPipParamsChangedForwarder,
+ mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
new file mode 100644
index 0000000..baa06f2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -0,0 +1,169 @@
+/*
+ * 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.recents
+
+import android.app.ActivityManager
+import android.graphics.Rect
+import android.os.Parcel
+import android.testing.AndroidTestingRunner
+import android.window.IWindowContainerToken
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT
+import com.android.wm.shell.util.SplitBounds
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+/**
+ * Tests for [GroupedRecentTaskInfo]
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class GroupedRecentTaskInfoTest : ShellTestCase() {
+
+ @Test
+ fun testSingleTask_hasCorrectType() {
+ assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_SINGLE)
+ }
+
+ @Test
+ fun testSingleTask_task1Set_task2Null() {
+ val group = singleTaskGroupInfo()
+ assertThat(group.taskInfo1.taskId).isEqualTo(1)
+ assertThat(group.taskInfo2).isNull()
+ }
+
+ @Test
+ fun testSingleTask_taskInfoList_hasOneTask() {
+ val list = singleTaskGroupInfo().taskInfoList
+ assertThat(list).hasSize(1)
+ assertThat(list[0].taskId).isEqualTo(1)
+ }
+
+ @Test
+ fun testSplitTasks_hasCorrectType() {
+ assertThat(splitTasksGroupInfo().type).isEqualTo(TYPE_SPLIT)
+ }
+
+ @Test
+ fun testSplitTasks_task1Set_task2Set_boundsSet() {
+ val group = splitTasksGroupInfo()
+ assertThat(group.taskInfo1.taskId).isEqualTo(1)
+ assertThat(group.taskInfo2?.taskId).isEqualTo(2)
+ assertThat(group.splitBounds).isNotNull()
+ }
+
+ @Test
+ fun testSplitTasks_taskInfoList_hasTwoTasks() {
+ val list = splitTasksGroupInfo().taskInfoList
+ assertThat(list).hasSize(2)
+ assertThat(list[0].taskId).isEqualTo(1)
+ assertThat(list[1].taskId).isEqualTo(2)
+ }
+
+ @Test
+ fun testFreeformTasks_hasCorrectType() {
+ assertThat(freeformTasksGroupInfo().type).isEqualTo(TYPE_FREEFORM)
+ }
+
+ @Test
+ fun testSplitTasks_taskInfoList_hasThreeTasks() {
+ val list = freeformTasksGroupInfo().taskInfoList
+ assertThat(list).hasSize(3)
+ assertThat(list[0].taskId).isEqualTo(1)
+ assertThat(list[1].taskId).isEqualTo(2)
+ assertThat(list[2].taskId).isEqualTo(3)
+ }
+
+ @Test
+ fun testParcelling_singleTask() {
+ val recentTaskInfo = singleTaskGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SINGLE)
+ assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(recentTaskInfoParcel.taskInfo2).isNull()
+ }
+
+ @Test
+ fun testParcelling_splitTasks() {
+ val recentTaskInfo = splitTasksGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT)
+ assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
+ assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
+ assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
+ }
+
+ @Test
+ fun testParcelling_freeformTasks() {
+ val recentTaskInfo = freeformTasksGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
+ assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3)
+ // Only compare task ids
+ val taskIdComparator = Correspondence.transforming<ActivityManager.RecentTaskInfo, Int>(
+ { it?.taskId }, "has taskId of"
+ )
+ assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
+ .containsExactly(1, 2, 3)
+ }
+
+ private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
+ taskId = id
+ token = WindowContainerToken(mock(IWindowContainerToken::class.java))
+ }
+
+ private fun singleTaskGroupInfo(): GroupedRecentTaskInfo {
+ val task = createTaskInfo(id = 1)
+ return GroupedRecentTaskInfo.forSingleTask(task)
+ }
+
+ private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val splitBounds = SplitBounds(Rect(), Rect(), 1, 2)
+ return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
+ }
+
+ private fun freeformTasksGroupInfo(): GroupedRecentTaskInfo {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val task3 = createTaskInfo(id = 3)
+ return GroupedRecentTaskInfo.forFreeformTasks(task1, task2, task3)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 81bb609..e9a1e25 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -20,6 +20,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -45,11 +48,13 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -179,6 +184,46 @@
}
@Test
+ public void testGetRecentTasks_groupActiveFreeformTasks() {
+ StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
+ DesktopMode.class).startMocking();
+ when(DesktopMode.isActive(any())).thenReturn(true);
+
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ setRawList(t1, t2, t3, t4);
+
+ mRecentTasksController.addActiveFreeformTask(1);
+ mRecentTasksController.addActiveFreeformTask(3);
+
+ ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
+ MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
+
+ // 2 freeform tasks should be grouped into one, 3 total recents entries
+ assertEquals(3, recentTasks.size());
+ GroupedRecentTaskInfo freeformGroup = recentTasks.get(0);
+ GroupedRecentTaskInfo singleGroup1 = recentTasks.get(1);
+ GroupedRecentTaskInfo singleGroup2 = recentTasks.get(2);
+
+ // Check that groups have expected types
+ assertEquals(GroupedRecentTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
+ assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup1.getType());
+ assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
+
+ // Check freeform group entries
+ assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
+ assertEquals(t3, freeformGroup.getTaskInfoList().get(1));
+
+ // Check single entries
+ assertEquals(t2, singleGroup1.getTaskInfo1());
+ assertEquals(t4, singleGroup2.getTaskInfo1());
+
+ mockitoSession.finishMocking();
+ }
+
+ @Test
public void testRemovedTaskRemovesSplit() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -254,6 +299,7 @@
/**
* Asserts that the recent tasks matches the given task ids.
+ *
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
@@ -262,22 +308,23 @@
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
GroupedRecentTaskInfo pair = recentTasks.get(i);
- int taskId1 = pair.mTaskInfo1.taskId;
+ int taskId1 = pair.getTaskInfo1().taskId;
flattenedTaskIds[2 * i] = taskId1;
- flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
- ? pair.mTaskInfo2.taskId
+ flattenedTaskIds[2 * i + 1] = pair.getTaskInfo2() != null
+ ? pair.getTaskInfo2().taskId
: -1;
- if (pair.mTaskInfo2 != null) {
- assertNotNull(pair.mSplitBounds);
- int leftTopTaskId = pair.mSplitBounds.leftTopTaskId;
- int bottomRightTaskId = pair.mSplitBounds.rightBottomTaskId;
+ if (pair.getTaskInfo2() != null) {
+ assertNotNull(pair.getSplitBounds());
+ int leftTopTaskId = pair.getSplitBounds().leftTopTaskId;
+ int bottomRightTaskId = pair.getSplitBounds().rightBottomTaskId;
// Unclear if pairs are ordered by split position, most likely not.
- assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId);
+ assertTrue(leftTopTaskId == taskId1
+ || leftTopTaskId == pair.getTaskInfo2().taskId);
assertTrue(bottomRightTaskId == taskId1
- || bottomRightTaskId == pair.mTaskInfo2.taskId);
+ || bottomRightTaskId == pair.getTaskInfo2().taskId);
} else {
- assertNull(pair.mSplitBounds);
+ assertNull(pair.getSplitBounds());
}
}
assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index a67853c..ae69b3d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -71,11 +71,11 @@
DisplayController displayController, DisplayImeController imeController,
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger, ShellExecutor mainExecutor,
+ ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, logger, mainExecutor, recentTasks);
+ transitions, transactionPool, mainExecutor, recentTasks);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 1d038f4..ea0033b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -95,7 +95,6 @@
@Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
- @Mock private SplitscreenEventLogger mLogger;
@Mock private IconProvider mIconProvider;
@Mock private ShellExecutor mMainExecutor;
private SplitLayout mSplitLayout;
@@ -127,7 +126,7 @@
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mLogger, mMainExecutor, Optional.empty());
+ mTransactionPool, mMainExecutor, Optional.empty());
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 4b68870..ea9390e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -97,8 +97,6 @@
@Mock
private TransactionPool mTransactionPool;
@Mock
- private SplitscreenEventLogger mLogger;
- @Mock
private ShellExecutor mMainExecutor;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
@@ -115,7 +113,7 @@
MockitoAnnotations.initMocks(this);
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger,
+ mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, Optional.empty()));
doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 650f360..7ccbe51 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2385,6 +2385,14 @@
return types.size() == 1 && types.contains(type);
}
+ /**
+ * @hide
+ * Return true if the audio device type is a Bluetooth LE Audio device.
+ */
+ public static boolean isLeAudioDeviceType(int type) {
+ return DEVICE_OUT_ALL_BLE_SET.contains(type);
+ }
+
/** @hide */
public static final int DEFAULT_MUTE_STREAMS_AFFECTED =
(1 << STREAM_MUSIC) |
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index e18642c..0c8cacd 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -79,20 +79,24 @@
/**
* This is a class for reading and writing Exif tags in various image file formats.
* <p>
+ * <b>Note:</b> This class has known issues on some versions of Android. It is recommended to use
+ * the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/exifinterface/media/ExifInterface.html">ExifInterface
+ * Library</a> since it offers a superset of the functionality of this class and is more easily
+ * updateable. In addition to the functionality of this class, it supports parsing extra metadata
+ * such as exposure and data compression information as well as setting extra metadata such as GPS
+ * and datetime information.
+ * <p>
* Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF,
* AVIF.
* <p>
- * Supported for writing: JPEG, PNG, WebP, DNG.
+ * Supported for writing: JPEG, PNG, WebP.
* <p>
* Note: JPEG and HEIF files may contain XMP data either inside the Exif data chunk or outside of
* it. This class will search both locations for XMP data, but if XMP data exist both inside and
* outside Exif, will favor the XMP data inside Exif over the one outside.
* <p>
- * Note: It is recommended to use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/exifinterface/media/ExifInterface.html">ExifInterface
- * Library</a> since it is a superset of this class. In addition to the functionalities of this
- * class, it supports parsing extra metadata such as exposure and data compression information
- * as well as setting extra metadata such as GPS and datetime information.
+
*/
public class ExifInterface {
private static final String TAG = "ExifInterface";
@@ -1294,7 +1298,6 @@
new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
- new ExifTag(TAG_XMP, 700, IFD_FORMAT_BYTE),
new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
@@ -2076,7 +2079,7 @@
* {@link #setAttribute(String,String)} to set all attributes to write and
* make a single call rather than multiple calls for each attribute.
* <p>
- * This method is supported for JPEG, PNG, WebP, and DNG files.
+ * This method is supported for JPEG, PNG, and WebP files.
* <p class="note">
* Note: after calling this method, any attempts to obtain range information
* from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
@@ -2088,11 +2091,15 @@
* <p>
* For PNG format, the Exif data will be stored as an "eXIf" chunk as per
* "Extensions to the PNG 1.2 Specification, Version 1.5.0".
+ * <p>
+ * <b>Warning:</b> Calling this method on a DNG-based instance of {@code ExifInterface} may
+ * result in the original image file being overwritten with invalid data on some versions of
+ * Android 13 (API 33).
*/
public void saveAttributes() throws IOException {
if (!isSupportedFormatForSavingAttributes()) {
throw new IOException("ExifInterface only supports saving attributes for JPEG, PNG, "
- + "WebP, and DNG formats.");
+ + "and WebP formats.");
}
if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
throw new IOException(
@@ -2150,10 +2157,6 @@
savePngAttributes(bufferedIn, bufferedOut);
} else if (mMimeType == IMAGE_TYPE_WEBP) {
saveWebpAttributes(bufferedIn, bufferedOut);
- } else if (mMimeType == IMAGE_TYPE_DNG || mMimeType == IMAGE_TYPE_UNKNOWN) {
- ByteOrderedDataOutputStream dataOutputStream =
- new ByteOrderedDataOutputStream(bufferedOut, ByteOrder.BIG_ENDIAN);
- writeExifSegment(dataOutputStream);
}
}
} catch (Exception e) {
@@ -5262,8 +5265,7 @@
private boolean isSupportedFormatForSavingAttributes() {
if (mIsSupportedFile && (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_PNG
- || mMimeType == IMAGE_TYPE_WEBP || mMimeType == IMAGE_TYPE_DNG
- || mMimeType == IMAGE_TYPE_UNKNOWN)) {
+ || mMimeType == IMAGE_TYPE_WEBP)) {
return true;
}
return false;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 86a94a9..82c3139 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -29,11 +29,12 @@
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
+import android.os.Trace;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.provider.Settings;
import android.util.Log;
-
+import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
import java.util.ArrayList;
@@ -136,13 +137,73 @@
*/
public void setAudioAttributes(AudioAttributes attributes)
throws IllegalArgumentException {
+ setAudioAttributesField(attributes);
+ // The audio attributes have to be set before the media player is prepared.
+ // Re-initialize it.
+ setUri(mUri, mVolumeShaperConfig);
+ createLocalMediaPlayer();
+ }
+
+ /**
+ * Same as {@link #setAudioAttributes(AudioAttributes)} except this one does not create
+ * the media player.
+ * @hide
+ */
+ public void setAudioAttributesField(@Nullable AudioAttributes attributes) {
if (attributes == null) {
throw new IllegalArgumentException("Invalid null AudioAttributes for Ringtone");
}
mAudioAttributes = attributes;
- // The audio attributes have to be set before the media player is prepared.
- // Re-initialize it.
- setUri(mUri, mVolumeShaperConfig);
+ }
+
+ /**
+ * Creates a local media player for the ringtone using currently set attributes.
+ * @hide
+ */
+ public void createLocalMediaPlayer() {
+ Trace.beginSection("createLocalMediaPlayer");
+ if (mUri == null) {
+ Log.e(TAG, "Could not create media player as no URI was provided.");
+ return;
+ }
+ destroyLocalPlayer();
+ // try opening uri locally before delegating to remote player
+ mLocalPlayer = new MediaPlayer();
+ try {
+ mLocalPlayer.setDataSource(mContext, mUri);
+ mLocalPlayer.setAudioAttributes(mAudioAttributes);
+ synchronized (mPlaybackSettingsLock) {
+ applyPlaybackProperties_sync();
+ }
+ if (mVolumeShaperConfig != null) {
+ mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
+ }
+ mLocalPlayer.prepare();
+
+ } catch (SecurityException | IOException e) {
+ destroyLocalPlayer();
+ if (!mAllowRemote) {
+ Log.w(TAG, "Remote playback not allowed: " + e);
+ }
+ }
+
+ if (LOGD) {
+ if (mLocalPlayer != null) {
+ Log.d(TAG, "Successfully created local player");
+ } else {
+ Log.d(TAG, "Problem opening; delegating to remote player");
+ }
+ }
+ Trace.endSection();
+ }
+
+ /**
+ * Returns whether a local player has been created for this ringtone.
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean hasLocalPlayer() {
+ return mLocalPlayer != null;
}
/**
@@ -336,8 +397,7 @@
}
/**
- * Set {@link Uri} to be used for ringtone playback. Attempts to open
- * locally, otherwise will delegate playback to remote
+ * Set {@link Uri} to be used for ringtone playback.
* {@link IRingtonePlayer}.
*
* @hide
@@ -348,6 +408,13 @@
}
/**
+ * @hide
+ */
+ public void setVolumeShaperConfig(@Nullable VolumeShaper.Configuration volumeShaperConfig) {
+ mVolumeShaperConfig = volumeShaperConfig;
+ }
+
+ /**
* Set {@link Uri} to be used for ringtone playback. Attempts to open
* locally, otherwise will delegate playback to remote
* {@link IRingtonePlayer}. Add {@link VolumeShaper} if required.
@@ -356,41 +423,10 @@
*/
public void setUri(Uri uri, @Nullable VolumeShaper.Configuration volumeShaperConfig) {
mVolumeShaperConfig = volumeShaperConfig;
- destroyLocalPlayer();
mUri = uri;
if (mUri == null) {
- return;
- }
-
- // TODO: detect READ_EXTERNAL and specific content provider case, instead of relying on throwing
-
- // try opening uri locally before delegating to remote player
- mLocalPlayer = new MediaPlayer();
- try {
- mLocalPlayer.setDataSource(mContext, mUri);
- mLocalPlayer.setAudioAttributes(mAudioAttributes);
- synchronized (mPlaybackSettingsLock) {
- applyPlaybackProperties_sync();
- }
- if (mVolumeShaperConfig != null) {
- mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
- }
- mLocalPlayer.prepare();
-
- } catch (SecurityException | IOException e) {
destroyLocalPlayer();
- if (!mAllowRemote) {
- Log.w(TAG, "Remote playback not allowed: " + e);
- }
- }
-
- if (LOGD) {
- if (mLocalPlayer != null) {
- Log.d(TAG, "Successfully created local player");
- } else {
- Log.d(TAG, "Problem opening; delegating to remote player");
- }
}
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 2772769..27db41c 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -480,8 +480,9 @@
if (mStopPreviousRingtone && mPreviousRingtone != null) {
mPreviousRingtone.stop();
}
-
- mPreviousRingtone = getRingtone(mContext, getRingtoneUri(position), inferStreamType());
+
+ mPreviousRingtone =
+ getRingtone(mContext, getRingtoneUri(position), inferStreamType(), true);
return mPreviousRingtone;
}
@@ -677,7 +678,7 @@
*/
public static Ringtone getRingtone(final Context context, Uri ringtoneUri) {
// Don't set the stream type
- return getRingtone(context, ringtoneUri, -1);
+ return getRingtone(context, ringtoneUri, -1, true);
}
/**
@@ -698,7 +699,34 @@
final Context context, Uri ringtoneUri,
@Nullable VolumeShaper.Configuration volumeShaperConfig) {
// Don't set the stream type
- return getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig);
+ return getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig, true);
+ }
+
+ /**
+ * @hide
+ */
+ public static Ringtone getRingtone(final Context context, Uri ringtoneUri,
+ @Nullable VolumeShaper.Configuration volumeShaperConfig,
+ boolean createLocalMediaPlayer) {
+ // Don't set the stream type
+ return getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig,
+ createLocalMediaPlayer);
+ }
+
+ /**
+ * @hide
+ */
+ public static Ringtone getRingtone(final Context context, Uri ringtoneUri,
+ @Nullable VolumeShaper.Configuration volumeShaperConfig,
+ AudioAttributes audioAttributes) {
+ // Don't set the stream type
+ Ringtone ringtone =
+ getRingtone(context, ringtoneUri, -1 /* streamType */, volumeShaperConfig, false);
+ if (ringtone != null) {
+ ringtone.setAudioAttributesField(audioAttributes);
+ ringtone.createLocalMediaPlayer();
+ }
+ return ringtone;
}
//FIXME bypass the notion of stream types within the class
@@ -707,14 +735,19 @@
* type. Normally, if you change the stream type on the returned
* {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
* an optimized route to avoid that.
- *
+ *
* @param streamType The stream type for the ringtone, or -1 if it should
* not be set (and the default used instead).
+ * @param createLocalMediaPlayer when true, the ringtone returned will be fully
+ * created otherwise, it will require the caller to create the media player manually
+ * {@link Ringtone#createLocalMediaPlayer()} in order to play the Ringtone.
* @see #getRingtone(Context, Uri)
*/
@UnsupportedAppUsage
- private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
- return getRingtone(context, ringtoneUri, streamType, null /* volumeShaperConfig */);
+ private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType,
+ boolean createLocalMediaPlayer) {
+ return getRingtone(context, ringtoneUri, streamType, null /* volumeShaperConfig */,
+ createLocalMediaPlayer);
}
//FIXME bypass the notion of stream types within the class
@@ -730,16 +763,21 @@
* @see #getRingtone(Context, Uri)
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private static Ringtone getRingtone(
- final Context context, Uri ringtoneUri, int streamType,
- @Nullable VolumeShaper.Configuration volumeShaperConfig) {
+ private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType,
+ @Nullable VolumeShaper.Configuration volumeShaperConfig,
+ boolean createLocalMediaPlayer) {
try {
final Ringtone r = new Ringtone(context, true);
if (streamType >= 0) {
//FIXME deprecated call
r.setStreamType(streamType);
}
+
+ r.setVolumeShaperConfig(volumeShaperConfig);
r.setUri(ringtoneUri, volumeShaperConfig);
+ if (createLocalMediaPlayer) {
+ r.createLocalMediaPlayer();
+ }
return r;
} catch (Exception ex) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index ead5174..0de7be0 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -43,6 +43,8 @@
<color name="settingslib_color_grey400">#bdc1c6</color>
<color name="settingslib_color_grey300">#dadce0</color>
<color name="settingslib_color_grey200">#e8eaed</color>
+ <color name="settingslib_color_grey100">#f1f3f4</color>
+ <color name="settingslib_color_grey50">#f8f9fa</color>
<color name="settingslib_color_orange600">#e8710a</color>
<color name="settingslib_color_orange400">#fa903e</color>
<color name="settingslib_color_orange300">#fcad70</color>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
new file mode 100644
index 0000000..93b6acc
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -0,0 +1,88 @@
+/*
+ * 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.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+
+import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieProperty;
+import com.airbnb.lottie.model.KeyPath;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Util class which dynamically changes the color of tags in a lottie json file between Dark Theme
+ * (DT) and Light Theme (LT). This class assumes the json file is for Dark Theme.
+ */
+public class LottieColorUtils {
+ private static final Map<String, Integer> DARK_TO_LIGHT_THEME_COLOR_MAP;
+
+ static {
+ HashMap<String, Integer> map = new HashMap<>();
+ map.put(
+ ".grey600",
+ R.color.settingslib_color_grey300);
+ map.put(
+ ".grey800",
+ R.color.settingslib_color_grey200);
+ map.put(
+ ".grey900",
+ R.color.settingslib_color_grey50);
+ map.put(
+ ".red400",
+ R.color.settingslib_color_red600);
+ map.put(
+ ".black",
+ android.R.color.white);
+ map.put(
+ ".blue400",
+ R.color.settingslib_color_blue600);
+ map.put(
+ ".green400",
+ R.color.settingslib_color_green600);
+ DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
+ }
+
+ private LottieColorUtils() {
+ }
+
+ private static boolean isDarkMode(Context context) {
+ return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ /** Applies dynamic colors based on DT vs. LT. The LottieAnimationView should be Dark Theme. */
+ public static void applyDynamicColors(Context context,
+ LottieAnimationView lottieAnimationView) {
+ // Assume the default for the lottie is dark mode
+ if (isDarkMode(context)) {
+ return;
+ }
+ for (String key : DARK_TO_LIGHT_THEME_COLOR_MAP.keySet()) {
+ final int color = context.getColor(DARK_TO_LIGHT_THEME_COLOR_MAP.get(key));
+ lottieAnimationView.addValueCallback(
+ new KeyPath("**", key, "**"),
+ LottieProperty.COLOR_FILTER,
+ frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+ }
+ }
+}
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 84c5a69..e050ae0 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -597,7 +597,7 @@
<string name="guest_reset_guest" msgid="6110013010356013758">"கெஸ்ட் அமர்வை மீட்டமை"</string>
<string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"கெஸ்ட்டை மீட்டமைக்கவா?"</string>
<string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"கெஸ்ட் பயனரை அகற்றவா?"</string>
- <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"மீட்டமை"</string>
+ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ரீசெட்"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"அகற்று"</string>
<string name="guest_resetting" msgid="7822120170191509566">"கெஸ்ட்டை மீட்டமைக்கிறது…"</string>
<string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"கெஸ்ட் அமர்வை ரீசெட் செய்யவா?"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
index 63a9f0c..adfa39e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -21,8 +21,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -59,9 +57,9 @@
void startActivityForResult(Intent intent, int resultCode);
- boolean startSystemActivityForResult(Intent intent, int resultCode);
-
int getPhotoSize();
+
+ boolean canCropPhoto();
}
interface ContextInjector {
@@ -84,7 +82,6 @@
private static final long DELAY_BEFORE_CROP_MILLIS = 150;
private static final String IMAGES_DIR = "multi_user";
- private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg";
private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg";
private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg";
@@ -94,7 +91,6 @@
private final ContextInjector mContextInjector;
private final File mImagesDir;
- private final Uri mPreCropPictureUri;
private final Uri mCropPictureUri;
private final Uri mTakePictureUri;
@@ -104,8 +100,6 @@
mImagesDir = new File(mContextInjector.getCacheDir(), IMAGES_DIR);
mImagesDir.mkdir();
- mPreCropPictureUri = mContextInjector
- .createTempImageUri(mImagesDir, PRE_CROP_PICTURE_FILE_NAME, !waiting);
mCropPictureUri =
mContextInjector.createTempImageUri(mImagesDir, CROP_PICTURE_FILE_NAME, !waiting);
mTakePictureUri =
@@ -137,7 +131,7 @@
return true;
case REQUEST_CODE_TAKE_PHOTO:
if (mTakePictureUri.equals(pictureUri)) {
- cropPhoto(pictureUri);
+ cropPhoto();
} else {
copyAndCropPhoto(pictureUri, false);
}
@@ -166,7 +160,7 @@
ThreadUtils.postOnBackgroundThread(() -> {
final ContentResolver cr = mContextInjector.getContentResolver();
try (InputStream in = cr.openInputStream(pictureUri);
- OutputStream out = cr.openOutputStream(mPreCropPictureUri)) {
+ OutputStream out = cr.openOutputStream(mTakePictureUri)) {
Streams.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to copy photo", e);
@@ -174,7 +168,7 @@
}
Runnable cropRunnable = () -> {
if (!mAvatarUi.isFinishing()) {
- cropPhoto(mPreCropPictureUri);
+ cropPhoto();
}
};
if (delayBeforeCrop) {
@@ -189,21 +183,22 @@
}
}
- private void cropPhoto(final Uri pictureUri) {
- // TODO: Use a public intent, when there is one.
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(pictureUri, "image/*");
- appendOutputExtra(intent, mCropPictureUri);
- appendCropExtras(intent);
- try {
- StrictMode.disableDeathOnFileUriExposure();
- if (mAvatarUi.startSystemActivityForResult(intent, REQUEST_CODE_CROP_PHOTO)) {
- return;
+ private void cropPhoto() {
+ if (mAvatarUi.canCropPhoto()) {
+ // TODO: Use a public intent, when there is one.
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setDataAndType(mTakePictureUri, "image/*");
+ appendOutputExtra(intent, mCropPictureUri);
+ appendCropExtras(intent);
+ try {
+ StrictMode.disableDeathOnFileUriExposure();
+ mAvatarUi.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO);
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
}
- } finally {
- StrictMode.enableDeathOnFileUriExposure();
+ } else {
+ onPhotoNotCropped(mTakePictureUri);
}
- onPhotoNotCropped(pictureUri);
}
private void appendOutputExtra(Intent intent, Uri pictureUri) {
@@ -325,23 +320,15 @@
}
@Override
- public boolean startSystemActivityForResult(Intent intent, int code) {
- ActivityInfo info = intent.resolveActivityInfo(mActivity.getPackageManager(),
- PackageManager.MATCH_SYSTEM_ONLY);
- if (info == null) {
- Log.w(TAG, "No system package activity could be found for code " + code);
- return false;
- }
- intent.setPackage(info.packageName);
- mActivity.startActivityForResult(intent, code);
- return true;
- }
-
- @Override
public int getPhotoSize() {
return mActivity.getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
}
+
+ @Override
+ public boolean canCropPhoto() {
+ return PhotoCapabilityUtils.canCropPhoto(mActivity);
+ }
}
static class ContextInjectorImpl implements ContextInjector {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
index d988111..3dc2fab 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AvatarPhotoControllerTest.java
@@ -34,7 +34,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
@@ -52,7 +51,6 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
@RunWith(AndroidJUnit4.class)
@@ -75,7 +73,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockAvatarUi.getPhotoSize()).thenReturn(PHOTO_SIZE);
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(true);
mImagesDir = new File(
InstrumentationRegistry.getTargetContext().getCacheDir(), "multi_user");
@@ -113,7 +110,9 @@
}
@Test
- public void takePhotoIsFollowedByCrop() throws IOException {
+ public void takePhotoIsFollowedByCropWhenSupported() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -122,12 +121,14 @@
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void takePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -137,11 +138,12 @@
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void takePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
@@ -149,12 +151,14 @@
mController.onActivityResult(
REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsFollowedByCrop() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -163,12 +167,14 @@
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
public void choosePhotoIsNotFollowedByCropWhenResultCodeNotOk() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "file.txt").createNewFile();
Intent intent = new Intent();
@@ -178,11 +184,12 @@
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_CANCELED, intent);
verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
- verify(mMockAvatarUi, never()).startSystemActivityForResult(any(), anyInt());
}
@Test
public void choosePhotoIsFollowedByCropWhenTakePhotoUriReturned() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(true);
+
new File(mImagesDir, "TakeEditUserPhoto.jpg").createNewFile();
Intent intent = new Intent();
@@ -190,11 +197,28 @@
mController.onActivityResult(
REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
- verifyStartSystemActivityForResult(
+ verifyStartActivityForResult(
"com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
}
@Test
+ public void choosePhotoIsNotFollowedByCropIntentWhenCropNotSupported() throws IOException {
+ when(mMockAvatarUi.canCropPhoto()).thenReturn(false);
+
+ File file = new File(mImagesDir, "file.txt");
+ saveBitmapToFile(file);
+
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(
+ "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
+ mController.onActivityResult(
+ REQUEST_CODE_CHOOSE_PHOTO, Activity.RESULT_OK, intent);
+
+ verify(mMockAvatarUi, never()).startActivityForResult(any(), anyInt());
+ verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
+ }
+
+ @Test
public void cropPhotoResultIsReturnedIfResultOkAndContent() {
Intent intent = new Intent();
intent.setData(mCropPhotoUri);
@@ -218,58 +242,11 @@
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS).times(0)).returnUriResult(mCropPhotoUri);
}
- @Test
- public void cropDoesNotUseTakePhotoUri() throws IOException {
- new File(mImagesDir, "file.txt").createNewFile();
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- Intent startIntent = verifyStartSystemActivityForResult(
- "com.android.camera.action.CROP", REQUEST_CODE_CROP_PHOTO);
- assertThat(startIntent.getData()).isNotEqualTo(mTakePhotoUri);
- }
-
- @Test
- public void internalCropUsedIfNoSystemCropperFound() throws IOException {
- when(mMockAvatarUi.startSystemActivityForResult(any(), anyInt())).thenReturn(false);
-
- File file = new File(mImagesDir, "file.txt");
- saveBitmapToFile(file);
-
- Intent intent = new Intent();
- intent.setData(Uri.parse(
- "content://com.android.settingslib.test/my_cache/multi_user/file.txt"));
- mController.onActivityResult(
- REQUEST_CODE_TAKE_PHOTO, Activity.RESULT_OK, intent);
-
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS)).returnUriResult(mCropPhotoUri);
-
- InputStream imageStream = mContext.getContentResolver().openInputStream(mCropPhotoUri);
- Bitmap bitmap = BitmapFactory.decodeStream(imageStream);
- assertThat(bitmap.getWidth()).isEqualTo(PHOTO_SIZE);
- assertThat(bitmap.getHeight()).isEqualTo(PHOTO_SIZE);
- }
-
- private Intent verifyStartActivityForResult(String action, int resultCode) {
+ private void verifyStartActivityForResult(String action, int resultCode) {
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
.startActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
- }
-
- private Intent verifyStartSystemActivityForResult(String action, int resultCode) {
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockAvatarUi, timeout(TIMEOUT_MILLIS))
- .startSystemActivityForResult(captor.capture(), eq(resultCode));
- Intent intent = captor.getValue();
- assertThat(intent.getAction()).isEqualTo(action);
- return intent;
+ assertThat(captor.getValue().getAction()).isEqualTo(action);
}
private void saveBitmapToFile(File file) throws IOException {
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
new file mode 100644
index 0000000..b006615
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+
+/**
+ * Checks for slow calls to ActivityManager.getCurrentUser() or UserManager.getUserInfo() and
+ * suggests using UserTracker instead. For more info, see: http://go/multi-user-in-systemui-slides.
+ */
+@Suppress("UnstableApiUsage")
+class SlowUserQueryDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames(): List<String> {
+ return listOf("getCurrentUser", "getUserInfo")
+ }
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ val evaluator = context.evaluator
+ if (
+ evaluator.isStatic(method) &&
+ method.name == "getCurrentUser" &&
+ method.containingClass?.qualifiedName == "android.app.ActivityManager"
+ ) {
+ context.report(
+ ISSUE_SLOW_USER_ID_QUERY,
+ method,
+ context.getNameLocation(node),
+ "ActivityManager.getCurrentUser() is slow. " +
+ "Use UserTracker.getUserId() instead."
+ )
+ }
+ if (
+ !evaluator.isStatic(method) &&
+ method.name == "getUserInfo" &&
+ method.containingClass?.qualifiedName == "android.os.UserManager"
+ ) {
+ context.report(
+ ISSUE_SLOW_USER_INFO_QUERY,
+ method,
+ context.getNameLocation(node),
+ "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val ISSUE_SLOW_USER_ID_QUERY: Issue =
+ Issue.create(
+ id = "SlowUserIdQuery",
+ briefDescription = "User ID queried using ActivityManager instead of UserTracker.",
+ explanation =
+ "ActivityManager.getCurrentUser() makes a binder call and is slow. " +
+ "Instead, inject a UserTracker and call UserTracker.getUserId(). For " +
+ "more info, see: http://go/multi-user-in-systemui-slides",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ @JvmField
+ val ISSUE_SLOW_USER_INFO_QUERY: Issue =
+ Issue.create(
+ id = "SlowUserInfoQuery",
+ briefDescription = "User info queried using UserManager instead of UserTracker.",
+ explanation =
+ "UserManager.getUserInfo() makes a binder call and is slow. " +
+ "Instead, inject a UserTracker and call UserTracker.getUserInfo(). For " +
+ "more info, see: http://go/multi-user-in-systemui-slides",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index c7c73d3..4879883 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -30,6 +30,8 @@
get() = listOf(
BindServiceViaContextDetector.ISSUE,
BroadcastSentViaContextDetector.ISSUE,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY,
GetMainLooperViaContextDetector.ISSUE,
RegisterReceiverViaContextDetector.ISSUE,
SoftwareBitmapDetector.ISSUE,
diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
new file mode 100644
index 0000000..2738f04
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
@@ -0,0 +1,194 @@
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class SlowUserQueryDetectorTest : LintDetectorTest() {
+
+ override fun getDetector(): Detector = SlowUserQueryDetector()
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ override fun getIssues(): List<Issue> =
+ listOf(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+
+ @Test
+ fun testGetCurrentUser() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.app.ActivityManager;
+
+ public class TestClass1 {
+ public void slewlyGetCurrentUser() {
+ ActivityManager.getCurrentUser();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "ActivityManager.getCurrentUser() is slow. " +
+ "Use UserTracker.getUserId() instead."
+ )
+ }
+
+ @Test
+ fun testGetUserInfo() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.os.UserManager;
+
+ public class TestClass2 {
+ public void slewlyGetUserInfo(UserManager userManager) {
+ userManager.getUserInfo();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+ )
+ }
+
+ @Test
+ fun testUserTrackerGetUserId() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import com.android.systemui.settings.UserTracker;
+
+ public class TestClass3 {
+ public void quicklyGetUserId(UserTracker userTracker) {
+ userTracker.getUserId();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testUserTrackerGetUserInfo() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import com.android.systemui.settings.UserTracker;
+
+ public class TestClass4 {
+ public void quicklyGetUserId(UserTracker userTracker) {
+ userTracker.getUserInfo();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectClean()
+ }
+
+ private val activityManagerStub: TestFile =
+ java(
+ """
+ package android.app;
+
+ public class ActivityManager {
+ public static int getCurrentUser() {};
+ }
+ """
+ )
+
+ private val userManagerStub: TestFile =
+ java(
+ """
+ package android.os;
+ import android.content.pm.UserInfo;
+ import android.annotation.UserIdInt;
+
+ public class UserManager {
+ public UserInfo getUserInfo(@UserIdInt int userId) {};
+ }
+ """
+ )
+
+ private val userIdIntStub: TestFile =
+ java(
+ """
+ package android.annotation;
+
+ public @interface UserIdInt {}
+ """
+ )
+
+ private val userInfoStub: TestFile =
+ java(
+ """
+ package android.content.pm;
+
+ public class UserInfo {}
+ """
+ )
+
+ private val userTrackerStub: TestFile =
+ java(
+ """
+ package com.android.systemui.settings;
+ import android.content.pm.UserInfo;
+
+ public interface UserTracker {
+ public int getUserId();
+ public UserInfo getUserInfo();
+ }
+ """
+ )
+
+ private val stubs =
+ arrayOf(activityManagerStub, userManagerStub, userIdIntStub, userInfoStub, userTrackerStub)
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
index 11477f9..6588e22 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
@@ -83,7 +83,11 @@
flowOf(
securityText?.let { text ->
SecurityButtonConfig(
- icon = Icon.Resource(R.drawable.ic_info_outline),
+ icon =
+ Icon.Resource(
+ R.drawable.ic_info_outline,
+ contentDescription = null,
+ ),
text = text,
isClickable = securityClickable,
)
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
new file mode 100644
index 0000000..02d76f4
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.systemui.user
+
+import android.content.Context
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.compose.gallery.R
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import com.android.systemui.util.mockito.mock
+
+object Fakes {
+ private val USER_TINT_COLORS =
+ arrayOf(
+ 0x000000,
+ 0x0000ff,
+ 0x00ff00,
+ 0x00ffff,
+ 0xff0000,
+ 0xff00ff,
+ 0xffff00,
+ 0xffffff,
+ )
+
+ fun fakeUserSwitcherViewModel(
+ context: Context,
+ userCount: Int,
+ ): UserSwitcherViewModel {
+ return UserSwitcherViewModel.Factory(
+ userInteractor =
+ UserInteractor(
+ repository =
+ FakeUserRepository().apply {
+ setUsers(
+ (0 until userCount).map { index ->
+ UserModel(
+ id = index,
+ name = Text.Loaded("user_$index"),
+ image =
+ checkNotNull(
+ AppCompatResources.getDrawable(
+ context,
+ R.drawable.ic_avatar_guest_user
+ )
+ ),
+ isSelected = index == 0,
+ isSelectable = true,
+ )
+ }
+ )
+ setActions(
+ UserActionModel.values().mapNotNull {
+ if (it == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
+ null
+ } else {
+ it
+ }
+ }
+ )
+ },
+ controller = mock(),
+ activityStarter = mock(),
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository =
+ FakeKeyguardRepository().apply { setKeyguardShowing(false) },
+ ),
+ ),
+ powerInteractor =
+ PowerInteractor(
+ repository = FakePowerRepository(),
+ )
+ )
+ .create(UserSwitcherViewModel::class.java)
+ }
+}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 51cc195..689938a 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -240,8 +240,6 @@
-packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -529,6 +527,8 @@
-packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
+-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
-packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
@@ -677,7 +677,6 @@
-packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -834,6 +833,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
new file mode 100644
index 0000000..57b3acd
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.android.keyguard.BouncerKeyguardMessageArea
+ android:id="@+id/bouncer_message_area"
+ style="@style/Keyguard.TextView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/keyguard_lock_padding"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:gravity="center"
+ android:singleLine="true" />
+</merge>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index e77e084..5486adb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -28,6 +28,7 @@
android:layout_gravity="center_horizontal|bottom"
android:gravity="bottom"
>
+ <include layout="@layout/keyguard_bouncer_message_area"/>
<Space
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 231ead8..2b7bdc2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -31,6 +31,7 @@
android:layout_gravity="center_horizontal|bottom"
android:clipChildren="false"
android:clipToPadding="false">
+ <include layout="@layout/keyguard_bouncer_message_area"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pattern_container"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 712f657..64ece47 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -17,23 +17,25 @@
*/
-->
-<com.android.keyguard.KeyguardPINView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/keyguard_pin_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- androidprv:layout_maxWidth="@dimen/keyguard_security_width"
- android:layout_gravity="center_horizontal|bottom"
- android:orientation="vertical"
- >
+<com.android.keyguard.KeyguardPINView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ androidprv:layout_maxWidth="@dimen/keyguard_security_width">
+<include layout="@layout/keyguard_bouncer_message_area"/>
- <androidx.constraintlayout.widget.ConstraintLayout
+<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pin_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_weight="1"
android:layoutDirection="ltr"
android:orientation="vertical">
@@ -79,6 +81,8 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false"
androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter"
@@ -186,8 +190,6 @@
</androidx.constraintlayout.widget.ConstraintLayout>
-
-
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index dae2e56..f2fe520 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -26,20 +26,17 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal|bottom">
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
-
+ <include layout="@layout/keyguard_bouncer_message_area" />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
<ImageView
android:id="@+id/keyguard_sim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tint="@color/background_protected"
android:src="@drawable/ic_lockscreen_sim"/>
-
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -52,14 +49,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eca_overlap" />
-
<RelativeLayout
android:id="@+id/row0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="4dp"
>
-
<com.android.keyguard.PasswordTextView
android:id="@+id/simPinEntry"
style="@style/Widget.TextView.Password"
@@ -195,7 +190,6 @@
/>
</LinearLayout>
</LinearLayout>
-
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
@@ -205,5 +199,4 @@
android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:layout_marginBottom="2dp"
android:gravity="center_horizontal"/>
-
</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 74f7820..a21ec29 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -27,12 +27,12 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal|bottom">
+ <include layout="@layout/keyguard_bouncer_message_area"/>
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
<ImageView
android:id="@+id/keyguard_sim"
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 625ffd3..a1d1266 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -50,7 +50,7 @@
<item name="android:background">@null</item>
<item name="android:textSize">32sp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:paddingBottom">-16dp</item>
</style>
<style name="Widget.TextView.Password" parent="@android:style/Widget.TextView">
diff --git a/packages/SystemUI/res/drawable/media_seekbar_thumb.xml b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml
new file mode 100644
index 0000000..5eb2bfd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml
@@ -0,0 +1,50 @@
+<!--
+ ~ 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="16dp"
+ android:width="4dp"
+ android:viewportHeight="16"
+ android:viewportWidth="4">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G"
+ android:translateX="2"
+ android:translateY="8">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M2 -6 C2,-6 2,6 2,6 C2,7.1 1.1,8 0,8 C0,8 0,8 0,8 C-1.1,8 -2,7.1 -2,6 C-2,6 -2,-6 -2,-6 C-2,-7.1 -1.1,-8 0,-8 C0,-8 0,-8 0,-8 C1.1,-8 2,-7.1 2,-6c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="1017"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index d633803..be76405 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -51,7 +51,7 @@
android:id="@+id/biometric_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal">
+ android:layout_gravity="center">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/biometric_icon"
@@ -61,6 +61,13 @@
android:contentDescription="@null"
android:scaleType="fitXY" />
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/biometric_icon_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:contentDescription="@null"
+ android:scaleType="fitXY" />
</FrameLayout>
<!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 0ca19d9..8df8c49 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -83,48 +83,6 @@
android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
android:visibility="gone" />
- <ImageView
- android:id="@+id/wallet_button"
- android:layout_height="@dimen/keyguard_affordance_fixed_height"
- android:layout_width="@dimen/keyguard_affordance_fixed_width"
- android:layout_gravity="bottom|end"
- android:scaleType="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_wallet_lockscreen"
- android:background="@drawable/keyguard_bottom_affordance_bg"
- android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
- android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
- android:contentDescription="@string/accessibility_wallet_button"
- android:visibility="gone" />
-
- <ImageView
- android:id="@+id/qr_code_scanner_button"
- android:layout_height="@dimen/keyguard_affordance_fixed_height"
- android:layout_width="@dimen/keyguard_affordance_fixed_width"
- android:layout_gravity="bottom|end"
- android:scaleType="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/ic_qr_code_scanner"
- android:background="@drawable/keyguard_bottom_affordance_bg"
- android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset"
- android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
- android:contentDescription="@string/accessibility_qr_code_scanner_button"
- android:visibility="gone" />
-
- <ImageView
- android:id="@+id/controls_button"
- android:layout_height="@dimen/keyguard_affordance_fixed_height"
- android:layout_width="@dimen/keyguard_affordance_fixed_width"
- android:layout_gravity="bottom|start"
- android:scaleType="center"
- android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/controls_icon"
- android:background="@drawable/keyguard_bottom_affordance_bg"
- android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset"
- android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
- android:contentDescription="@string/quick_controls_title"
- android:visibility="gone" />
-
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 86f8ce2..0c57b934 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -88,7 +88,7 @@
android:layout_marginTop="@dimen/status_bar_height"
android:layout_gravity="top|center_horizontal"
android:gravity="center_horizontal">
- <com.android.keyguard.KeyguardMessageArea
+ <com.android.keyguard.AuthKeyguardMessageArea
android:id="@+id/keyguard_message_area"
style="@style/Keyguard.TextView"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/raw/biometricprompt_fingerprint_to_error_landscape.json b/packages/SystemUI/res/raw/biometricprompt_fingerprint_to_error_landscape.json
new file mode 100644
index 0000000..3d33b2a
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_fingerprint_to_error_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Symbol_Fingerprint_To_Error_Landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_landscape_base.json b/packages/SystemUI/res/raw/biometricprompt_landscape_base.json
new file mode 100644
index 0000000..3781eee
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_landscape_base.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}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_portrait_base_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_portrait_base_bottomright.json
new file mode 100644
index 0000000..4950666
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_portrait_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}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json b/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json
new file mode 100644
index 0000000..b161609
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_portrait_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":".grey905","cl":"grey905","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":".grey904","cl":"grey904","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":".grey903","cl":"grey903","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":".grey902","cl":"grey902","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":".grey901","cl":"grey901","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}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_landscape.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_landscape.json
new file mode 100644
index 0000000..034ac87
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_fingerprint_landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_bottomright.json
new file mode 100644
index 0000000..e5cc565e
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_fingerprint_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 12","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_topleft.json
new file mode 100644
index 0000000..b082265
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_fingerprint_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_fingerprint_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 12","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short_For_ErrorToFingerprint","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_landscape.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_landscape.json
new file mode 100644
index 0000000..befe3bb
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_success_landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[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":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_bottomright.json
new file mode 100644
index 0000000..d75b335
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_success_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[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":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 13","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_topleft.json
new file mode 100644
index 0000000..e6b2db1
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_error_to_success_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_error_to_success_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":112,"s":[0]},{"t":122,"s":[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":112,"op":1012,"st":112,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":118,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":257,"s":[100]},{"t":277,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":108,"op":1280,"st":108,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 13","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_ErrorToSuccess","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":792,"st":-108,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_bottomright.json
new file mode 100644
index 0000000..0da143c
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_error_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 14","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_topleft.json
new file mode 100644
index 0000000..15457c7
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_error_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_error_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 14","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":830,"st":-70,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_landscape.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_landscape.json
new file mode 100644
index 0000000..f39894b
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_landscape.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_success_landscape","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[170,77.667,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_bottomright.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_bottomright.json
new file mode 100644
index 0000000..6d4f4e2
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_bottomright.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_success_portrait_bottomright","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 15","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_topleft.json b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_topleft.json
new file mode 100644
index 0000000..b7bb0d5
--- /dev/null
+++ b/packages/SystemUI/res/raw/biometricprompt_symbol_fingerprint_to_success_portrait_topleft.json
@@ -0,0 +1 @@
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_symbol_fingerprint_to_success_portrait_topleft","ddd":0,"assets":[{"id":"comp_0","nm":"Fingerprint_Animation_ForConfirm_Short","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle fill 5","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-52.056,-2.445],[-17.306,32.25],[52.194,-37.194]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"t":249,"s":[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":239,"op":1139,"st":239,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":239,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":249,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":388,"s":[100]},{"t":408,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.450980392157,0.709803921569,0.470588235294,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":239,"op":1411,"st":239,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"circle fill 3","td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".white","cl":"white","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,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":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":90,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.8,"y":0},"t":108,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,-49.945],[-0.056,14.056]],"c":false}]},{"t":118,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.111,14.055],[-0.056,14.056]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-0.056,35.389],[-0.111,50.111]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"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.049,42.698],"ix":2},"a":{"a":0,"k":[0.049,42.698],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":80,"s":[0,0]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":90,"s":[100,100]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.8,0.8],"y":[0,0]},"t":108,"s":[100,100]},{"t":118,"s":[0,0]}],"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 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":80,"op":980,"st":80,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":80,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":90,"s":[100]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":108,"s":[100]},{"t":118,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,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,62.45],[-62.45,0],[0,-62.45],[62.45,0]],"o":[[0,-62.45],[62.45,0],[0,62.45],[-62.45,0]],"v":[[-113.08,0],[0,-113.08],[113.08,0],[0,113.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[206,150],"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 4","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333393172,0.403921598547,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":80,"op":1252,"st":80,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null 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.833,"y":0.833},"o":{"x":0.333,"y":0.333},"t":85,"s":[209.333,136.333,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":440,"s":[209.333,136.333,0],"to":[1.944,2.278,0],"ti":[3.056,-2.278,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":450,"s":[221,150,0],"to":[-3.056,2.278,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":460,"s":[191,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":470,"s":[221,150,0],"to":[0,0,0],"ti":[2.5,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":480,"s":[191,150,0],"to":[-2.5,0,0],"ti":[-2.5,0,0]},{"t":490,"s":[206,150,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[132,132,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 15","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":2,"ty":0,"nm":"Fingerprint_Animation_ForConfirm_Short","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[0,-92.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[30,30,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":661,"st":-239,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index c0071cb..80628f9 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -35,9 +35,6 @@
<!-- How many lines to show in the security footer -->
<integer name="qs_security_footer_maxLines">1</integer>
- <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
- <bool name="allow_force_nav_bar_handle_opaque">false</bool>
-
<bool name="config_use_large_screen_shade_header">true</bool>
<!-- Whether to show the side fps hint while on bouncer -->
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index a0bf072..3d8da8a 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -23,7 +23,7 @@
<dimen name="status_view_margin_horizontal">124dp</dimen>
<dimen name="keyguard_clock_top_margin">80dp</dimen>
<dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
- <dimen name="bouncer_user_switcher_y_trans">90dp</dimen>
+ <dimen name="bouncer_user_switcher_y_trans">200dp</dimen>
<dimen name="large_screen_shade_header_left_padding">24dp</dimen>
<dimen name="qqs_layout_padding_bottom">40dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ec22c60..a802723 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -587,9 +587,6 @@
when the double-press power gesture is used. Ignored if empty. -->
<string translatable="false" name="config_cameraGesturePackage"></string>
- <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
- <bool name="allow_force_nav_bar_handle_opaque">true</bool>
-
<!-- Whether a transition of ACTIVITY_TYPE_DREAM to the home app should play a home sound
effect -->
<bool name="config_playHomeSoundAfterDream">false</bool>
@@ -617,8 +614,9 @@
<!-- Which face help messages to surface when fingerprint is also enrolled.
Message ids correspond with the acquired ids in BiometricFaceConstants -->
<integer-array name="config_face_help_msgs_when_fingerprint_enrolled">
- <item>25</item>
- <item>26</item>
+ <item>3</item> <!-- TOO_DARK -->
+ <item>25</item> <!-- DARK_GLASSES -->
+ <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
</integer-array>
<!-- Whether the communal service should be enabled -->
@@ -629,11 +627,15 @@
<!-- This value is used when calculating whether the device is in ambient light mode. It is
light mode when the light sensor sample value exceeds above this value. -->
- <integer name="config_ambientLightModeThreshold">10</integer>
+ <item name="config_ambientLightModeThreshold" translatable="false" format="float" type="dimen">
+ 0.8
+ </item>
<!-- This value is used when calculating whether the device is in ambient dark mode. It is
dark mode when the light sensor sample value drops below this value. -->
- <integer name="config_ambientDarkModeThreshold">5</integer>
+ <item name="config_ambientDarkModeThreshold" translatable="false" format="float" type="dimen">
+ 0.4
+ </item>
<!-- This value is used when calculating whether the device is in ambient light mode. Each
sample contains light sensor events from this span of time duration. -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a36a518..ac3eb7e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -573,6 +573,7 @@
</style>
<style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
+ <item name="android:thumb">@drawable/media_seekbar_thumb</item>
<item name="android:thumbTint">?android:attr/textColorPrimary</item>
<item name="android:progressDrawable">@drawable/media_squiggly_progress</item>
<item name="android:progressTint">?android:attr/textColorPrimary</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index a030bf6..e77c650 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -42,13 +42,6 @@
void onOverviewShown(boolean fromHome) = 6;
/**
- * Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
- * and home handle & background in gestural mode). The {@param animate} is currently only
- * supported for 2 button mode.
- */
- void setNavBarButtonAlpha(float alpha, boolean animate) = 19;
-
- /**
* Proxies motion events from the homescreen UI to the status bar. Only called when
* swipe down is detected on WORKSPACE. The sender guarantees the following order of events on
* the tracking pointer.
diff --git a/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt
new file mode 100644
index 0000000..82ce1ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.keyguard
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.util.AttributeSet
+
+/**
+ * Displays security messages for auth outside of the security method (pin, password, pattern), like
+ * biometric auth.
+ */
+class AuthKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
+ KeyguardMessageArea(context, attrs) {
+ override fun updateTextColor() {
+ setTextColor(ColorStateList.valueOf(Color.WHITE))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
new file mode 100644
index 0000000..0075ddd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.graphics.Color
+import android.util.AttributeSet
+import com.android.settingslib.Utils
+
+/** Displays security messages for the keyguard bouncer. */
+class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
+ KeyguardMessageArea(context, attrs) {
+ private val DEFAULT_COLOR = -1
+ private var mDefaultColorState: ColorStateList? = null
+ private var mNextMessageColorState: ColorStateList? = ColorStateList.valueOf(DEFAULT_COLOR)
+
+ override fun updateTextColor() {
+ var colorState = mDefaultColorState
+ mNextMessageColorState?.defaultColor?.let { color ->
+ if (color != DEFAULT_COLOR) {
+ colorState = mNextMessageColorState
+ mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR)
+ }
+ }
+ setTextColor(colorState)
+ }
+
+ override fun setNextMessageColor(colorState: ColorStateList?) {
+ mNextMessageColorState = colorState
+ }
+
+ override fun onThemeChanged() {
+ val array: TypedArray =
+ mContext.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary))
+ val newTextColors: ColorStateList = ColorStateList.valueOf(array.getColor(0, Color.RED))
+ array.recycle()
+ mDefaultColorState = newTextColors
+ super.onThemeChanged()
+ }
+
+ override fun reloadColor() {
+ mDefaultColorState = Utils.getColorAttr(context, android.R.attr.textColorPrimary)
+ super.reloadColor()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index b8fcb10..92ba619 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -50,7 +50,6 @@
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
private CountDownTimer mCountdownTimer;
- protected KeyguardMessageAreaController mMessageAreaController;
private boolean mDismissing;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected boolean mResumed;
@@ -80,14 +79,13 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController) {
- super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ messageAreaControllerFactory);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = messageAreaControllerFactory.create(kma);
}
abstract void resetState();
@@ -95,7 +93,6 @@
@Override
public void onInit() {
super.onInit();
- mMessageAreaController.init();
}
@Override
@@ -134,6 +131,10 @@
@Override
public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (mMessageAreaController == null) {
+ return;
+ }
+
if (colorState != null) {
mMessageAreaController.setNextMessageColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 87300c3..f26b905 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -17,9 +17,11 @@
package com.android.keyguard;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.telephony.TelephonyManager;
+import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.util.LatencyTracker;
@@ -44,7 +46,7 @@
private final EmergencyButton mEmergencyButton;
private final EmergencyButtonController mEmergencyButtonController;
private boolean mPaused;
-
+ protected KeyguardMessageAreaController<BouncerKeyguardMessageArea> mMessageAreaController;
// The following is used to ignore callbacks from SecurityViews that are no longer current
// (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
@@ -72,12 +74,24 @@
protected KeyguardInputViewController(T view, SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
- EmergencyButtonController emergencyButtonController) {
+ EmergencyButtonController emergencyButtonController,
+ @Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
super(view);
mSecurityMode = securityMode;
mKeyguardSecurityCallback = keyguardSecurityCallback;
mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
mEmergencyButtonController = emergencyButtonController;
+ if (messageAreaControllerFactory != null) {
+ try {
+ BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
+ mMessageAreaController = messageAreaControllerFactory.create(kma);
+ mMessageAreaController.init();
+ mMessageAreaController.setIsVisible(true);
+ } catch (IllegalArgumentException exception) {
+ Log.e("KeyguardInputViewController",
+ "Ensure that a BouncerKeyguardMessageArea is included in the layout");
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 5ab2fd0..c79fc2c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -17,9 +17,7 @@
package com.android.keyguard;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
@@ -33,7 +31,6 @@
import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.lang.ref.WeakReference;
@@ -41,7 +38,7 @@
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
+public abstract class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
@@ -50,15 +47,11 @@
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
- private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
- private ColorStateList mDefaultColorState;
private CharSequence mMessage;
- private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
- private boolean mBouncerShowing;
- private boolean mAltBouncerShowing;
+ private boolean mIsVisible;
/**
* Container that wraps the KeyguardMessageArea - may be null if current view hierarchy doesn't
* contain {@link R.id.keyguard_message_area_container}.
@@ -96,23 +89,11 @@
mContainer.setLayoutParams(lp);
}
- @Override
- public void setNextMessageColor(ColorStateList colorState) {
- mNextMessageColorState = colorState;
- }
-
- void onThemeChanged() {
- TypedArray array = mContext.obtainStyledAttributes(new int[] {
- android.R.attr.textColorPrimary
- });
- ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED));
- array.recycle();
- mDefaultColorState = newTextColors;
+ protected void onThemeChanged() {
update();
}
- void reloadColor() {
- mDefaultColorState = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
+ protected void reloadColor() {
update();
}
@@ -151,17 +132,6 @@
setMessage(message);
}
- public static KeyguardMessageArea findSecurityMessageDisplay(View v) {
- KeyguardMessageArea messageArea = v.findViewById(R.id.keyguard_message_area);
- if (messageArea == null) {
- messageArea = v.getRootView().findViewById(R.id.keyguard_message_area);
- }
- if (messageArea == null) {
- throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
- }
- return messageArea;
- }
-
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
@@ -177,40 +147,23 @@
void update() {
CharSequence status = mMessage;
- setVisibility(TextUtils.isEmpty(status) || (!mBouncerShowing && !mAltBouncerShowing)
- ? INVISIBLE : VISIBLE);
+ setVisibility(TextUtils.isEmpty(status) || (!mIsVisible) ? INVISIBLE : VISIBLE);
setText(status);
- ColorStateList colorState = mDefaultColorState;
- if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) {
- colorState = mNextMessageColorState;
- mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
- }
- if (mAltBouncerShowing) {
- // alt bouncer has a black scrim, so always show the text in white
- colorState = ColorStateList.valueOf(Color.WHITE);
- }
- setTextColor(colorState);
+ updateTextColor();
}
/**
* Set whether the bouncer is fully showing
*/
- public void setBouncerShowing(boolean bouncerShowing) {
- if (mBouncerShowing != bouncerShowing) {
- mBouncerShowing = bouncerShowing;
+ public void setIsVisible(boolean isVisible) {
+ if (mIsVisible != isVisible) {
+ mIsVisible = isVisible;
update();
}
}
- /**
- * Set whether the alt bouncer is showing
- */
- void setAltBouncerShowing(boolean showing) {
- if (mAltBouncerShowing != showing) {
- mAltBouncerShowing = showing;
- update();
- }
- }
+ /** Set the text color */
+ protected abstract void updateTextColor();
/**
* Runnable used to delay accessibility announcements.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 3ec8ce9..c2802f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -26,11 +26,14 @@
import javax.inject.Inject;
-/** Controller for a {@link KeyguardMessageAreaController}. */
-public class KeyguardMessageAreaController extends ViewController<KeyguardMessageArea> {
+/**
+ * Controller for a {@link KeyguardMessageAreaController}.
+ * @param <T> A subclass of KeyguardMessageArea.
+ */
+public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
+ extends ViewController<T> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
- private boolean mAltBouncerShowing;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
public void onFinishedGoingToSleep(int why) {
@@ -59,7 +62,7 @@
}
};
- private KeyguardMessageAreaController(KeyguardMessageArea view,
+ protected KeyguardMessageAreaController(T view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController) {
super(view);
@@ -83,17 +86,10 @@
}
/**
- * Set whether alt bouncer is showing
+ * Indicate that view is visible and can display messages.
*/
- public void setAltBouncerShowing(boolean showing) {
- mView.setAltBouncerShowing(showing);
- }
-
- /**
- * Set bouncer is fully showing
- */
- public void setBouncerShowing(boolean showing) {
- mView.setBouncerShowing(showing);
+ public void setIsVisible(boolean isVisible) {
+ mView.setIsVisible(isVisible);
}
public void setMessage(CharSequence s) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 6844b65..20fa8f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -16,23 +16,25 @@
package com.android.keyguard;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_APPEAR;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_PIN_DISAPPEAR;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.view.View;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
-import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
/**
@@ -40,7 +42,7 @@
*/
public class KeyguardPINView extends KeyguardPinBasedInputView {
- private final AppearAnimationUtils mAppearAnimationUtils;
+ ValueAnimator mAppearAnimator = ValueAnimator.ofFloat(0f, 1f);
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
private ConstraintLayout mContainer;
@@ -54,7 +56,6 @@
public KeyguardPINView(Context context, AttributeSet attrs) {
super(context, attrs);
- mAppearAnimationUtils = new AppearAnimationUtils(context);
mDisappearAnimationUtils = new DisappearAnimationUtils(context,
125, 0.6f /* translationScale */,
0.45f /* delayScale */, AnimationUtils.loadInterpolator(
@@ -169,25 +170,20 @@
@Override
public void startAppearAnimation() {
- enableClipping(false);
- setAlpha(1f);
- setTranslationY(mAppearAnimationUtils.getStartTranslation());
- AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
- 0, mAppearAnimationUtils.getInterpolator(),
- getAnimationListener(CUJ_LOCKSCREEN_PIN_APPEAR));
- mAppearAnimationUtils.startAnimation2d(mViews,
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- }
- });
+ if (mAppearAnimator.isRunning()) {
+ mAppearAnimator.cancel();
+ }
+ mAppearAnimator.setDuration(650);
+ mAppearAnimator.addUpdateListener(animation -> animate(animation.getAnimatedFraction()));
+ mAppearAnimator.start();
}
public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
final Runnable finishRunnable) {
+ if (mAppearAnimator.isRunning()) {
+ mAppearAnimator.cancel();
+ }
- enableClipping(false);
setTranslationY(0);
DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
@@ -195,7 +191,6 @@
disappearAnimationUtils.createAnimation(
this, 0, 200, mDisappearYTranslation, false,
mDisappearAnimationUtils.getInterpolator(), () -> {
- enableClipping(true);
if (finishRunnable != null) {
finishRunnable.run();
}
@@ -204,14 +199,32 @@
return true;
}
- private void enableClipping(boolean enable) {
- mContainer.setClipToPadding(enable);
- mContainer.setClipChildren(enable);
- setClipChildren(enable);
- }
-
@Override
public boolean hasOverlappingRendering() {
return false;
}
+
+ /** Animate subviews according to expansion or time. */
+ private void animate(float progress) {
+ for (int i = 0; i < mViews.length; i++) {
+ View[] row = mViews[i];
+ for (View view : row) {
+ if (view == null) {
+ continue;
+ }
+
+ float scaledProgress = MathUtils.constrain(
+ (progress - 0.075f * i) / (1f - 0.075f * mViews.length),
+ 0f,
+ 1f
+ );
+ view.setAlpha(scaledProgress);
+ Interpolator interpolator = Interpolators.STANDARD_ACCELERATE;
+ view.setTranslationY(40 - (40 * interpolator.getInterpolation(scaledProgress)));
+ if (view instanceof NumPadAnimationListener) {
+ ((NumPadAnimationListener) view).setProgress(scaledProgress);
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 1862fc7..afc2590 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -71,7 +71,7 @@
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
- KeyguardMessageArea mSecurityMessageDisplay;
+ BouncerKeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
private ConstraintLayout mContainer;
@@ -120,7 +120,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
+ mSecurityMessageDisplay = findViewById(R.id.bouncer_message_area);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 9aa6f03..9871645 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -59,12 +59,9 @@
private final LatencyTracker mLatencyTracker;
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
- private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
private final DevicePostureController mPostureController;
private final DevicePostureController.Callback mPostureCallback =
posture -> mView.onDevicePostureChanged(posture);
-
- private KeyguardMessageAreaController mMessageAreaController;
private LockPatternView mLockPatternView;
private CountDownTimer mCountdownTimer;
private AsyncTask<?, ?, ?> mPendingLockCheck;
@@ -201,15 +198,13 @@
EmergencyButtonController emergencyButtonController,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
DevicePostureController postureController) {
- super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ messageAreaControllerFactory);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
- mMessageAreaControllerFactory = messageAreaControllerFactory;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = mMessageAreaControllerFactory.create(kma);
mLockPatternView = mView.findViewById(R.id.lockPatternView);
mPostureController = postureController;
}
@@ -217,7 +212,6 @@
@Override
public void onInit() {
super.onInit();
- mMessageAreaController.init();
}
@Override
@@ -346,6 +340,9 @@
@Override
public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (mMessageAreaController == null) {
+ return;
+ }
if (colorState != null) {
mMessageAreaController.setNextMessageColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index d0baf3d..f73c98e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -665,7 +665,8 @@
// When using EXACTLY spec, measure will use the layout width if > 0. Set before
// measuring the child
lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec);
- measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
+ measureChildWithMargins(view, updatedWidthMeasureSpec, 0,
+ heightMeasureSpec, 0);
maxWidth = Math.max(maxWidth,
view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
@@ -1306,7 +1307,6 @@
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mUserSwitcherViewGroup.setTranslationY(yTrans);
- mViewFlipper.setTranslationY(-yTrans);
} else {
// Attempt to reposition a bit higher to make up for this frame being a bit lower
// on the device
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 3aa5ada..bddf4b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -146,7 +146,8 @@
protected NullKeyguardInputViewController(SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
EmergencyButtonController emergencyButtonController) {
- super(null, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(null, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ null);
}
@Override
@@ -156,7 +157,6 @@
@Override
public void onStartingToHide() {
-
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index acbea1b..7d6f377 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -50,12 +50,10 @@
viewsIdToTranslate =
setOf(
ViewIdToTranslate(R.id.keyguard_status_area, LEFT, filterNever),
- ViewIdToTranslate(R.id.controls_button, LEFT, filterNever),
ViewIdToTranslate(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
ViewIdToTranslate(
R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
- ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever),
ViewIdToTranslate(R.id.start_button, LEFT, filterNever),
ViewIdToTranslate(R.id.end_button, RIGHT, filterNever)),
progressProvider = unfoldProgressProvider)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 4984300..4283832 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -466,6 +466,7 @@
FACE_AUTH_TRIGGERED_TRUST_DISABLED);
}
+ mLogger.logTrustChanged(wasTrusted, enabled, userId);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -481,12 +482,16 @@
final boolean userHasTrust = getUserHasTrust(userId);
if (userHasTrust && trustGrantedMessages != null) {
for (String msg : trustGrantedMessages) {
- if (!TextUtils.isEmpty(msg)) {
- message = msg;
+ message = msg;
+ if (!TextUtils.isEmpty(message)) {
break;
}
}
}
+
+ if (message != null) {
+ mLogger.logShowTrustGrantedMessage(message.toString());
+ }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -743,6 +748,7 @@
mFingerprintCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
+ mLogger.d("onFingerprintAuthenticated");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -986,6 +992,7 @@
mFaceCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED);
+ mLogger.d("onFaceAuthenticated");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -3445,6 +3452,7 @@
mUserFaceAuthenticated.clear();
mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser);
mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser);
+ mLogger.d("clearBiometricRecognized");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -3694,6 +3702,9 @@
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardUpdateMonitor state:");
+ pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
+ pw.println(" getUserUnlockedWithBiometric()="
+ + getUserUnlockedWithBiometric(getCurrentUser()));
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
pw.println(" " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt b/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt
new file mode 100644
index 0000000..f449edf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimationListener.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.keyguard
+
+/** Interface for classes to track animation progress. */
+interface NumPadAnimationListener {
+ /** Track the progress of the animation. */
+ fun setProgress(progress: Float)
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index c91c899..e0cafae 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -48,6 +48,10 @@
private int mTextColorPrimary;
private int mTextColorPressed;
private int mStyle;
+ private float mStartRadius;
+ private float mEndRadius;
+ private int mHeight;
+
private static final int EXPAND_ANIMATION_MS = 100;
private static final int EXPAND_COLOR_ANIMATION_MS = 50;
private static final int CONTRACT_ANIMATION_DELAY_MS = 33;
@@ -80,12 +84,20 @@
mContractAnimatorSet.start();
}
+ public void setProgress(float progress) {
+ mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
+ int height = (int) (mHeight * 0.8f + mHeight * 0.2 * progress);
+ int difference = mHeight - height;
+ mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2);
+ }
+
void onLayout(int height) {
- float startRadius = height / 2f;
- float endRadius = height / 4f;
- mBackground.setCornerRadius(startRadius);
- mExpandAnimator.setFloatValues(startRadius, endRadius);
- mContractAnimator.setFloatValues(endRadius, startRadius);
+ mHeight = height;
+ mStartRadius = height / 2f;
+ mEndRadius = height / 4f;
+ mBackground.setCornerRadius(mStartRadius);
+ mExpandAnimator.setFloatValues(mStartRadius, mEndRadius);
+ mContractAnimator.setFloatValues(mEndRadius, mStartRadius);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 8099f75..37060987c 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -30,7 +30,7 @@
/**
* Similar to the {@link NumPadKey}, but displays an image.
*/
-public class NumPadButton extends AlphaOptimizedImageButton {
+public class NumPadButton extends AlphaOptimizedImageButton implements NumPadAnimationListener {
@Nullable
private NumPadAnimator mAnimator;
@@ -104,4 +104,11 @@
a.recycle();
((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
}
+
+ @Override
+ public void setProgress(float progress) {
+ if (mAnimator != null) {
+ mAnimator.setProgress(progress);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 4aed251..0a4880e 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -37,7 +37,10 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
-public class NumPadKey extends ViewGroup {
+/**
+ * Viewgroup for the bouncer numpad button, specifically for digits.
+ */
+public class NumPadKey extends ViewGroup implements NumPadAnimationListener {
// list of "ABC", etc per digit, starting with '0'
static String sKlondike[];
@@ -221,4 +224,11 @@
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
+
+ @Override
+ public void setProgress(float progress) {
+ if (mAnimator != null) {
+ mAnimator.setProgress(progress);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
index 7c86a1d..777bd19 100644
--- a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
+++ b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -20,7 +20,8 @@
public interface SecurityMessageDisplay {
- void setNextMessageColor(ColorStateList colorState);
+ /** Set text color for the next security message. */
+ default void setNextMessageColor(ColorStateList colorState) {}
void setMessage(CharSequence msg);
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 2bc98f1..7a00cd9 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -340,4 +340,40 @@
bool1 = dismissKeyguard
}, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
}
+
+ fun logShowTrustGrantedMessage(
+ message: String
+ ) {
+ logBuffer.log(TAG, DEBUG, {
+ str1 = message
+ }, { "showTrustGrantedMessage message$str1" })
+ }
+
+ fun logTrustChanged(
+ wasTrusted: Boolean,
+ isNowTrusted: Boolean,
+ userId: Int
+ ) {
+ logBuffer.log(TAG, DEBUG, {
+ bool1 = wasTrusted
+ bool2 = isNowTrusted
+ int1 = userId
+ }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" })
+ }
+
+ fun logKeyguardStateUpdate(
+ secure: Boolean,
+ canDismissLockScreen: Boolean,
+ trusted: Boolean,
+ trustManaged: Boolean
+
+ ) {
+ logBuffer.log("KeyguardState", DEBUG, {
+ bool1 = secure
+ bool2 = canDismissLockScreen
+ bool3 = trusted
+ bool4 = trustManaged
+ }, { "#update secure=$bool1 canDismissKeyguard=$bool2" +
+ " trusted=$bool3 trustManaged=$bool4" })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
index 40d1eff..e4c197f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
@@ -29,8 +29,9 @@
/** Face/Fingerprint combined icon animator for BiometricPrompt. */
class AuthBiometricFingerprintAndFaceIconController(
context: Context,
- iconView: LottieAnimationView
-) : AuthBiometricFingerprintIconController(context, iconView) {
+ iconView: LottieAnimationView,
+ iconViewOverlay: LottieAnimationView
+) : AuthBiometricFingerprintIconController(context, iconView, iconViewOverlay) {
override val actsAsConfirmButton: Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
index 7371442..7f5a67f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
@@ -39,5 +39,5 @@
override fun onPointerDown(failedModalities: Set<Int>) = failedModalities.contains(TYPE_FACE)
override fun createIconController(): AuthIconController =
- AuthBiometricFingerprintAndFaceIconController(mContext, mIconView)
+ AuthBiometricFingerprintAndFaceIconController(mContext, mIconView, mIconViewOverlay)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 9b5f54a..b40b356 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -18,7 +18,12 @@
import android.annotation.RawRes
import android.content.Context
+import android.hardware.fingerprint.FingerprintManager
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.View
import com.airbnb.lottie.LottieAnimationView
+import com.android.settingslib.widget.LottieColorUtils
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
@@ -32,14 +37,18 @@
/** Fingerprint only icon animator for BiometricPrompt. */
open class AuthBiometricFingerprintIconController(
context: Context,
- iconView: LottieAnimationView
+ iconView: LottieAnimationView,
+ protected val iconViewOverlay: LottieAnimationView
) : AuthIconController(context, iconView) {
+ private val isSideFps: Boolean
var iconLayoutParamSize: Pair<Int, Int> = Pair(1, 1)
set(value) {
if (field == value) {
return
}
+ iconViewOverlay.layoutParams.width = value.first
+ iconViewOverlay.layoutParams.height = value.second
iconView.layoutParams.width = value.first
iconView.layoutParams.height = value.second
field = value
@@ -50,9 +59,53 @@
R.dimen.biometric_dialog_fingerprint_icon_width),
context.resources.getDimensionPixelSize(
R.dimen.biometric_dialog_fingerprint_icon_height))
+ var sideFps = false
+ (context.getSystemService(Context.FINGERPRINT_SERVICE)
+ as FingerprintManager?)?.let { fpm ->
+ for (prop in fpm.sensorPropertiesInternal) {
+ if (prop.isAnySidefpsType) {
+ sideFps = true
+ }
+ }
+ }
+ isSideFps = sideFps
+ val displayInfo = DisplayInfo()
+ context.display?.getDisplayInfo(displayInfo)
+ if (isSideFps && displayInfo.rotation == Surface.ROTATION_180) {
+ iconView.rotation = 180f
+ }
}
- override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ private fun updateIconSideFps(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ val displayInfo = DisplayInfo()
+ context.display?.getDisplayInfo(displayInfo)
+ val rotation = displayInfo.rotation
+ val iconAnimation = getSideFpsAnimationForTransition(rotation)
+ val iconViewOverlayAnimation =
+ getSideFpsOverlayAnimationForTransition(lastState, newState, rotation) ?: return
+
+ if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
+ iconView.setAnimation(iconAnimation)
+ iconViewOverlay.setAnimation(iconViewOverlayAnimation)
+ }
+
+ val iconContentDescription = getIconContentDescription(newState)
+ if (iconContentDescription != null) {
+ iconView.contentDescription = iconContentDescription
+ iconViewOverlay.contentDescription = iconContentDescription
+ }
+
+ iconView.frame = 0
+ iconViewOverlay.frame = 0
+ if (shouldAnimateForTransition(lastState, newState)) {
+ iconView.playAnimation()
+ iconViewOverlay.playAnimation()
+ }
+ LottieColorUtils.applyDynamicColors(context, iconView)
+ LottieColorUtils.applyDynamicColors(context, iconViewOverlay)
+ }
+
+ private fun updateIconNormal(@BiometricState lastState: Int, @BiometricState newState: Int) {
val icon = getAnimationForTransition(lastState, newState) ?: return
if (!(lastState == STATE_AUTHENTICATING_ANIMATING_IN && newState == STATE_AUTHENTICATING)) {
@@ -68,6 +121,16 @@
if (shouldAnimateForTransition(lastState, newState)) {
iconView.playAnimation()
}
+ LottieColorUtils.applyDynamicColors(context, iconView)
+ }
+
+ override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
+ if (isSideFps) {
+ updateIconSideFps(lastState, newState)
+ } else {
+ iconViewOverlay.visibility = View.GONE
+ updateIconNormal(lastState, newState)
+ }
}
private fun getIconContentDescription(@BiometricState newState: Int): CharSequence? {
@@ -125,4 +188,89 @@
}
return if (id != null) return id else null
}
+
+ @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
+ }
+
+ @RawRes
+ private fun getSideFpsOverlayAnimationForTransition(
+ @BiometricState oldState: Int,
+ @BiometricState newState: Int,
+ rotation: Int
+ ): Int? = when (newState) {
+ STATE_HELP,
+ STATE_ERROR -> {
+ when (rotation) {
+ Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ else -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ }
+ }
+ STATE_AUTHENTICATING_ANIMATING_IN,
+ STATE_AUTHENTICATING -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright
+ else -> R.raw.biometricprompt_symbol_error_to_fingerprint_landscape
+ }
+ } else {
+ when (rotation) {
+ Surface.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_fingerprint_to_error_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+ else -> R.raw.biometricprompt_fingerprint_to_error_landscape
+ }
+ }
+ }
+ STATE_AUTHENTICATED -> {
+ if (oldState == STATE_ERROR || oldState == STATE_HELP) {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_error_to_success_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_error_to_success_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_error_to_success_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright
+ else -> R.raw.biometricprompt_symbol_error_to_success_landscape
+ }
+ } else {
+ when (rotation) {
+ Surface.ROTATION_0 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ Surface.ROTATION_90 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft
+ Surface.ROTATION_180 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ Surface.ROTATION_270 ->
+ R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
+ else -> R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
+ }
+ }
+ }
+ else -> null
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
index 9cce066..2066634 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
@@ -86,7 +86,7 @@
override fun supportsSmallDialog() = false
override fun createIconController(): AuthIconController =
- AuthBiometricFingerprintIconController(mContext, mIconView)
+ AuthBiometricFingerprintIconController(mContext, mIconView, mIconViewOverlay)
fun updateOverrideIconLayoutParamsSize() {
udfpsAdapter?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index fc5cf9f..e94b1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -133,6 +133,7 @@
private TextView mSubtitleView;
private TextView mDescriptionView;
private View mIconHolderView;
+ protected LottieAnimationView mIconViewOverlay;
protected LottieAnimationView mIconView;
protected TextView mIndicatorView;
@@ -651,6 +652,7 @@
mTitleView = findViewById(R.id.title);
mSubtitleView = findViewById(R.id.subtitle);
mDescriptionView = findViewById(R.id.description);
+ mIconViewOverlay = findViewById(R.id.biometric_icon_overlay);
mIconView = findViewById(R.id.biometric_icon);
mIconHolderView = findViewById(R.id.biometric_icon_frame);
mIndicatorView = findViewById(R.id.indicator);
@@ -689,6 +691,11 @@
mIconController = createIconController();
if (mIconController.getActsAsConfirmButton()) {
+ mIconViewOverlay.setOnClickListener((view)->{
+ if (mState == STATE_PENDING_CONFIRMATION) {
+ updateState(STATE_AUTHENTICATED);
+ }
+ });
mIconView.setOnClickListener((view) -> {
if (mState == STATE_PENDING_CONFIRMATION) {
updateState(STATE_AUTHENTICATED);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
new file mode 100644
index 0000000..f2d8aaa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.systemui.biometrics
+
+/**
+ * Provides whether an acquired error message should be shown immediately when its received (see
+ * [shouldDefer]) or should be shown when the biometric error is received [getDeferredMessage].
+ * @property excludedMessages messages that are excluded from counts
+ * @property messagesToDefer messages that shouldn't show immediately when received, but may be
+ * shown later if the message is the most frequent message processed and meets [THRESHOLD]
+ * percentage of all messages (excluding [excludedMessages])
+ */
+class BiometricMessageDeferral(
+ private val excludedMessages: Set<Int>,
+ private val messagesToDefer: Set<Int>
+) {
+ private val msgCounts: MutableMap<Int, Int> = HashMap() // msgId => frequency of msg
+ private val msgIdToCharSequence: MutableMap<Int, CharSequence> = HashMap() // msgId => message
+ private var totalRelevantMessages = 0
+ private var mostFrequentMsgIdToDefer: Int? = null
+
+ /** Reset all saved counts. */
+ fun reset() {
+ totalRelevantMessages = 0
+ msgCounts.clear()
+ msgIdToCharSequence.clear()
+ }
+
+ /** Whether the given message should be deferred instead of being shown immediately. */
+ fun shouldDefer(acquiredMsgId: Int): Boolean {
+ return messagesToDefer.contains(acquiredMsgId)
+ }
+
+ /**
+ * Adds the acquiredMsgId to the counts if it's not in [excludedMessages]. We still count
+ * messages that shouldn't be deferred in these counts.
+ */
+ fun processMessage(acquiredMsgId: Int, helpString: CharSequence) {
+ if (excludedMessages.contains(acquiredMsgId)) {
+ return
+ }
+
+ totalRelevantMessages++
+ msgIdToCharSequence[acquiredMsgId] = helpString
+
+ val newAcquiredMsgCount = msgCounts.getOrDefault(acquiredMsgId, 0) + 1
+ msgCounts[acquiredMsgId] = newAcquiredMsgCount
+ if (
+ messagesToDefer.contains(acquiredMsgId) &&
+ (mostFrequentMsgIdToDefer == null ||
+ newAcquiredMsgCount > msgCounts.getOrDefault(mostFrequentMsgIdToDefer!!, 0))
+ ) {
+ mostFrequentMsgIdToDefer = acquiredMsgId
+ }
+ }
+
+ /**
+ * Get the most frequent deferred message that meets the [THRESHOLD] percentage of processed
+ * messages excluding [excludedMessages].
+ * @return null if no messages have been deferred OR deferred messages didn't meet the
+ * [THRESHOLD] percentage of messages to show.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ mostFrequentMsgIdToDefer?.let {
+ if (msgCounts.getOrDefault(it, 0) > (THRESHOLD * totalRelevantMessages)) {
+ return msgIdToCharSequence[mostFrequentMsgIdToDefer]
+ }
+ }
+
+ return null
+ }
+ companion object {
+ const val THRESHOLD = .5f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 0b65966..6c45af2 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -24,11 +24,15 @@
* [Icon.Resource] to a resource.
*/
sealed class Icon {
+ abstract val contentDescription: ContentDescription?
+
data class Loaded(
val drawable: Drawable,
+ override val contentDescription: ContentDescription?,
) : Icon()
data class Resource(
@DrawableRes val res: Int,
+ override val contentDescription: ContentDescription?,
) : Icon()
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
index d6433aa..93ae637 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
@@ -21,14 +21,16 @@
object ContentDescriptionViewBinder {
fun bind(
- contentDescription: ContentDescription,
+ contentDescription: ContentDescription?,
view: View,
) {
- when (contentDescription) {
- is ContentDescription.Loaded -> view.contentDescription = contentDescription.description
- is ContentDescription.Resource -> {
- view.contentDescription = view.context.resources.getString(contentDescription.res)
+ view.contentDescription =
+ when (contentDescription) {
+ null -> null
+ is ContentDescription.Loaded -> contentDescription.description
+ is ContentDescription.Resource -> {
+ view.context.resources.getString(contentDescription.res)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
index aecee2a..108e22b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
@@ -24,6 +24,7 @@
icon: Icon,
view: ImageView,
) {
+ ContentDescriptionViewBinder.bind(icon.contentDescription, view)
when (icon) {
is Icon.Loaded -> view.setImageDrawable(icon.drawable)
is Icon.Resource -> view.setImageResource(icon.res)
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index d89c0be..b598554 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -101,6 +101,11 @@
* Called when the always on suppression state changes. See {@link #isAlwaysOnSuppressed()}.
*/
default void onAlwaysOnSuppressedChanged(boolean suppressed) {}
+
+ /**
+ * Called when the dozing state may have been updated.
+ */
+ default void onDozingChanged(boolean isDozing) {}
}
interface PulseCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 1482303..586585c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -66,6 +66,10 @@
public static final UnreleasedFlag FSI_REQUIRES_KEYGUARD =
new UnreleasedFlag(110, true);
+ public static final UnreleasedFlag INSTANT_VOICE_REPLY = new UnreleasedFlag(111, true);
+
+ // next id: 112
+
/***************************************/
// 200 - keyguard/lockscreen
@@ -88,13 +92,6 @@
public static final ResourceBooleanFlag FACE_SCANNING_ANIM =
new ResourceBooleanFlag(205, R.bool.config_enableFaceScanningAnimation);
- /**
- * Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old
- * one.
- */
- public static final ReleasedFlag MODERN_BOTTOM_AREA = new ReleasedFlag(206, true);
-
-
public static final UnreleasedFlag LOCKSCREEN_CUSTOM_CLOCKS = new UnreleasedFlag(207);
/**
@@ -187,6 +184,9 @@
// 801 - region sampling
public static final UnreleasedFlag REGION_SAMPLING = new UnreleasedFlag(801);
+ // 802 - wallpaper rendering
+ public static final UnreleasedFlag USE_CANVAS_RENDERER = new UnreleasedFlag(802);
+
/***************************************/
// 900 - media
public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 6dfbd42..2da9232 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -29,6 +29,7 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -189,6 +190,9 @@
return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
: TRANSIT_OLD_KEYGUARD_GOING_AWAY;
} else if (type == TRANSIT_KEYGUARD_OCCLUDE) {
+ boolean isOccludeByDream = apps.length > 0 && apps[0].taskInfo.topActivityType
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM;
+ if (isOccludeByDream) return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
return TRANSIT_OLD_KEYGUARD_OCCLUDE;
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
@@ -303,6 +307,12 @@
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE,
occludeAnimationAdapter);
+ final RemoteAnimationAdapter occludeByDreamAnimationAdapter =
+ new RemoteAnimationAdapter(
+ mKeyguardViewMediator.getOccludeByDreamAnimationRunner(), 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM,
+ occludeByDreamAnimationAdapter);
+
final RemoteAnimationAdapter unoccludeAnimationAdapter =
new RemoteAnimationAdapter(
mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d4ef467..d4e0f061 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -887,6 +887,86 @@
private IRemoteAnimationRunner mOccludeAnimationRunner =
new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController);
+ private final IRemoteAnimationRunner mOccludeByDreamAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Nullable private ValueAnimator mOccludeByDreamAnimator;
+
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ if (mOccludeByDreamAnimator != null) {
+ mOccludeByDreamAnimator.cancel();
+ }
+ setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
+ if (DEBUG) {
+ Log.d(TAG, "Occlude by Dream animation cancelled. Occluded state is now: "
+ + mOccluded);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ setOccluded(true /* isOccluded */, true /* animate */);
+
+ if (apps == null || apps.length == 0 || apps[0] == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No apps provided to the OccludeByDream runner; "
+ + "skipping occluding animation.");
+ }
+ finishedCallback.onAnimationFinished();
+ return;
+ }
+
+ final RemoteAnimationTarget primary = apps[0];
+ final boolean isDream = (apps[0].taskInfo.topActivityType
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM);
+ if (!isDream) {
+ Log.w(TAG, "The occluding app isn't Dream; "
+ + "finishing up. Please check that the config is correct.");
+ finishedCallback.onAnimationFinished();
+ return;
+ }
+
+ final SyncRtSurfaceTransactionApplier applier =
+ new SyncRtSurfaceTransactionApplier(
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+
+ mContext.getMainExecutor().execute(() -> {
+ if (mOccludeByDreamAnimator != null) {
+ mOccludeByDreamAnimator.cancel();
+ }
+
+ mOccludeByDreamAnimator = ValueAnimator.ofFloat(0f, 1f);
+ // Use the same duration as for the UNOCCLUDE.
+ mOccludeByDreamAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION);
+ mOccludeByDreamAnimator.setInterpolator(Interpolators.LINEAR);
+ mOccludeByDreamAnimator.addUpdateListener(
+ animation -> {
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder
+ paramsBuilder =
+ new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(primary.leash)
+ .withAlpha(animation.getAnimatedFraction());
+ applier.scheduleApply(paramsBuilder.build());
+ });
+ mOccludeByDreamAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ mOccludeByDreamAnimator = null;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ mOccludeByDreamAnimator.start();
+ });
+ }
+ };
+
/**
* Animation controller for activities that unocclude the keyguard. This does not use the
* ActivityLaunchAnimator since we're just translating down, rather than emerging from a view
@@ -1682,6 +1762,10 @@
return mOccludeAnimationRunner;
}
+ public IRemoteAnimationRunner getOccludeByDreamAnimationRunner() {
+ return mOccludeByDreamAnimationRunner;
+ }
+
public IRemoteAnimationRunner getUnoccludeAnimationRunner() {
return mUnoccludeAnimationRunner;
}
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 e52d9ee..840a4b2 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
@@ -20,6 +20,7 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.doze.DozeHost
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -28,6 +29,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
/** Defines interface for classes that encapsulate application state for the keyguard. */
interface KeyguardRepository {
@@ -102,6 +104,7 @@
constructor(
statusBarStateController: StatusBarStateController,
keyguardStateController: KeyguardStateController,
+ dozeHost: DozeHost,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -136,19 +139,21 @@
awaitClose { keyguardStateController.removeCallback(callback) }
}
- override val isDozing: Flow<Boolean> = conflatedCallbackFlow {
- val callback =
- object : StatusBarStateController.StateListener {
- override fun onDozingChanged(isDozing: Boolean) {
- trySendWithFailureLogging(isDozing, TAG, "updated isDozing")
- }
+ override val isDozing: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : DozeHost.Callback {
+ override fun onDozingChanged(isDozing: Boolean) {
+ trySendWithFailureLogging(isDozing, TAG, "updated isDozing")
+ }
+ }
+ dozeHost.addCallback(callback)
+ trySendWithFailureLogging(false, TAG, "initial isDozing: false")
+
+ awaitClose { dozeHost.removeCallback(callback) }
}
+ .distinctUntilChanged()
- statusBarStateController.addCallback(callback)
- trySendWithFailureLogging(statusBarStateController.isDozing, TAG, "initial isDozing")
-
- awaitClose { statusBarStateController.removeCallback(callback) }
- }
override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9a69e26..95acc0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -32,6 +32,7 @@
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onStart
@SysUISingleton
class KeyguardQuickAffordanceInteractor
@@ -88,7 +89,15 @@
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
val configs = registry.getAll(position)
- return combine(configs.map { config -> config.state }) { states ->
+ return combine(
+ configs.map { config ->
+ // We emit an initial "Hidden" value to make sure that there's always an initial
+ // value and avoid subtle bugs where the downstream isn't receiving any values
+ // because one config implementation is not emitting an initial value. For example,
+ // see b/244296596.
+ config.state.onStart { emit(KeyguardQuickAffordanceConfig.State.Hidden) }
+ }
+ ) { states ->
val index = states.indexOfFirst { it is KeyguardQuickAffordanceConfig.State.Visible }
if (index != -1) {
val visibleState = states[index] as KeyguardQuickAffordanceConfig.State.Visible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 8f32ff9..ac2c9b1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -94,6 +94,7 @@
hasFavorites = favorites?.isNotEmpty() == true,
hasServiceInfos = serviceInfos.isNotEmpty(),
iconResourceId = component.getTileImageId(),
+ visibility = component.getVisibility(),
),
TAG,
)
@@ -110,9 +111,16 @@
isFeatureEnabled: Boolean,
hasFavorites: Boolean,
hasServiceInfos: Boolean,
+ visibility: ControlsComponent.Visibility,
@DrawableRes iconResourceId: Int?,
): KeyguardQuickAffordanceConfig.State {
- return if (isFeatureEnabled && hasFavorites && hasServiceInfos && iconResourceId != null) {
+ return if (
+ isFeatureEnabled &&
+ hasFavorites &&
+ hasServiceInfos &&
+ iconResourceId != null &&
+ visibility == ControlsComponent.Visibility.AVAILABLE
+ ) {
KeyguardQuickAffordanceConfig.State.Visible(
icon = ContainedDrawable.WithResource(iconResourceId),
contentDescriptionResourceId = component.getTileTitleId(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index ae4c7c7..6baf6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -41,6 +41,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.NotifPanelEvents
import com.android.systemui.statusbar.CrossFadeHelper
@@ -401,7 +402,7 @@
}
/**
- * Is the doze animation currently Running
+ * Is the dream overlay currently active
*/
private var dreamOverlayActive: Boolean = false
private set(value) {
@@ -412,6 +413,17 @@
}
/**
+ * Is the dream media complication currently active
+ */
+ private var dreamMediaComplicationActive: Boolean = false
+ private set(value) {
+ if (field != value) {
+ field = value
+ updateDesiredLocation(forceNoAnimation = true)
+ }
+ }
+
+ /**
* The current cross fade progress. 0.5f means it's just switching
* between the start and the end location and the content is fully faded, while 0.75f means
* that we're halfway faded in again in the target state.
@@ -500,6 +512,12 @@
})
dreamOverlayStateController.addCallback(object : DreamOverlayStateController.Callback {
+ override fun onComplicationsChanged() {
+ dreamMediaComplicationActive = dreamOverlayStateController.complications.any {
+ it is MediaDreamComplication
+ }
+ }
+
override fun onStateChanged() {
dreamOverlayStateController.isOverlayActive.also { dreamOverlayActive = it }
}
@@ -1068,7 +1086,7 @@
val onLockscreen = (!bypassController.bypassEnabled &&
(statusbarState == StatusBarState.KEYGUARD))
val location = when {
- dreamOverlayActive -> LOCATION_DREAM_OVERLAY
+ dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 75fa2f1..0b9b32b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -96,8 +96,9 @@
mToken = token;
mRingtone = new Ringtone(getContextForUser(user), false);
- mRingtone.setAudioAttributes(aa);
+ mRingtone.setAudioAttributesField(aa);
mRingtone.setUri(uri, volumeShaperConfig);
+ mRingtone.createLocalMediaPlayer();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index acd04f2..dc1488e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -19,6 +19,7 @@
import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
import android.content.Context;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -38,6 +39,9 @@
* the media complication as appropriate
*/
public class MediaDreamSentinel extends CoreStartable {
+ private static final String TAG = "MediaDreamSentinel";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
private final MediaDataManager.Listener mListener = new MediaDataManager.Listener() {
private boolean mAdded;
@Override
@@ -46,11 +50,17 @@
@Override
public void onMediaDataRemoved(@NonNull String key) {
+ final boolean hasActiveMedia = mMediaDataManager.hasActiveMedia();
+ if (DEBUG) {
+ Log.d(TAG, "onMediaDataRemoved(" + key + "), mAdded=" + mAdded + ", hasActiveMedia="
+ + hasActiveMedia);
+ }
+
if (!mAdded) {
return;
}
- if (mMediaDataManager.hasActiveMedia()) {
+ if (hasActiveMedia) {
return;
}
@@ -71,11 +81,24 @@
return;
}
+ final boolean hasActiveMedia = mMediaDataManager.hasActiveMedia();
+ if (DEBUG) {
+ Log.d(TAG, "onMediaDataLoaded(" + key + "), mAdded=" + mAdded + ", hasActiveMedia="
+ + hasActiveMedia);
+ }
+
+ // Media data can become inactive without triggering onMediaDataRemoved.
+ if (mAdded && !hasActiveMedia) {
+ mAdded = false;
+ mDreamOverlayStateController.removeComplication(mMediaEntryComplication);
+ return;
+ }
+
if (mAdded) {
return;
}
- if (!mMediaDataManager.hasActiveMedia()) {
+ if (!hasActiveMedia) {
return;
}
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 35a6c74..5d6d683 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
@@ -34,15 +34,14 @@
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
-import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
-import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
+import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -56,18 +55,16 @@
context: Context,
@MediaTttReceiverLogger logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
-) : MediaTttChipControllerCommon<ChipReceiverInfo>(
+) : TemporaryViewDisplayController<ChipReceiverInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
@@ -119,18 +116,18 @@
uiEventLogger.logReceiverStateChange(chipState)
if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
- removeChip(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
+ removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
return
}
if (appIcon == null) {
- displayChip(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
+ displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
return
}
appIcon.loadDrawableAsync(
context,
Icon.OnDrawableLoadedListener { drawable ->
- displayChip(ChipReceiverInfo(routeInfo, drawable, appName))
+ displayView(ChipReceiverInfo(routeInfo, drawable, appName))
},
// Notify the listener on the main handler since the listener will update
// the UI.
@@ -138,19 +135,19 @@
)
}
- override fun updateChipView(newChipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
- super.updateChipView(newChipInfo, currentChipView)
+ override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
+ super.updateView(newInfo, currentView)
val iconName = setIcon(
- currentChipView,
- newChipInfo.routeInfo.clientPackageName,
- newChipInfo.appIconDrawableOverride,
- newChipInfo.appNameOverride
+ currentView,
+ newInfo.routeInfo.clientPackageName,
+ newInfo.appIconDrawableOverride,
+ newInfo.appNameOverride
)
- currentChipView.contentDescription = iconName
+ currentView.contentDescription = iconName
}
- override fun animateChipIn(chipView: ViewGroup) {
- val appIconView = chipView.requireViewById<View>(R.id.app_icon)
+ override fun animateViewIn(view: ViewGroup) {
+ val appIconView = view.requireViewById<View>(R.id.app_icon)
appIconView.animate()
.translationYBy(-1 * getTranslationAmount().toFloat())
.setDuration(30.frames)
@@ -160,8 +157,8 @@
.setDuration(5.frames)
.start()
// Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
- appIconView.postOnAnimation { chipView.requestAccessibilityFocus() }
- startRipple(chipView.requireViewById(R.id.ripple))
+ appIconView.postOnAnimation { view.requestAccessibilityFocus() }
+ startRipple(view.requireViewById(R.id.ripple))
}
override fun getIconSize(isAppIcon: Boolean): Int? =
@@ -216,7 +213,7 @@
val routeInfo: MediaRoute2Info,
val appIconDrawableOverride: Drawable?,
val appNameOverride: CharSequence?
-) : ChipInfoCommon {
+) : TemporaryViewInfo {
override fun getTimeoutMs() = DEFAULT_TIMEOUT_MILLIS
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index a153cb6..bde588c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -25,7 +25,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
-import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
+import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
/**
* A class enumerating all the possible states of the media tap-to-transfer chip on the sender
@@ -120,7 +120,7 @@
// state, but that may take too long to go through the binder and the user may be
// confused ast o why the UI hasn't changed yet. So, we immediately change the UI
// here.
- controllerSender.displayChip(
+ controllerSender.displayView(
ChipSenderInfo(
TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, undoCallback
)
@@ -155,7 +155,7 @@
// state, but that may take too long to go through the binder and the user may be
// confused as to why the UI hasn't changed yet. So, we immediately change the UI
// here.
- controllerSender.displayChip(
+ controllerSender.displayView(
ChipSenderInfo(
TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, undoCallback
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 9335489..0c1ebd7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -33,14 +33,13 @@
import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
-import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
-import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
+import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -53,17 +52,15 @@
context: Context,
@MediaTttSenderLogger logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
-) : MediaTttChipControllerCommon<ChipSenderInfo>(
+) : TemporaryViewDisplayController<ChipSenderInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
@@ -106,53 +103,52 @@
uiEventLogger.logSenderStateChange(chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
- removeChip(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
+ removeView(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
} else {
- displayChip(ChipSenderInfo(chipState, routeInfo, undoCallback))
+ displayView(ChipSenderInfo(chipState, routeInfo, undoCallback))
}
}
- /** Displays the chip view for the given state. */
- override fun updateChipView(
- newChipInfo: ChipSenderInfo,
- currentChipView: ViewGroup
+ override fun updateView(
+ newInfo: ChipSenderInfo,
+ currentView: ViewGroup
) {
- super.updateChipView(newChipInfo, currentChipView)
+ super.updateView(newInfo, currentView)
- val chipState = newChipInfo.state
+ val chipState = newInfo.state
// App icon
- val iconName = setIcon(currentChipView, newChipInfo.routeInfo.clientPackageName)
+ val iconName = setIcon(currentView, newInfo.routeInfo.clientPackageName)
// Text
- val otherDeviceName = newChipInfo.routeInfo.name.toString()
+ val otherDeviceName = newInfo.routeInfo.name.toString()
val chipText = chipState.getChipTextString(context, otherDeviceName)
- currentChipView.requireViewById<TextView>(R.id.text).text = chipText
+ currentView.requireViewById<TextView>(R.id.text).text = chipText
// Loading
- currentChipView.requireViewById<View>(R.id.loading).visibility =
+ currentView.requireViewById<View>(R.id.loading).visibility =
chipState.isMidTransfer.visibleIfTrue()
// Undo
- val undoView = currentChipView.requireViewById<View>(R.id.undo)
+ val undoView = currentView.requireViewById<View>(R.id.undo)
val undoClickListener = chipState.undoClickListener(
- this, newChipInfo.routeInfo, newChipInfo.undoCallback, uiEventLogger
+ this, newInfo.routeInfo, newInfo.undoCallback, uiEventLogger
)
undoView.setOnClickListener(undoClickListener)
undoView.visibility = (undoClickListener != null).visibleIfTrue()
// Failure
- currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
+ currentView.requireViewById<View>(R.id.failure_icon).visibility =
chipState.isTransferFailure.visibleIfTrue()
// For accessibility
- currentChipView.requireViewById<ViewGroup>(
+ currentView.requireViewById<ViewGroup>(
R.id.media_ttt_sender_chip_inner
).contentDescription = "$iconName $chipText"
}
- override fun animateChipIn(chipView: ViewGroup) {
- val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
+ override fun animateViewIn(view: ViewGroup) {
+ val chipInnerView = view.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
ViewHierarchyAnimator.animateAddition(
chipInnerView,
ViewHierarchyAnimator.Hotspot.TOP,
@@ -165,14 +161,14 @@
)
}
- override fun removeChip(removalReason: String) {
+ override fun removeView(removalReason: String) {
// Don't remove the chip if we're mid-transfer since the user should still be able to
// see the status of the transfer. (But do remove it if it's finally timed out.)
- if (chipInfo?.state?.isMidTransfer == true &&
- removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
+ if (info?.state?.isMidTransfer == true &&
+ removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT) {
return
}
- super.removeChip(removalReason)
+ super.removeView(removalReason)
}
private fun Boolean.visibleIfTrue(): Int {
@@ -188,7 +184,7 @@
val state: ChipStateSender,
val routeInfo: MediaRoute2Info,
val undoCallback: IUndoMediaTransferCallback? = null
-) : ChipInfoCommon {
+) : TemporaryViewInfo {
override fun getTimeoutMs() = state.timeout
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 75e48d2..30947e8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -35,7 +35,6 @@
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
@@ -229,10 +228,7 @@
private Locale mLocale;
private int mLayoutDirection;
- private boolean mAllowForceNavBarHandleOpaque;
- private boolean mForceNavBarHandleOpaque;
private Optional<Long> mHomeButtonLongPressDurationMs;
- private boolean mIsCurrentUserSetup;
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -379,37 +375,6 @@
}
@Override
- public void onNavBarButtonAlphaChanged(float alpha, boolean animate) {
- if (!mIsCurrentUserSetup) {
- // If the current user is not yet setup, then don't update any button alphas
- return;
- }
- if (QuickStepContract.isLegacyMode(mNavBarMode)) {
- // Don't allow the bar buttons to be affected by the alpha
- return;
- }
-
- ButtonDispatcher buttonDispatcher = null;
- boolean forceVisible = false;
- if (QuickStepContract.isGesturalMode(mNavBarMode)) {
- // Disallow home handle animations when in gestural
- animate = false;
- forceVisible = mAllowForceNavBarHandleOpaque && mForceNavBarHandleOpaque;
- buttonDispatcher = mView.getHomeHandle();
- if (getBarTransitions() != null) {
- getBarTransitions().setBackgroundOverrideAlpha(alpha);
- }
- } else if (QuickStepContract.isSwipeUpMode(mNavBarMode)) {
- buttonDispatcher = mView.getBackButton();
- }
- if (buttonDispatcher != null) {
- buttonDispatcher.setVisibility(
- (forceVisible || alpha > 0) ? View.VISIBLE : View.INVISIBLE);
- buttonDispatcher.setAlpha(forceVisible ? 1f : alpha, animate);
- }
- }
-
- @Override
public void onHomeRotationEnabled(boolean enabled) {
mView.getRotationButtonController().setHomeRotationEnabled(enabled);
}
@@ -456,15 +421,10 @@
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
- mForceNavBarHandleOpaque = properties.getBoolean(
- NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
- }
-
if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) {
mHomeButtonLongPressDurationMs = Optional.of(
- properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0)
- ).filter(duration -> duration != 0);
+ properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0))
+ .filter(duration -> duration != 0);
if (mView != null) {
reconfigureHomeLongClick();
}
@@ -472,14 +432,6 @@
}
};
- private final DeviceProvisionedController.DeviceProvisionedListener mUserSetupListener =
- new DeviceProvisionedController.DeviceProvisionedListener() {
- @Override
- public void onUserSetupChanged() {
- mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
- }
- };
-
private final NotificationShadeDepthController.DepthListener mDepthListener =
new NotificationShadeDepthController.DepthListener() {
boolean mHasBlurs;
@@ -660,12 +612,6 @@
mCommandQueue.addCallback(this);
mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mNavBarHelper.init();
- mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
- R.bool.allow_force_nav_bar_handle_opaque);
- mForceNavBarHandleOpaque = mDeviceConfigProxy.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- NAV_BAR_HANDLE_FORCE_OPAQUE,
- /* defaultValue = */ true);
mHomeButtonLongPressDurationMs = Optional.of(mDeviceConfigProxy.getLong(
DeviceConfig.NAMESPACE_SYSTEMUI,
HOME_BUTTON_LONG_PRESS_DURATION_MS,
@@ -685,8 +631,6 @@
// Respect the latest disabled-flags.
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
- mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
- mDeviceProvisionedController.addCallback(mUserSetupListener);
mNotificationShadeDepthController.addListener(mDepthListener);
}
@@ -698,7 +642,6 @@
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mNavBarHelper.destroy();
- mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
mDeviceConfigProxy.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 56298fa..920f463 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -16,6 +16,7 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -30,15 +31,11 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.PagedTileLayout.PageListener;
-import com.android.systemui.qs.QSHost.Callback;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
@@ -47,16 +44,26 @@
import javax.inject.Inject;
-/** */
+/**
+ * Performs the animated transition between the QQS and QS views.
+ *
+ * <p>The transition is driven externally via {@link #setPosition(float)}, where 0 is a fully
+ * collapsed QQS and one a fully expanded QS.
+ *
+ * <p>This implementation maintains a set of {@code TouchAnimator} to transition the properties of
+ * views both in QQS and QS. These {@code TouchAnimator} are re-created lazily if contents of either
+ * view change, see {@link #requestAnimatorUpdate()}.
+ *
+ * <p>During the transition, both QS and QQS are visible. For overlapping tiles (Whenever the QS
+ * shows the first page), the corresponding QS tiles are hidden until QS is fully expanded.
+ */
@QSScope
-public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
- OnAttachStateChangeListener, Tunable {
+public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener,
+ TouchAnimator.Listener, OnLayoutChangeListener,
+ OnAttachStateChangeListener {
private static final String TAG = "QSAnimator";
- private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
- private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
-
private static final float EXPANDED_TILE_DELAY = .86f;
//Non first page delays
private static final float QS_TILE_LABEL_FADE_OUT_START = 0.15f;
@@ -65,7 +72,6 @@
public static final float SHORT_PARALLAX_AMOUNT = 0.1f;
-
/**
* List of all views that will be reset when clearing animation state
* see {@link #clearAnimationState()} }
@@ -125,14 +131,11 @@
private boolean mNeedsAnimatorUpdate = false;
private boolean mOnKeyguard;
- private boolean mAllowFancy;
- private boolean mFullRows;
private int mNumQuickTiles;
private int mLastQQSTileHeight;
private float mLastPosition;
private final QSTileHost mHost;
private final Executor mExecutor;
- private final TunerService mTunerService;
private boolean mShowCollapsedOnKeyguard;
private boolean mTranslateWhileExpanding;
private int mQQSTop;
@@ -153,7 +156,6 @@
mQuickStatusBarHeader = quickStatusBarHeader;
mHost = qsTileHost;
mExecutor = executor;
- mTunerService = tunerService;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
@@ -199,7 +201,6 @@
setCurrentPosition();
}
-
private void setCurrentPosition() {
setPosition(mLastPosition);
}
@@ -210,30 +211,15 @@
}
@Override
- public void onViewAttachedToWindow(@Nullable View v) {
- mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
- MOVE_FULL_ROWS);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mHost.removeCallback(this);
- mTunerService.removeTunable(this);
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (ALLOW_FANCY_ANIMATION.equals(key)) {
- mAllowFancy = TunerService.parseIntegerSwitch(newValue, true);
- if (!mAllowFancy) {
- clearAnimationState();
- }
- } else if (MOVE_FULL_ROWS.equals(key)) {
- mFullRows = TunerService.parseIntegerSwitch(newValue, true);
- }
+ public void onViewAttachedToWindow(@NonNull View view) {
updateAnimators();
}
+ @Override
+ public void onViewDetachedFromWindow(@NonNull View v) {
+ mHost.removeCallback(this);
+ }
+
private void addNonFirstPageAnimators(int page) {
Pair<HeightExpansionAnimator, TouchAnimator> pair = createSecondaryPageAnimators(page);
if (pair != null) {
@@ -339,8 +325,7 @@
View view = mQs.getView();
// This case: less tiles to animate in small displays.
- if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()
- && mAllowFancy) {
+ if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()) {
// Quick tiles.
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
@@ -422,7 +407,7 @@
mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
mAllViews.add(quickTileView.getSecondaryLabel());
- } else if (mFullRows && isIconInAnimatedRow(count)) {
+ } else if (isIconInAnimatedRow(count)) {
firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
@@ -457,44 +442,42 @@
}
}
- if (mAllowFancy) {
- animateBrightnessSlider(firstPageBuilder);
+ animateBrightnessSlider(firstPageBuilder);
- mFirstPageAnimator = firstPageBuilder
- // Fade in the tiles/labels as we reach the final position.
- .addFloat(tileLayout, "alpha", 0, 1)
- .addFloat(quadraticInterpolatorBuilder.build(), "position", 0, 1)
- .setListener(this)
- .build();
+ mFirstPageAnimator = firstPageBuilder
+ // Fade in the tiles/labels as we reach the final position.
+ .addFloat(tileLayout, "alpha", 0, 1)
+ .addFloat(quadraticInterpolatorBuilder.build(), "position", 0, 1)
+ .setListener(this)
+ .build();
- // Fade in the media player as we reach the final position
- Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
- if (mQsPanelController.shouldUseHorizontalLayout()
- && mQsPanelController.mMediaHost.hostView != null) {
- builder.addFloat(mQsPanelController.mMediaHost.hostView, "alpha", 0, 1);
- } else {
- // In portrait, media view should always be visible
- mQsPanelController.mMediaHost.hostView.setAlpha(1.0f);
- }
- mAllPagesDelayedAnimator = builder.build();
- translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
- qqsTranslationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
- translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
- if (mOnFirstPage) {
- // Only recreate this animator if we're in the first page. That way we know that
- // the first page is attached and has the proper positions/measures.
- mQQSTranslationYAnimator = qqsTranslationYBuilder.build();
- }
- mTranslationYAnimator = translationYBuilder.build();
- mTranslationXAnimator = translationXBuilder.build();
- if (mQQSTileHeightAnimator != null) {
- mQQSTileHeightAnimator.setInterpolator(
- mQSExpansionPathInterpolator.getYInterpolator());
- }
- if (mOtherFirstPageTilesHeightAnimator != null) {
- mOtherFirstPageTilesHeightAnimator.setInterpolator(
- mQSExpansionPathInterpolator.getYInterpolator());
- }
+ // Fade in the media player as we reach the final position
+ Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
+ if (mQsPanelController.shouldUseHorizontalLayout()
+ && mQsPanelController.mMediaHost.hostView != null) {
+ builder.addFloat(mQsPanelController.mMediaHost.hostView, "alpha", 0, 1);
+ } else {
+ // In portrait, media view should always be visible
+ mQsPanelController.mMediaHost.hostView.setAlpha(1.0f);
+ }
+ mAllPagesDelayedAnimator = builder.build();
+ translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
+ qqsTranslationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
+ translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
+ if (mOnFirstPage) {
+ // Only recreate this animator if we're in the first page. That way we know that
+ // the first page is attached and has the proper positions/measures.
+ mQQSTranslationYAnimator = qqsTranslationYBuilder.build();
+ }
+ mTranslationYAnimator = translationYBuilder.build();
+ mTranslationXAnimator = translationXBuilder.build();
+ if (mQQSTileHeightAnimator != null) {
+ mQQSTileHeightAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
+ }
+ if (mOtherFirstPageTilesHeightAnimator != null) {
+ mOtherFirstPageTilesHeightAnimator.setInterpolator(
+ mQSExpansionPathInterpolator.getYInterpolator());
}
mNonfirstPageAlphaAnimator = nonFirstPageAlphaBuilder
.addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -568,7 +551,7 @@
if (animator == null) {
animator = new HeightExpansionAnimator(
- this, mLastQQSTileHeight, tileView.getMeasuredHeight());
+ this, mLastQQSTileHeight, tileView.getMeasuredHeight());
animator.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
}
animator.addView(tileView);
@@ -639,7 +622,7 @@
}
private void getRelativePositionInt(int[] loc1, View view, View parent) {
- if(view == parent || view == null) return;
+ if (view == parent || view == null) return;
// Ignore tile pages as they can have some offset we don't want to take into account in
// RTL.
if (!isAPage(view)) {
@@ -672,7 +655,6 @@
}
}
mLastPosition = position;
- if (!mAllowFancy) return;
if (mOnFirstPage) {
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
@@ -806,30 +788,31 @@
private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
- float mLastT = -1;
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float t = valueAnimator.getAnimatedFraction();
- final int viewCount = mViews.size();
- int height = (Integer) valueAnimator.getAnimatedValue();
- for (int i = 0; i < viewCount; i++) {
- View v = mViews.get(i);
- if (v instanceof HeightOverrideable) {
- ((HeightOverrideable) v).setHeightOverride(height);
- } else {
- v.setBottom(v.getTop() + height);
+ float mLastT = -1;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float t = valueAnimator.getAnimatedFraction();
+ final int viewCount = mViews.size();
+ int height = (Integer) valueAnimator.getAnimatedValue();
+ for (int i = 0; i < viewCount; i++) {
+ View v = mViews.get(i);
+ if (v instanceof HeightOverrideable) {
+ ((HeightOverrideable) v).setHeightOverride(height);
+ } else {
+ v.setBottom(v.getTop() + height);
+ }
+ }
+ if (t == 0f) {
+ mListener.onAnimationAtStart();
+ } else if (t == 1f) {
+ mListener.onAnimationAtEnd();
+ } else if (mLastT <= 0 || mLastT == 1) {
+ mListener.onAnimationStarted();
+ }
+ mLastT = t;
}
- }
- if (t == 0f) {
- mListener.onAnimationAtStart();
- } else if (t == 1f) {
- mListener.onAnimationAtEnd();
- } else if (mLastT <= 0 || mLastT == 1) {
- mListener.onAnimationStarted();
- }
- mLastT = t;
- }
- };
+ };
HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) {
mListener = listener;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index b20d7ba..67bf300 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -97,7 +97,8 @@
super(rootView);
mFooterText = mView.findViewById(R.id.footer_text);
mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon);
- mFooterIcon = new Icon.Resource(R.drawable.ic_info_outline);
+ mFooterIcon = new Icon.Resource(
+ R.drawable.ic_info_outline, /* contentDescription= */ null);
mContext = rootView.getContext();
mSecurityController = securityController;
mMainHandler = mainHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index f632274..bd75c75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -75,6 +75,7 @@
import com.android.systemui.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.common.shared.model.ContentDescription;
import com.android.systemui.common.shared.model.Icon;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
@@ -243,16 +244,17 @@
isWorkProfileOn).toString();
Icon icon;
+ ContentDescription contentDescription = null;
if (isParentalControlsEnabled) {
- icon = new Icon.Loaded(securityModel.getDeviceAdminIcon());
+ icon = new Icon.Loaded(securityModel.getDeviceAdminIcon(), contentDescription);
} else if (vpnName != null || vpnNameWorkProfile != null) {
if (securityModel.isVpnBranded()) {
- icon = new Icon.Resource(R.drawable.stat_sys_branded_vpn);
+ icon = new Icon.Resource(R.drawable.stat_sys_branded_vpn, contentDescription);
} else {
- icon = new Icon.Resource(R.drawable.stat_sys_vpn_ic);
+ icon = new Icon.Resource(R.drawable.stat_sys_vpn_ic, contentDescription);
}
} else {
- icon = new Icon.Resource(R.drawable.ic_info_outline);
+ icon = new Icon.Resource(R.drawable.ic_info_outline, contentDescription);
}
return new SecurityButtonConfig(icon, text, isClickable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 8dd506e..28ddead 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -31,7 +31,6 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
-import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
@@ -233,10 +232,8 @@
val icon = model.icon
val iconView = button.icon
- val contentDescription = model.contentDescription
IconViewBinder.bind(icon, iconView)
- ContentDescriptionViewBinder.bind(contentDescription, iconView)
if (model.iconTint != null) {
iconView.setColorFilter(model.iconTint, PorterDuff.Mode.SRC_IN)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
index 4c0879e..2ad0513 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
@@ -18,7 +18,6 @@
import android.annotation.DrawableRes
import android.view.View
-import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
/**
@@ -29,7 +28,6 @@
val icon: Icon,
val iconTint: Int?,
@DrawableRes val background: Int,
- val contentDescription: ContentDescription,
// TODO(b/230830644): Replace View by an Expandable interface that can expand in either dialog
// or activity.
val onClick: (View) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index b556a3e..a935338 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -138,10 +138,12 @@
/** The model for the settings button. */
val settings: FooterActionsButtonViewModel =
FooterActionsButtonViewModel(
- Icon.Resource(R.drawable.ic_settings),
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ ),
iconTint = null,
R.drawable.qs_footer_action_circle,
- ContentDescription.Resource(R.string.accessibility_quick_settings_settings),
this::onSettingsButtonClicked,
)
@@ -149,14 +151,16 @@
val power: FooterActionsButtonViewModel? =
if (showPowerButton) {
FooterActionsButtonViewModel(
- Icon.Resource(android.R.drawable.ic_lock_power_off),
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ ),
iconTint =
Utils.getColorAttrDefaultColor(
context,
com.android.internal.R.attr.textColorOnAccent,
),
R.drawable.qs_footer_action_circle_color,
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu),
this::onPowerButtonClicked,
)
} else {
@@ -252,10 +256,12 @@
}
return FooterActionsButtonViewModel(
- Icon.Loaded(icon),
+ Icon.Loaded(
+ icon,
+ ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
+ ),
iconTint,
R.drawable.qs_footer_action_circle,
- ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
this::onUserSwitcherClicked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 30862b7..3788ad9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -61,6 +61,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.PatternMatcher;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -174,7 +175,6 @@
private boolean mBound;
private boolean mIsEnabled;
private int mCurrentBoundedUserId = -1;
- private float mNavBarButtonAlpha;
private boolean mInputFocusTransferStarted;
private float mInputFocusTransferStartY;
private long mInputFocusTransferStartMillis;
@@ -296,12 +296,6 @@
}
@Override
- public void setNavBarButtonAlpha(float alpha, boolean animate) {
- verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () ->
- notifyNavBarButtonAlphaChanged(alpha, animate));
- }
-
- @Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () ->
notifyAssistantProgress(progress));
@@ -581,6 +575,12 @@
AssistUtils assistUtils,
DumpManager dumpManager) {
super(broadcastDispatcher);
+
+ // b/241601880: This component shouldn't be running for a non-primary user
+ if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) {
+ Log.e(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable());
+ }
+
mContext = context;
mPipOptional = pipOptional;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
@@ -603,9 +603,6 @@
mBackAnimation = backAnimation;
mUiEventLogger = uiEventLogger;
- // Assumes device always starts with back button until launcher tells it that it does not
- mNavBarButtonAlpha = 1.0f;
-
dumpManager.registerDumpable(getClass().getSimpleName(), this);
// Listen for nav bar mode changes
@@ -807,7 +804,6 @@
mConnectionCallbacks.add(listener);
}
listener.onConnectionChanged(mOverviewProxy != null);
- listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
}
@Override
@@ -837,17 +833,10 @@
if (mOverviewProxy != null) {
mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
mOverviewProxy = null;
- notifyNavBarButtonAlphaChanged(1f, false /* animate */);
notifyConnectionChanged();
}
}
- private void notifyNavBarButtonAlphaChanged(float alpha, boolean animate) {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onNavBarButtonAlphaChanged(alpha, animate);
- }
- }
-
private void notifyHomeRotationEnabled(boolean enabled) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled);
@@ -1076,7 +1065,6 @@
pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis);
pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius);
pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows);
- pw.print(" mNavBarButtonAlpha="); pw.println(mNavBarButtonAlpha);
pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
pw.print(" mNavBarMode="); pw.println(mNavBarMode);
mSysUiState.dump(pw, args);
@@ -1091,8 +1079,6 @@
default void onQuickScrubStarted() {}
/** Notify the recents app (overview) is started by 3-button navigation. */
default void onToggleRecentApps() {}
- /** Notify changes in the nav bar button alpha */
- default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
default void onHomeRotationEnabled(boolean enabled) {}
default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
default void onTaskbarAutohideSuspend(boolean suspend) {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 91c6a9c..c3b265f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -117,7 +117,6 @@
import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
@@ -140,7 +139,6 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -217,7 +215,6 @@
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.Utils;
import com.android.systemui.util.time.SystemClock;
-import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.PrintWriter;
@@ -325,9 +322,6 @@
private final FragmentService mFragmentService;
private final ScrimController mScrimController;
private final PrivacyDotViewController mPrivacyDotViewController;
- private final QuickAccessWalletController mQuickAccessWalletController;
- private final QRCodeScannerController mQRCodeScannerController;
- private final ControlsComponent mControlsComponent;
private final NotificationRemoteInputManager mRemoteInputManager;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@@ -696,8 +690,8 @@
};
private final CameraGestureHelper mCameraGestureHelper;
- private final Provider<KeyguardBottomAreaViewModel> mKeyguardBottomAreaViewModelProvider;
- private final Provider<KeyguardBottomAreaInteractor> mKeyguardBottomAreaInteractorProvider;
+ private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
+ private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@@ -746,8 +740,6 @@
NavigationModeController navigationModeController,
FragmentService fragmentService,
ContentResolver contentResolver,
- QuickAccessWalletController quickAccessWalletController,
- QRCodeScannerController qrCodeScannerController,
RecordingController recordingController,
LargeScreenShadeHeaderController largeScreenShadeHeaderController,
ScreenOffAnimationController screenOffAnimationController,
@@ -755,7 +747,6 @@
PanelExpansionStateManager panelExpansionStateManager,
NotificationRemoteInputManager remoteInputManager,
Optional<SysUIUnfoldComponent> unfoldComponent,
- ControlsComponent controlsComponent,
InteractionJankMonitor interactionJankMonitor,
QsFrameTranslateController qsFrameTranslateController,
SysUiState sysUiState,
@@ -768,8 +759,8 @@
ShadeTransitionController shadeTransitionController,
SystemClock systemClock,
CameraGestureHelper cameraGestureHelper,
- Provider<KeyguardBottomAreaViewModel> keyguardBottomAreaViewModelProvider,
- Provider<KeyguardBottomAreaInteractor> keyguardBottomAreaInteractorProvider) {
+ KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
+ KeyguardBottomAreaInteractor keyguardBottomAreaInteractor) {
super(view,
falsingManager,
dozeLog,
@@ -791,9 +782,6 @@
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
mPrivacyDotViewController = privacyDotViewController;
- mQuickAccessWalletController = quickAccessWalletController;
- mQRCodeScannerController = qrCodeScannerController;
- mControlsComponent = controlsComponent;
mMetricsLogger = metricsLogger;
mConfigurationController = configurationController;
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
@@ -897,7 +885,7 @@
mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
- mKeyguardBottomAreaViewModelProvider = keyguardBottomAreaViewModelProvider;
+ mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
onFinishInflate();
keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -951,7 +939,7 @@
}
});
mCameraGestureHelper = cameraGestureHelper;
- mKeyguardBottomAreaInteractorProvider = keyguardBottomAreaInteractorProvider;
+ mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
}
@VisibleForTesting
@@ -1276,17 +1264,7 @@
}
private void initBottomArea() {
- if (mFeatureFlags.isEnabled(Flags.MODERN_BOTTOM_AREA)) {
- mKeyguardBottomArea.init(mKeyguardBottomAreaViewModelProvider.get(), mFalsingManager);
- } else {
- // TODO(b/235403546): remove this method call when the new implementation is complete
- // and these are not needed.
- mKeyguardBottomArea.init(
- mFalsingManager,
- mQuickAccessWalletController,
- mControlsComponent,
- mQRCodeScannerController);
- }
+ mKeyguardBottomArea.init(mKeyguardBottomAreaViewModel, mFalsingManager);
}
@VisibleForTesting
@@ -1403,7 +1381,6 @@
}
mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
- mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mStackScrollerMeasuringPass++;
requestScrollerTopPaddingUpdate(animate);
@@ -1453,7 +1430,7 @@
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
- mKeyguardBottomAreaInteractorProvider.get().setClockPosition(
+ mKeyguardBottomAreaInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
@@ -3296,8 +3273,7 @@
getExpandedFraction());
float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
alpha *= mBottomAreaShadeAlpha;
- mKeyguardBottomArea.setComponentAlphas(alpha);
- mKeyguardBottomAreaInteractorProvider.get().setAlpha(alpha);
+ mKeyguardBottomAreaInteractor.setAlpha(alpha);
mLockIconViewController.setAlpha(alpha);
}
@@ -3496,8 +3472,7 @@
}
private void updateDozingVisibilities(boolean animate) {
- mKeyguardBottomArea.setDozing(mDozing, animate);
- mKeyguardBottomAreaInteractorProvider.get().setAnimateDozingTransitions(animate);
+ mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
if (!mDozing && animate) {
mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
}
@@ -3799,8 +3774,7 @@
mView.setDozing(dozing);
mDozing = dozing;
mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
- mKeyguardBottomArea.setDozing(mDozing, animate);
- mKeyguardBottomAreaInteractorProvider.get().setAnimateDozingTransitions(animate);
+ mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
mKeyguardStatusBarViewController.setDozing(mDozing);
if (dozing) {
@@ -3849,7 +3823,6 @@
public void dozeTimeTick() {
mLockIconViewController.dozeTimeTick();
- mKeyguardBottomArea.dozeTimeTick();
mKeyguardStatusViewController.dozeTimeTick();
if (mInterpolatedDarkAmount > 0) {
positionClockAndNotifications();
@@ -4672,7 +4645,6 @@
public void onDozeAmountChanged(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
- mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
positionClockAndNotifications();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e93f605..abafecc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -29,6 +29,7 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
@@ -138,6 +139,13 @@
return mView.findViewById(R.id.keyguard_bouncer_container);
}
+ /**
+ * @return Location where to place the KeyguardMessageArea
+ */
+ public AuthKeyguardMessageArea getKeyguardMessageArea() {
+ return mView.findViewById(R.id.keyguard_message_area);
+ }
+
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() {
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index 621a609..9b3fe92 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -26,6 +26,7 @@
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
@@ -49,6 +50,7 @@
private val dockManager: DockManager,
private val centralSurfaces: CentralSurfaces,
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+ private val statusBarStateController: StatusBarStateController,
tunerService: TunerService,
dumpManager: DumpManager
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
@@ -74,7 +76,8 @@
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
- if (singleTapEnabled &&
+ if (statusBarStateController.isPulsing &&
+ singleTapEnabled &&
!dockManager.isDocked &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)
@@ -89,7 +92,8 @@
}
override fun onDoubleTap(e: MotionEvent): Boolean {
- if ((doubleTapEnabled || singleTapEnabled) &&
+ if (statusBarStateController.isPulsing &&
+ (doubleTapEnabled || singleTapEnabled) &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseDoubleTap
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 47dc5c2..408c61f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,6 +19,10 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_FIRST_FRAME_RECEIVED;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -78,6 +82,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
+import com.android.systemui.biometrics.BiometricMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -178,7 +183,7 @@
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
private String mBiometricErrorMessageToShowOnScreenOn;
- private final Set<Integer> mCoExFaceHelpMsgIdsToShow;
+ private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
private boolean mInited;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -249,11 +254,11 @@
mScreenLifecycle = screenLifecycle;
mScreenLifecycle.addObserver(mScreenObserver);
- mCoExFaceHelpMsgIdsToShow = new HashSet<>();
+ mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
int[] msgIds = context.getResources().getIntArray(
com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled);
for (int msgId : msgIds) {
- mCoExFaceHelpMsgIdsToShow.add(msgId);
+ mCoExFaceAcquisitionMsgIdsToShow.add(msgId);
}
mHandler = new Handler(mainLooper) {
@@ -687,11 +692,11 @@
/**
* Returns the indication text indicating that trust has been granted.
*
- * @return {@code null} or an empty string if a trust indication text should not be shown.
+ * @return an empty string if a trust indication text should not be shown.
*/
@VisibleForTesting
String getTrustGrantedIndication() {
- return TextUtils.isEmpty(mTrustGrantedIndication)
+ return mTrustGrantedIndication == null
? mContext.getString(R.string.keyguard_indication_trust_unlocked)
: mTrustGrantedIndication.toString();
}
@@ -927,7 +932,7 @@
return; // udfps affordance is highlighted, no need to show action to unlock
} else if (mKeyguardUpdateMonitor.isFaceEnrolled()) {
String message = mContext.getString(R.string.keyguard_retry);
- mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
}
} else {
final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -990,7 +995,7 @@
mTopIndicationView == null ? null : mTopIndicationView.getText()));
pw.println(" computePowerIndication(): " + computePowerIndication());
pw.println(" trustGrantedIndication: " + getTrustGrantedIndication());
- pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceHelpMsgIdsToShow);
+ pw.println(" mCoExFaceHelpMsgIdsToShow=" + mCoExFaceAcquisitionMsgIdsToShow);
mRotateTextViewController.dump(pw, args);
}
@@ -1048,6 +1053,17 @@
return;
}
+ if (biometricSourceType == FACE) {
+ if (msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED) {
+ mFaceAcquiredMessageDeferral.reset();
+ } else {
+ mFaceAcquiredMessageDeferral.processMessage(msgId, helpString);
+ if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) {
+ return;
+ }
+ }
+ }
+
final boolean faceAuthSoftError = biometricSourceType == FACE
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
@@ -1055,19 +1071,24 @@
final boolean isUnlockWithFingerprintPossible =
mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
getCurrentUser());
- if (faceAuthSoftError
- && isUnlockWithFingerprintPossible
- && !mCoExFaceHelpMsgIdsToShow.contains(msgId)) {
+ 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;
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
+ mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- if (faceAuthFailed && isUnlockWithFingerprintPossible) {
+ if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
+ showBiometricMessage(
+ helpString,
+ mContext.getString(R.string.keyguard_suggest_fingerprint)
+ );
+ } else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
showBiometricMessage(
mContext.getString(R.string.keyguard_face_failed),
mContext.getString(R.string.keyguard_suggest_fingerprint)
@@ -1090,41 +1111,51 @@
@Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
- if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
- return;
+ CharSequence deferredFaceMessage = null;
+ if (biometricSourceType == FACE) {
+ deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ mFaceAcquiredMessageDeferral.reset();
}
- if (biometricSourceType == FACE
- && msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS) {
- // suppress all face UNABLE_TO_PROCESS errors
+ if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) {
if (DEBUG) {
- Log.d(TAG, "skip showing FACE_ERROR_UNABLE_TO_PROCESS errString="
- + errString);
+ Log.d(TAG, "suppressingBiometricError msgId=" + msgId
+ + " source=" + biometricSourceType);
}
- } else if (biometricSourceType == FACE
- && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ } else if (biometricSourceType == FACE && msgId == FaceManager.FACE_ERROR_TIMEOUT) {
+ // Co-ex: show deferred message OR nothing
if (mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- getCurrentUser())) {
- // no message if fingerprint is also enrolled
+ 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;
}
- // The face timeout message is not very actionable, let's ask the user to
+ // Face-only: The face timeout message is not very actionable, let's ask the user to
// manually retry.
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(
- mContext.getResources().getString(R.string.keyguard_try_fingerprint),
- mInitialTextColorState
+ 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.showBouncerMessage(errString, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString);
} else {
@@ -1134,8 +1165,9 @@
private boolean shouldSuppressBiometricError(int msgId,
BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
- if (biometricSourceType == BiometricSourceType.FINGERPRINT)
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
return shouldSuppressFingerprintError(msgId, updateMonitor);
+ }
if (biometricSourceType == FACE) {
return shouldSuppressFaceError(msgId, updateMonitor);
}
@@ -1161,7 +1193,8 @@
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
- || msgId == FaceManager.FACE_ERROR_CANCELED);
+ || msgId == FaceManager.FACE_ERROR_CANCELED
+ || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS);
}
@@ -1200,10 +1233,11 @@
boolean isStrongBiometric) {
super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric);
hideBiometricMessage();
-
- if (biometricSourceType == FACE
- && !mKeyguardBypassController.canBypass()) {
- showActionToUnlock();
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.reset();
+ if (!mKeyguardBypassController.canBypass()) {
+ showActionToUnlock();
+ }
}
}
@@ -1274,4 +1308,14 @@
}
}
};
+
+ private final BiometricMessageDeferral mFaceAcquiredMessageDeferral =
+ new BiometricMessageDeferral(
+ Set.of(
+ FACE_ACQUIRED_GOOD,
+ FACE_ACQUIRED_START,
+ FACE_ACQUIRED_FIRST_FRAME_RECEIVED
+ ),
+ Set.of(FACE_ACQUIRED_TOO_DARK)
+ );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index f6c4a31..cb13fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -81,7 +81,6 @@
}
lateinit var root: View
- private var blurRoot: View? = null
private var keyguardAnimator: Animator? = null
private var notificationAnimator: Animator? = null
private var updateScheduled: Boolean = false
@@ -235,7 +234,7 @@
val opaque = scrimsVisible && !blursDisabledForAppLaunch
Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
- blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
+ blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
lastAppliedBlur = blur
wallpaperController.setNotificationShadeZoom(zoomOut)
listeners.forEach {
@@ -271,7 +270,6 @@
override fun onAnimationEnd(animation: Animator?) {
keyguardAnimator = null
wakeAndUnlockBlurRadius = 0f
- scheduleUpdate()
}
})
start()
@@ -302,7 +300,6 @@
override fun onDozeAmountChanged(linear: Float, eased: Float) {
wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
- scheduleUpdate()
}
}
@@ -439,12 +436,11 @@
shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized).toInt())
}
- private fun scheduleUpdate(viewToBlur: View? = null) {
+ private fun scheduleUpdate() {
if (updateScheduled) {
return
}
updateScheduled = true
- blurRoot = viewToBlur
choreographer.postFrameCallback(updateBlurCallback)
}
@@ -495,16 +491,11 @@
*/
private var pendingRadius = -1
- /**
- * View on {@link Surface} that wants depth.
- */
- private var view: View? = null
-
private var springAnimation = SpringAnimation(this, object :
FloatPropertyCompat<DepthAnimation>("blurRadius") {
override fun setValue(rect: DepthAnimation?, value: Float) {
radius = value
- scheduleUpdate(view)
+ scheduleUpdate()
}
override fun getValue(rect: DepthAnimation?): Float {
@@ -519,11 +510,10 @@
springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 }
}
- fun animateTo(newRadius: Int, viewToBlur: View? = null) {
- if (pendingRadius == newRadius && view == viewToBlur) {
+ fun animateTo(newRadius: Int) {
+ if (pendingRadius == newRadius) {
return
}
- view = viewToBlur
pendingRadius = newRadius
springAnimation.animateToFinalPosition(newRadius.toFloat())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cea3deb..41c0367 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -90,6 +92,12 @@
super(context, attrs);
}
+ @VisibleForTesting
+ public NotificationShelf(Context context, AttributeSet attrs, boolean showNotificationShelf) {
+ super(context, attrs);
+ mShowNotificationShelf = showNotificationShelf;
+ }
+
@Override
@VisibleForTesting
public void onFinishInflate() {
@@ -175,7 +183,11 @@
if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) {
float expansion = ambientState.getExpansionFraction();
- viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
+ if (ambientState.isBouncerInTransit()) {
+ viewState.alpha = aboutToShowBouncerProgress(expansion);
+ } else {
+ viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
+ }
} else {
viewState.alpha = 1f - ambientState.getHideAmount();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 3627b40..52f4c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -144,11 +144,6 @@
public void invalidateNotifications(String reason) {
mNotifFilter.invalidateList(reason);
}
-
- @Override
- public void maybeCancelSummary(NotificationEntry entry) {
- // no-op
- }
};
private boolean isInterceptingDismissal(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ea28452..ce465bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.util.MathUtils;
+import androidx.annotation.VisibleForTesting;
+
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -137,7 +139,17 @@
* True right after we swipe up on lockscreen and have not finished the fling down that follows.
* False when we stop flinging or leave lockscreen.
*/
- private boolean mNeedFlingAfterLockscreenSwipeUp = false;
+ private boolean mIsFlingRequiredAfterLockScreenSwipeUp = false;
+
+ @VisibleForTesting
+ public boolean isFlingRequiredAfterLockScreenSwipeUp() {
+ return mIsFlingRequiredAfterLockScreenSwipeUp;
+ }
+
+ @VisibleForTesting
+ public void setFlingRequiredAfterLockScreenSwipeUp(boolean value) {
+ mIsFlingRequiredAfterLockScreenSwipeUp = value;
+ }
/**
* @return Height of the notifications panel without top padding when expansion completes.
@@ -181,7 +193,7 @@
public void setSwipingUp(boolean isSwipingUp) {
if (!isSwipingUp && mIsSwipingUp) {
// Just stopped swiping up.
- mNeedFlingAfterLockscreenSwipeUp = true;
+ mIsFlingRequiredAfterLockScreenSwipeUp = true;
}
mIsSwipingUp = isSwipingUp;
}
@@ -196,10 +208,10 @@
/**
* @param isFlinging Whether we are flinging the shade open or closed.
*/
- public void setIsFlinging(boolean isFlinging) {
+ public void setFlinging(boolean isFlinging) {
if (isOnKeyguard() && !isFlinging && mIsFlinging) {
// Just stopped flinging.
- mNeedFlingAfterLockscreenSwipeUp = false;
+ mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mIsFlinging = isFlinging;
}
@@ -508,7 +520,7 @@
public void setStatusBarState(int statusBarState) {
if (mStatusBarState != StatusBarState.KEYGUARD) {
- mNeedFlingAfterLockscreenSwipeUp = false;
+ mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mStatusBarState = statusBarState;
}
@@ -576,7 +588,7 @@
* @return Whether we need to do a fling down after swiping up on lockscreen.
*/
public boolean isFlingingAfterSwipeUpOnLockscreen() {
- return mIsFlinging && mNeedFlingAfterLockscreenSwipeUp;
+ return mIsFlinging && mIsFlingRequiredAfterLockScreenSwipeUp;
}
/**
@@ -744,7 +756,8 @@
pw.println("mIsSwipingUp=" + mIsSwipingUp);
pw.println("mPanelTracking=" + mPanelTracking);
pw.println("mIsFlinging=" + mIsFlinging);
- pw.println("mNeedFlingAfterLockscreenSwipeUp=" + mNeedFlingAfterLockscreenSwipeUp);
+ pw.println("mIsFlingRequiredAfterLockScreenSwipeUp="
+ + mIsFlingRequiredAfterLockScreenSwipeUp);
pw.println("mZDistanceBetweenElements=" + mZDistanceBetweenElements);
pw.println("mBaseZHeight=" + mBaseZHeight);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 1fb265f..5fbaa51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -2794,10 +2794,9 @@
if (mDebugRemoveAnimation) {
Log.d(TAG, "generateRemove " + key
+ "\nmIsExpanded " + mIsExpanded
- + "\nmAnimationsEnabled " + mAnimationsEnabled
- + "\n!invisible group " + !isChildInInvisibleGroup(child));
+ + "\nmAnimationsEnabled " + mAnimationsEnabled);
}
- if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
+ if (mIsExpanded && mAnimationsEnabled) {
if (!mChildrenToAddAnimated.contains(child)) {
if (mDebugRemoveAnimation) {
Log.d(TAG, "needsAnimation = true " + key);
@@ -2845,26 +2844,6 @@
return hasAddEvent && mAddedHeadsUpChildren.contains(child);
}
- // TODO (b/162832756): remove since this won't happen in new pipeline (we prune groups in
- // ShadeListBuilder)
- /**
- * @param child the child to query
- * @return whether a view is not a top level child but a child notification and that group is
- * not expanded
- */
- @ShadeViewRefactor(RefactorComponent.ADAPTER)
- private boolean isChildInInvisibleGroup(View child) {
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- NotificationEntry groupSummary =
- mGroupMembershipManager.getGroupSummary(row.getEntry());
- if (groupSummary != null && groupSummary.getRow() != row) {
- return row.getVisibility() == View.INVISIBLE;
- }
- }
- return false;
- }
-
/**
* Updates the scroll position when a child was removed
*
@@ -5042,7 +5021,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setPanelFlinging(boolean flinging) {
- mAmbientState.setIsFlinging(flinging);
+ mAmbientState.setFlinging(flinging);
if (!flinging) {
// re-calculate the stack height which was frozen while flinging
updateStackPosition();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 20b6d0b..f37243a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -40,6 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -220,6 +221,9 @@
ViewGroup getBouncerContainer();
+ /** Get the Keyguard Message Area that displays auth messages. */
+ AuthKeyguardMessageArea getKeyguardMessageArea();
+
int getStatusBarHeight();
void updateQsExpansionEnabled();
@@ -390,8 +394,6 @@
void fadeKeyguardAfterLaunchTransition(Runnable beforeFading,
Runnable endRunnable, Runnable cancelRunnable);
- void fadeKeyguardWhilePulsing();
-
void animateKeyguardUnoccluding();
void startLaunchTransitionTimeout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 6426aef..cebb4b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -120,6 +120,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -1582,6 +1583,11 @@
}
@Override
+ public AuthKeyguardMessageArea getKeyguardMessageArea() {
+ return mNotificationShadeWindowViewController.getKeyguardMessageArea();
+ }
+
+ @Override
public int getStatusBarHeight() {
return mStatusBarWindowController.getStatusBarHeight();
}
@@ -3019,19 +3025,6 @@
}
/**
- * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
- * fading.
- */
- @Override
- public void fadeKeyguardWhilePulsing() {
- mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
- ()-> {
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }).start();
- }
-
- /**
* Plays the animation when an activity that was occluding Keyguard goes away.
*/
@Override
@@ -3320,14 +3313,12 @@
// show the bouncer/lockscreen.
if (!mKeyguardViewMediator.isHiding()
&& !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
- if (mState == StatusBarState.SHADE_LOCKED
- && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
+ if (mState == StatusBarState.SHADE_LOCKED) {
// shade is showing while locked on the keyguard, so go back to showing the
// lock screen where users can use the UDFPS affordance to enter the device
mStatusBarKeyguardViewManager.reset(true);
- } else if ((mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing())
- || mState == StatusBarState.SHADE_LOCKED) {
+ } else if (mState == StatusBarState.KEYGUARD
+ && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) {
mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index ddff7d8..24ce5e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -28,8 +28,6 @@
import android.view.MotionEvent;
import android.view.View;
-import androidx.annotation.Nullable;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.assist.AssistManager;
@@ -50,11 +48,9 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.unfold.FoldAodAnimationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.Assert;
import java.util.ArrayList;
-import java.util.Optional;
import javax.inject.Inject;
@@ -80,8 +76,6 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
- @Nullable
- private final FoldAodAnimationController mFoldAodAnimationController;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
private final BatteryController mBatteryController;
private final ScrimController mScrimController;
@@ -114,7 +108,6 @@
Lazy<AssistManager> assistManagerLazy,
DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
PulseExpansionHandler pulseExpansionHandler,
- Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
NotificationShadeWindowController notificationShadeWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
AuthController authController,
@@ -138,8 +131,6 @@
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
- mFoldAodAnimationController = sysUIUnfoldComponent
- .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
}
// TODO: we should try to not pass status bar in here if we can avoid it.
@@ -167,6 +158,7 @@
}
void firePowerSaveChanged(boolean active) {
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onPowerSaveChanged(active);
}
@@ -177,6 +169,7 @@
entry.setPulseSuppressed(true);
mNotificationIconAreaController.updateAodNotificationIcons();
};
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onNotificationAlerted(pulseSuppressedListener);
}
@@ -193,11 +186,13 @@
@Override
public void addCallback(@NonNull Callback callback) {
+ Assert.isMainThread();
mCallbacks.add(callback);
}
@Override
public void removeCallback(@NonNull Callback callback) {
+ Assert.isMainThread();
mCallbacks.remove(callback);
}
@@ -212,12 +207,10 @@
}
void updateDozing() {
- // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
- boolean
- dozing =
- mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
- || mBiometricUnlockControllerLazy.get().getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ Assert.isMainThread();
+
+ boolean dozing =
+ mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
// When in wake-and-unlock we may not have received a change to StatusBarState
// but we still should not be dozing, manually set to false.
if (mBiometricUnlockControllerLazy.get().getMode()
@@ -225,10 +218,10 @@
dozing = false;
}
- mStatusBarStateController.setIsDozing(dozing);
- if (mFoldAodAnimationController != null) {
- mFoldAodAnimationController.setIsDozing(dozing);
+ for (Callback callback : mCallbacks) {
+ callback.onDozingChanged(dozing);
}
+ mStatusBarStateController.setIsDozing(dozing);
}
@Override
@@ -452,6 +445,7 @@
return;
}
mAlwaysOnSuppressed = suppressed;
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onAlwaysOnSuppressedChanged(suppressed);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
deleted file mode 100644
index dc77d10..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * Copyright (C) 2014 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.statusbar.phone;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
-import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
-
-import android.app.ActivityTaskManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.service.quickaccesswallet.GetWalletCardsError;
-import android.service.quickaccesswallet.GetWalletCardsResponse;
-import android.service.quickaccesswallet.QuickAccessWalletClient;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
-import android.view.WindowInsets;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.Interpolators;
-import com.android.systemui.controls.dagger.ControlsComponent;
-import com.android.systemui.controls.management.ControlsListingController;
-import com.android.systemui.controls.ui.ControlsActivity;
-import com.android.systemui.controls.ui.ControlsUiController;
-import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.wallet.controller.QuickAccessWalletController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
- * text.
- */
-public class KeyguardBottomAreaView extends FrameLayout {
-
- private static final String TAG = "CentralSurfaces/KeyguardBottomAreaView";
- private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
-
- private ImageView mWalletButton;
- private ImageView mQRCodeScannerButton;
- private ImageView mControlsButton;
- private boolean mHasCard = false;
- private final WalletCardRetriever mCardRetriever = new WalletCardRetriever();
- private QuickAccessWalletController mQuickAccessWalletController;
- private QRCodeScannerController mQRCodeScannerController;
- private ControlsComponent mControlsComponent;
- private boolean mControlServicesAvailable = false;
-
- @Nullable private View mAmbientIndicationArea;
- private ViewGroup mIndicationArea;
- private TextView mIndicationText;
- private TextView mIndicationTextBottom;
- private ViewGroup mOverlayContainer;
-
- private ActivityStarter mActivityStarter;
- private KeyguardStateController mKeyguardStateController;
- private FalsingManager mFalsingManager;
-
- private boolean mDozing;
- private int mIndicationBottomMargin;
- private int mIndicationPadding;
- private float mDarkAmount;
- private int mBurnInXOffset;
- private int mBurnInYOffset;
-
- private final ControlsListingController.ControlsListingCallback mListingCallback =
- serviceInfos -> post(() -> {
- boolean available = !serviceInfos.isEmpty();
-
- if (available != mControlServicesAvailable) {
- mControlServicesAvailable = available;
- updateControlsVisibility();
- updateAffordanceColors();
- }
- });
-
- private final KeyguardStateController.Callback mKeyguardStateCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- if (mKeyguardStateController.isShowing()) {
- if (mQuickAccessWalletController != null) {
- mQuickAccessWalletController.queryWalletCards(mCardRetriever);
- }
- }
- }
- };
-
- @Nullable private KeyguardBottomAreaViewBinder.Binding mBinding;
- private boolean mUsesBinder;
-
- public KeyguardBottomAreaView(Context context) {
- this(context, null);
- }
-
- public KeyguardBottomAreaView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * Initializes the view.
- */
- public void init(
- final KeyguardBottomAreaViewModel viewModel,
- final FalsingManager falsingManager) {
- Log.i(TAG, System.identityHashCode(this) + " initialized with a binder");
- mUsesBinder = true;
- mBinding = KeyguardBottomAreaViewBinder.bind(this, viewModel, falsingManager);
- }
-
- /**
- * Initializes the {@link KeyguardBottomAreaView} with the given dependencies
- *
- * @deprecated Use
- * {@link #init(KeyguardBottomAreaViewModel, FalsingManager)} instead
- */
- @Deprecated
- public void init(
- FalsingManager falsingManager,
- QuickAccessWalletController controller,
- ControlsComponent controlsComponent,
- QRCodeScannerController qrCodeScannerController) {
- if (mUsesBinder) {
- return;
- }
-
- Log.i(TAG, "initialized without a binder");
- mFalsingManager = falsingManager;
-
- mQuickAccessWalletController = controller;
- mQuickAccessWalletController.setupWalletChangeObservers(
- mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
- mQuickAccessWalletController.updateWalletPreference();
- mQuickAccessWalletController.queryWalletCards(mCardRetriever);
- updateWalletVisibility();
-
- mControlsComponent = controlsComponent;
- mControlsComponent.getControlsListingController().ifPresent(
- c -> c.addCallback(mListingCallback));
-
- mQRCodeScannerController = qrCodeScannerController;
- mQRCodeScannerController.registerQRCodeScannerChangeObservers(
- QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
- QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE);
- updateQRCodeButtonVisibility();
-
- updateAffordanceColors();
- }
-
- /**
- * Initializes this instance of {@link KeyguardBottomAreaView} based on the given instance of
- * another {@link KeyguardBottomAreaView}
- */
- public void initFrom(KeyguardBottomAreaView oldBottomArea) {
- if (mUsesBinder) {
- return;
- }
-
- // if it exists, continue to use the original ambient indication container
- // instead of the newly inflated one
- if (mAmbientIndicationArea != null) {
- // remove old ambient indication from its parent
- View originalAmbientIndicationView =
- oldBottomArea.findViewById(R.id.ambient_indication_container);
- ((ViewGroup) originalAmbientIndicationView.getParent())
- .removeView(originalAmbientIndicationView);
-
- // remove current ambient indication from its parent (discard)
- ViewGroup ambientIndicationParent = (ViewGroup) mAmbientIndicationArea.getParent();
- int ambientIndicationIndex =
- ambientIndicationParent.indexOfChild(mAmbientIndicationArea);
- ambientIndicationParent.removeView(mAmbientIndicationArea);
-
- // add the old ambient indication to this view
- ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex);
- mAmbientIndicationArea = originalAmbientIndicationView;
-
- // update burn-in offsets
- dozeTimeTick();
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- if (mUsesBinder) {
- return;
- }
-
- mOverlayContainer = findViewById(R.id.overlay_container);
- mWalletButton = findViewById(R.id.wallet_button);
- mQRCodeScannerButton = findViewById(R.id.qr_code_scanner_button);
- mControlsButton = findViewById(R.id.controls_button);
- mIndicationArea = findViewById(R.id.keyguard_indication_area);
- mAmbientIndicationArea = findViewById(R.id.ambient_indication_container);
- mIndicationText = findViewById(R.id.keyguard_indication_text);
- mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
- mIndicationBottomMargin = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom);
- mBurnInYOffset = getResources().getDimensionPixelSize(
- R.dimen.default_burn_in_prevention_offset);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mKeyguardStateController.addCallback(mKeyguardStateCallback);
- setClipChildren(false);
- setClipToPadding(false);
- mActivityStarter = Dependency.get(ActivityStarter.class);
-
- mIndicationPadding = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_area_padding);
- updateWalletVisibility();
- updateQRCodeButtonVisibility();
- updateControlsVisibility();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mUsesBinder) {
- return;
- }
-
- final IntentFilter filter = new IntentFilter();
- filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- mKeyguardStateController.addCallback(mKeyguardStateCallback);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mUsesBinder) {
- return;
- }
-
- mKeyguardStateController.removeCallback(mKeyguardStateCallback);
-
- if (mQuickAccessWalletController != null) {
- mQuickAccessWalletController.unregisterWalletChangeObservers(
- WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
- }
-
- if (mQRCodeScannerController != null) {
- mQRCodeScannerController.unregisterQRCodeScannerChangeObservers(
- QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
- QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE);
- }
-
- if (mControlsComponent != null) {
- mControlsComponent.getControlsListingController().ifPresent(
- c -> c.removeCallback(mListingCallback));
- }
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (mUsesBinder) {
- if (mBinding != null) {
- mBinding.onConfigurationChanged();
- }
- return;
- }
-
- mIndicationBottomMargin = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom);
- mBurnInYOffset = getResources().getDimensionPixelSize(
- R.dimen.default_burn_in_prevention_offset);
- MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
- if (mlp.bottomMargin != mIndicationBottomMargin) {
- mlp.bottomMargin = mIndicationBottomMargin;
- mIndicationArea.setLayoutParams(mlp);
- }
-
- // Respect font size setting.
- mIndicationTextBottom.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material));
- mIndicationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.text_size_small_material));
-
- ViewGroup.LayoutParams lp = mWalletButton.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
- mWalletButton.setLayoutParams(lp);
-
- lp = mQRCodeScannerButton.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
- mQRCodeScannerButton.setLayoutParams(lp);
-
- lp = mControlsButton.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
- mControlsButton.setLayoutParams(lp);
-
- mIndicationPadding = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_area_padding);
-
- updateWalletVisibility();
- updateQRCodeButtonVisibility();
- updateAffordanceColors();
- }
-
- private void updateWalletVisibility() {
- if (mUsesBinder) {
- return;
- }
-
- if (mDozing
- || mQuickAccessWalletController == null
- || !mQuickAccessWalletController.isWalletEnabled()
- || !mHasCard) {
- mWalletButton.setVisibility(GONE);
-
- if (mControlsButton.getVisibility() == GONE) {
- mIndicationArea.setPadding(0, 0, 0, 0);
- }
- } else {
- mWalletButton.setVisibility(VISIBLE);
- mWalletButton.setOnClickListener(this::onWalletClick);
- mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
- }
- }
-
- private void updateControlsVisibility() {
- if (mUsesBinder) {
- return;
- }
-
- if (mControlsComponent == null) return;
-
- mControlsButton.setImageResource(mControlsComponent.getTileImageId());
- mControlsButton.setContentDescription(getContext()
- .getString(mControlsComponent.getTileTitleId()));
- updateAffordanceColors();
-
- boolean hasFavorites = mControlsComponent.getControlsController()
- .map(c -> c.getFavorites().size() > 0)
- .orElse(false);
- if (mDozing
- || !hasFavorites
- || !mControlServicesAvailable
- || mControlsComponent.getVisibility() != AVAILABLE) {
- mControlsButton.setVisibility(GONE);
- if (mWalletButton.getVisibility() == GONE) {
- mIndicationArea.setPadding(0, 0, 0, 0);
- }
- } else {
- mControlsButton.setVisibility(VISIBLE);
- mControlsButton.setOnClickListener(this::onControlsClick);
- mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
- }
- }
-
- public void setDarkAmount(float darkAmount) {
- if (mUsesBinder) {
- return;
- }
-
- if (darkAmount == mDarkAmount) {
- return;
- }
- mDarkAmount = darkAmount;
- dozeTimeTick();
- }
-
- /**
- * Returns a list of animators to use to animate the indication areas.
- */
- public List<ViewPropertyAnimator> getIndicationAreaAnimators() {
- if (mUsesBinder) {
- return checkNotNull(mBinding).getIndicationAreaAnimators();
- }
-
- List<ViewPropertyAnimator> animators =
- new ArrayList<>(mAmbientIndicationArea != null ? 2 : 1);
- animators.add(mIndicationArea.animate());
- if (mAmbientIndicationArea != null) {
- animators.add(mAmbientIndicationArea.animate());
- }
- return animators;
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- private void startFinishDozeAnimation() {
- long delay = 0;
- if (mWalletButton.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mWalletButton, delay);
- }
- if (mQRCodeScannerButton.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mQRCodeScannerButton, delay);
- }
- if (mControlsButton.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mControlsButton, delay);
- }
- }
-
- private void startFinishDozeAnimationElement(View element, long delay) {
- element.setAlpha(0f);
- element.setTranslationY(element.getHeight() / 2);
- element.animate()
- .alpha(1f)
- .translationY(0f)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setStartDelay(delay)
- .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
- }
-
- public void setDozing(boolean dozing, boolean animate) {
- if (mUsesBinder) {
- return;
- }
-
- mDozing = dozing;
-
- updateWalletVisibility();
- updateControlsVisibility();
- updateQRCodeButtonVisibility();
-
- if (dozing) {
- mOverlayContainer.setVisibility(INVISIBLE);
- } else {
- mOverlayContainer.setVisibility(VISIBLE);
- if (animate) {
- startFinishDozeAnimation();
- }
- }
- }
-
- public void dozeTimeTick() {
- if (mUsesBinder) {
- return;
- }
-
- int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */)
- - mBurnInYOffset;
- mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
- if (mAmbientIndicationArea != null) {
- mAmbientIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
- }
- }
-
- public void setAntiBurnInOffsetX(int burnInXOffset) {
- if (mUsesBinder) {
- return;
- }
-
- if (mBurnInXOffset == burnInXOffset) {
- return;
- }
- mBurnInXOffset = burnInXOffset;
- mIndicationArea.setTranslationX(burnInXOffset);
- if (mAmbientIndicationArea != null) {
- mAmbientIndicationArea.setTranslationX(burnInXOffset);
- }
- }
-
- /**
- * Sets the alpha of various sub-components, for example the indication areas and bottom quick
- * action buttons. Does not set the alpha of the lock icon.
- */
- public void setComponentAlphas(float alpha) {
- if (mUsesBinder) {
- return;
- }
-
- setImportantForAccessibility(
- alpha == 0f
- ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- if (mAmbientIndicationArea != null) {
- mAmbientIndicationArea.setAlpha(alpha);
- }
- mIndicationArea.setAlpha(alpha);
- mWalletButton.setAlpha(alpha);
- mQRCodeScannerButton.setAlpha(alpha);
- mControlsButton.setAlpha(alpha);
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- int bottom = insets.getDisplayCutout() != null
- ? insets.getDisplayCutout().getSafeInsetBottom() : 0;
- if (isPaddingRelative()) {
- setPaddingRelative(getPaddingStart(), getPaddingTop(), getPaddingEnd(), bottom);
- } else {
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottom);
- }
- return insets;
- }
-
- private void updateQRCodeButtonVisibility() {
- if (mUsesBinder) {
- return;
- }
-
- if (mQuickAccessWalletController != null
- && mQuickAccessWalletController.isWalletEnabled()) {
- // Don't enable if quick access wallet is enabled
- return;
- }
-
- if (mQRCodeScannerController != null
- && mQRCodeScannerController.isEnabledForLockScreenButton()) {
- mQRCodeScannerButton.setVisibility(VISIBLE);
- mQRCodeScannerButton.setOnClickListener(this::onQRCodeScannerClicked);
- mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
- } else {
- mQRCodeScannerButton.setVisibility(GONE);
- if (mControlsButton.getVisibility() == GONE) {
- mIndicationArea.setPadding(0, 0, 0, 0);
- }
- }
- }
-
- private void onQRCodeScannerClicked(View view) {
- if (mUsesBinder) {
- return;
- }
-
- Intent intent = mQRCodeScannerController.getIntent();
- if (intent != null) {
- try {
- ActivityTaskManager.getService().startActivityAsUser(
- null, getContext().getBasePackageName(),
- getContext().getAttributionTag(), intent,
- intent.resolveTypeIfNeeded(getContext().getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
- UserHandle.CURRENT.getIdentifier());
- } catch (RemoteException e) {
- // This is unexpected. Nonetheless, just log the error and prevent the UI from
- // crashing
- Log.e(TAG, "Unexpected intent: " + intent
- + " when the QR code scanner button was clicked");
- }
- }
- }
-
- private void updateAffordanceColors() {
- if (mUsesBinder) {
- return;
- }
-
- int iconColor = Utils.getColorAttrDefaultColor(
- mContext,
- com.android.internal.R.attr.textColorPrimary);
- mWalletButton.getDrawable().setTint(iconColor);
- mControlsButton.getDrawable().setTint(iconColor);
- mQRCodeScannerButton.getDrawable().setTint(iconColor);
-
- ColorStateList bgColor = Utils.getColorAttr(
- mContext,
- com.android.internal.R.attr.colorSurface);
- mWalletButton.setBackgroundTintList(bgColor);
- mControlsButton.setBackgroundTintList(bgColor);
- mQRCodeScannerButton.setBackgroundTintList(bgColor);
- }
-
- private void onWalletClick(View v) {
- if (mUsesBinder) {
- return;
- }
-
- // More coming here; need to inform the user about how to proceed
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
-
- ActivityLaunchAnimator.Controller animationController = createLaunchAnimationController(v);
- mQuickAccessWalletController.startQuickAccessUiIntent(
- mActivityStarter, animationController, mHasCard);
- }
-
- protected ActivityLaunchAnimator.Controller createLaunchAnimationController(View view) {
- return ActivityLaunchAnimator.Controller.fromView(view, null);
- }
-
- private void onControlsClick(View v) {
- if (mUsesBinder) {
- return;
- }
-
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
-
- Intent intent = new Intent(mContext, ControlsActivity.class)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
- .putExtra(ControlsUiController.EXTRA_ANIMATE, true);
-
- ActivityLaunchAnimator.Controller controller =
- v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */)
- : null;
- if (mControlsComponent.getVisibility() == AVAILABLE) {
- mActivityStarter.startActivity(intent, true /* dismissShade */, controller,
- true /* showOverLockscreenWhenLocked */);
- } else {
- mActivityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */, controller);
- }
- }
-
- private class WalletCardRetriever implements
- QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
-
- @Override
- public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
- mHasCard = !response.getWalletCards().isEmpty();
- Drawable tileIcon = mQuickAccessWalletController.getWalletClient().getTileIcon();
- if (tileIcon != null) {
- mWalletButton.setImageDrawable(tileIcon);
- }
- post(() -> {
- updateWalletVisibility();
- updateAffordanceColors();
- });
- }
-
- @Override
- public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) {
- mHasCard = false;
- post(() -> {
- updateWalletVisibility();
- updateAffordanceColors();
- });
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
new file mode 100644
index 0000000..4897c52
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 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.statusbar.phone
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewPropertyAnimator
+import android.view.WindowInsets
+import android.widget.FrameLayout
+import com.android.systemui.R
+import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import com.android.systemui.plugins.FalsingManager
+
+/**
+ * Renders the bottom area of the lock-screen. Concerned primarily with the quick affordance UI
+ * elements. A secondary concern is the interaction of the quick affordance elements with the
+ * indication area between them, though the indication area is primarily controlled elsewhere.
+ */
+class KeyguardBottomAreaView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+) :
+ FrameLayout(
+ context,
+ attrs,
+ defStyleAttr,
+ defStyleRes,
+ ) {
+
+ private var ambientIndicationArea: View? = null
+ private lateinit var binding: KeyguardBottomAreaViewBinder.Binding
+
+ /** Initializes the view. */
+ fun init(
+ viewModel: KeyguardBottomAreaViewModel,
+ falsingManager: FalsingManager,
+ ) {
+ binding = bind(this, viewModel, falsingManager)
+ }
+
+ /**
+ * Initializes this instance of [KeyguardBottomAreaView] based on the given instance of another
+ * [KeyguardBottomAreaView]
+ */
+ fun initFrom(oldBottomArea: KeyguardBottomAreaView) {
+ // if it exists, continue to use the original ambient indication container
+ // instead of the newly inflated one
+ ambientIndicationArea?.let { nonNullAmbientIndicationArea ->
+ // remove old ambient indication from its parent
+ val originalAmbientIndicationView =
+ oldBottomArea.findViewById<View>(R.id.ambient_indication_container)
+ (originalAmbientIndicationView.parent as ViewGroup).removeView(
+ originalAmbientIndicationView
+ )
+
+ // remove current ambient indication from its parent (discard)
+ val ambientIndicationParent = nonNullAmbientIndicationArea.parent as ViewGroup
+ val ambientIndicationIndex =
+ ambientIndicationParent.indexOfChild(nonNullAmbientIndicationArea)
+ ambientIndicationParent.removeView(nonNullAmbientIndicationArea)
+
+ // add the old ambient indication to this view
+ ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex)
+ ambientIndicationArea = originalAmbientIndicationView
+ }
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ ambientIndicationArea = findViewById(R.id.ambient_indication_container)
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ binding.onConfigurationChanged()
+ }
+
+ /** Returns a list of animators to use to animate the indication areas. */
+ val indicationAreaAnimators: List<ViewPropertyAnimator>
+ get() = binding.getIndicationAreaAnimators()
+
+ override fun hasOverlappingRendering(): Boolean {
+ return false
+ }
+
+ override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
+ val bottom = insets.displayCutout?.safeInsetBottom ?: 0
+ if (isPaddingRelative) {
+ setPaddingRelative(paddingStart, paddingTop, paddingEnd, bottom)
+ } else {
+ setPadding(paddingLeft, paddingTop, paddingRight, bottom)
+ }
+ return insets
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f106e54..4c5c23c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -44,7 +44,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardMessageArea;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -122,7 +122,7 @@
private final DreamOverlayStateController mDreamOverlayStateController;
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
- private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -311,7 +311,7 @@
mBypassController = bypassController;
mNotificationContainer = notificationContainer;
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
- KeyguardMessageArea.findSecurityMessageDisplay(container));
+ centralSurfaces.getKeyguardMessageArea());
registerListeners();
}
@@ -378,11 +378,9 @@
return;
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
- } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
- && mKeyguardUpdateManager.isUdfpsEnrolled()) {
+ } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
- // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
- // affordance to enter the device (or swipe up to the input bouncer)
+ // CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
@@ -616,7 +614,8 @@
private void updateAlternateAuthShowing(boolean updateScrim) {
final boolean isShowingAltAuth = isShowingAlternateAuth();
if (mKeyguardMessageAreaController != null) {
- mKeyguardMessageAreaController.setAltBouncerShowing(isShowingAltAuth);
+ mKeyguardMessageAreaController.setIsVisible(isShowingAltAuth);
+ mKeyguardMessageAreaController.setMessage("");
}
mBypassController.setAltBouncerShowing(isShowingAltAuth);
mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAltAuth);
@@ -837,30 +836,24 @@
});
} else {
executeAfterKeyguardGoneAction();
- boolean wakeUnlockPulsing =
- mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
mCentralSurfaces.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
- if (wakeUnlockPulsing) {
- mCentralSurfaces.fadeKeyguardWhilePulsing();
+
+ boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
+ if (!staying) {
+ mNotificationShadeWindowController.setKeyguardFadingAway(true);
+ mCentralSurfaces.hideKeyguard();
+ // hide() will happen asynchronously and might arrive after the scrims
+ // were already hidden, this means that the transition callback won't
+ // be triggered anymore and StatusBarWindowController will be forever in
+ // the fadingAway state.
+ mCentralSurfaces.updateScrimController();
wakeAndUnlockDejank();
} else {
- boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- if (!staying) {
- mNotificationShadeWindowController.setKeyguardFadingAway(true);
- mCentralSurfaces.hideKeyguard();
- // hide() will happen asynchronously and might arrive after the scrims
- // were already hidden, this means that the transition callback won't
- // be triggered anymore and StatusBarWindowController will be forever in
- // the fadingAway state.
- mCentralSurfaces.updateScrimController();
- wakeAndUnlockDejank();
- } else {
- mCentralSurfaces.hideKeyguard();
- mCentralSurfaces.finishKeyguardFadingAway();
- mBiometricUnlockController.finishKeyguardFadingAway();
- }
+ mCentralSurfaces.hideKeyguard();
+ mCentralSurfaces.finishKeyguardFadingAway();
+ mBiometricUnlockController.finishKeyguardFadingAway();
}
updateStates();
@@ -1043,7 +1036,6 @@
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
mCentralSurfaces.setBouncerShowing(bouncerShowing);
- mKeyguardMessageAreaController.setBouncerShowing(bouncerShowing);
}
if (occluded != mLastOccluded || mFirstUpdate) {
@@ -1192,7 +1184,8 @@
}
}
- public void showBouncerMessage(String message, ColorStateList colorState) {
+ /** Display security message to relevant KeyguardMessageArea. */
+ public void setKeyguardMessage(String message, ColorStateList colorState) {
if (isShowingAlternateAuth()) {
if (mKeyguardMessageAreaController != null) {
mKeyguardMessageAreaController.setMessage(message);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 1987528..3c243ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -16,8 +16,15 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
+import android.content.Context
import android.graphics.Color
import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
@@ -41,6 +48,7 @@
class WifiViewModel @Inject constructor(
statusBarPipelineFlags: StatusBarPipelineFlags,
private val constants: WifiConstants,
+ private val context: Context,
private val logger: ConnectivityPipelineLogger,
private val interactor: WifiInteractor,
) {
@@ -61,18 +69,43 @@
}
}
+ /** The content description for the wifi icon. */
+ private val contentDescription: Flow<ContentDescription?> = interactor.wifiNetwork.map {
+ when (it) {
+ is WifiNetworkModel.CarrierMerged -> null
+ is WifiNetworkModel.Inactive ->
+ ContentDescription.Loaded(
+ "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
+ )
+ is WifiNetworkModel.Active ->
+ when (it.level) {
+ null -> null
+ else -> {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[it.level])
+ when {
+ it.isValidated -> ContentDescription.Loaded(levelDesc)
+ else -> ContentDescription.Loaded(
+ "$levelDesc,${context.getString(NO_INTERNET)}"
+ )
+ }
+ }
+ }
+ }
+ }
+
/**
* The wifi icon that should be displayed. Null if we shouldn't display any icon.
*/
val wifiIcon: Flow<Icon?> = combine(
interactor.isForceHidden,
- iconResId
- ) { isForceHidden, iconResId ->
+ iconResId,
+ contentDescription,
+ ) { isForceHidden, iconResId, contentDescription ->
when {
isForceHidden ||
iconResId == null ||
iconResId <= 0 -> null
- else -> Icon.Resource(iconResId)
+ else -> Icon.Resource(iconResId, contentDescription)
}
}
@@ -94,4 +127,10 @@
} else {
flowOf(Color.CYAN)
}
+
+ companion object {
+ @StringRes
+ @VisibleForTesting
+ internal val NO_INTERNET = R.string.data_connection_no_internet
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index bdac888..f4d08e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -31,6 +31,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -60,6 +61,7 @@
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy;
+ private final KeyguardUpdateMonitorLogger mLogger;
private boolean mCanDismissLockScreen;
private boolean mShowing;
@@ -107,8 +109,10 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
+ KeyguardUpdateMonitorLogger logger,
DumpManager dumpManager) {
mContext = context;
+ mLogger = logger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
@@ -245,6 +249,8 @@
mTrusted = trusted;
mTrustManaged = trustManaged;
mFaceAuthEnabled = faceAuthEnabled;
+ mLogger.logKeyguardStateUpdate(
+ mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged);
notifyUnlockedChanged();
}
Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
rename to packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 3a0ac1b..734eeec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
import android.annotation.LayoutRes
import android.annotation.SuppressLint
@@ -37,30 +37,30 @@
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
/**
- * A superclass controller that provides common functionality for showing chips on the sender device
- * and the receiver device.
+ * A generic controller that can temporarily display a new view in a new window.
*
- * Subclasses need to override and implement [updateChipView], which is where they can control what
+ * Subclasses need to override and implement [updateView], which is where they can control what
* gets displayed to the user.
*
* The generic type T is expected to contain all the information necessary for the subclasses to
- * display the chip in a certain state, since they receive <T> in [updateChipView].
+ * display the view in a certain state, since they receive <T> in [updateView].
+ *
+ * TODO(b/245610654): Remove all the media-specific logic from this class.
*/
-abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
- internal val context: Context,
- internal val logger: MediaTttLogger,
- internal val windowManager: WindowManager,
- private val viewUtil: ViewUtil,
- @Main private val mainExecutor: DelayableExecutor,
- private val accessibilityManager: AccessibilityManager,
- private val configurationController: ConfigurationController,
- private val powerManager: PowerManager,
- @LayoutRes private val chipLayoutRes: Int,
+abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
+ internal val context: Context,
+ internal val logger: MediaTttLogger,
+ internal val windowManager: WindowManager,
+ @Main private val mainExecutor: DelayableExecutor,
+ private val accessibilityManager: AccessibilityManager,
+ private val configurationController: ConfigurationController,
+ private val powerManager: PowerManager,
+ @LayoutRes private val viewLayoutRes: Int,
) {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
@@ -85,31 +85,31 @@
*/
internal abstract val windowLayoutParams: WindowManager.LayoutParams
- /** The chip view currently being displayed. Null if the chip is not being displayed. */
- private var chipView: ViewGroup? = null
+ /** The view currently being displayed. Null if the view is not being displayed. */
+ private var view: ViewGroup? = null
- /** The chip info currently being displayed. Null if the chip is not being displayed. */
- internal var chipInfo: T? = null
+ /** The info currently being displayed. Null if the view is not being displayed. */
+ internal var info: T? = null
- /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
- private var cancelChipViewTimeout: Runnable? = null
+ /** A [Runnable] that, when run, will cancel the pending timeout of the view. */
+ private var cancelViewTimeout: Runnable? = null
/**
- * Displays the chip with the provided [newChipInfo].
+ * Displays the view with the provided [newInfo].
*
- * This method handles inflating and attaching the view, then delegates to [updateChipView] to
- * display the correct information in the chip.
+ * This method handles inflating and attaching the view, then delegates to [updateView] to
+ * display the correct information in the view.
*/
- fun displayChip(newChipInfo: T) {
- val currentChipView = chipView
+ fun displayView(newInfo: T) {
+ val currentView = view
- if (currentChipView != null) {
- updateChipView(newChipInfo, currentChipView)
+ if (currentView != null) {
+ updateView(newInfo, currentView)
} else {
- // The chip is new, so set up all our callbacks and inflate the view
+ // The view is new, so set up all our callbacks and inflate the view
configurationController.addCallback(displayScaleListener)
- // Wake the screen if necessary so the user will see the chip. (Per b/239426653, we want
- // the chip to show over the dream state, so we should only wake up if the screen is
+ // Wake the screen if necessary so the user will see the view. (Per b/239426653, we want
+ // 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(
@@ -119,79 +119,79 @@
)
}
- inflateAndUpdateChip(newChipInfo)
+ inflateAndUpdateView(newInfo)
}
- // Cancel and re-set the chip timeout each time we get a new state.
+ // Cancel and re-set the view timeout each time we get a new state.
val timeout = accessibilityManager.getRecommendedTimeoutMillis(
- newChipInfo.getTimeoutMs().toInt(),
- // Not all chips have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
+ newInfo.getTimeoutMs().toInt(),
+ // Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
// include it just to be safe.
FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
)
- cancelChipViewTimeout?.run()
- cancelChipViewTimeout = mainExecutor.executeDelayed(
- { removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
+ cancelViewTimeout?.run()
+ cancelViewTimeout = mainExecutor.executeDelayed(
+ { removeView(TemporaryDisplayRemovalReason.REASON_TIMEOUT) },
timeout.toLong()
)
}
- /** Inflates a new chip view, updates it with [newChipInfo], and adds the view to the window. */
- private fun inflateAndUpdateChip(newChipInfo: T) {
- val newChipView = LayoutInflater
+ /** Inflates a new view, updates it with [newInfo], and adds the view to the window. */
+ private fun inflateAndUpdateView(newInfo: T) {
+ val newView = LayoutInflater
.from(context)
- .inflate(chipLayoutRes, null) as ViewGroup
- chipView = newChipView
- updateChipView(newChipInfo, newChipView)
- windowManager.addView(newChipView, windowLayoutParams)
- animateChipIn(newChipView)
+ .inflate(viewLayoutRes, null) as ViewGroup
+ view = newView
+ updateView(newInfo, newView)
+ windowManager.addView(newView, windowLayoutParams)
+ animateViewIn(newView)
}
- /** Removes then re-inflates the chip. */
- private fun reinflateChip() {
- val currentChipInfo = chipInfo
- if (chipView == null || currentChipInfo == null) { return }
+ /** Removes then re-inflates the view. */
+ private fun reinflateView() {
+ val currentInfo = info
+ if (view == null || currentInfo == null) { return }
- windowManager.removeView(chipView)
- inflateAndUpdateChip(currentChipInfo)
+ windowManager.removeView(view)
+ inflateAndUpdateView(currentInfo)
}
private val displayScaleListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
- reinflateChip()
+ reinflateView()
}
}
/**
- * Hides the chip.
+ * Hides the view.
*
- * @param removalReason a short string describing why the chip was removed (timeout, state
+ * @param removalReason a short string describing why the view was removed (timeout, state
* change, etc.)
*/
- open fun removeChip(removalReason: String) {
- if (chipView == null) { return }
+ open fun removeView(removalReason: String) {
+ if (view == null) { return }
logger.logChipRemoval(removalReason)
configurationController.removeCallback(displayScaleListener)
- windowManager.removeView(chipView)
- chipView = null
- chipInfo = null
- // No need to time the chip out since it's already gone
- cancelChipViewTimeout?.run()
+ windowManager.removeView(view)
+ view = null
+ info = null
+ // No need to time the view out since it's already gone
+ cancelViewTimeout?.run()
}
/**
- * A method implemented by subclasses to update [currentChipView] based on [newChipInfo].
+ * A method implemented by subclasses to update [currentView] based on [newInfo].
*/
@CallSuper
- open fun updateChipView(newChipInfo: T, currentChipView: ViewGroup) {
- chipInfo = newChipInfo
+ open fun updateView(newInfo: T, currentView: ViewGroup) {
+ info = newInfo
}
/**
- * A method that can be implemented by subclcasses to do custom animations for when the chip
+ * A method that can be implemented by subclasses to do custom animations for when the view
* appears.
*/
- open fun animateChipIn(chipView: ViewGroup) {}
+ open fun animateViewIn(view: ViewGroup) {}
/**
* Returns the size that the icon should be, or null if no size override is needed.
@@ -209,12 +209,12 @@
* @return the content description of the icon.
*/
internal fun setIcon(
- currentChipView: ViewGroup,
+ currentView: ViewGroup,
appPackageName: String?,
appIconDrawableOverride: Drawable? = null,
appNameOverride: CharSequence? = null,
): CharSequence {
- val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
+ val appIconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
val iconInfo = getIconInfo(appPackageName)
getIconSize(iconInfo.isAppIcon)?.let { size ->
@@ -264,9 +264,9 @@
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
// UpdateMediaTapToTransferReceiverDisplayTest
private const val WINDOW_TITLE = "Media Transfer Chip View"
-private val TAG = MediaTttChipControllerCommon::class.simpleName!!
+private val TAG = TemporaryViewDisplayController::class.simpleName!!
-object MediaTttRemovalReason {
+object TemporaryDisplayRemovalReason {
const val REASON_TIMEOUT = "TIMEOUT"
const val REASON_SCREEN_TAP = "SCREEN_TAP"
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
rename to packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
index a29c588..4fe753a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
/**
- * A superclass chip state that will be subclassed by the sender chip and receiver chip.
+ * A superclass view state used with [TemporaryViewDisplayController].
*/
-interface ChipInfoCommon {
+interface TemporaryViewInfo {
/**
- * Returns the amount of time the given chip state should display on the screen before it times
+ * Returns the amount of time the given view state should display on the screen before it times
* out and disappears.
*/
- fun getTimeoutMs(): Long
+ fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
}
const val DEFAULT_TIMEOUT_MILLIS = 10000L
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 8f127fd..0f06144 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -18,22 +18,31 @@
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
-import android.os.Handler
import android.os.PowerManager
import android.provider.Settings
+import androidx.annotation.VisibleForTesting
import androidx.core.view.OneShotPreDrawListener
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.ScreenOffAnimation
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
+import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.settings.GlobalSettings
-import java.util.concurrent.Executor
+import dagger.Lazy
import java.util.function.Consumer
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
/**
* Controls folding to AOD animation: when AOD is enabled and foldable device is folded we play a
@@ -43,16 +52,16 @@
class FoldAodAnimationController
@Inject
constructor(
- @Main private val handler: Handler,
- @Main private val executor: Executor,
+ @Main private val executor: DelayableExecutor,
private val context: Context,
private val deviceStateManager: DeviceStateManager,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings,
private val latencyTracker: LatencyTracker,
+ private val keyguardInteractor: Lazy<KeyguardInteractor>,
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
- private lateinit var mCentralSurfaces: CentralSurfaces
+ private lateinit var centralSurfaces: CentralSurfaces
private var isFolded = false
private var isFoldHandled = true
@@ -64,12 +73,13 @@
private var shouldPlayAnimation = false
private var isAnimationPlaying = false
+ private var cancelAnimation: Runnable? = null
private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
private val foldToAodLatencyTracker = FoldToAodLatencyTracker()
private val startAnimationRunnable = Runnable {
- mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
+ centralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
/* startAction= */ { foldToAodLatencyTracker.onAnimationStarted() },
/* endAction= */ { setAnimationState(playing = false) },
/* cancelAction= */ { setAnimationState(playing = false) },
@@ -77,10 +87,14 @@
}
override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
- this.mCentralSurfaces = centralSurfaces
+ this.centralSurfaces = centralSurfaces
deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
+
+ centralSurfaces.notificationPanelViewController.view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) }
+ }
}
/** Returns true if we should run fold to AOD animation */
@@ -94,7 +108,7 @@
override fun startAnimation(): Boolean =
if (shouldStartAnimation()) {
setAnimationState(playing = true)
- mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
+ centralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
true
} else {
setAnimationState(playing = false)
@@ -104,8 +118,8 @@
override fun onStartedWakingUp() {
if (isAnimationPlaying) {
foldToAodLatencyTracker.cancel()
- handler.removeCallbacks(startAnimationRunnable)
- mCentralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
+ cancelAnimation?.run()
+ centralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
}
setAnimationState(playing = false)
@@ -138,13 +152,13 @@
// We should play the folding to AOD animation
setAnimationState(playing = true)
- mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
+ centralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
// We don't need to wait for the scrim as it is already displayed
// but we should wait for the initial animation preparations to be drawn
// (setting initial alpha/translation)
OneShotPreDrawListener.add(
- mCentralSurfaces.notificationPanelViewController.view,
+ centralSurfaces.notificationPanelViewController.view,
onReady
)
} else {
@@ -165,18 +179,14 @@
fun onScreenTurnedOn() {
if (shouldPlayAnimation) {
- handler.removeCallbacks(startAnimationRunnable)
+ cancelAnimation?.run()
// Post starting the animation to the next frame to avoid junk due to inset changes
- handler.post(startAnimationRunnable)
+ cancelAnimation = executor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0)
shouldPlayAnimation = false
}
}
- fun setIsDozing(dozing: Boolean) {
- isDozing = dozing
- }
-
override fun isAnimationPlaying(): Boolean = isAnimationPlaying
override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
@@ -204,6 +214,11 @@
statusListeners.remove(listener)
}
+ @VisibleForTesting
+ internal suspend fun listenForDozing(scope: CoroutineScope): Job {
+ return scope.launch { keyguardInteractor.get().isDozing.collect { isDozing = it } }
+ }
+
interface FoldAodAnimationStatus {
fun onFoldToAodAnimationChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 7baebf4..f71d596 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -16,11 +16,15 @@
package com.android.systemui.util.kotlin
+import java.util.concurrent.atomic.AtomicReference
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.zip
+import kotlinx.coroutines.launch
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -29,15 +33,16 @@
*
* Useful for code that needs to compare the current value to the previous value.
*/
-fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> {
- // same as current flow, but with the very first event skipped
- val nextEvents = drop(1)
- // zip current flow and nextEvents; transform will receive a pair of old and new value. This
- // works because zip will suppress emissions until both flows have emitted something; since in
- // this case both flows are emitting at the same rate, but the current flow just has one extra
- // thing emitted at the start, the effect is that zip will cache the most recent value while
- // waiting for the next emission from nextEvents.
- return zip(nextEvents, transform)
+fun <T, R> Flow<T>.pairwiseBy(transform: suspend (old: T, new: T) -> R): Flow<R> = flow {
+ val noVal = Any()
+ var previousValue: Any? = noVal
+ collect { newVal ->
+ if (previousValue != noVal) {
+ @Suppress("UNCHECKED_CAST")
+ emit(transform(previousValue as T, newVal))
+ }
+ previousValue = newVal
+ }
}
/**
@@ -74,10 +79,19 @@
/**
* Returns a new [Flow] that combines the [Set] changes between each emission from [this] using
* [transform].
+ *
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
+ * a change event to be emitted that contains no removals, and all elements from that first [Set]
+ * as additions.
+ *
+ * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
+ * until a second [Set] has been emitted from the upstream [Flow].
*/
fun <T, R> Flow<Set<T>>.setChangesBy(
transform: suspend (removed: Set<T>, added: Set<T>) -> R,
-): Flow<R> = onStart { emit(emptySet()) }.distinctUntilChanged()
+ emitFirstEvent: Boolean = true,
+): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
+ .distinctUntilChanged()
.pairwiseBy { old: Set<T>, new: Set<T> ->
// If an element was present in the old set, but not the new one, then it was removed
val removed = old - new
@@ -86,8 +100,18 @@
transform(removed, added)
}
-/** Returns a new [Flow] that produces the [Set] changes between each emission from [this]. */
-fun <T> Flow<Set<T>>.setChanges(): Flow<SetChanges<T>> = setChangesBy(::SetChanges)
+/**
+ * Returns a new [Flow] that produces the [Set] changes between each emission from [this].
+ *
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
+ * a change event to be emitted that contains no removals, and all elements from that first [Set]
+ * as additions.
+ *
+ * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
+ * until a second [Set] has been emitted from the upstream [Flow].
+ */
+fun <T> Flow<Set<T>>.setChanges(emitFirstEvent: Boolean = true): Flow<SetChanges<T>> =
+ setChangesBy(::SetChanges, emitFirstEvent)
/** Contains the difference in elements between two [Set]s. */
data class SetChanges<T>(
@@ -96,3 +120,35 @@
/** Elements that are present in the second [Set] but not in the first. */
val added: Set<T>,
)
+
+/**
+ * Returns a new [Flow] that emits at the same rate as [this], but combines the emitted value with
+ * the most recent emission from [other] using [transform].
+ *
+ * Note that the returned Flow will not emit anything until [other] has emitted at least one value.
+ */
+fun <A, B, C> Flow<A>.sample(other: Flow<B>, transform: suspend (A, B) -> C): Flow<C> = flow {
+ coroutineScope {
+ val noVal = Any()
+ val sampledRef = AtomicReference(noVal)
+ val job = launch(Dispatchers.Unconfined) {
+ other.collect { sampledRef.set(it) }
+ }
+ collect {
+ val sampled = sampledRef.get()
+ if (sampled != noVal) {
+ @Suppress("UNCHECKED_CAST")
+ emit(transform(it, sampled as B))
+ }
+ }
+ job.cancel()
+ }
+}
+
+/**
+ * Returns a new [Flow] that emits at the same rate as [this], but emits the most recently emitted
+ * value from [other] instead.
+ *
+ * Note that the returned Flow will not emit anything until [other] has emitted at least one value.
+ */
+fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt
new file mode 100644
index 0000000..2e551f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.systemui.util.kotlin
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Runs the given [blocks] in parallel, returning the result of the first one to complete, and
+ * cancelling all others.
+ */
+suspend fun <R> race(vararg blocks: suspend () -> R): R = coroutineScope {
+ val completion = CompletableDeferred<R>()
+ val raceJob = launch {
+ for (block in blocks) {
+ launch { completion.complete(block()) }
+ }
+ }
+ completion.await().also { raceJob.cancel() }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 8c41374..4e77514 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -311,18 +311,6 @@
}
@Override
- public void notifyMaybeCancelSummary(String key) {
- sysuiMainExecutor.execute(() -> {
- final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
- if (entry != null) {
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(entry);
- }
- }
- });
- }
-
- @Override
public void updateNotificationBubbleButton(String key) {
sysuiMainExecutor.execute(() -> {
final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
@@ -646,15 +634,5 @@
* filtered from the shade.
*/
void invalidateNotifications(@NonNull String reason);
-
- /**
- * Called on a bubbled entry that has been removed when there are no longer
- * bubbled entries in its group.
- *
- * Checks whether its group has any other (non-bubbled) children. If it doesn't,
- * removes all remnants of the group's summary from the notification pipeline.
- * TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
- */
- void maybeCancelSummary(@NonNull NotificationEntry entry);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
rename to packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
index 013c298..0a9c745 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
@@ -33,14 +33,14 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class KeyguardMessageAreaTest extends SysuiTestCase {
+public class AuthKeyguardMessageAreaTest extends SysuiTestCase {
private KeyguardMessageArea mKeyguardMessageArea;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
- mKeyguardMessageArea.setBouncerShowing(true);
+ mKeyguardMessageArea = new AuthKeyguardMessageArea(mContext, null);
+ mKeyguardMessageArea.setIsVisible(true);
}
@Test
@@ -53,7 +53,7 @@
@Test
public void testHiddenWhenBouncerHidden() {
- mKeyguardMessageArea.setBouncerShowing(false);
+ mKeyguardMessageArea.setIsVisible(false);
mKeyguardMessageArea.setVisibility(View.INVISIBLE);
mKeyguardMessageArea.setMessage("oobleck");
assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 90f7fda..8bbaf3d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -56,7 +56,7 @@
@Mock
private PasswordTextView mPasswordEntry;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -85,7 +85,7 @@
when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
- when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
+ when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 8293cc2..69524e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -89,8 +89,8 @@
@Test
public void testSetBouncerVisible() {
- mMessageAreaController.setBouncerShowing(true);
- verify(mKeyguardMessageArea).setBouncerShowing(true);
+ mMessageAreaController.setIsVisible(true);
+ verify(mKeyguardMessageArea).setIsVisible(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index ec85603..b89dbd9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -64,9 +64,10 @@
@Mock
lateinit var keyguardViewController: KeyguardViewController
@Mock
- private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
- private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -74,7 +75,8 @@
fun setup() {
MockitoAnnotations.initMocks(this)
Mockito.`when`(
- keyguardPasswordView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area)
+ keyguardPasswordView
+ .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area)
).thenReturn(mKeyguardMessageArea)
Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea))
.thenReturn(mKeyguardMessageAreaController)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 616a105..3262a77 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -66,10 +66,11 @@
var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
@Mock
- private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
- private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock
private lateinit var mLockPatternView: LockPatternView
@@ -83,7 +84,8 @@
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
- `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
+ `when`(mKeyguardPatternView
+ .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea)
`when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
.thenReturn(mLockPatternView)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 7bc8e8a..97d556b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -51,7 +51,7 @@
@Mock
private PasswordTextView mPasswordEntry;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -90,7 +90,7 @@
when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
when(mPinBasedInputView.getButtons()).thenReturn(mButtons);
- when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
+ when(mPinBasedInputView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
when(mPinBasedInputView.findViewById(R.id.delete_button))
.thenReturn(mDeleteButton);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index d68e8bd..c6ebaa8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -41,6 +41,7 @@
import android.hardware.biometrics.BiometricSourceType;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsetsController;
@@ -117,7 +118,7 @@
@Mock
private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private ConfigurationController mConfigurationController;
@Mock
@@ -163,9 +164,10 @@
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
+ mKeyguardPasswordView = spy((KeyguardPasswordView) LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_password_view, null));
when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
- when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
+ when(mKeyguardPasswordView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
new file mode 100644
index 0000000..419fedf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.systemui.biometrics
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BiometricMessageDeferralTest : SysuiTestCase() {
+
+ @Test
+ fun testProcessNoMessages_noDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf())
+
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testProcessNonDeferredMessages_noDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // WHEN there are no deferred messages processed
+ for (i in 0..3) {
+ biometricMessageDeferral.processMessage(4, "test")
+ }
+
+ // THEN getDeferredMessage is null
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testAllProcessedMessagesWereDeferred() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
+
+ // WHEN all the processed messages are a deferred message
+ for (i in 0..3) {
+ biometricMessageDeferral.processMessage(1, "test")
+ }
+
+ // THEN deferredMessage will return the string associated with the deferred msgId
+ assertEquals("test", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testReturnsMostFrequentDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // WHEN there's two msgId=1 processed and one msgId=2 processed
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(2, "msgId-2")
+
+ // THEN the most frequent deferred message is that meets the threshold is returned
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_mustMeetThreshold() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
+
+ // WHEN more nonDeferredMessages are shown than the deferred message
+ val totalMessages = 10
+ val nonDeferredMessagesCount =
+ (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
+ for (i in 0 until nonDeferredMessagesCount) {
+ biometricMessageDeferral.processMessage(4, "non-deferred-msg")
+ }
+ for (i in nonDeferredMessagesCount until totalMessages) {
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ }
+
+ // THEN there's no deferred message because it didn't meet the threshold
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_manyExcludedMessages_getDeferredMessage() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1))
+
+ // WHEN more excludedMessages are shown than the deferred message
+ val totalMessages = 10
+ val excludedMessagesCount = (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
+ for (i in 0 until excludedMessagesCount) {
+ biometricMessageDeferral.processMessage(3, "excluded-msg")
+ }
+ for (i in excludedMessagesCount until totalMessages) {
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ }
+
+ // THEN there IS a deferred message because the deferred msg meets the threshold amongst the
+ // non-excluded messages
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testResetClearsOutCounts() {
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
+
+ // GIVEN two msgId=1 events processed
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+ biometricMessageDeferral.processMessage(1, "msgId-1")
+
+ // WHEN counts are reset and then a single deferred message is processed (msgId=2)
+ biometricMessageDeferral.reset()
+ biometricMessageDeferral.processMessage(2, "msgId-2")
+
+ // THEN msgId-2 is the deferred message since the two msgId=1 events were reset
+ assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testShouldDefer() {
+ // GIVEN should defer msgIds 1 and 2
+ val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1, 2))
+
+ // THEN shouldDefer returns true for ids 1 & 2
+ assertTrue(biometricMessageDeferral.shouldDefer(1))
+ assertTrue(biometricMessageDeferral.shouldDefer(2))
+
+ // THEN should defer returns false for ids 3 & 4
+ assertFalse(biometricMessageDeferral.shouldDefer(3))
+ assertFalse(biometricMessageDeferral.shouldDefer(4))
+ }
+}
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 3aa2266..ba1e168 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
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
+import com.android.systemui.doze.DozeHost
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
@@ -40,6 +41,7 @@
class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
private lateinit var underTest: KeyguardRepositoryImpl
@@ -48,7 +50,12 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- underTest = KeyguardRepositoryImpl(statusBarStateController, keyguardStateController)
+ underTest =
+ KeyguardRepositoryImpl(
+ statusBarStateController,
+ keyguardStateController,
+ dozeHost,
+ )
}
@Test
@@ -129,8 +136,8 @@
var latest: Boolean? = null
val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- val captor = argumentCaptor<StatusBarStateController.StateListener>()
- verify(statusBarStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<DozeHost.Callback>()
+ verify(dozeHost).addCallback(captor.capture())
captor.value.onDozingChanged(true)
assertThat(latest).isTrue()
@@ -139,7 +146,7 @@
assertThat(latest).isFalse()
job.cancel()
- verify(statusBarStateController).removeCallback(captor.value)
+ verify(dozeHost).removeCallback(captor.value)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 9acd21c..9a91ea91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -51,18 +51,19 @@
@Parameters(
name =
"feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" +
- " while locked = {3} - expected visible = {4}"
+ " while locked = {3}, visibility is AVAILABLE {4} - expected visible = {5}"
)
@JvmStatic
fun data() =
- (0 until 16)
+ (0 until 32)
.map { combination ->
arrayOf(
- /* isFeatureEnabled= */ combination and 0b1000 != 0,
- /* hasFavorites= */ combination and 0b0100 != 0,
- /* hasServiceInfos= */ combination and 0b0010 != 0,
- /* canShowWhileLocked= */ combination and 0b0001 != 0,
- /* isVisible= */ combination == 0b1111,
+ /* isFeatureEnabled= */ combination and 0b10000 != 0,
+ /* hasFavorites= */ combination and 0b01000 != 0,
+ /* hasServiceInfos= */ combination and 0b00100 != 0,
+ /* canShowWhileLocked= */ combination and 0b00010 != 0,
+ /* visibilityAvailable= */ combination and 0b00001 != 0,
+ /* isVisible= */ combination == 0b11111,
)
}
.toList()
@@ -81,7 +82,8 @@
@JvmField @Parameter(1) var hasFavorites: Boolean = false
@JvmField @Parameter(2) var hasServiceInfos: Boolean = false
@JvmField @Parameter(3) var canShowWhileLocked: Boolean = false
- @JvmField @Parameter(4) var isVisible: Boolean = false
+ @JvmField @Parameter(4) var isVisibilityAvailable: Boolean = false
+ @JvmField @Parameter(5) var isVisibleExpected: Boolean = false
@Before
fun setUp() {
@@ -93,6 +95,14 @@
.thenReturn(Optional.of(controlsListingController))
whenever(component.canShowWhileLockedSetting)
.thenReturn(MutableStateFlow(canShowWhileLocked))
+ whenever(component.getVisibility())
+ .thenReturn(
+ if (isVisibilityAvailable) {
+ ControlsComponent.Visibility.AVAILABLE
+ } else {
+ ControlsComponent.Visibility.UNAVAILABLE
+ }
+ )
underTest =
HomeControlsKeyguardQuickAffordanceConfig(
@@ -128,7 +138,7 @@
assertThat(values.last())
.isInstanceOf(
- if (isVisible) {
+ if (isVisibleExpected) {
KeyguardQuickAffordanceConfig.State.Visible::class.java
} else {
KeyguardQuickAffordanceConfig.State.Hidden::class.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 059487d..dede4ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -69,6 +69,7 @@
val controlsController = mock<ControlsController>()
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
@@ -87,6 +88,7 @@
val controlsController = mock<ControlsController>()
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
index d3fc29f..19d8412 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
@@ -36,6 +36,7 @@
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -110,6 +111,10 @@
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
+ // The interactor has an onStart { emit(Hidden) } to cover for upstream configs that don't
+ // produce an initial value. We yield to give the coroutine time to emit the first real
+ // value from our config.
+ yield()
assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
@@ -136,6 +141,10 @@
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
+ // The interactor has an onStart { emit(Hidden) } to cover for upstream configs that don't
+ // produce an initial value. We yield to give the coroutine time to emit the first real
+ // value from our config.
+ yield()
assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 14b85b8..c612091 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -224,7 +224,10 @@
repository.setAnimateDozingTransitions(false)
yield()
- assertThat(values).isEqualTo(listOf(false, true, false))
+ // Note the extra false value in the beginning. This is to cover for the initial value
+ // inserted by the quick affordance interactor which it does to cover for config
+ // implementations that don't emit an initial value.
+ assertThat(values).isEqualTo(listOf(false, false, true, false))
job.cancel()
}
@@ -372,6 +375,10 @@
var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.startButton.onEach { latest = it }.launchIn(this)
+ // The interactor has an onStart { emit(Hidden) } to cover for upstream configs that don't
+ // produce an initial value. We yield to give the coroutine time to emit the first real
+ // value from our config.
+ yield()
assertQuickAffordanceViewModel(
viewModel = latest,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 18bfd04..954b438 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.testing.FakeNotifPanelEvents
import com.android.systemui.statusbar.StatusBarState
@@ -38,6 +39,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
@@ -79,6 +82,9 @@
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
private lateinit var statusBarCallback: ArgumentCaptor<(StatusBarStateController.StateListener)>
+ @Captor
+ private lateinit var dreamOverlayCallback:
+ ArgumentCaptor<(DreamOverlayStateController.Callback)>
@JvmField
@Rule
val mockito = MockitoJUnit.rule()
@@ -113,6 +119,7 @@
fakeHandler,)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture())
+ verify(dreamOverlayStateController).addCallback(dreamOverlayCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS, QS_TOP)
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS, QQS_TOP)
@@ -332,6 +339,27 @@
assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
}
+ @Test
+ fun testDream() {
+ goToDream()
+ setMediaDreamComplicationEnabled(true)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_DREAM_OVERLAY),
+ nullable(),
+ eq(false),
+ anyLong(),
+ anyLong())
+ clearInvocations(mediaCarouselController)
+
+ setMediaDreamComplicationEnabled(false)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_QQS),
+ any(MediaHostState::class.java),
+ eq(false),
+ anyLong(),
+ anyLong())
+ }
+
private fun enableSplitShade() {
context.getOrCreateTestableResources().addOverride(
R.bool.config_use_split_notification_shade, true
@@ -343,6 +371,8 @@
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ whenever(dreamOverlayStateController.isOverlayActive).thenReturn(false)
+ dreamOverlayCallback.value.onStateChanged()
clearInvocations(mediaCarouselController)
}
@@ -354,6 +384,17 @@
)
}
+ private fun goToDream() {
+ whenever(dreamOverlayStateController.isOverlayActive).thenReturn(true)
+ dreamOverlayCallback.value.onStateChanged()
+ }
+
+ private fun setMediaDreamComplicationEnabled(enabled: Boolean) {
+ val complications = if (enabled) listOf(mock<MediaDreamComplication>()) else emptyList()
+ whenever(dreamOverlayStateController.complications).thenReturn(complications)
+ dreamOverlayCallback.value.onComplicationsChanged()
+ }
+
private fun expandQS() {
mediaHierarchyManager.qsExpansion = 1.0f
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 2e864dc..0bfc034 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -72,13 +72,13 @@
}
@Test
- public void testComplicationAddition() {
+ public void testOnMediaDataLoaded_complicationAddition() {
final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
-
sentinel.start();
final MediaDataManager.Listener listener = captureMediaDataListener();
+
when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */ true,
/* receivedSmartspaceCardLatency= */ 0, /* isSsReactived= */ false);
@@ -90,6 +90,19 @@
verify(mDreamOverlayStateController).addComplication(eq(mMediaEntryComplication));
verify(mDreamOverlayStateController, never()).addComplication(
not(eq(mMediaEntryComplication)));
+ }
+
+ @Test
+ public void testOnMediaDataRemoved_complicationRemoval() {
+ final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+ mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
+ sentinel.start();
+
+ final MediaDataManager.Listener listener = captureMediaDataListener();
+
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
+ /* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
listener.onMediaDataRemoved(mKey);
verify(mDreamOverlayStateController, never()).removeComplication(any());
@@ -100,7 +113,30 @@
}
@Test
- public void testMediaDreamSentinel_mediaComplicationDisabled_doNotAddComplication() {
+ public void testOnMediaDataLoaded_complicationRemoval() {
+ final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+ mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
+ sentinel.start();
+
+ final MediaDataManager.Listener listener = captureMediaDataListener();
+
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
+ /* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
+ verify(mDreamOverlayStateController, never()).removeComplication(any());
+
+ listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
+ /* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
+ verify(mDreamOverlayStateController, never()).removeComplication(any());
+
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
+ listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
+ /* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
+ verify(mDreamOverlayStateController).removeComplication(eq(mMediaEntryComplication));
+ }
+
+ @Test
+ public void testOnMediaDataLoaded_mediaComplicationDisabled_doesNotAddComplication() {
when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(false);
final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
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 171d893..e7b4593 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
@@ -41,7 +41,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -74,8 +73,6 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -102,7 +99,6 @@
context,
logger,
windowManager,
- viewUtil,
FakeExecutor(FakeSystemClock()),
accessibilityManager,
configurationController,
@@ -182,7 +178,7 @@
@Test
fun setIcon_isAppIcon_usesAppIconSize() {
- controllerReceiver.displayChip(getChipReceiverInfo())
+ controllerReceiver.displayView(getChipReceiverInfo())
val chipView = getChipView()
controllerReceiver.setIcon(chipView, PACKAGE_NAME)
@@ -198,7 +194,7 @@
@Test
fun setIcon_notAppIcon_usesGenericIconSize() {
- controllerReceiver.displayChip(getChipReceiverInfo())
+ controllerReceiver.displayView(getChipReceiverInfo())
val chipView = getChipView()
controllerReceiver.setIcon(chipView, appPackageName = null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 1061e3c..52b6eed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -42,7 +42,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -51,8 +50,8 @@
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -75,8 +74,6 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -110,7 +107,6 @@
context,
logger,
windowManager,
- viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -309,7 +305,7 @@
@Test
fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToStartCast()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -325,7 +321,7 @@
@Test
fun almostCloseToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToEndCast()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -341,7 +337,7 @@
@Test
fun transferToReceiverTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToReceiverTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -357,7 +353,7 @@
@Test
fun transferToThisDeviceTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToThisDeviceTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -373,7 +369,7 @@
@Test
fun transferToReceiverSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToReceiverSucceeded()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -387,7 +383,7 @@
@Test
fun transferToReceiverSucceeded_nullUndoRunnable_noUndo() {
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback = null))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -398,7 +394,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -414,7 +410,7 @@
}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
@@ -425,7 +421,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
@@ -440,7 +436,7 @@
@Test
fun transferToThisDeviceSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToThisDeviceSucceeded()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -454,7 +450,7 @@
@Test
fun transferToThisDeviceSucceeded_nullUndoRunnable_noUndo() {
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback = null))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -465,7 +461,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -481,7 +477,7 @@
}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
@@ -492,7 +488,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
@@ -507,7 +503,7 @@
@Test
fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToReceiverFailed()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -523,7 +519,7 @@
@Test
fun transferToThisDeviceFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToThisDeviceFailed()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -538,24 +534,24 @@
@Test
fun changeFromAlmostCloseToStartToTransferTriggered_loadingIconAppears() {
- controllerSender.displayChip(almostCloseToStartCast())
- controllerSender.displayChip(transferToReceiverTriggered())
+ controllerSender.displayView(almostCloseToStartCast())
+ controllerSender.displayView(transferToReceiverTriggered())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_loadingIconDisappears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(transferToReceiverSucceeded())
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(transferToReceiverSucceeded())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_undoButtonAppears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(
transferToReceiverSucceeded(
object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
@@ -568,26 +564,26 @@
@Test
fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() {
- controllerSender.displayChip(transferToReceiverSucceeded())
- controllerSender.displayChip(almostCloseToStartCast())
+ controllerSender.displayView(transferToReceiverSucceeded())
+ controllerSender.displayView(almostCloseToStartCast())
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(transferToReceiverFailed())
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(transferToReceiverFailed())
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
- fun transferToReceiverTriggeredThenRemoveChip_chipStillDisplayed() {
- controllerSender.displayChip(transferToReceiverTriggered())
+ fun transferToReceiverTriggeredThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToReceiverTriggered())
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
@@ -596,9 +592,9 @@
@Test
fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() {
val state = transferToReceiverTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
@@ -606,11 +602,11 @@
}
@Test
- fun transferToThisDeviceTriggeredThenRemoveChip_chipStillDisplayed() {
- controllerSender.displayChip(transferToThisDeviceTriggered())
+ fun transferToThisDeviceTriggeredThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToThisDeviceTriggered())
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
@@ -619,9 +615,9 @@
@Test
fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() {
val state = transferToThisDeviceTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index e4751d1..2a4996f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -70,9 +70,13 @@
val underTest = utils.footerActionsViewModel(showPowerButton = false)
val settings = underTest.settings
- assertThat(settings.contentDescription)
- .isEqualTo(ContentDescription.Resource(R.string.accessibility_quick_settings_settings))
- assertThat(settings.icon).isEqualTo(Icon.Resource(R.drawable.ic_settings))
+ assertThat(settings.icon)
+ .isEqualTo(
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ )
+ )
assertThat(settings.background).isEqualTo(R.drawable.qs_footer_action_circle)
assertThat(settings.iconTint).isNull()
}
@@ -87,11 +91,13 @@
val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true)
val power = underTestWithPower.power
assertThat(power).isNotNull()
- assertThat(power!!.contentDescription)
+ assertThat(power!!.icon)
.isEqualTo(
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ )
)
- assertThat(power.icon).isEqualTo(Icon.Resource(android.R.drawable.ic_lock_power_off))
assertThat(power.background).isEqualTo(R.drawable.qs_footer_action_circle_color)
assertThat(power.iconTint)
.isEqualTo(
@@ -164,14 +170,13 @@
utils.setUserSwitcherEnabled(settings, true, userId)
val userSwitcher = currentUserSwitcher()
assertThat(userSwitcher).isNotNull()
- assertThat(userSwitcher!!.contentDescription)
- .isEqualTo(ContentDescription.Loaded("Signed in as foo"))
- assertThat(userSwitcher.icon).isEqualTo(Icon.Loaded(picture))
+ assertThat(userSwitcher!!.icon)
+ .isEqualTo(Icon.Loaded(picture, ContentDescription.Loaded("Signed in as foo")))
assertThat(userSwitcher.background).isEqualTo(R.drawable.qs_footer_action_circle)
// Change the current user name.
userSwitcherControllerWrapper.currentUserName = "bar"
- assertThat(currentUserSwitcher()?.contentDescription)
+ assertThat(currentUserSwitcher()?.icon?.contentDescription)
.isEqualTo(ContentDescription.Loaded("Signed in as bar"))
fun iconTint(): Int? = currentUserSwitcher()!!.iconTint
@@ -243,7 +248,7 @@
// Map any SecurityModel into a non-null SecurityButtonConfig.
val buttonConfig =
SecurityButtonConfig(
- icon = Icon.Resource(0),
+ icon = Icon.Resource(res = 0, contentDescription = null),
text = "foo",
isClickable = true,
)
@@ -340,7 +345,7 @@
assertThat(foregroundServices.displayText).isTrue()
securityToConfig = {
SecurityButtonConfig(
- icon = Icon.Resource(0),
+ icon = Icon.Resource(res = 0, contentDescription = null),
text = "foo",
isClickable = true,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index f267013..3224a6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -547,8 +547,6 @@
mNavigationModeController,
mFragmentService,
mContentResolver,
- mQuickAccessWalletController,
- mQrCodeScannerController,
mRecordingController,
mLargeScreenShadeHeaderController,
mScreenOffAnimationController,
@@ -556,7 +554,6 @@
mPanelExpansionStateManager,
mNotificationRemoteInputManager,
mSysUIUnfoldComponent,
- mControlsComponent,
mInteractionJankMonitor,
mQsFrameTranslateController,
mSysUiState,
@@ -569,8 +566,8 @@
mShadeTransitionController,
mSystemClock,
mock(CameraGestureHelper.class),
- () -> mKeyguardBottomAreaViewModel,
- () -> mKeyguardBottomAreaInteractor);
+ mKeyguardBottomAreaViewModel,
+ mKeyguardBottomAreaInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 43fc8983..2adc389 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -19,8 +19,10 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
+import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.keyguard.LockIconViewController
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
@@ -246,6 +248,18 @@
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
+
+ @Test
+ fun testGetBouncerContainer() {
+ underTest.bouncerContainer
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
+ }
+
+ @Test
+ fun testGetKeyguardMessageArea() {
+ underTest.keyguardMessageArea
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
+ }
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index d2970a6..97c0bb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
@@ -63,6 +64,8 @@
private lateinit var tunerService: TunerService
@Mock
private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
private lateinit var underTest: PulsingGestureListener
@@ -77,6 +80,7 @@
dockManager,
centralSurfaces,
ambientDisplayConfiguration,
+ statusBarStateController,
tunerService,
dumpManager
)
@@ -85,6 +89,8 @@
@Test
fun testGestureDetector_singleTapEnabled() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -102,6 +108,8 @@
@Test
fun testGestureDetector_doubleTapEnabled() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -119,6 +127,8 @@
@Test
fun testGestureDetector_singleTapEnabled_falsing() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -135,7 +145,23 @@
}
@Test
+ fun testGestureDetector_notPulsing_noFalsingCheck() {
+ whenever(statusBarStateController.isPulsing).thenReturn(false)
+
+ // GIVEN tap is enabled, prox not covered
+ whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
+ // WHEN there's a tap
+ underTest.onSingleTapConfirmed(downEv)
+
+ // THEN the falsing manager never gets a call (because the device wasn't pulsing
+ // during the tap)
+ verify(falsingManager, never()).isFalseTap(anyInt())
+ }
+
+ @Test
fun testGestureDetector_doubleTapEnabled_falsing() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -153,6 +179,8 @@
@Test
fun testGestureDetector_singleTapEnabled_proxCovered() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -170,6 +198,8 @@
@Test
fun testGestureDetector_doubleTapEnabled_proxCovered() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
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 798f47d..ac8874b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -19,6 +19,7 @@
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
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_ERROR_TIMEOUT;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
@@ -65,7 +66,6 @@
import android.graphics.Color;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.Looper;
@@ -602,7 +602,7 @@
String message = mContext.getString(R.string.keyguard_unlock);
mController.setVisible(true);
- mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
@@ -636,10 +636,10 @@
when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
mController.setVisible(true);
- mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
+ mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any());
}
@Test
@@ -651,7 +651,7 @@
mController.setVisible(true);
mController.getKeyguardCallback().onBiometricError(
- FaceManager.FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE);
+ FACE_ERROR_TIMEOUT, message, BiometricSourceType.FACE);
verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
}
@@ -698,8 +698,7 @@
BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT
};
for (int msgId : msgIds) {
mKeyguardUpdateMonitorCallback.onBiometricHelp(
@@ -728,8 +727,7 @@
BiometricFaceConstants.FACE_ACQUIRED_TOO_LEFT,
BiometricFaceConstants.FACE_ACQUIRED_TOO_HIGH,
BiometricFaceConstants.FACE_ACQUIRED_TOO_LOW,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
- BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT
};
for (int msgId : msgIds) {
final String numberedHelpString = helpString + msgId;
@@ -743,6 +741,64 @@
}
@Test
+ public void sendTooDarkFaceHelpMessages_onTimeout_noFpEnrolled() {
+ createController();
+
+ // GIVEN fingerprint NOT enrolled
+ when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ 0)).thenReturn(false);
+
+ // WHEN help message received
+ final String helpString = "helpMsg";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
+ helpString,
+ BiometricSourceType.FACE
+ );
+
+ // THEN help message not shown yet
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+
+ // WHEN face timeout error received
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout",
+ BiometricSourceType.FACE);
+
+ // THEN the low light message shows with suggestion to swipe up to unlock
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_unlock));
+ }
+
+ @Test
+ public void sendTooDarkFaceHelpMessages_onTimeout_fingerprintEnrolled() {
+ createController();
+
+ // GIVEN fingerprint enrolled
+ when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ 0)).thenReturn(true);
+
+ // WHEN help message received
+ final String helpString = "helpMsg";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
+ helpString,
+ BiometricSourceType.FACE
+ );
+
+ // THEN help message not shown yet
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+
+ // WHEN face timeout error received
+ mKeyguardUpdateMonitorCallback.onBiometricError(FACE_ERROR_TIMEOUT, "face timeout",
+ BiometricSourceType.FACE);
+
+ // THEN the low light message shows and suggests trying fingerprint
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, helpString);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ mContext.getString(R.string.keyguard_suggest_fingerprint));
+ }
+
+ @Test
public void updateMonitor_listenerUpdatesIndication() {
createController();
String restingIndication = "Resting indication";
@@ -994,14 +1050,14 @@
}
@Test
- public void onTrustGrantedMessageDoesShowsOnTrustGranted() {
+ public void onTrustGrantedMessageShowsOnTrustGranted() {
createController();
mController.setVisible(true);
// GIVEN trust is granted
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
- // WHEN the showTrustGranted message is called
+ // WHEN the showTrustGranted method is called
final String trustGrantedMsg = "testing trust granted message";
mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg);
@@ -1012,6 +1068,38 @@
}
@Test
+ public void onTrustGrantedMessage_nullMessage_showsDefaultMessage() {
+ createController();
+ mController.setVisible(true);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called with a null message
+ mController.getKeyguardCallback().showTrustGrantedMessage(null);
+
+ // THEN verify the default trust granted message shows
+ verifyIndicationMessage(
+ INDICATION_TYPE_TRUST,
+ getContext().getString(R.string.keyguard_indication_trust_unlocked));
+ }
+
+ @Test
+ public void onTrustGrantedMessage_emptyString_showsNoMessage() {
+ createController();
+ mController.setVisible(true);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called with an EMPTY string
+ mController.getKeyguardCallback().showTrustGrantedMessage("");
+
+ // THEN verify NO trust message is shown
+ verifyNoMessage(INDICATION_TYPE_TRUST);
+ }
+
+ @Test
public void coEx_faceSuccess_showsPressToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index b166b73..6446fb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -48,7 +48,6 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyString
@@ -56,6 +55,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@@ -139,7 +139,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -147,7 +147,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0.01f, expanded = false, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -157,7 +157,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0f, expanded = false, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -168,15 +168,15 @@
onPanelExpansionChanged_apliesBlur_ifShade()
clearInvocations(shadeAnimation)
notificationShadeDepthController.onPanelExpansionChanged(event)
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
notificationShadeDepthController.onPanelExpansionChanged(
event.copy(fraction = 0.9f, tracking = true))
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
notificationShadeDepthController.onPanelExpansionChanged(
event.copy(fraction = 0.8f, tracking = false))
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -186,7 +186,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0.6f, expanded = true, tracking = true, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -212,7 +212,7 @@
statusBarState = StatusBarState.KEYGUARD
statusBarStateListener.onStateChanged(statusBarState)
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -395,13 +395,13 @@
@Test
fun brightnessMirrorVisible_whenVisible() {
notificationShadeDepthController.brightnessMirrorVisible = true
- verify(brightnessSpring).animateTo(eq(maxBlur), any())
+ verify(brightnessSpring).animateTo(eq(maxBlur))
}
@Test
fun brightnessMirrorVisible_whenHidden() {
notificationShadeDepthController.brightnessMirrorVisible = false
- verify(brightnessSpring).animateTo(eq(0), any())
+ verify(brightnessSpring).animateTo(eq(0))
}
@Test
@@ -424,7 +424,7 @@
fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
`when`(shadeAnimation.radius).thenReturn(0f)
notificationShadeDepthController.blursDisabledForAppLaunch = true
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
}
private fun enableSplitShade() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
new file mode 100644
index 0000000..11798a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -0,0 +1,390 @@
+/*
+ * 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.systemui.statusbar.notification.stack
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val MAX_PULSE_HEIGHT = 100000f
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AmbientStateTest : SysuiTestCase() {
+
+ private val dumpManager = mock<DumpManager>()
+ private val sectionProvider = StackScrollAlgorithm.SectionProvider { _, _ -> false }
+ private val bypassController = StackScrollAlgorithm.BypassController { false }
+ private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
+
+ private lateinit var sut: AmbientState
+
+ @Before
+ fun setUp() {
+ sut =
+ AmbientState(
+ context,
+ dumpManager,
+ sectionProvider,
+ bypassController,
+ statusBarKeyguardViewManager,
+ )
+ }
+
+ // region isDimmed
+ @Test
+ fun isDimmed_whenTrue_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+
+ @Test
+ fun isDimmed_whenFalse_shouldReturnFalse() {
+ sut.arrangeDimmed(false)
+
+ assertThat(sut.isDimmed).isFalse()
+ }
+
+ @Test
+ fun isDimmed_whenDozeAmountIsEmpty_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+ sut.dozeAmount = 0f
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+
+ @Test
+ fun isDimmed_whenPulseExpandingIsFalse_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+ sut.arrangePulseExpanding(false)
+ sut.dozeAmount = 1f // arrangePulseExpanding changes dozeAmount
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+ // endregion
+
+ // region pulseHeight
+ @Test
+ fun pulseHeight_whenValueChanged_shouldCallListener() {
+ var listenerCalledCount = 0
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+ sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
+
+ sut.pulseHeight = 0f
+
+ assertThat(listenerCalledCount).isEqualTo(1)
+ }
+
+ @Test
+ fun pulseHeight_whenSetSameValue_shouldDoNothing() {
+ var listenerCalledCount = 0
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+ sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
+
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(listenerCalledCount).isEqualTo(0)
+ }
+
+ @Test
+ fun pulseHeight_whenValueIsFull_shouldReturn0() {
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun pulseHeight_whenValueIsNotFull_shouldReturnValue() {
+ val expected = MAX_PULSE_HEIGHT - 0.1f
+ sut.pulseHeight = expected
+
+ assertThat(sut.pulseHeight).isEqualTo(expected)
+ }
+ // endregion
+
+ // region statusBarState
+ @Test
+ fun statusBarState_whenPreviousStateIsNotKeyguardAndChange_shouldSetIsFlingRequiredToFalse() {
+ sut.setStatusBarState(StatusBarState.SHADE)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun statusBarState_whenPreviousStateIsKeyguardAndChange_shouldDoNothing() {
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setStatusBarState(StatusBarState.SHADE)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region hideAmount
+ @Test
+ fun hideAmount_whenSetToFullValue_shouldReturnZeroFromPulseHeight() {
+ sut.hideAmount = 0f
+ sut.pulseHeight = 1f
+
+ sut.hideAmount = 1f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun hideAmount_whenSetToAnyNotFullValue_shouldDoNothing() {
+ sut.hideAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.hideAmount = 0f
+
+ assertThat(sut.pulseHeight).isEqualTo(1f)
+ }
+ // endregion
+
+ // region dozeAmount
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetToFullDozing_shouldReturnZeroFromPulseHeight() {
+ sut.dozeAmount = 0f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 1f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetToFullAwake_shouldReturnZeroFromPulseHeight() {
+ sut.dozeAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 0f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetAnyValueNotFullAwakeOrDozing_shouldDoNothing() {
+ sut.dozeAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 0.5f
+
+ assertThat(sut.pulseHeight).isEqualTo(1f)
+ }
+ // endregion
+
+ // region trackedHeadsUpRow
+ @Test
+ fun trackedHeadsUpRow_whenIsAboveTheShelf_shouldReturnInstance() {
+ sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(true) }
+
+ assertThat(sut.trackedHeadsUpRow).isNotNull()
+ }
+
+ @Test
+ fun trackedHeadsUpRow_whenIsNotAboveTheShelf_shouldReturnNull() {
+ sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(false) }
+
+ assertThat(sut.trackedHeadsUpRow).isNull()
+ }
+ // endregion
+
+ // region isSwipingUp
+ @Test
+ fun isSwipingUp_whenValueChangedToTrue_shouldRequireFling() {
+ sut.isSwipingUp = false
+ sut.isFlingRequiredAfterLockScreenSwipeUp = false
+
+ sut.isSwipingUp = true
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun isSwipingUp_whenValueChangedToFalse_shouldRequireFling() {
+ sut.isSwipingUp = true
+ sut.isFlingRequiredAfterLockScreenSwipeUp = false
+
+ sut.isSwipingUp = false
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region isFlinging
+ @Test
+ fun isFlinging_shouldNotNeedFling() {
+ sut.arrangeFlinging(true)
+
+ sut.setFlinging(false)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun isFlinging_whenNotOnLockScreen_shouldDoNothing() {
+ sut.arrangeFlinging(true)
+ sut.setStatusBarState(StatusBarState.SHADE)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setFlinging(false)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+
+ @Test
+ fun isFlinging_whenValueChangedToTrue_shouldDoNothing() {
+ sut.arrangeFlinging(false)
+
+ sut.setFlinging(true)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region scrollY
+ @Test
+ fun scrollY_shouldSetValueGreaterThanZero() {
+ sut.scrollY = 0
+
+ sut.scrollY = 1
+
+ assertThat(sut.scrollY).isEqualTo(1)
+ }
+
+ @Test
+ fun scrollY_shouldNotSetValueLessThanZero() {
+ sut.scrollY = 0
+
+ sut.scrollY = -1
+
+ assertThat(sut.scrollY).isEqualTo(0)
+ }
+ // endregion
+
+ // region setOverScrollAmount
+ fun setOverScrollAmount_shouldSetValueOnTop() {
+ sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ true)
+
+ val resultOnTop = sut.getOverScrollAmount(/* top = */ true)
+ val resultOnBottom = sut.getOverScrollAmount(/* top = */ false)
+
+ assertThat(resultOnTop).isEqualTo(10f)
+ assertThat(resultOnBottom).isEqualTo(0f)
+ }
+
+ fun setOverScrollAmount_shouldSetValueOnBottom() {
+ sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ false)
+
+ val resultOnTop = sut.getOverScrollAmount(/* top */ true)
+ val resultOnBottom = sut.getOverScrollAmount(/* top */ false)
+
+ assertThat(resultOnTop).isEqualTo(0f)
+ assertThat(resultOnBottom).isEqualTo(10f)
+ }
+ // endregion
+
+ // region IsPulseExpanding
+ @Test
+ fun isPulseExpanding_shouldReturnTrue() {
+ sut.arrangePulseExpanding(true)
+
+ assertThat(sut.isPulseExpanding).isTrue()
+ }
+
+ @Test
+ fun isPulseExpanding_whenPulseHeightIsMax_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+
+ @Test
+ fun isPulseExpanding_whenDozeAmountIsZero_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.dozeAmount = 0f
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+
+ @Test
+ fun isPulseExpanding_whenHideAmountIsFull_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.hideAmount = 1f
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+ // endregion
+
+ // region isOnKeyguard
+ @Test
+ fun isOnKeyguard_whenStatusBarStateIsKeyguard_shouldReturnTrue() {
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(sut.isOnKeyguard).isTrue()
+ }
+
+ @Test
+ fun isOnKeyguard_whenStatusBarStateIsNotKeyguard_shouldReturnFalse() {
+ sut.setStatusBarState(StatusBarState.SHADE)
+
+ assertThat(sut.isOnKeyguard).isFalse()
+ }
+ // endregion
+}
+
+// region Arrange helper methods.
+private fun AmbientState.arrangeDimmed(value: Boolean) {
+ isDimmed = value
+ dozeAmount = if (value) 0f else 1f
+ arrangePulseExpanding(!value)
+}
+
+private fun AmbientState.arrangePulseExpanding(value: Boolean) {
+ if (value) {
+ dozeAmount = 1f
+ hideAmount = 0f
+ pulseHeight = 0f
+ } else {
+ dozeAmount = 0f
+ hideAmount = 1f
+ pulseHeight = MAX_PULSE_HEIGHT
+ }
+}
+
+private fun AmbientState.arrangeFlinging(value: Boolean) {
+ setStatusBarState(StatusBarState.KEYGUARD)
+ setFlinging(value)
+ isFlingRequiredAfterLockScreenSwipeUp = true
+}
+// endregion
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 3f19036..7741813 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -3,15 +3,22 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
+import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import junit.framework.Assert.*
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
+import com.android.systemui.util.mockito.mock
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.*
+import org.mockito.Mockito.mock
import org.mockito.Mockito.`when` as whenever
/**
@@ -22,13 +29,18 @@
@RunWithLooper
class NotificationShelfTest : SysuiTestCase() {
- private val shelf = NotificationShelf(context, /* attrs */ null)
+ private val shelf = NotificationShelf(
+ context,
+ /* attrs */ null,
+ /* showNotificationShelf */true
+ )
private val shelfState = shelf.viewState as NotificationShelf.ShelfState
private val ambientState = mock(AmbientState::class.java)
+ private val hostLayoutController: NotificationStackScrollLayoutController = mock()
@Before
fun setUp() {
- shelf.bind(ambientState, /* hostLayoutController */ null)
+ shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
}
@@ -37,7 +49,7 @@
setFractionToShade(0f)
setOnLockscreen(true)
- shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f);
+ shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f)
assertTrue(shelf.actualWidth == 10)
shelf.updateActualWidth(/* fractionToShade */ 0.5f, /* shortestWidth */ 10f)
@@ -155,7 +167,7 @@
whenever(expandableView.actualHeight).thenReturn(20)
whenever(expandableView.minHeight).thenReturn(20)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -182,7 +194,7 @@
whenever(expandableView.actualHeight).thenReturn(20)
whenever(expandableView.minHeight).thenReturn(20)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -209,7 +221,7 @@
whenever(expandableView.actualHeight).thenReturn(25)
whenever(expandableView.minHeight).thenReturn(25)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -236,7 +248,7 @@
whenever(expandableView.actualHeight).thenReturn(10)
whenever(expandableView.minHeight).thenReturn(10)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(false)
whenever(ambientState.isExpansionChanging).thenReturn(false)
@@ -251,6 +263,42 @@
assertEquals(0f, amountInShelf)
}
+ @Test
+ fun updateState_expansionChanging_shelfTransparent() {
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.25f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun updateState_expansionChangingWhileBouncerInTransit_shelfTransparent() {
+ whenever(ambientState.isBouncerInTransit).thenReturn(true)
+
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.85f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun updateState_expansionChanging_shelfAlphaUpdated() {
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.6f,
+ expectedAlpha = ShadeInterpolation.getContentAlpha(0.6f)
+ )
+ }
+
+ @Test
+ fun updateState_expansionChangingWhileBouncerInTransit_shelfAlphaUpdated() {
+ whenever(ambientState.isBouncerInTransit).thenReturn(true)
+
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.95f,
+ expectedAlpha = aboutToShowBouncerProgress(0.95f)
+ )
+ }
+
private fun setFractionToShade(fraction: Float) {
whenever(ambientState.fractionToShade).thenReturn(fraction)
}
@@ -258,4 +306,19 @@
private fun setOnLockscreen(isOnLockscreen: Boolean) {
whenever(ambientState.isOnKeyguard).thenReturn(isOnLockscreen)
}
-}
\ No newline at end of file
+
+ private fun updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction: Float,
+ expectedAlpha: Float
+ ) {
+ whenever(ambientState.lastVisibleBackgroundChild)
+ .thenReturn(ExpandableNotificationRow(mContext, null))
+ whenever(ambientState.isExpansionChanging).thenReturn(true)
+ whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
+ whenever(hostLayoutController.speedBumpIndex).thenReturn(0)
+
+ shelf.updateState(StackScrollAlgorithmState(), ambientState)
+
+ assertEquals(expectedAlpha, shelf.viewState.alpha)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 3c22edc..6ae021b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -250,7 +250,7 @@
// Validate that when the animation ends the stackEndHeight is recalculated immediately
clearInvocations(mAmbientState);
mStackScroller.setPanelFlinging(false);
- verify(mAmbientState).setIsFlinging(eq(false));
+ verify(mAmbientState).setFlinging(eq(false));
verify(mAmbientState).setStackEndHeight(anyFloat());
verify(mAmbientState).setStackHeight(anyFloat());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 24eff5f..40aec82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -20,7 +20,10 @@
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -35,7 +38,6 @@
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
-
private val ambientState = AmbientState(
context,
dumpManager,
@@ -115,29 +117,54 @@
}
@Test
- fun resetViewStates_isExpansionChanging_viewBecomesTransparent() {
+ fun resetViewStates_expansionChanging_notificationBecomesTransparent() {
whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
- ambientState.isExpansionChanging = true
- ambientState.expansionFraction = 0.25f
- stackScrollAlgorithm.initView(context)
-
- stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
-
- val expected = getContentAlpha(0.25f)
- assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.25f,
+ expectedAlpha = 0.0f
+ )
}
@Test
- fun resetViewStates_isExpansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
+ fun resetViewStates_expansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.85f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun resetViewStates_expansionChanging_notificationAlphaUpdated() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.6f,
+ expectedAlpha = getContentAlpha(0.6f)
+ )
+ }
+
+ @Test
+ fun resetViewStates_expansionChangingWhileBouncerInTransit_notificationAlphaUpdated() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.95f,
+ expectedAlpha = aboutToShowBouncerProgress(0.95f)
+ )
+ }
+
+ @Test
+ fun resetViewStates_expansionChanging_shelfUpdated() {
+ ambientState.shelf = notificationShelf
ambientState.isExpansionChanging = true
- ambientState.expansionFraction = 0.25f
+ ambientState.expansionFraction = 0.6f
stackScrollAlgorithm.initView(context)
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
- val expected = aboutToShowBouncerProgress(0.25f)
- assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ verify(notificationShelf).updateState(
+ /* algorithmState= */any(),
+ /* ambientState= */eq(ambientState)
+ )
}
@Test
@@ -479,6 +506,19 @@
/* originalCornerRoundness= */ 1f)
assertEquals(1f, currentRoundness)
}
+
+ private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction: Float,
+ expectedAlpha: Float
+ ) {
+ ambientState.isExpansionChanging = true
+ ambientState.expansionFraction = expansionFraction
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.alpha).isEqualTo(expectedAlpha)
+ }
}
private fun mockExpandableNotificationRow(): ExpandableNotificationRow {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 5c9871a..9de9db1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -29,6 +29,7 @@
import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -61,10 +62,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
public class DozeServiceHostTest extends SysuiTestCase {
private DozeServiceHost mDozeServiceHost;
@@ -92,6 +93,7 @@
@Mock private View mAmbientIndicationContainer;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private AuthController mAuthController;
+ @Mock private DozeHost.Callback mCallback;
@Before
public void setup() {
@@ -100,7 +102,7 @@
mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
mBatteryController, mScrimController, () -> mBiometricUnlockController,
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
- mKeyguardUpdateMonitor, mPulseExpansionHandler, Optional.empty(),
+ mKeyguardUpdateMonitor, mPulseExpansionHandler,
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
mAuthController, mNotificationIconAreaController);
@@ -114,16 +116,19 @@
@Test
public void testStartStopDozing() {
+ mDozeServiceHost.addCallback(mCallback);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
assertFalse(mDozeServiceHost.getDozingRequested());
mDozeServiceHost.startDozing();
+ verify(mCallback).onDozingChanged(eq(true));
verify(mStatusBarStateController).setIsDozing(eq(true));
verify(mCentralSurfaces).updateIsKeyguard();
mDozeServiceHost.stopDozing();
+ verify(mCallback).onDozingChanged(eq(false));
verify(mStatusBarStateController).setIsDozing(eq(false));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
deleted file mode 100644
index 3440fa5..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.android.systemui.statusbar.phone
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.LayoutInflater
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.assist.AssistManager
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.policy.AccessibilityController
-import com.android.systemui.statusbar.policy.FlashlightController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.tuner.TunerService
-import java.util.concurrent.Executor
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class KeyguardBottomAreaTest : SysuiTestCase() {
-
- @Mock
- private lateinit var mCentralSurfaces: CentralSurfaces
- private lateinit var mKeyguardBottomArea: KeyguardBottomAreaView
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- // Mocked dependencies
- mDependency.injectMockDependency(AccessibilityController::class.java)
- mDependency.injectMockDependency(ActivityStarter::class.java)
- mDependency.injectMockDependency(AssistManager::class.java)
- mDependency.injectTestDependency(Executor::class.java, Executor { it.run() })
- mDependency.injectMockDependency(FlashlightController::class.java)
- mDependency.injectMockDependency(KeyguardStateController::class.java)
- mDependency.injectMockDependency(TunerService::class.java)
-
- mKeyguardBottomArea = LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
- }
-
- @Test
- fun initFrom_doesntCrash() {
- val other = LayoutInflater.from(mContext).inflate(R.layout.keyguard_bottom_area,
- null, false) as KeyguardBottomAreaView
-
- other.initFrom(mKeyguardBottomArea)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 2dcdcfc..e790d85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -51,6 +51,7 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -277,6 +278,17 @@
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
verify(mCentralSurfaces).animateKeyguardUnoccluding();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 6c6d9e1..43103a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -17,7 +17,10 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
@@ -31,6 +34,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -67,6 +71,7 @@
underTest = WifiViewModel(
statusBarPipelineFlags,
constants,
+ context,
logger,
interactor
)
@@ -115,7 +120,12 @@
.launchIn(this)
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
- assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_NETWORK)
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_NO_NETWORK)
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_NO_CONNECTION))
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(NO_INTERNET))
job.cancel()
}
@@ -169,7 +179,12 @@
.launchIn(this)
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
- assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_FULL_ICONS[level])
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_FULL_ICONS[level])
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
+ assertThat(icon.contentDescription?.getAsString())
+ .doesNotContain(context.getString(NO_INTERNET))
job.cancel()
}
@@ -193,7 +208,12 @@
.launchIn(this)
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
- assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(NO_INTERNET))
job.cancel()
}
@@ -261,6 +281,13 @@
job.cancel()
}
+ private fun ContentDescription.getAsString(): String? {
+ return when (this) {
+ is ContentDescription.Loaded -> this.description
+ is ContentDescription.Resource -> context.getString(this.res)
+ }
+ }
+
companion object {
private const val NETWORK_ID = 2
private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active(NETWORK_ID, ssid = "AB")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index 4a8170f..8f363ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -31,6 +31,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -57,6 +58,8 @@
private DumpManager mDumpManager;
@Mock
private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
+ @Mock
+ private KeyguardUpdateMonitorLogger mLogger;
@Before
public void setup() {
@@ -66,6 +69,7 @@
mKeyguardUpdateMonitor,
mLockPatternUtils,
mKeyguardUnlockAnimationControllerLazy,
+ mLogger,
mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
similarity index 61%
rename from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index f133068..e616c26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
import android.content.Context
import android.content.pm.ApplicationInfo
@@ -30,6 +30,7 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -38,7 +39,6 @@
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.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -52,8 +52,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-class MediaTttChipControllerCommonTest : SysuiTestCase() {
- private lateinit var controllerCommon: TestControllerCommon
+class TemporaryViewDisplayControllerTest : SysuiTestCase() {
+ private lateinit var underTest: TestController
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
@@ -72,8 +72,6 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var powerManager: PowerManager
@Before
@@ -97,11 +95,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
- controllerCommon = TestControllerCommon(
+ underTest = TestController(
context,
logger,
windowManager,
- viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -110,43 +107,43 @@
}
@Test
- fun displayChip_chipAdded() {
- controllerCommon.displayChip(getState())
+ fun displayView_viewAdded() {
+ underTest.displayView(getState())
verify(windowManager).addView(any(), any())
}
@Test
- fun displayChip_screenOff_screenWakes() {
+ fun displayView_screenOff_screenWakes() {
whenever(powerManager.isScreenOn).thenReturn(false)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(powerManager).wakeUp(any(), any(), any())
}
@Test
- fun displayChip_screenAlreadyOn_screenNotWoken() {
+ fun displayView_screenAlreadyOn_screenNotWoken() {
whenever(powerManager.isScreenOn).thenReturn(true)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(powerManager, never()).wakeUp(any(), any(), any())
}
@Test
- fun displayChip_twice_chipNotAddedTwice() {
- controllerCommon.displayChip(getState())
+ fun displayView_twice_viewNotAddedTwice() {
+ underTest.displayView(getState())
reset(windowManager)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(windowManager, never()).addView(any(), any())
}
@Test
- fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
+ fun displayView_viewDoesNotDisappearsBeforeTimeout() {
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MS - 1)
@@ -155,9 +152,9 @@
}
@Test
- fun displayChip_chipDisappearsAfterTimeout() {
+ fun displayView_viewDisappearsAfterTimeout() {
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MS + 1)
@@ -166,176 +163,176 @@
}
@Test
- fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
- // First, display the chip
+ fun displayView_calledAgainBeforeTimeout_timeoutReset() {
+ // First, display the view
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
- // After some time, re-display the chip
+ // After some time, re-display the view
val waitTime = 1000L
fakeClock.advanceTime(waitTime)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
// Wait until the timeout for the first display would've happened
fakeClock.advanceTime(TIMEOUT_MS - waitTime + 1)
- // Verify we didn't hide the chip
+ // Verify we didn't hide the view
verify(windowManager, never()).removeView(any())
}
@Test
- fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
- // First, display the chip
+ fun displayView_calledAgainBeforeTimeout_eventuallyTimesOut() {
+ // First, display the view
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
- // After some time, re-display the chip
+ // After some time, re-display the view
fakeClock.advanceTime(1000L)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
- // Ensure we still hide the chip eventually
+ // Ensure we still hide the view eventually
fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
}
@Test
- fun displayScaleChange_chipReinflatedWithMostRecentState() {
- controllerCommon.displayChip(getState(name = "First name"))
- controllerCommon.displayChip(getState(name = "Second name"))
+ fun displayScaleChange_viewReinflatedWithMostRecentState() {
+ underTest.displayView(getState(name = "First name"))
+ underTest.displayView(getState(name = "Second name"))
reset(windowManager)
getConfigurationListener().onDensityOrFontScaleChanged()
verify(windowManager).removeView(any())
verify(windowManager).addView(any(), any())
- assertThat(controllerCommon.mostRecentChipInfo?.name).isEqualTo("Second name")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
}
@Test
- fun removeChip_chipRemovedAndRemovalLogged() {
- // First, add the chip
- controllerCommon.displayChip(getState())
+ fun removeView_viewRemovedAndRemovalLogged() {
+ // First, add the view
+ underTest.displayView(getState())
// Then, remove it
val reason = "test reason"
- controllerCommon.removeChip(reason)
+ underTest.removeView(reason)
verify(windowManager).removeView(any())
verify(logger).logChipRemoval(reason)
}
@Test
- fun removeChip_noAdd_viewNotRemoved() {
- controllerCommon.removeChip("reason")
+ fun removeView_noAdd_viewNotRemoved() {
+ underTest.removeView("reason")
verify(windowManager, never()).removeView(any())
}
@Test
fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, appPackageName = null, appIconDrawableOverride = null)
+ underTest.setIcon(view, appPackageName = null, appIconDrawableOverride = null)
- assertThat(chipView.getAppIconView().drawable).isNotNull()
+ assertThat(view.getAppIconView().drawable).isNotNull()
}
@Test
fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(
- chipView, appPackageName = "fakePackageName", appIconDrawableOverride = null
+ underTest.setIcon(
+ view, appPackageName = "fakePackageName", appIconDrawableOverride = null
)
- assertThat(chipView.getAppIconView().drawable).isNotNull()
+ assertThat(view.getAppIconView().drawable).isNotNull()
}
@Test
fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME, appIconDrawableOverride = null, null)
+ underTest.setIcon(view, PACKAGE_NAME, appIconDrawableOverride = null, null)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
+ assertThat(view.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
}
@Test
fun setIcon_hasAppIconDrawable_iconIsDrawable() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
val drawable = context.getDrawable(R.drawable.ic_alarm)!!
- controllerCommon.setIcon(chipView, PACKAGE_NAME, drawable, null)
+ underTest.setIcon(view, PACKAGE_NAME, drawable, null)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
+ assertThat(view.getAppIconView().drawable).isEqualTo(drawable)
}
@Test
fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, appPackageName = null, appNameOverride = null)
+ underTest.setIcon(view, appPackageName = null, appNameOverride = null)
- assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+ assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
}
@Test
fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(
- chipView, appPackageName = "fakePackageName", appNameOverride = null
+ underTest.setIcon(
+ view, appPackageName = "fakePackageName", appNameOverride = null
)
- assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+ assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
}
@Test
fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appNameOverride = null)
+ underTest.setIcon(view, PACKAGE_NAME, null, appNameOverride = null)
- assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
}
@Test
fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
val appName = "Override App Name"
- controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appName)
+ underTest.setIcon(view, PACKAGE_NAME, null, appName)
- assertThat(chipView.getAppIconView().contentDescription).isEqualTo(appName)
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(appName)
}
@Test
fun setIcon_iconSizeMatchesGetIconSize() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME)
- chipView.measure(
+ underTest.setIcon(view, PACKAGE_NAME)
+ view.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
- assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
+ assertThat(view.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
+ assertThat(view.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
}
- private fun getState(name: String = "name") = ChipInfo(name)
+ private fun getState(name: String = "name") = ViewInfo(name)
- private fun getChipView(): ViewGroup {
+ private fun getView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as ViewGroup
@@ -349,37 +346,35 @@
return callbackCaptor.value
}
- inner class TestControllerCommon(
+ inner class TestController(
context: Context,
logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
- ) : MediaTttChipControllerCommon<ChipInfo>(
+ ) : TemporaryViewDisplayController<ViewInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
powerManager,
R.layout.media_ttt_chip,
) {
- var mostRecentChipInfo: ChipInfo? = null
+ var mostRecentViewInfo: ViewInfo? = null
override val windowLayoutParams = commonWindowLayoutParams
- override fun updateChipView(newChipInfo: ChipInfo, currentChipView: ViewGroup) {
- super.updateChipView(newChipInfo, currentChipView)
- mostRecentChipInfo = newChipInfo
+ override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) {
+ super.updateView(newInfo, currentView)
+ mostRecentViewInfo = newInfo
}
override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE
}
- inner class ChipInfo(val name: String) : ChipInfoCommon {
+ inner class ViewInfo(val name: String) : TemporaryViewInfo {
override fun getTimeoutMs() = 1L
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f51f783..8645298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -18,24 +18,28 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
-import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.util.FoldableDeviceStates
import com.android.systemui.unfold.util.FoldableTestUtils
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,7 +53,6 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@RunWithLooper
class FoldAodAnimationControllerTest : SysuiTestCase() {
@Mock lateinit var deviceStateManager: DeviceStateManager
@@ -74,26 +77,15 @@
private lateinit var deviceStates: FoldableDeviceStates
- private lateinit var testableLooper: TestableLooper
+ lateinit var keyguardRepository: FakeKeyguardRepository
- lateinit var foldAodAnimationController: FoldAodAnimationController
+ lateinit var underTest: FoldAodAnimationController
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- testableLooper = TestableLooper.get(this)
- foldAodAnimationController =
- FoldAodAnimationController(
- Handler(testableLooper.looper),
- context.mainExecutor,
- context,
- deviceStateManager,
- wakefulnessLifecycle,
- globalSettings,
- latencyTracker,
- )
- .apply { initialize(centralSurfaces, lightRevealScrim) }
deviceStates = FoldableTestUtils.findDeviceStates(context)
whenever(notificationPanelViewController.view).thenReturn(viewGroup)
@@ -107,60 +99,102 @@
val onActionStarted = it.arguments[0] as Runnable
onActionStarted.run()
}
- verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
- foldAodAnimationController.setIsDozing(dozing = true)
- setAodEnabled(enabled = true)
- sendFoldEvent(folded = false)
+ keyguardRepository = FakeKeyguardRepository()
+ val keyguardInteractor = KeyguardInteractor(repository = keyguardRepository)
+
+ // Needs to be run on the main thread
+ runBlocking(IMMEDIATE) {
+ underTest =
+ FoldAodAnimationController(
+ fakeExecutor,
+ context,
+ deviceStateManager,
+ wakefulnessLifecycle,
+ globalSettings,
+ latencyTracker,
+ { keyguardInteractor },
+ )
+ .apply { initialize(centralSurfaces, lightRevealScrim) }
+
+ verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
+
+ setAodEnabled(enabled = true)
+ sendFoldEvent(folded = false)
+ }
}
@Test
- fun onFolded_aodDisabled_doesNotLogLatency() {
- setAodEnabled(enabled = false)
+ fun onFolded_aodDisabled_doesNotLogLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = false)
- fold()
- simulateScreenTurningOn()
+ yield()
- verifyNoMoreInteractions(latencyTracker)
- }
+ fold()
+ simulateScreenTurningOn()
+
+ verifyNoMoreInteractions(latencyTracker)
+
+ job.cancel()
+ }
@Test
- fun onFolded_aodEnabled_logsLatency() {
- setAodEnabled(enabled = true)
+ fun onFolded_aodEnabled_logsLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = true)
- fold()
- simulateScreenTurningOn()
+ yield()
- verify(latencyTracker).onActionStart(any())
- verify(latencyTracker).onActionEnd(any())
- }
+ fold()
+ simulateScreenTurningOn()
+
+ verify(latencyTracker).onActionStart(any())
+ verify(latencyTracker).onActionEnd(any())
+
+ job.cancel()
+ }
@Test
- fun onFolded_animationCancelled_doesNotLogLatency() {
- setAodEnabled(enabled = true)
+ fun onFolded_animationCancelled_doesNotLogLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = true)
- fold()
- foldAodAnimationController.onScreenTurningOn({})
- foldAodAnimationController.onStartedWakingUp()
- testableLooper.processAllMessages()
+ yield()
- verify(latencyTracker).onActionStart(any())
- verify(latencyTracker).onActionCancel(any())
- }
+ fold()
+ underTest.onScreenTurningOn({})
+ underTest.onStartedWakingUp()
+ fakeExecutor.runAllReady()
+
+ verify(latencyTracker).onActionStart(any())
+ verify(latencyTracker).onActionCancel(any())
+
+ job.cancel()
+ }
private fun simulateScreenTurningOn() {
- foldAodAnimationController.onScreenTurningOn({})
- foldAodAnimationController.onScreenTurnedOn()
- testableLooper.processAllMessages()
+ underTest.onScreenTurningOn({})
+ underTest.onScreenTurnedOn()
+ fakeExecutor.runAllReady()
}
private fun fold() = sendFoldEvent(folded = true)
- private fun setAodEnabled(enabled: Boolean) =
- foldAodAnimationController.onAlwaysOnChanged(alwaysOn = enabled)
+ private fun setAodEnabled(enabled: Boolean) = underTest.onAlwaysOnChanged(alwaysOn = enabled)
private fun sendFoldEvent(folded: Boolean) {
val state = if (folded) deviceStates.folded else deviceStates.unfolded
foldStateListenerCaptor.value.onStateChanged(state)
}
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 092e82c..7df7077 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -28,12 +28,14 @@
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Test
import org.junit.runner.RunWith
@@ -127,6 +129,53 @@
)
)
}
+
+ @Test
+ fun dontEmitFirstEvent() = runBlocking {
+ assertThatFlow(flowOf(setOf(1, 2), setOf(2, 3)).setChanges(emitFirstEvent = false))
+ .emitsExactly(
+ SetChanges(
+ removed = setOf(1),
+ added = setOf(3),
+ )
+ )
+ }
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class SampleFlowTest : SysuiTestCase() {
+ @Test
+ fun simple() = runBlocking {
+ assertThatFlow(flow { yield(); emit(1) }.sample(flowOf(2)) { a, b -> a to b })
+ .emitsExactly(1 to 2)
+ }
+
+ @Test
+ fun otherFlowNoValueYet() = runBlocking {
+ assertThatFlow(flowOf(1).sample(emptyFlow<Unit>()))
+ .emitsNothing()
+ }
+
+ @Test
+ fun multipleSamples() = runBlocking {
+ val samplee = MutableSharedFlow<Int>()
+ val sampler = flow {
+ emit(1)
+ samplee.emit(1)
+ emit(2)
+ samplee.emit(2)
+ samplee.emit(3)
+ emit(3)
+ emit(4)
+ }
+ assertThatFlow(sampler.sample(samplee) { a, b -> a to b })
+ .emitsExactly(
+ 2 to 1,
+ 3 to 3,
+ 4 to 3,
+ )
+ }
}
private fun <T> assertThatFlow(flow: Flow<T>) = object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
new file mode 100644
index 0000000..6848b83
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.systemui.util.kotlin
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RaceSuspendTest : SysuiTestCase() {
+ @Test
+ fun raceSimple() = runBlocking {
+ val winner = CompletableDeferred<Int>()
+ val result = async {
+ race(
+ { winner.await() },
+ { awaitCancellation() },
+ )
+ }
+ winner.complete(1)
+ assertThat(result.await()).isEqualTo(1)
+ }
+
+ @Test
+ fun raceImmediate() = runBlocking {
+ assertThat(
+ race<Int>(
+ { 1 },
+ { 2 },
+ )
+ ).isEqualTo(1)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 11eb4e3..42b434a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -12,6 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
*/
package com.android.systemui.keyguard.data.repository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index f539dbd..8d171be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -26,6 +26,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatcher
import org.mockito.Mockito
+import org.mockito.stubbing.OngoingStubbing
/**
* Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
@@ -77,8 +78,18 @@
* Helper function for creating new mocks, without the need to pass in a [Class] instance.
*
* Generic T is nullable because implicitly bounded by Any?.
+ *
+ * @param apply builder function to simplify stub configuration by improving type inference.
*/
-inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java)
+inline fun <reified T : Any> mock(apply: T.() -> Unit = {}): T = Mockito.mock(T::class.java)
+ .apply(apply)
+
+/**
+ * Helper function for stubbing methods without the need to use backticks.
+ *
+ * @see Mockito.when
+ */
+fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall)
/**
* A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when
@@ -118,3 +129,17 @@
*/
inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
kotlinArgumentCaptor<T>().apply { block() }.value
+
+/**
+ * Variant of [withArgCaptor] for capturing multiple arguments.
+ *
+ * val captor = argumentCaptor<Foo>()
+ * verify(...).someMethod(captor.capture())
+ * val captured: List<Foo> = captor.allValues
+ *
+ * becomes:
+ *
+ * val capturedList = captureMany<Foo> { verify(...).someMethod(capture()) }
+ */
+inline fun <reified T : Any> captureMany(block: KotlinArgumentCaptor<T>.() -> Unit): List<T> =
+ kotlinArgumentCaptor<T>().apply{ block() }.allValues
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 01cee33..b5fdaca 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -41,7 +41,12 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
import android.service.autofill.Dataset;
+import android.text.TextUtils;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
@@ -61,7 +66,7 @@
* Reasons why presentation was not shown. These are wrappers around
* {@link com.android.os.AtomsProto.AutofillPresentationEventReported.PresentationEventResult}.
*/
- @IntDef(prefix = { "NOT_SHOWN_REASON" }, value = {
+ @IntDef(prefix = {"NOT_SHOWN_REASON"}, value = {
NOT_SHOWN_REASON_ANY_SHOWN,
NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED,
NOT_SHOWN_REASON_VIEW_CHANGED,
@@ -72,6 +77,7 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface NotShownReason {}
+
public static final int NOT_SHOWN_REASON_ANY_SHOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
public static final int NOT_SHOWN_REASON_VIEW_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
@@ -172,6 +178,45 @@
});
}
+ public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
+ mEventInternal.ifPresent(event -> {
+ event.mDisplayPresentationType = UI_TYPE_INLINE;
+ String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
+ Settings.Secure.DEFAULT_INPUT_METHOD, userId);
+ if (TextUtils.isEmpty(imeString)) {
+ Slog.w(TAG, "No default IME found");
+ return;
+ }
+ ComponentName imeComponent = ComponentName.unflattenFromString(imeString);
+ if (imeComponent == null) {
+ Slog.w(TAG, "No default IME found");
+ return;
+ }
+ int imeUid;
+ String packageName = imeComponent.getPackageName();
+ try {
+ imeUid = context.getPackageManager().getApplicationInfoAsUser(packageName,
+ PackageManager.ApplicationInfoFlags.of(0), userId).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Couldn't find packageName: " + packageName);
+ return;
+ }
+ event.mInlineSuggestionHostUid = imeUid;
+ });
+ }
+
+ public void maybeSetAutofillServiceUid(int uid) {
+ mEventInternal.ifPresent(event -> {
+ event.mAutofillServiceUid = uid;
+ });
+ }
+
+ public void maybeSetIsNewRequest(boolean isRequestTriggered) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsRequestTriggered = isRequestTriggered;
+ });
+ }
+
public void logAndEndEvent() {
if (!mEventInternal.isPresent()) {
Slog.w(TAG, "Shouldn't be logging AutofillPresentationEventReported again for same "
@@ -190,7 +235,10 @@
+ " mCountNotShownImePresentationNotDrawn="
+ event.mCountNotShownImePresentationNotDrawn
+ " mCountNotShownImeUserNotSeen=" + event.mCountNotShownImeUserNotSeen
- + " mDisplayPresentationType=" + event.mDisplayPresentationType);
+ + " mDisplayPresentationType=" + event.mDisplayPresentationType
+ + " mAutofillServiceUid=" + event.mAutofillServiceUid
+ + " mInlineSuggestionHostUid=" + event.mInlineSuggestionHostUid
+ + " mIsRequestTriggered=" + event.mIsRequestTriggered);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -208,7 +256,10 @@
event.mCountFilteredUserTyping,
event.mCountNotShownImePresentationNotDrawn,
event.mCountNotShownImeUserNotSeen,
- event.mDisplayPresentationType);
+ event.mDisplayPresentationType,
+ event.mAutofillServiceUid,
+ event.mInlineSuggestionHostUid,
+ event.mIsRequestTriggered);
mEventInternal = Optional.empty();
}
@@ -222,6 +273,9 @@
int mCountNotShownImePresentationNotDrawn;
int mCountNotShownImeUserNotSeen;
int mDisplayPresentationType = AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
+ int mAutofillServiceUid = -1;
+ int mInlineSuggestionHostUid = -1;
+ boolean mIsRequestTriggered;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0fe9f8f..bd9c03a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -66,6 +66,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.ServiceInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -77,6 +78,7 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Parcelable;
+import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -2968,6 +2970,7 @@
mSessionFlags.mFillDialogDisabled = true;
}
mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break;
case ACTION_VALUE_CHANGED:
@@ -3057,6 +3060,7 @@
}
mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
return;
}
@@ -3292,7 +3296,8 @@
// shown, inflated, and filtered.
mPresentationStatsEventLogger.maybeSetCountShown(
response.getDatasets(), mCurrentViewId);
- mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_INLINE);
+ mPresentationStatsEventLogger.maybeSetInlinePresentationAndSuggestionHostUid(
+ mContext, userId);
return;
}
}
@@ -4639,4 +4644,9 @@
return "UNKNOWN_SESSION_STATE_" + sessionState;
}
}
+
+ private int getAutofillServiceUid() {
+ ServiceInfo serviceInfo = mService.getServiceInfo();
+ return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid;
+ }
}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index c4efbd7..7fa1e13 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -40,3 +40,7 @@
per-file CarUserSwitchingDialog.java = keunyoung@google.com, felipeal@google.com, gurunagarajan@google.com
per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
+
+# Multiuser
+per-file User* = file:/MULTIUSER_OWNERS
+
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0c4ea97..202250d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3355,8 +3355,7 @@
dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
}
- if ((device == AudioSystem.DEVICE_OUT_BLE_HEADSET
- || device == AudioSystem.DEVICE_OUT_BLE_BROADCAST)
+ if (AudioSystem.isLeAudioDeviceType(device)
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
@@ -4112,8 +4111,7 @@
dispatchAbsoluteVolumeChanged(streamType, info, index);
}
- if ((device == AudioSystem.DEVICE_OUT_BLE_HEADSET
- || device == AudioSystem.DEVICE_OUT_BLE_BROADCAST)
+ if (AudioSystem.isLeAudioDeviceType(device)
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
if (DEBUG_VOL) {
@@ -6952,7 +6950,8 @@
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
}
if (isAbsoluteVolumeDevice(audioSystemDeviceOut)
- || isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)) {
+ || isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
+ || AudioSystem.isLeAudioDeviceType(audioSystemDeviceOut)) {
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
}
return AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
@@ -7719,7 +7718,9 @@
int index;
if (isFullyMuted()) {
index = 0;
- } else if (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)) {
+ } else if (isAbsoluteVolumeDevice(device)
+ || isA2dpAbsoluteVolumeDevice(device)
+ || AudioSystem.isLeAudioDeviceType(device)) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
index = (mIndexMax + 5)/10;
@@ -7741,7 +7742,8 @@
if (isFullyMuted()) {
index = 0;
} else if (isAbsoluteVolumeDevice(device)
- || isA2dpAbsoluteVolumeDevice(device)) {
+ || isA2dpAbsoluteVolumeDevice(device)
+ || AudioSystem.isLeAudioDeviceType(device)) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if (isFullVolumeDevice(device)) {
index = (mIndexMax + 5)/10;
@@ -8162,7 +8164,8 @@
int streamDevice = getDeviceForStream(streamType);
if ((device != streamDevice)
&& (isAbsoluteVolumeDevice(device)
- || isA2dpAbsoluteVolumeDevice(device))) {
+ || isA2dpAbsoluteVolumeDevice(device)
+ || AudioSystem.isLeAudioDeviceType(device))) {
mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
}
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index b5835ce..30a9e0a7 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -433,7 +433,7 @@
case VOL_SET_LE_AUDIO_VOL:
return new StringBuilder("setLeAudioVolume:")
.append(" index:").append(mVal1)
- .append(" gain dB:").append(mVal2)
+ .append(" maxIndex:").append(mVal2)
.toString();
case VOL_SET_AVRCP_VOL:
return new StringBuilder("setAvrcpVolume:")
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 9a7c6626..d0f5470 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -412,9 +412,8 @@
}
return;
}
- /* leaudio expect volume value in range 0 to 255
- */
- int volume = (index * (BT_LE_AUDIO_MAX_VOL - BT_LE_AUDIO_MIN_VOL)) / maxIndex ;
+ /* leaudio expect volume value in range 0 to 255 */
+ int volume = (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex);
if (AudioService.DEBUG_VOL) {
Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx="
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index b3e7e31..93841fe 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -49,6 +49,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
/**
@@ -104,11 +105,7 @@
private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
- private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
- // a public client is one that needs an anonymized version of the playback configurations, we
- // keep track of whether there is at least one to know when we need to create the list of
- // playback configurations that do not contain uid/pid/package name information.
- private boolean mHasPublicClients = false;
+ private final ConcurrentLinkedQueue<PlayMonitorClient> mClients = new ConcurrentLinkedQueue<>();
private final Object mPlayerLock = new Object();
@GuardedBy("mPlayerLock")
@@ -458,11 +455,9 @@
+ DateFormat.getTimeInstance().format(new Date()));
synchronized(mPlayerLock) {
pw.println("\n playback listeners:");
- synchronized(mClients) {
- for (PlayMonitorClient pmc : mClients) {
- pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)")
- + pmc.toString());
- }
+ for (PlayMonitorClient pmc : mClients) {
+ pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)")
+ + pmc.toString());
}
pw.println("\n");
// all players
@@ -534,48 +529,33 @@
* @param iplayerReleased indicates if the change was due to a player being released
*/
private void dispatchPlaybackChange(boolean iplayerReleased) {
- synchronized (mClients) {
- // typical use case, nobody is listening, don't do any work
- if (mClients.isEmpty()) {
- return;
- }
- }
if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
final List<AudioPlaybackConfiguration> configsSystem;
- // list of playback configurations for "public consumption". It is only computed if there
+ // list of playback configurations for "public consumption". It is computed lazy if there
// are non-system playback activity listeners.
- final List<AudioPlaybackConfiguration> configsPublic;
+ List<AudioPlaybackConfiguration> configsPublic = null;
synchronized (mPlayerLock) {
if (mPlayers.isEmpty()) {
return;
}
- configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
+ configsSystem = new ArrayList<>(mPlayers.values());
}
- synchronized (mClients) {
- // was done at beginning of method, but could have changed
- if (mClients.isEmpty()) {
- return;
- }
- configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null;
- final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
- while (clientIterator.hasNext()) {
- final PlayMonitorClient pmc = clientIterator.next();
- try {
- // do not spam the logs if there are problems communicating with this client
- if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) {
- if (pmc.mIsPrivileged) {
- pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem,
- iplayerReleased);
- } else {
- // non-system clients don't have the control interface IPlayer, so
- // they don't need to flush commands when a player was released
- pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false);
- }
+
+ final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
+ while (clientIterator.hasNext()) {
+ final PlayMonitorClient pmc = clientIterator.next();
+ // do not spam the logs if there are problems communicating with this client
+ if (!pmc.reachedMaxErrorCount()) {
+ if (pmc.isPrivileged()) {
+ pmc.dispatchPlaybackConfigChange(configsSystem,
+ iplayerReleased);
+ } else {
+ if (configsPublic == null) {
+ configsPublic = anonymizeForPublicConsumption(configsSystem);
}
- } catch (RemoteException e) {
- pmc.mErrorCount++;
- Log.e(TAG, "Error (" + pmc.mErrorCount +
- ") trying to dispatch playback config change to " + pmc, e);
+ // non-system clients don't have the control interface IPlayer, so
+ // they don't need to flush commands when a player was released
+ pmc.dispatchPlaybackConfigChange(configsPublic, false);
}
}
}
@@ -798,14 +778,9 @@
if (pcdb == null) {
return;
}
- synchronized(mClients) {
- final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
- if (pmc.init()) {
- if (!isPrivileged) {
- mHasPublicClients = true;
- }
- mClients.add(pmc);
- }
+ final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
+ if (pmc.init()) {
+ mClients.add(pmc);
}
}
@@ -813,23 +788,14 @@
if (pcdb == null) {
return;
}
- synchronized(mClients) {
- final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
- boolean hasPublicClients = false;
- // iterate over the clients to remove the dispatcher to remove, and reevaluate at
- // the same time if we still have a public client.
- while (clientIterator.hasNext()) {
- PlayMonitorClient pmc = clientIterator.next();
- if (pcdb.asBinder().equals(pmc.mDispatcherCb.asBinder())) {
- pmc.release();
- clientIterator.remove();
- } else {
- if (!pmc.mIsPrivileged) {
- hasPublicClients = true;
- }
- }
+ final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
+ // iterate over the clients to remove the dispatcher
+ while (clientIterator.hasNext()) {
+ PlayMonitorClient pmc = clientIterator.next();
+ if (pmc.equalsDispatcher(pcdb)) {
+ pmc.release();
+ clientIterator.remove();
}
- mHasPublicClients = hasPublicClients;
}
}
@@ -857,24 +823,34 @@
// can afford to be static because only one PlaybackActivityMonitor ever instantiated
static PlaybackActivityMonitor sListenerDeathMonitor;
- final IPlaybackConfigDispatcher mDispatcherCb;
- final boolean mIsPrivileged;
-
- int mErrorCount = 0;
// number of errors after which we don't update this client anymore to not spam the logs
- static final int MAX_ERRORS = 5;
+ private static final int MAX_ERRORS = 5;
+
+ private final IPlaybackConfigDispatcher mDispatcherCb;
+
+ @GuardedBy("this")
+ private final boolean mIsPrivileged;
+ @GuardedBy("this")
+ private boolean mIsReleased = false;
+ @GuardedBy("this")
+ private int mErrorCount = 0;
PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
mDispatcherCb = pcdb;
mIsPrivileged = isPrivileged;
}
+ @Override
public void binderDied() {
Log.w(TAG, "client died");
sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
}
- boolean init() {
+ synchronized boolean init() {
+ if (mIsReleased) {
+ // Do not init after release
+ return false;
+ }
try {
mDispatcherCb.asBinder().linkToDeath(this, 0);
return true;
@@ -884,8 +860,43 @@
}
}
- void release() {
+ synchronized void release() {
mDispatcherCb.asBinder().unlinkToDeath(this, 0);
+ mIsReleased = true;
+ }
+
+ void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+ boolean flush) {
+ synchronized (this) {
+ if (mIsReleased) {
+ // Do not dispatch anything after release
+ return;
+ }
+ }
+ try {
+ mDispatcherCb.dispatchPlaybackConfigChange(configs, flush);
+ } catch (RemoteException e) {
+ synchronized (this) {
+ mErrorCount++;
+ Log.e(TAG, "Error (" + mErrorCount
+ + ") trying to dispatch playback config change to " + this, e);
+ }
+ }
+ }
+
+ synchronized boolean isPrivileged() {
+ return mIsPrivileged;
+ }
+
+ synchronized boolean reachedMaxErrorCount() {
+ return mErrorCount >= MAX_ERRORS;
+ }
+
+ synchronized boolean equalsDispatcher(IPlaybackConfigDispatcher pcdb) {
+ if (pcdb == null) {
+ return false;
+ }
+ return pcdb.asBinder().equals(mDispatcherCb.asBinder());
}
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index b425420..aec60de 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -582,13 +582,18 @@
}
@Override
- public boolean isCameraDisabled() {
+ public boolean isCameraDisabled(int userId) {
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
Slog.e(TAG, "Failed to get the device policy manager service");
return false;
}
- return dpm.getCameraDisabled(null);
+ try {
+ return dpm.getCameraDisabled(null, userId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
}
};
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 477b8da..d8aa469 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -106,7 +106,7 @@
private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
@VisibleForTesting
- static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000;
+ static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000;
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 80ce70d..3707c8e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1211,7 +1211,7 @@
if (info.userId == userId
&& info.agent.isTrusted()
&& info.agent.shouldDisplayTrustGrantedMessage()
- && !TextUtils.isEmpty(info.agent.getMessage())) {
+ && info.agent.getMessage() != null) {
trustGrantedMessages.add(info.agent.getMessage().toString());
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index af22f80..6538ee3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2120,7 +2120,7 @@
mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord);
- updateEnterpriseThumbnailDrawable(mAtmService.mUiContext);
+ updateEnterpriseThumbnailDrawable(mAtmService.getUiContext());
}
/**
@@ -7433,7 +7433,7 @@
}
final Rect frame = win.getRelativeFrame();
final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId
- ? mAtmService.mUiContext.getDrawable(R.drawable.ic_account_circle)
+ ? mAtmService.getUiContext().getDrawable(R.drawable.ic_account_circle)
: mEnterpriseThumbnailDrawable;
final HardwareBuffer thumbnail = getDisplayContent().mAppTransition
.createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 7c25b53..0707b81 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -384,6 +384,14 @@
callingUid, realCallingUid, UserHandle.USER_NULL);
final SparseArray<String> startingUidPkgs = new SparseArray<>();
final long origId = Binder.clearCallingIdentity();
+
+ SafeActivityOptions bottomOptions = null;
+ if (options != null) {
+ // To ensure the first N-1 activities (N == total # of activities) are also launched
+ // into the correct display, use a copy of the passed-in options (keeping only
+ // display-related info) for these activities.
+ bottomOptions = options.selectiveCloneDisplayOptions();
+ }
try {
intents = ArrayUtils.filterNotNull(intents, Intent[]::new);
final ActivityStarter[] starters = new ActivityStarter[intents.length];
@@ -432,7 +440,7 @@
final boolean top = i == intents.length - 1;
final SafeActivityOptions checkedOptions = top
? options
- : null;
+ : bottomOptions;
starters[i] = obtainStarter(intent, reason)
.setIntentGrants(intentGrants)
.setCaller(caller)
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e993496..75e24a8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -346,7 +346,7 @@
* This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
* change at runtime. Use mContext for non-UI purposes.
*/
- final Context mUiContext;
+ private final Context mUiContext;
final ActivityThread mSystemThread;
H mH;
UiHandler mUiHandler;
@@ -1041,6 +1041,10 @@
}
}
+ Context getUiContext() {
+ return mUiContext;
+ }
+
UserManagerService getUserManager() {
if (mUserManager == null) {
IBinder b = ServiceManager.getService(Context.USER_SERVICE);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 53f2c71..5c1a877 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -39,6 +39,7 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -758,7 +759,8 @@
if (isKeyguardGoingAwayTransitOld(transit) && enter) {
a = mTransitionAnimation.loadKeyguardExitAnimation(mNextAppTransitionFlags,
transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
- } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
+ } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM) {
a = null;
} else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) {
a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
@@ -1170,6 +1172,9 @@
case TRANSIT_OLD_KEYGUARD_OCCLUDE: {
return "TRANSIT_OLD_KEYGUARD_OCCLUDE";
}
+ case TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM: {
+ return "TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM";
+ }
case TRANSIT_OLD_KEYGUARD_UNOCCLUDE: {
return "TRANSIT_OLD_KEYGUARD_UNOCCLUDE";
}
@@ -1425,6 +1430,7 @@
static boolean isKeyguardOccludeTransitOld(@TransitionOldType int transit) {
return transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM
|| transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 5ac5f2e..5599f2c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -38,6 +38,7 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -363,8 +364,14 @@
// When there is a closing app, the keyguard has already been occluded by an
// activity, and another activity has started on top of that activity, so normal
// app transition animation should be used.
- return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE
- : TRANSIT_OLD_ACTIVITY_OPEN;
+ if (!closingApps.isEmpty()) {
+ return TRANSIT_OLD_ACTIVITY_OPEN;
+ }
+ if (!openingApps.isEmpty() && openingApps.valueAt(0).getActivityType()
+ == ACTIVITY_TYPE_DREAM) {
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
+ }
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE;
case TRANSIT_KEYGUARD_UNOCCLUDE:
return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 35a39c0..028d4b3 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -310,7 +310,7 @@
mBackNaviAnimationController = new BackNaviAnimationController(
backAnimationAdaptor.getRunner(), this,
currentActivity.getDisplayId());
- prepareBackToHomeTransition(currentTask, prevTask);
+ prepareBackToHomeTransition(currentActivity, prevTask);
infoBuilder.setPrepareAnimation(true);
}
} else {
@@ -489,8 +489,8 @@
mWindowManagerService = wm;
}
- private void prepareBackToHomeTransition(Task currentTask, Task homeTask) {
- final DisplayContent dc = currentTask.getDisplayContent();
+ private void prepareBackToHomeTransition(ActivityRecord currentActivity, Task homeTask) {
+ final DisplayContent dc = currentActivity.getDisplayContent();
final ActivityRecord homeActivity = homeTask.getTopNonFinishingActivity();
if (!homeActivity.mVisibleRequested) {
homeActivity.setVisibility(true);
@@ -499,7 +499,7 @@
dc.ensureActivitiesVisible(
null /* starting */, 0 /* configChanges */,
false /* preserveWindows */, true);
- mBackNaviAnimationController.initialize(homeActivity, currentTask.getTopMostActivity());
+ mBackNaviAnimationController.initialize(homeActivity, currentActivity);
}
void finishAnimation() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 720b082..ca02e7d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1614,24 +1614,6 @@
}
config = new Configuration();
computeScreenConfiguration(config);
- } else if (!(mTransitionController.isCollecting(this)
- // If waiting for a remote display change, don't prematurely update configuration.
- || mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange())) {
- // No obvious action we need to take, but if our current state mismatches the
- // activity manager's, update it, disregarding font scale, which should remain set
- // to the value of the previous configuration.
- // Here we're calling Configuration#unset() instead of setToDefaults() because we
- // need to keep override configs clear of non-empty values (e.g. fontSize).
- final Configuration currentConfig = getRequestedOverrideConfiguration();
- mTmpConfiguration.unset();
- mTmpConfiguration.updateFrom(currentConfig);
- computeScreenConfiguration(mTmpConfiguration);
- if (currentConfig.diff(mTmpConfiguration) != 0) {
- mWaitingForConfig = true;
- setLayoutNeeded();
- mDisplayRotation.prepareNormalRotationAnimation();
- config = new Configuration(mTmpConfiguration);
- }
}
return config;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1636d9f..b26de07 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -421,7 +421,7 @@
mService = service;
mContext = displayContent.isDefaultDisplay ? service.mContext
: service.mContext.createDisplayContext(displayContent.getDisplay());
- mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext
+ mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.getUiContext()
: service.mAtmService.mSystemThread
.getSystemUiContext(displayContent.getDisplayId());
mDisplayContent = displayContent;
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 2879e33..8bacacd 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -116,6 +116,34 @@
}
/**
+ * To ensure that two activities, one using this object, and the other using the
+ * SafeActivityOptions returned from this function, are launched into the same display through
+ * ActivityStartController#startActivities, all display-related information, i.e.
+ * displayAreaToken, launchDisplayId and callerDisplayId, are cloned.
+ */
+ @Nullable SafeActivityOptions selectiveCloneDisplayOptions() {
+ final ActivityOptions options = cloneLaunchingDisplayOptions(mOriginalOptions);
+ final ActivityOptions callerOptions = cloneLaunchingDisplayOptions(mCallerOptions);
+ if (options == null && callerOptions == null) {
+ return null;
+ }
+
+ final SafeActivityOptions safeOptions = new SafeActivityOptions(options,
+ mOriginalCallingPid, mOriginalCallingUid);
+ safeOptions.mCallerOptions = callerOptions;
+ safeOptions.mRealCallingPid = mRealCallingPid;
+ safeOptions.mRealCallingUid = mRealCallingUid;
+ return safeOptions;
+ }
+
+ private ActivityOptions cloneLaunchingDisplayOptions(ActivityOptions options) {
+ return options == null ? null : ActivityOptions.makeBasic()
+ .setLaunchTaskDisplayArea(options.getLaunchTaskDisplayArea())
+ .setLaunchDisplayId(options.getLaunchDisplayId())
+ .setCallerDisplayId((options.getCallerDisplayId()));
+ }
+
+ /**
* Overrides options with options from a caller and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
* method.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 436cc4d..91db278 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4465,6 +4465,14 @@
// transferring the transform on the leash to the task, reset this state once we're
// moving out of pip
setCanAffectSystemUiFlags(true);
+ // Turn on userLeaveHint so other app can enter PiP mode.
+ mTaskSupervisor.mUserLeaving = true;
+ // Allow entering PiP from current top most activity when we are leaving PiP.
+ final Task topFocused = mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (topFocused != null) {
+ final ActivityRecord ar = topFocused.getTopResumedActivity();
+ enableEnterPipOnTaskSwitch(ar, null /* toFrontTask */, ar, null /* opts */);
+ }
mRootWindowContainer.notifyActivityPipModeChanged(this, null);
}
if (likelyResolvedMode == WINDOWING_MODE_PINNED) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 135fcd4..0e1a6de 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -577,17 +577,16 @@
t.setLayer(targetLeash, target.getLastLayer());
target.getRelativePosition(tmpPos);
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
- final Rect clipRect;
// No need to clip the display in case seeing the clipped content when during the
// display rotation. No need to clip activities because they rely on clipping on
// task layers.
if (target.asDisplayContent() != null || target.asActivityRecord() != null) {
- clipRect = null;
+ t.setCrop(targetLeash, null /* crop */);
} else {
- clipRect = target.getRequestedOverrideBounds();
- clipRect.offset(-tmpPos.x, -tmpPos.y);
+ // Crop to the requested bounds.
+ final Rect clipRect = target.getRequestedOverrideBounds();
+ t.setWindowCrop(targetLeash, clipRect.width(), clipRect.height());
}
- t.setCrop(targetLeash, clipRect);
t.setCornerRadius(targetLeash, 0);
t.setShadowRadius(targetLeash, 0);
t.setMatrix(targetLeash, 1, 0, 0, 1);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 06fb4b0..6aca14f0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8178,7 +8178,8 @@
}
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)
+ || isCameraServerUid(caller));
if (parent) {
Preconditions.checkCallAuthorization(
@@ -9689,6 +9690,10 @@
return UserHandle.isSameApp(caller.getUid(), Process.SHELL_UID);
}
+ private boolean isCameraServerUid(CallerIdentity caller) {
+ return UserHandle.isSameApp(caller.getUid(), Process.CAMERASERVER_UID);
+ }
+
private @UserIdInt int getCurrentForegroundUserId() {
try {
UserInfo currentUser = mInjector.getIActivityManager().getCurrentUser();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index bbd7695..95e9f20 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2993,6 +2993,7 @@
// Add a decor insets provider window.
final WindowState navbar = createNavBarWithProvidedInsets(squareDisplay);
squareDisplay.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(navbar);
+ squareDisplay.sendNewConfiguration();
final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
// create a fixed portrait activity
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 20b1120..2fccd64 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -220,10 +220,7 @@
// Check that changes are reported
Configuration c = new Configuration(newDisp1.getRequestedOverrideConfiguration());
c.windowConfiguration.setBounds(new Rect(0, 0, 1000, 1300));
- newDisp1.onRequestedOverrideConfigurationChanged(c);
- mAtm.mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
- newDisp1.mDisplayId, false /* markFrozenIfConfigChanged */,
- false /* deferResume */);
+ newDisp1.performDisplayOverrideConfigUpdate(c);
assertEquals(0, added.size());
assertEquals(1, changed.size());
assertEquals(0, removed.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 7111852..e57ad5d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -17,9 +17,12 @@
package com.android.server.wm;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
import android.app.ActivityOptions;
import android.platform.test.annotations.Presubmit;
+import android.window.WindowContainerToken;
import androidx.test.filters.MediumTest;
@@ -43,4 +46,21 @@
final ActivityOptions result = options.mergeActivityOptions(opts1, opts2);
assertEquals(6, result.getLaunchDisplayId());
}
+
+ @Test
+ public void test_selectiveCloneDisplayOptions() {
+ final WindowContainerToken token = mock(WindowContainerToken.class);
+ final int launchDisplayId = 5;
+ final int callerDisplayId = 6;
+
+ final SafeActivityOptions clone = new SafeActivityOptions(ActivityOptions.makeBasic()
+ .setLaunchTaskDisplayArea(token)
+ .setLaunchDisplayId(launchDisplayId)
+ .setCallerDisplayId(callerDisplayId))
+ .selectiveCloneDisplayOptions();
+
+ assertSame(clone.getOriginalOptions().getLaunchTaskDisplayArea(), token);
+ assertEquals(clone.getOriginalOptions().getLaunchDisplayId(), launchDisplayId);
+ assertEquals(clone.getOriginalOptions().getCallerDisplayId(), callerDisplayId);
+ }
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 25216a3..66ad6f1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -173,5 +173,17 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".GameActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
+ android:immersive="true"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:configChanges="screenSize"
+ android:label="GameApp"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
new file mode 100644
index 0000000..0b4693d
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_orange_light">
+
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 6cda482..42a37df 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -87,4 +87,9 @@
public static final ComponentName NOTIFICATION_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".NotificationActivity");
+
+ public static final String GAME_ACTIVITY_LAUNCHER_NAME = "GameApp";
+ public static final ComponentName GAME_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".GameActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
new file mode 100644
index 0000000..ef75d4d
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
@@ -0,0 +1,77 @@
+/*
+ * 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.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
+public class GameActivity extends Activity implements SurfaceHolder.Callback {
+ private SurfaceHolder mSurfaceHolder;
+ private SurfaceView mSurfaceView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_surfaceview);
+
+ mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
+ mSurfaceView.setZOrderOnTop(true);
+ mSurfaceHolder = mSurfaceView.getHolder();
+ mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
+ mSurfaceHolder.addCallback(this);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ hideSystemBars();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.BLUE);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ private void hideSystemBars() {
+ WindowInsetsControllerCompat windowInsetsController =
+ ViewCompat.getWindowInsetsController(getWindow().getDecorView());
+ if (windowInsetsController == null) {
+ return;
+ }
+ // Configure the behavior of the hidden system bars.
+ windowInsetsController.setSystemBarsBehavior(
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ );
+ // Hide both the status bar and the navigation bar.
+ windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());
+ }
+}