对于EEA版本的需求 应该是按HOME出花瓣的现象的.代码也同步到我的下载资源里面,需要的可以下载,对比过去,会快一点
From dbc202e769d3c3e9f0c30124d2857770fa85ab41 Mon Sep 17 00:00:00 2001
From: Lance Chang <jinwoong@google.com>
Date: Wed, 05 Jul 2017 19:55:22 -0700
Subject: [PATCH] Home button animation sample code for Android O
This CL implements the home button animation on top of AOSP SystemUI
codebase for Android OEM partners. This change MUST NOT be merged to
any internal branch.
Bug: 34622877
Test: lunch aosp target && make
Change-Id: Iea114eb6a82a26b83258fbec2ad1e2fc324ff6ae
---
diff --git a/packages/SystemUI/opa/res/drawable/halo.xml b/packages/SystemUI/opa/res/drawable/halo.xml
new file mode 100644
index 0000000..c9a95f5
--- /dev/null
+++ b/packages/SystemUI/opa/res/drawable/halo.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="ring"
+ android:innerRadius="@dimen/halo_inner_radius"
+ android:thickness="@dimen/halo_thickness"
+ android:useLevel="false">
+
+ <solid android:color="@android:color/white" />
+
+ <size
+ android:height="@dimen/halo_diameter"
+ android:width="@dimen/halo_diameter" />
+</shape>
diff --git a/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_blue.xml b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_blue.xml
new file mode 100644
index 0000000..7e5a66c
--- /dev/null
+++ b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_blue.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <size
+ android:width="@dimen/opa_dot_diam"
+ android:height="@dimen/opa_dot_diam" />
+ <solid
+ android:color="#FF4285F4" />
+</shape>
diff --git a/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_green.xml b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_green.xml
new file mode 100644
index 0000000..d11b906
--- /dev/null
+++ b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_green.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <size
+ android:width="@dimen/opa_dot_diam"
+ android:height="@dimen/opa_dot_diam" />
+ <solid
+ android:color="#FF34A853" />
+</shape>
diff --git a/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_red.xml b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_red.xml
new file mode 100644
index 0000000..4483b0f
--- /dev/null
+++ b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_red.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <size
+ android:width="@dimen/opa_dot_diam"
+ android:height="@dimen/opa_dot_diam" />
+ <solid
+ android:color="#FFEA4335" />
+</shape>
diff --git a/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_yellow.xml b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_yellow.xml
new file mode 100644
index 0000000..141ff8e
--- /dev/null
+++ b/packages/SystemUI/opa/res/drawable/ic_sysbar_opa_yellow.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <size
+ android:width="@dimen/opa_dot_diam"
+ android:height="@dimen/opa_dot_diam" />
+ <solid
+ android:color="#FFFBBC05" />
+</shape>
diff --git a/packages/SystemUI/opa/res/layout/home.xml b/packages/SystemUI/opa/res/layout/home.xml
new file mode 100644
index 0000000..90bc1c5
--- /dev/null
+++ b/packages/SystemUI/opa/res/layout/home.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<com.google.android.systemui.OpaLayout
+ android:id="@+id/home"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="@dimen/navigation_key_width"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingEnd="@dimen/navigation_key_padding"
+ android:paddingStart="@dimen/navigation_key_padding">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/red"
+ style="@style/DotStyle"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_sysbar_opa_red"/>
+
+ <ImageView
+ android:id="@+id/blue"
+ style="@style/DotStyle"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_sysbar_opa_blue" />
+
+ <ImageView
+ android:id="@+id/green"
+ style="@style/DotStyle"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_sysbar_opa_green" />
+
+ <ImageView
+ android:id="@+id/yellow"
+ style="@style/DotStyle"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_sysbar_opa_yellow" />
+
+ </RelativeLayout>
+
+ <ImageView
+ android:id="@+id/white"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:importantForAccessibility="no" />
+
+ <com.android.systemui.statusbar.policy.KeyButtonView
+ android:id="@+id/home_button"
+ android:layout_width="@dimen/navigation_key_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_home"
+ android:scaleType="center"
+ systemui:keyCode="3" />
+
+ <ImageView
+ android:id="@+id/halo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:importantForAccessibility="no"
+ android:src="@drawable/halo" />
+
+</com.google.android.systemui.OpaLayout>
diff --git a/packages/SystemUI/opa/res/values/config.xml b/packages/SystemUI/opa/res/values/config.xml
new file mode 100644
index 0000000..e47c50c
--- /dev/null
+++ b/packages/SystemUI/opa/res/values/config.xml
@@ -0,0 +1,4 @@
+<resources>
+ <!-- SystemUIFactory component -->
+ <string name="config_systemUIFactoryComponent" translatable="false">com.google.android.systemui.SystemUIGoogleFactory</string>
+</resources>
diff --git a/packages/SystemUI/opa/res/values/dimens.xml b/packages/SystemUI/opa/res/values/dimens.xml
new file mode 100644
index 0000000..ee7ee9e
--- /dev/null
+++ b/packages/SystemUI/opa/res/values/dimens.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <!-- The inner radius of the halo. -->
+ <dimen name="halo_inner_radius">10dp</dimen>
+
+ <!-- The thickness of the halo. -->
+ <dimen name="halo_thickness">1dp</dimen>
+
+ <!-- The diameter of the halo. This is 2*(halo_inner_radius + halo_thickness). -->
+ <dimen name="halo_diameter">22dp</dimen>
+
+ <!-- The diameter of the opa dots -->
+ <dimen name="opa_dot_diam">10dp</dimen>
+
+ <!-- The translation for diamond -->
+ <dimen name="opa_diamond_translation">16dp</dimen>
+
+ <!-- The y translation into line for red and yellow -->
+ <dimen name="opa_line_y_translation">16dp</dimen>
+
+ <!-- The x translation into line for red and yellow -->
+ <dimen name="opa_line_x_trans_ry">15dp</dimen>
+
+ <!-- The x translation into line for green and blue -->
+ <dimen name="opa_line_x_trans_bg">30dp</dimen>
+
+ <!-- The x collapse for blue and green -->
+ <dimen name="opa_line_x_collapse_bg">46dp</dimen>
+
+ <!-- The x collapse for red and yellow -->
+ <dimen name="opa_line_x_collapse_ry">15dp</dimen>
+</resources>
diff --git a/packages/SystemUI/opa/res/values/styles.xml b/packages/SystemUI/opa/res/values/styles.xml
new file mode 100644
index 0000000..1f0b2eb
--- /dev/null
+++ b/packages/SystemUI/opa/res/values/styles.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="DotStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_centerHorizontal">true</item>
+ <item name="android:layout_centerVertical">true</item>
+ </style>
+</resources>
diff --git a/packages/SystemUI/opa/src/com/google/android/systemui/AssistManagerGoogle.java b/packages/SystemUI/opa/src/com/google/android/systemui/AssistManagerGoogle.java
new file mode 100644
index 0000000..b10a67a
--- /dev/null
+++ b/packages/SystemUI/opa/src/com/google/android/systemui/AssistManagerGoogle.java
@@ -0,0 +1,108 @@
+package com.google.android.systemui;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+/**
+ * GoogleSystemUI-specific flavor of AssistManager.
+ */
+public class AssistManagerGoogle extends AssistManager {
+
+ private final ContentResolver mContentResolver;
+ private final ContentObserver mContentObserver = new AssistantSettingsObserver();
+ private final OpaEnableDispatcher mOpaEnableDispatcher;
+ private final AssistantStateReceiver mEnableReceiver = new AssistantStateReceiver();
+
+ private final KeyguardUpdateMonitorCallback mUserSwitchCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onUserSwitching(int userId) {
+ updateAssistantEnabledState();
+ // Unregister and re-register observer for current user when user switches.
+ unregisterSettingsObserver();
+ registerSettingsObserver();
+ // Unregister and re-register the opa enabled for current user
+ unregisterEnableReceiver();
+ registerEnableReceiver(userId);
+ }
+ };
+
+ public AssistManagerGoogle(DeviceProvisionedController controller, Context context) {
+ super(controller, context);
+ mContentResolver = context.getContentResolver();
+ mOpaEnableDispatcher = new OpaEnableDispatcher(context, mAssistUtils);
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUserSwitchCallback);
+ // Register Assistant Settings observer
+ registerSettingsObserver();
+ registerEnableReceiver(UserHandle.USER_CURRENT);
+ }
+
+ @Override
+ public boolean shouldShowOrb() {
+ return false;
+ }
+
+ /**
+ * Register AssistantStateReceiver for userId.
+ */
+ private void registerEnableReceiver(int userId) {
+ mContext.registerReceiverAsUser(mEnableReceiver, new UserHandle(userId),
+ new IntentFilter(mEnableReceiver.OPA_ENABLE_ACTION), null, null);
+ }
+
+ /**
+ * Unregister AssistantStateReceiver
+ */
+ private void unregisterEnableReceiver() {
+ mContext.unregisterReceiver(mEnableReceiver);
+ }
+
+ /**
+ * Update Assistant enabled state depending on cached value.
+ */
+ private void updateAssistantEnabledState() {
+ boolean isEnabled = UserSettingsUtils.load(mContentResolver);
+ mOpaEnableDispatcher.dispatchOpaEnabled(isEnabled);
+ }
+
+ /**
+ * Register AssistantSettingsObserver for current user.
+ */
+ private void registerSettingsObserver() {
+ mContentResolver.registerContentObserver(Secure.getUriFor(Secure.ASSISTANT),
+ false /* notifyForDescendants */, mContentObserver,
+ KeyguardUpdateMonitor.getCurrentUser());
+ }
+
+ /**
+ * Unregister AssistantSettingsObserver
+ */
+ private void unregisterSettingsObserver() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ /**
+ * Content observer that watches for changes to assistant-related settings.
+ */
+ private class AssistantSettingsObserver extends ContentObserver {
+ public AssistantSettingsObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateAssistantEnabledState();
+ }
+ }
+}
diff --git a/packages/SystemUI/opa/src/com/google/android/systemui/AssistantStateReceiver.java b/packages/SystemUI/opa/src/com/google/android/systemui/AssistantStateReceiver.java
new file mode 100644
index 0000000..f6b2924
--- /dev/null
+++ b/packages/SystemUI/opa/src/com/google/android/systemui/AssistantStateReceiver.java
@@ -0,0 +1,26 @@
+package com.google.android.systemui;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.internal.app.AssistUtils;
+
+/**
+ * A receiver that receives a broadcast to tell SystemUI whether or not to enable OPA.
+ */
+public class AssistantStateReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "AssistantStateReceiver";
+ public static final String OPA_ENABLE_ACTION = "com.google.android.systemui.OPA_ENABLED";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final boolean enabled = intent.getBooleanExtra("OPA_ENABLED", false);
+ Log.i(TAG, "Received " + intent + " with enabled = " + enabled);
+
+ UserSettingsUtils.save(context.getContentResolver(), enabled);
+ new OpaEnableDispatcher(context, new AssistUtils(context)).dispatchOpaEnabled(enabled);
+ }
+}
diff --git a/packages/SystemUI/opa/src/com/google/android/systemui/OpaEnableDispatcher.java b/packages/SystemUI/opa/src/com/google/android/systemui/OpaEnableDispatcher.java
new file mode 100644
index 0000000..ded823b
--- /dev/null
+++ b/packages/SystemUI/opa/src/com/google/android/systemui/OpaEnableDispatcher.java
@@ -0,0 +1,54 @@
+package com.google.android.systemui;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.view.View;
+
+import com.android.internal.app.AssistUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.statusbar.phone.ButtonDispatcher;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to dispatch the current OPA enabled state to the UI.
+ */
+public class OpaEnableDispatcher {
+
+ private final Context mContext;
+ private final AssistUtils mAssistUtils;
+ private static final String OPA_COMPONENT_NAME = "com.google.android.googlequicksearchbox/" +
+ "com.google.android.voiceinteraction.GsaVoiceInteractionService";
+
+ public OpaEnableDispatcher(Context context, AssistUtils assistUtils) {
+ mContext = context;
+ mAssistUtils = assistUtils;
+ }
+
+ public void dispatchOpaEnabled(boolean enabled) {
+ dispatchUnchecked(enabled && isGsaCurrentAssistant());
+ }
+
+ private void dispatchUnchecked(boolean enabled) {
+ StatusBar bar = ((SystemUIApplication) mContext.getApplicationContext()).getComponent(
+ StatusBar.class);
+ if (bar == null) {
+ return;
+ }
+ ButtonDispatcher homeDispatcher = bar.getNavigationBarView().getHomeButton();
+ ArrayList<View> views = homeDispatcher.getViews();
+ for (int i = 0; i < views.size(); ++i) {
+ View v = views.get(i);
+ ((OpaLayout) v).setOpaEnabled(enabled);
+ }
+ }
+
+ private boolean isGsaCurrentAssistant() {
+ ComponentName assistant = mAssistUtils.getAssistComponentForUser(
+ KeyguardUpdateMonitor.getCurrentUser());
+ return assistant != null
+ && OPA_COMPONENT_NAME.equals(assistant.flattenToString());
+ }
+}
diff --git a/packages/SystemUI/opa/src/com/google/android/systemui/OpaLayout.java b/packages/SystemUI/opa/src/com/google/android/systemui/OpaLayout.java
new file mode 100644
index 0000000..b00cedb
--- /dev/null
+++ b/packages/SystemUI/opa/src/com/google/android/systemui/OpaLayout.java
@@ -0,0 +1,826 @@
+package com.google.android.systemui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.SystemClock;
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.MotionEvent;
+import android.view.RenderNodeAnimator;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.statusbar.policy.KeyButtonView;
+
+/**
+ * Custom ViewGroup intended to replace the Home Button when OPA is enabled.
+ * Implements long-press and tap animations.
+ */
+public class OpaLayout extends FrameLayout implements ButtonInterface {
+ private static final String TAG = "OpaLayout";
+
+ /** Minimum time to spend in diamond animation. */
+ private static final int MIN_DIAMOND_DURATION = 100;
+ /** Length of time to spend in the diamond animation. */
+ private static final int DIAMOND_ANIMATION_DURATION = 200;
+ /** Length of time to spend shrinking the halo. */
+ private static final int HALO_ANIMATION_DURATION = 100;
+ /** Length of time to spend changing Y for straight line animation. */
+ private static final int LINE_ANIMATION_DURATION_Y = 133;
+ /** Length of time to spend changing X for straight line animation. */
+ private static final int LINE_ANIMATION_DURATION_X = 225;
+ /** Length of time for collapse animation for green and blue. */
+ private static final int COLLAPSE_ANIMATION_DURATION_BG = 100;
+ /** Length of time for collapse animation for yellow and red. */
+ private static final int COLLAPSE_ANIMATION_DURATION_RY = 83;
+ /** Length of time for dots fullsize animation. */
+ private static final int DOTS_RESIZE_DURATION = 200;
+ /** Length of time for home button resize animation. */
+ private static final int HOME_RESIZE_DURATION = 83;
+ /** Length of time for home reappear animation. */
+ private static final int HOME_REAPPEAR_DURATION = 150;
+ /** Length of time for retract animation. */
+ private static final int RETRACT_ANIMATION_DURATION = 300;
+ private static final int RETRACT_ALPHA_OFFSET = 50;
+ private static final int ALPHA_ANIMATION_LENGTH = 50;
+
+ /** Offset between the start of collapse animation and the start of the home reappear. */
+ private static final int HOME_REAPPEAR_ANIMATION_OFFSET = 33;
+
+ /** Scale factor for dots size change during diamond animation. */
+ private static final float DIAMOND_DOTS_SCALE_FACTOR = 0.8f;
+ /** Scale factor for home size change during diamond animation. */
+ private static final float DIAMOND_HOME_SCALE_FACTOR = 0.625f;
+ /** Scale factor for halo size change during diamond animation. */
+ private static final float HALO_SCALE_FACTOR = 10.0f/21.0f;
+
+ /** Standard 80/40 interpolator */
+ private final Interpolator mFastOutSlowInInterpolator = Interpolators.FAST_OUT_SLOW_IN;
+ /** 0/80 path interpolator for home button disappearance. */
+ private final Interpolator mHomeDisappearInterpolator = new PathInterpolator(.65f, 0f ,1f ,1f);
+ /** Outgoing interpolator for the collapse animation. */
+ private final Interpolator mCollapseInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
+ /** 100/40 interpolator for dot resize to full size. */
+ private final Interpolator mDotsFullSizeInterpolator = new PathInterpolator(0.4f, 0f, 0f, 1f);
+ /** 100/40 interpolator for retract animation. */
+ private final Interpolator mRetractInterpolator = new PathInterpolator(0.4f, 0f, 0f, 1f);
+ /** Interpolator for diamond animation. */
+ private final Interpolator mDiamondInterpolator = new PathInterpolator(.2f,0f,.2f,1f);
+
+ private static final int ANIMATION_STATE_NONE = 0;
+ private static final int ANIMATION_STATE_DIAMOND = 1;
+ private static final int ANIMATION_STATE_RETRACT = 2;
+ private static final int ANIMATION_STATE_OTHER = 3;
+
+ /** Starts line animation on long press. */
+ private final Runnable mCheckLongPress = new Runnable() {
+ @Override
+ public void run() {
+ if (mIsPressed) {
+ mLongClicked = true;
+ }
+ }
+ };
+
+ /** Starts retract animation before diamond animation has completed. */
+ private final Runnable mRetract = new Runnable() {
+ @Override
+ public void run() {
+ // Cancel the diamond animation. We want to start retract from the current state.
+ cancelCurrentAnimation();
+ startRetractAnimation();
+ }
+ };
+
+ // Time that the animation started.
+ private long mStartTime;
+
+ private View mBlue;
+ private View mRed;
+ private View mYellow;
+ private View mGreen;
+ private ImageView mWhite;
+ private ImageView mHalo;
+ private KeyButtonView mHome;
+
+ private View mTop;
+ private View mBottom;
+ private View mRight;
+ private View mLeft;
+
+ private int mAnimationState = ANIMATION_STATE_NONE;
+
+ private final ArraySet<Animator> mCurrentAnimators = new ArraySet<>();
+ private final ArrayList<View> mAnimatedViews = new ArrayList<>();
+
+ private boolean mIsVertical;
+ private boolean mLongClicked;
+ /** Track pressed state locally. */
+ private boolean mIsPressed;
+ private boolean mOpaEnabled;
+
+ public OpaLayout(@NonNull Context context) {
+ super(context);
+ }
+
+ public OpaLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public OpaLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public OpaLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mBlue = findViewById(R.id.blue);
+ mRed = findViewById(R.id.red);
+ mYellow = findViewById(R.id.yellow);
+ mGreen = findViewById(R.id.green);
+ mWhite = (ImageView) findViewById(R.id.white);
+ mHalo = (ImageView) findViewById(R.id.halo);
+ mHome = (KeyButtonView) findViewById(R.id.home_button);
+ mHalo.setImageDrawable(KeyButtonDrawable.create(
+ new ContextThemeWrapper(getContext(), R.style.DualToneLightTheme)
+ .getDrawable(R.drawable.halo),
+ new ContextThemeWrapper(getContext(), R.style.DualToneDarkTheme)
+ .getDrawable(R.drawable.halo)));
+
+ mAnimatedViews.add(mBlue);
+ mAnimatedViews.add(mRed);
+ mAnimatedViews.add(mYellow);
+ mAnimatedViews.add(mGreen);
+ mAnimatedViews.add(mWhite);
+ mAnimatedViews.add(mHalo);
+ skipToStartingValue();
+
+ setOpaEnabled(UserSettingsUtils.load(getContext().getContentResolver()));
+ }
+
+ @Override
+ public void setOnLongClickListener(@Nullable OnLongClickListener l) {
+ // Make sure that the collapse animation is synced with the long click.
+ mHome.setOnLongClickListener(v -> {
+ l.onLongClick(mHome);
+ return true;
+ });
+ }
+
+ @Override
+ public void setOnTouchListener(OnTouchListener l) {
+ mHome.setOnTouchListener(l);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Do nothing if opa is not enabled or animators are disabled.
+ if (!mOpaEnabled || !ValueAnimator.areAnimatorsEnabled()) {
+ return false;
+ }
+ final int action = ev.getAction();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ // If an animation is in progress, do not allow it to be interrupted by another
+ // down event UNLESS the current animation is a retract (that returns the dots
+ // to a resting state), in which case we finish it immediately and start the new
+ // animation.
+ if (!mCurrentAnimators.isEmpty()) {
+ if (mAnimationState == ANIMATION_STATE_RETRACT) {
+ endCurrentAnimation();
+ } else {
+ return false;
+ }
+ }
+ mStartTime = SystemClock.elapsedRealtime();
+ mLongClicked = false;
+ mIsPressed = true;
+ startDiamondAnimation();
+ removeCallbacks(mCheckLongPress);
+ postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mAnimationState == ANIMATION_STATE_DIAMOND) {
+ // Enforce the minimum duration.
+ final long targetTime = MIN_DIAMOND_DURATION -
+ (SystemClock.elapsedRealtime() - mStartTime);
+ removeCallbacks(mRetract);
+ postDelayed(mRetract, targetTime);
+ removeCallbacks(mCheckLongPress);
+ return false;
+ }
+ final boolean doRetract = mIsPressed && !mLongClicked;
+ mIsPressed = false;
+ if (doRetract) {
+ mRetract.run();
+ }
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public void setImageDrawable(@Nullable Drawable drawable) {
+ mWhite.setImageDrawable(drawable);
+ }
+
+ @Override
+ public void abortCurrentGesture() {
+ mHome.abortCurrentGesture();
+ }
+
+ private void startDiamondAnimation() {
+ if (isAttachedToWindow()) {
+ mCurrentAnimators.clear();
+ mCurrentAnimators.addAll(getDiamondAnimatorSet());
+ mAnimationState = ANIMATION_STATE_DIAMOND;
+ startAll(mCurrentAnimators);
+ } else {
+ skipToStartingValue();
+ }
+ }
+
+ private void startRetractAnimation() {
+ if (isAttachedToWindow()) {
+ mCurrentAnimators.clear();
+ mCurrentAnimators.addAll(getRetractAnimatorSet());
+ mAnimationState = ANIMATION_STATE_RETRACT;
+ startAll(mCurrentAnimators);
+ } else {
+ skipToStartingValue();
+ }
+ }
+
+ private void startLineAnimation() {
+ if (isAttachedToWindow()) {
+ mCurrentAnimators.clear();
+ mCurrentAnimators.addAll(getLineAnimatorSet());
+ mAnimationState = ANIMATION_STATE_OTHER;
+ startAll(mCurrentAnimators);
+ } else {
+ skipToStartingValue();
+ }
+ }
+
+ private void startCollapseAnimation() {
+ if (isAttachedToWindow()) {
+ mCurrentAnimators.clear();
+ mCurrentAnimators.addAll(getCollapseAnimatorSet());
+ mAnimationState = ANIMATION_STATE_OTHER;
+ startAll(mCurrentAnimators);
+ } else {
+ skipToStartingValue();
+ }
+ }
+
+ /**
+ * @return A RenderNodeAnimator that scales the x of the view by factor.
+ */
+ private Animator getScaleAnimatorX(View v, float factor, int duration,
+ Interpolator interpolator) {
+ final RenderNodeAnimator animator = new RenderNodeAnimator(RenderNodeAnimator.SCALE_X, factor);
+ animator.setTarget(v);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ return animator;
+ }
+
+ /**
+ * @return A RenderNodeAnimator that scales the y of the view by factor.
+ */
+ private Animator getScaleAnimatorY(View v, float factor, int duration,
+ Interpolator interpolator) {
+ final RenderNodeAnimator animator = new RenderNodeAnimator(RenderNodeAnimator.SCALE_Y, factor);
+ animator.setTarget(v);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ return animator;
+ }
+
+ /**
+ * @return A RenderNodeAnimator that translates the view deltaX from its current x.
+ */
+ private Animator getDeltaAnimatorX(View v, Interpolator interpolator, float deltaX,
+ int duration) {
+ final RenderNodeAnimator animator = new RenderNodeAnimator(RenderNodeAnimator.X, v.getX() + deltaX);
+ animator.setTarget(v);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ return animator;
+ }
+
+ /**
+ * @return A RenderNodeAnimator that translates the view deltaY from its current y.
+ */
+ private Animator getDeltaAnimatorY(View v, Interpolator interpolator, float deltaY,
+ int duration) {
+ final RenderNodeAnimator animator = new RenderNodeAnimator(RenderNodeAnimator.Y, v.getY() + deltaY);
+ animator.setTarget(v);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ return animator;
+ }
+
+ /**
+ * @return a RenderNodeAnimator that moves the view back to its initial inflated X-position.
+ */
+ private Animator getTranslationAnimatorX(View v, Interpolator interpolator, int duration) {
+ final RenderNodeAnimator animator = new RenderNodeAnimator(RenderNodeAnimator.TRANSLATION_X, 0);
+ animator.setTarget(v);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ return animator;
+ }
+
+ /**
+ * @return a RenderNodeAnimator that moves the view back to its initial inflated Y-position.
+ */
+ private Animator getTranslationAnimatorY(View v, Interpolator interpolator, int duration) {
+ final RenderNodeAnimator animator = new RenderNodeAnimator(RenderNodeAnimator.TRANSLATION_Y, 0);
+ animator.setTarget(v);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ return animator;
+ }
+
+ private Animator getAlphaAnimator(View v, float alpha, int duration,
+ Interpolator interpolator) {
+ return getAlphaAnimator(v, alpha, duration, 0 /* startDelay */, interpolator);
+ }
+
+ /**
+ * @return a RenderNodeAnimator that moves the view back to its initial inflated Y-position.
+ */
+ private Animator getAlphaAnimator(View v, float alpha, int duration, int startDelay,
+ Interpolator interpolator) {
+ final RenderNodeAnimator animator = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, alpha);
+ animator.setTarget(v);
+ animator.setInterpolator(interpolator);
+ animator.setDuration(duration);
+ animator.setStartDelay(startDelay);
+ return animator;
+ }
+
+ private void startAll(ArraySet<Animator> animators) {
+ for (int i = animators.size() - 1; i >= 0; i--) {
+ animators.valueAt(i).start();
+ }
+ }
+
+ /**
+ * Get the pixel value of the given resource dimension (in dp).
+ */
+ private float getPxVal(int id) {
+ return getResources().getDimensionPixelOffset(id);
+ }
+
+ private ArraySet<Animator> getDiamondAnimatorSet() {
+ final ArraySet<Animator> animators = new ArraySet<>();
+
+ // Animate top
+ animators.add(getDeltaAnimatorY(mTop, mDiamondInterpolator,
+ -getPxVal(R.dimen.opa_diamond_translation), DIAMOND_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mTop, DIAMOND_DOTS_SCALE_FACTOR, DIAMOND_ANIMATION_DURATION,
+ mFastOutSlowInInterpolator));
+ animators.add(getScaleAnimatorY(mTop, DIAMOND_DOTS_SCALE_FACTOR, DIAMOND_ANIMATION_DURATION,
+ mFastOutSlowInInterpolator));
+ animators.add(getAlphaAnimator(mTop, 1.0f,
+ ALPHA_ANIMATION_LENGTH, Interpolators.LINEAR));
+
+ // Animate bottom
+ animators.add(getDeltaAnimatorY(mBottom, mDiamondInterpolator,
+ getPxVal(R.dimen.opa_diamond_translation), DIAMOND_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mBottom, DIAMOND_DOTS_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getScaleAnimatorY(mBottom, DIAMOND_DOTS_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getAlphaAnimator(mBottom, 1.0f,
+ ALPHA_ANIMATION_LENGTH, Interpolators.LINEAR));
+
+ // Animate left
+ animators.add(getDeltaAnimatorX(mLeft, mDiamondInterpolator,
+ -getPxVal(R.dimen.opa_diamond_translation), DIAMOND_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mLeft, DIAMOND_DOTS_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getScaleAnimatorY(mLeft, DIAMOND_DOTS_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getAlphaAnimator(mLeft, 1.0f,
+ ALPHA_ANIMATION_LENGTH, Interpolators.LINEAR));
+
+ // Animate right
+ animators.add(getDeltaAnimatorX(mRight, mDiamondInterpolator,
+ getPxVal(R.dimen.opa_diamond_translation), DIAMOND_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mRight, DIAMOND_DOTS_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getScaleAnimatorY(mRight, DIAMOND_DOTS_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getAlphaAnimator(mRight, 1.0f,
+ ALPHA_ANIMATION_LENGTH, Interpolators.LINEAR));
+
+ // Animate home
+ animators.add(getScaleAnimatorX(mWhite, DIAMOND_HOME_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getScaleAnimatorY(mWhite, DIAMOND_HOME_SCALE_FACTOR,
+ DIAMOND_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+
+ // Animate halo
+ animators.add(getScaleAnimatorX(mHalo, HALO_SCALE_FACTOR,
+ HALO_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getScaleAnimatorY(mHalo, HALO_SCALE_FACTOR,
+ HALO_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getAlphaAnimator(mHalo, 0.0f, HALO_ANIMATION_DURATION,
+ mFastOutSlowInInterpolator));
+
+ getLongestAnim(animators).addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCurrentAnimators.clear();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ startLineAnimation();
+ }
+ });
+ return animators;
+ }
+
+ private ArraySet<Animator> getRetractAnimatorSet() {
+ final ArraySet<Animator> animators = new ArraySet<>();
+
+ // We don't need separate landscape and portrait cases for this animation since we're
+ // animating in both x and y directions in either case (just in case we start retraction
+ // after the diamond is complete).
+
+ // Animate red
+ animators.add(getTranslationAnimatorX(mRed, mRetractInterpolator, RETRACT_ANIMATION_DURATION));
+ animators.add(getTranslationAnimatorY(mRed, mRetractInterpolator, RETRACT_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mRed, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getScaleAnimatorY(mRed, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getAlphaAnimator(mRed, 0.0f, ALPHA_ANIMATION_LENGTH,
+ RETRACT_ALPHA_OFFSET, Interpolators.LINEAR));
+
+ // Animate blue
+ animators.add(
+ getTranslationAnimatorX(mBlue, mRetractInterpolator, RETRACT_ANIMATION_DURATION));
+ animators.add(
+ getTranslationAnimatorY(mBlue, mRetractInterpolator, RETRACT_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mBlue, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getScaleAnimatorY(mBlue, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getAlphaAnimator(mBlue, 0.0f, ALPHA_ANIMATION_LENGTH,
+ RETRACT_ALPHA_OFFSET, Interpolators.LINEAR));
+
+
+ // Animate green
+ animators.add(getTranslationAnimatorX(mGreen, mRetractInterpolator,
+ RETRACT_ANIMATION_DURATION));
+ animators.add(getTranslationAnimatorY(mGreen, mRetractInterpolator,
+ RETRACT_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mGreen, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getScaleAnimatorY(mGreen, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getAlphaAnimator(mGreen, 0.0f, ALPHA_ANIMATION_LENGTH,
+ RETRACT_ALPHA_OFFSET, Interpolators.LINEAR));
+
+ // Animate yellow
+ animators.add(getTranslationAnimatorX(mYellow, mRetractInterpolator,
+ RETRACT_ANIMATION_DURATION));
+ animators.add(getTranslationAnimatorY(mYellow, mRetractInterpolator,
+ RETRACT_ANIMATION_DURATION));
+ animators.add(getScaleAnimatorX(mYellow, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getScaleAnimatorY(mYellow, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getAlphaAnimator(mYellow, 0.0f, ALPHA_ANIMATION_LENGTH,
+ RETRACT_ALPHA_OFFSET, Interpolators.LINEAR));
+
+ // Animate home
+ animators.add(getScaleAnimatorX(mWhite, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+ animators.add(getScaleAnimatorY(mWhite, 1.0f,
+ RETRACT_ANIMATION_DURATION, mRetractInterpolator));
+
+ // Animate halo
+ animators.add(getScaleAnimatorX(mHalo, 1.0f,
+ RETRACT_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getScaleAnimatorY(mHalo, 1.0f,
+ RETRACT_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+ animators.add(getAlphaAnimator(mHalo, 1.0f,
+ RETRACT_ANIMATION_DURATION, mFastOutSlowInInterpolator));
+
+ getLongestAnim(animators).addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimators.clear();
+ mAnimationState = ANIMATION_STATE_NONE;
+ skipToStartingValue();
+ }
+ });
+ return animators;
+ }
+
+ private ArraySet<Animator> getCollapseAnimatorSet() {
+ final ArraySet<Animator> animators = new ArraySet<>();
+
+ // Animate red
+ animators.add(mIsVertical
+ ? getDeltaAnimatorY(mRed, mCollapseInterpolator,
+ -getPxVal(R.dimen.opa_line_x_collapse_ry), COLLAPSE_ANIMATION_DURATION_RY)
+ : getDeltaAnimatorX(mRed, mCollapseInterpolator,
+ getPxVal(R.dimen.opa_line_x_collapse_ry), COLLAPSE_ANIMATION_DURATION_RY));
+ animators.add(getScaleAnimatorX(mRed, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getScaleAnimatorY(mRed, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getAlphaAnimator(mRed, 0.0f, ALPHA_ANIMATION_LENGTH,
+ HOME_REAPPEAR_ANIMATION_OFFSET, Interpolators.LINEAR));
+
+ // Animate blue
+ animators.add(mIsVertical
+ ? getDeltaAnimatorY(mBlue, mCollapseInterpolator,
+ -getPxVal(R.dimen.opa_line_x_collapse_bg), COLLAPSE_ANIMATION_DURATION_BG)
+ : getDeltaAnimatorX(mBlue, mCollapseInterpolator,
+ getPxVal(R.dimen.opa_line_x_collapse_bg), COLLAPSE_ANIMATION_DURATION_BG));
+ animators.add(getScaleAnimatorX(mBlue, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getScaleAnimatorY(mBlue, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getAlphaAnimator(mBlue, 0.0f, ALPHA_ANIMATION_LENGTH,
+ HOME_REAPPEAR_ANIMATION_OFFSET, Interpolators.LINEAR));
+
+ // Animate yellow
+ animators.add(mIsVertical
+ ? getDeltaAnimatorY(mYellow, mCollapseInterpolator,
+ getPxVal(R.dimen.opa_line_x_collapse_ry), COLLAPSE_ANIMATION_DURATION_RY)
+ : getDeltaAnimatorX(mYellow, mCollapseInterpolator,
+ -getPxVal(R.dimen.opa_line_x_collapse_ry), COLLAPSE_ANIMATION_DURATION_RY));
+ animators.add(getScaleAnimatorX(mYellow, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getScaleAnimatorY(mYellow, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getAlphaAnimator(mYellow, 0.0f, ALPHA_ANIMATION_LENGTH,
+ HOME_REAPPEAR_ANIMATION_OFFSET, Interpolators.LINEAR));
+
+ // Animate green
+ animators.add(mIsVertical
+ ? getDeltaAnimatorY(mGreen, mCollapseInterpolator,
+ getPxVal(R.dimen.opa_line_x_collapse_bg), COLLAPSE_ANIMATION_DURATION_BG)
+ : getDeltaAnimatorX(mGreen, mCollapseInterpolator,
+ -getPxVal(R.dimen.opa_line_x_collapse_bg), COLLAPSE_ANIMATION_DURATION_BG));
+ animators.add(getScaleAnimatorX(mGreen, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getScaleAnimatorY(mGreen, 1.0f, DOTS_RESIZE_DURATION,
+ mDotsFullSizeInterpolator));
+ animators.add(getAlphaAnimator(mGreen, 0.0f, ALPHA_ANIMATION_LENGTH,
+ HOME_REAPPEAR_ANIMATION_OFFSET, Interpolators.LINEAR));
+
+ // Animate home and halo reappearance after a delay.
+ final Animator homeScaleX = getScaleAnimatorX(mWhite, 1.0f, HOME_REAPPEAR_DURATION,
+ mFastOutSlowInInterpolator);
+ final Animator homeScaleY = getScaleAnimatorY(mWhite, 1.0f, HOME_REAPPEAR_DURATION,
+ mFastOutSlowInInterpolator);
+ final Animator haloScaleX = getScaleAnimatorX(mHalo, 1.0f, HOME_REAPPEAR_DURATION,
+ mFastOutSlowInInterpolator);
+ final Animator haloScaleY = getScaleAnimatorY(mHalo, 1.0f, HOME_REAPPEAR_DURATION,
+ mFastOutSlowInInterpolator);
+ final Animator haloAlpha = getAlphaAnimator(mHalo, 1.0f, HOME_REAPPEAR_DURATION,
+ mFastOutSlowInInterpolator);
+ homeScaleX.setStartDelay(HOME_REAPPEAR_ANIMATION_OFFSET);
+ homeScaleY.setStartDelay(HOME_REAPPEAR_ANIMATION_OFFSET);
+ haloScaleX.setStartDelay(HOME_REAPPEAR_ANIMATION_OFFSET);
+ haloScaleY.setStartDelay(HOME_REAPPEAR_ANIMATION_OFFSET);
+ haloAlpha.setStartDelay(HOME_REAPPEAR_ANIMATION_OFFSET);
+ animators.add(homeScaleX);
+ animators.add(homeScaleY);
+ animators.add(haloScaleX);
+ animators.add(haloScaleY);
+ animators.add(haloAlpha);
+
+ getLongestAnim(animators).addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimators.clear();
+ mAnimationState = ANIMATION_STATE_NONE;
+ skipToStartingValue();
+ }
+ });
+ return animators;
+ }
+
+ private ArraySet<Animator> getLineAnimatorSet() {
+ final ArraySet<Animator> animators = new ArraySet<>();
+ if (mIsVertical) {
+ // Animate red
+ animators.add(getDeltaAnimatorY(mRed, mFastOutSlowInInterpolator,
+ getPxVal(R.dimen.opa_line_x_trans_ry), LINE_ANIMATION_DURATION_X));
+ animators.add(getDeltaAnimatorX(mRed, mFastOutSlowInInterpolator,
+ getPxVal(R.dimen.opa_line_y_translation), LINE_ANIMATION_DURATION_Y));
+
+ // Animate blue
+ animators.add(getDeltaAnimatorY(mBlue, mFastOutSlowInInterpolator,
+ getPxVal(R.dimen.opa_line_x_trans_bg), LINE_ANIMATION_DURATION_X));
+
+ //Animate yellow
+ animators.add(getDeltaAnimatorY(mYellow, mFastOutSlowInInterpolator,
+ -getPxVal(R.dimen.opa_line_x_trans_ry), LINE_ANIMATION_DURATION_X));
+ animators.add(getDeltaAnimatorX(mYellow, mFastOutSlowInInterpolator,
+ -getPxVal(R.dimen.opa_line_y_translation), LINE_ANIMATION_DURATION_Y));
+
+ // Animate green
+ animators.add(getDeltaAnimatorY(mGreen, mFastOutSlowInInterpolator,
+ -getPxVal(R.dimen.opa_line_x_trans_bg), LINE_ANIMATION_DURATION_X));
+ } else {
+ // Animate red
+ animators.add(getDeltaAnimatorX(mRed, mFastOutSlowInInterpolator,
+ -getPxVal(R.dimen.opa_line_x_trans_ry), LINE_ANIMATION_DURATION_X));
+ animators.add(getDeltaAnimatorY(mRed, mFastOutSlowInInterpolator,
+ getPxVal(R.dimen.opa_line_y_translation), LINE_ANIMATION_DURATION_Y));
+
+ // Animate blue
+ animators.add(getDeltaAnimatorX(mBlue, mFastOutSlowInInterpolator,
+ -getPxVal(R.dimen.opa_line_x_trans_bg), LINE_ANIMATION_DURATION_X));
+
+ //Animate yellow
+ animators.add(getDeltaAnimatorX(mYellow, mFastOutSlowInInterpolator,
+ getPxVal(R.dimen.opa_line_x_trans_ry), LINE_ANIMATION_DURATION_X));
+ animators.add(getDeltaAnimatorY(mYellow, mFastOutSlowInInterpolator,
+ -getPxVal(R.dimen.opa_line_y_translation), LINE_ANIMATION_DURATION_Y));
+
+ // Animate green
+ animators.add(getDeltaAnimatorX(mGreen, mFastOutSlowInInterpolator,
+ getPxVal(R.dimen.opa_line_x_trans_bg), LINE_ANIMATION_DURATION_X));
+ }
+
+ // Animate home and halo
+ animators.add(getScaleAnimatorX(mWhite, 0.0f, HOME_RESIZE_DURATION,
+ mHomeDisappearInterpolator));
+ animators.add(getScaleAnimatorY(mWhite, 0.0f, HOME_RESIZE_DURATION,
+ mHomeDisappearInterpolator));
+ animators.add(getScaleAnimatorX(mHalo, 0.0f, HOME_RESIZE_DURATION,
+ mHomeDisappearInterpolator));
+ animators.add(getScaleAnimatorY(mHalo, 0.0f, HOME_RESIZE_DURATION,
+ mHomeDisappearInterpolator));
+
+ getLongestAnim(animators).addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ startCollapseAnimation();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCurrentAnimators.clear();
+ }
+ });
+ return animators;
+ }
+
+ public void setOpaEnabled(boolean enabled) {
+ Log.i(TAG, "Setting opa enabled to " + enabled);
+ mOpaEnabled = enabled;
+
+ final int visibility = enabled ? VISIBLE : INVISIBLE;
+ mBlue.setVisibility(visibility);
+ mRed.setVisibility(visibility);
+ mYellow.setVisibility(visibility);
+ mGreen.setVisibility(visibility);
+ mHalo.setVisibility(visibility);
+ }
+
+ /**
+ * Cancels the current animation, stopping it in its tracks.
+ */
+ private void cancelCurrentAnimation() {
+ if (!mCurrentAnimators.isEmpty()) {
+ // Do not finish animation since we want to start retract from current state. We only
+ // need to remove the listeners so the animation doesn't jump to the end.
+ for (int i = mCurrentAnimators.size() - 1; i >= 0; i--) {
+ final Animator a = mCurrentAnimators.valueAt(i);
+ a.removeAllListeners();
+ a.cancel();
+ }
+ mCurrentAnimators.clear();
+ mAnimationState = ANIMATION_STATE_NONE;
+ }
+ }
+
+ /**
+ * Ends the current animation, winding it to the end values.
+ */
+ private void endCurrentAnimation() {
+ if (!mCurrentAnimators.isEmpty()) {
+ for (int i = mCurrentAnimators.size() - 1; i >= 0; i--) {
+ final Animator a = mCurrentAnimators.valueAt(i);
+ a.removeAllListeners();
+ a.end();
+ }
+ mCurrentAnimators.clear();
+ mAnimationState = ANIMATION_STATE_NONE;
+ }
+ }
+
+ /**
+ * @return the animator with the longest {@link Animator#getTotalDuration} from the set
+ */
+ private Animator getLongestAnim(ArraySet<Animator> animators) {
+ long longestDuration = Long.MIN_VALUE;
+ Animator longestAnim = null;
+ for (int i = animators.size() - 1; i >= 0; i--) {
+ Animator a = animators.valueAt(i);
+ if (a.getTotalDuration() > longestDuration) {
+ longestAnim = a;
+ longestDuration = a.getTotalDuration();
+ }
+ }
+ return longestAnim;
+ }
+
+ /**
+ * Forcibly moves views to their starting position and values.
+ */
+ private void skipToStartingValue() {
+ final int size = mAnimatedViews.size();
+ View v;
+ for (int i = 0; i < size; ++i) {
+ v = mAnimatedViews.get(i);
+ v.setScaleY(1.0f);
+ v.setScaleX(1.0f);
+ v.setTranslationY(0);
+ v.setTranslationX(0);
+ v.setAlpha(0.0f);
+ }
+
+ mHalo.setAlpha(1.0f);
+ mWhite.setAlpha(1.0f);
+
+ mAnimationState = ANIMATION_STATE_NONE;
+ }
+
+ @Override
+ public void setVertical(boolean vertical) {
+ mIsVertical = vertical;
+
+ if (mIsVertical) {
+ mTop = mGreen;
+ mBottom = mBlue;
+ mRight = mYellow;
+ mLeft = mRed;
+ } else {
+ mTop = mRed;
+ mBottom = mYellow;
+ mLeft = mBlue;
+ mRight = mGreen;
+ }
+ }
+
+ @Override
+ public void setCarMode(boolean carMode) {
+ setOpaEnabled(!carMode);
+ }
+
+ @Override
+ public void setDarkIntensity(float intensity) {
+ if (mWhite.getDrawable() instanceof KeyButtonDrawable) {
+ ((KeyButtonDrawable) mWhite.getDrawable()).setDarkIntensity(intensity);
+ }
+ ((KeyButtonDrawable) mHalo.getDrawable()).setDarkIntensity(intensity);
+
+ // Since we reuse the same drawable for multiple views, we need to invalidate the view
+ // manually.
+ mWhite.invalidate();
+ mHalo.invalidate();
+
+ mHome.setDarkIntensity(intensity);
+ }
+}
diff --git a/packages/SystemUI/opa/src/com/google/android/systemui/SystemUIGoogleFactory.java b/packages/SystemUI/opa/src/com/google/android/systemui/SystemUIGoogleFactory.java
new file mode 100644
index 0000000..a7463f1
--- /dev/null
+++ b/packages/SystemUI/opa/src/com/google/android/systemui/SystemUIGoogleFactory.java
@@ -0,0 +1,23 @@
+package com.google.android.systemui;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.Dependency.DependencyProvider;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+/**
+ * Factory for Google specific behavior classes.
+ */
+public class SystemUIGoogleFactory extends SystemUIFactory {
+
+ @Override
+ public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
+ Context context) {
+ providers.put(AssistManager.class, () -> new AssistManagerGoogle(
+ Dependency.get(DeviceProvisionedController.class), context));
+ }
+}
diff --git a/packages/SystemUI/opa/src/com/google/android/systemui/UserSettingsUtils.java b/packages/SystemUI/opa/src/com/google/android/systemui/UserSettingsUtils.java
new file mode 100644
index 0000000..5a0dcf7
--- /dev/null
+++ b/packages/SystemUI/opa/src/com/google/android/systemui/UserSettingsUtils.java
@@ -0,0 +1,25 @@
+package com.google.android.systemui;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+
+/**
+ * A utility class to read/write the most recent status of Assistant from/to the persistent store.
+ */
+public class UserSettingsUtils {
+ private static final String OPA_ENABLED_SETTING = "systemui.google.opa_enabled";
+
+ public static void save(ContentResolver cr, boolean enabled) {
+ final int user = KeyguardUpdateMonitor.getCurrentUser();
+ final int en = enabled ? 1 : 0;
+ Settings.Secure.putIntForUser(cr, OPA_ENABLED_SETTING, en, user);
+ }
+
+ public static boolean load(ContentResolver cr) {
+ final int user = KeyguardUpdateMonitor.getCurrentUser();
+ final int en = Settings.Secure.getIntForUser(cr, OPA_ENABLED_SETTING, 0, user);
+ return en != 0;
+ }
+}
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 8cc2ce2..e480b59 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -15,6 +15,7 @@
-keep class com.android.systemui.statusbar.tv.TvStatusBar
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
+-keep class com.google.android.systemui.SystemUIGoogleFactory
-keepclassmembers class ** {
public void onBusEvent(**);