summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI')
-rw-r--r--packages/SystemUI/res/drawable/assist_orb_navbar_scrim.xml25
-rw-r--r--packages/SystemUI/res/drawable/assist_orb_scrim.xml (renamed from packages/SystemUI/res/drawable/search_panel_scrim.xml)0
-rw-r--r--packages/SystemUI/res/layout/assist_orb.xml54
-rw-r--r--packages/SystemUI/res/layout/status_bar_search_panel.xml43
-rw-r--r--packages/SystemUI/res/values-land/styles.xml6
-rw-r--r--packages/SystemUI/res/values-sw600dp/styles.xml6
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml23
-rw-r--r--packages/SystemUI/res/values/styles.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/SearchPanelCircleView.java592
-rw-r--r--packages/SystemUI/src/com/android/systemui/SearchPanelView.java402
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java292
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java285
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPanel.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java109
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java8
22 files changed, 878 insertions, 1266 deletions
diff --git a/packages/SystemUI/res/drawable/assist_orb_navbar_scrim.xml b/packages/SystemUI/res/drawable/assist_orb_navbar_scrim.xml
new file mode 100644
index 0000000..52ed76d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/assist_orb_navbar_scrim.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:type="linear"
+ android:angle="90"
+ android:startColor="#33000000"
+ android:endColor="#00000000" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/search_panel_scrim.xml b/packages/SystemUI/res/drawable/assist_orb_scrim.xml
index bbb2617..bbb2617 100644
--- a/packages/SystemUI/res/drawable/search_panel_scrim.xml
+++ b/packages/SystemUI/res/drawable/assist_orb_scrim.xml
diff --git a/packages/SystemUI/res/layout/assist_orb.xml b/packages/SystemUI/res/layout/assist_orb.xml
new file mode 100644
index 0000000..ab0a0a5
--- /dev/null
+++ b/packages/SystemUI/res/layout/assist_orb.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<!-- Extends FrameLayout -->
+<com.android.systemui.assist.AssistOrbContainer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.systemui.statusbar.AlphaOptimizedView
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/assist_orb_scrim_height"
+ android:layout_gravity="bottom"
+ android:id="@+id/assist_orb_scrim"
+ android:background="@drawable/assist_orb_scrim"/>
+
+ <com.android.systemui.assist.AssistOrbView
+ android:id="@+id/assist_orb"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/search_logo"/>
+ </com.android.systemui.assist.AssistOrbView>
+
+ <com.android.systemui.statusbar.AlphaOptimizedView
+ android:id="@+id/assist_orb_navbar_scrim"
+ android:layout_height="@dimen/assist_orb_navbar_scrim_height"
+ android:layout_width="match_parent"
+ android:layout_gravity="bottom"
+ android:elevation="50dp"
+ android:outlineProvider="none"
+ android:background="@drawable/assist_orb_navbar_scrim"/>
+
+</com.android.systemui.assist.AssistOrbContainer> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_search_panel.xml b/packages/SystemUI/res/layout/status_bar_search_panel.xml
deleted file mode 100644
index e0520ef..0000000
--- a/packages/SystemUI/res/layout/status_bar_search_panel.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
-**
-** Copyright 2012, 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.
-*/
--->
-
-<!-- Extends FrameLayout -->
-<com.android.systemui.SearchPanelView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/search_panel_container"
- android:layout_height="match_parent"
- android:layout_width="match_parent">
-
- <com.android.systemui.statusbar.AlphaOptimizedView
- style="@style/SearchPanelScrim"
- android:id="@+id/search_panel_scrim"
- android:background="@drawable/search_panel_scrim" />
-
- <com.android.systemui.SearchPanelCircleView
- style="@style/SearchPanelCircle"
- android:id="@+id/search_panel_circle">
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/search_logo" />
- </com.android.systemui.SearchPanelCircleView>
-
-</com.android.systemui.SearchPanelView>
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index e58fbb1..8919198 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -18,10 +18,4 @@
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
<item name="android:layout_width">360dp</item>
</style>
-
- <style name="SearchPanelScrim">
- <item name="android:layout_width">@dimen/search_panel_scrim_height</item>
- <item name="android:layout_height">match_parent</item>
- <item name="android:layout_gravity">right</item>
- </style>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 156fa65..4d7d6b5 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -19,12 +19,6 @@
<item name="android:layout_width">480dp</item>
</style>
- <style name="SearchPanelScrim">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">@dimen/search_panel_scrim_height</item>
- <item name="android:layout_gravity">bottom</item>
- </style>
-
<style name="UserDetailView">
<item name="numColumns">4</item>
</style>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d4aeab6..89a7220 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -108,8 +108,7 @@
<color name="notification_guts_text_color">#b2FFFFFF</color>
<color name="notification_guts_btn_color">#FFFFFFFF</color>
- <color name="search_panel_circle_color">#ffffff</color>
- <color name="search_panel_ripple_color">#ffbbbbbb</color>
+ <color name="assist_orb_color">#ffffff</color>
<color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
<color name="doze_small_icon_background_color">#ff434343</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c24cd64..c9e1fee 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -471,22 +471,23 @@
<dimen name="go_to_full_shade_appearing_translation">200dp</dimen>
<!-- The diameter of the search panel circle. -->
- <dimen name="search_panel_circle_size">88dp</dimen>
+ <dimen name="assist_orb_size">144dp</dimen>
- <!-- The margin to the edge of the screen from where the circle starts to appear -->
- <dimen name="search_panel_circle_base_margin">80dp</dimen>
+ <!-- The margin to the edge of the screen from where the orb starts to appear -->
+ <dimen name="assist_orb_base_margin">22dp</dimen>
- <!-- The amount the circle translates when appearing -->
- <dimen name="search_panel_circle_travel_distance">80dp</dimen>
+ <!-- The amount the orb translates when appearing -->
+ <dimen name="assist_orb_travel_distance">26dp</dimen>
- <!-- The elevation of the search panel circle -->
- <dimen name="search_panel_circle_elevation">12dp</dimen>
+ <!-- The elevation of the orb -->
+ <dimen name="assist_orb_elevation">12dp</dimen>
- <!-- The height of the scrim behind the search panel circle. -->
- <dimen name="search_panel_scrim_height">250dp</dimen>
+ <!-- The height of the scrim behind the orb. -->
+ <dimen name="assist_orb_scrim_height">250dp</dimen>
- <!-- How far the user needs to drag up to invoke search. -->
- <dimen name="search_panel_threshold">100dp</dimen>
+ <!-- The height of the scrim behind the search panel circle. Should be navigation_bar_height
+ + 8dp. -->
+ <dimen name="assist_orb_navbar_scrim_height">56dp</dimen>
<!-- The width/height of the phone/camera/unlock icon view on keyguard. -->
<dimen name="keyguard_affordance_height">56dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 07fcb82..e491b1b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -246,12 +246,6 @@
<item name="android:layout_height">match_parent</item>
</style>
- <style name="SearchPanelScrim">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">@dimen/search_panel_scrim_height</item>
- <item name="android:layout_gravity">bottom</item>
- </style>
-
<style name="UserDetailView">
<item name="numColumns">3</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelCircleView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelCircleView.java
deleted file mode 100644
index f33e2b8..0000000
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelCircleView.java
+++ /dev/null
@@ -1,592 +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;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
-import java.util.ArrayList;
-
-public class SearchPanelCircleView extends FrameLayout {
-
- private final int mCircleMinSize;
- private final int mBaseMargin;
- private final int mStaticOffset;
- private final Paint mBackgroundPaint = new Paint();
- private final Paint mRipplePaint = new Paint();
- private final Rect mCircleRect = new Rect();
- private final Rect mStaticRect = new Rect();
- private final Interpolator mFastOutSlowInInterpolator;
- private final Interpolator mAppearInterpolator;
- private final Interpolator mDisappearInterpolator;
-
- private boolean mClipToOutline;
- private final int mMaxElevation;
- private boolean mAnimatingOut;
- private float mOutlineAlpha;
- private float mOffset;
- private float mCircleSize;
- private boolean mHorizontal;
- private boolean mCircleHidden;
- private ImageView mLogo;
- private boolean mDraggedFarEnough;
- private boolean mOffsetAnimatingIn;
- private float mCircleAnimationEndValue;
- private ArrayList<Ripple> mRipples = new ArrayList<Ripple>();
-
- private ValueAnimator mOffsetAnimator;
- private ValueAnimator mCircleAnimator;
- private ValueAnimator mFadeOutAnimator;
- private ValueAnimator.AnimatorUpdateListener mCircleUpdateListener
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- applyCircleSize((float) animation.getAnimatedValue());
- updateElevation();
- }
- };
- private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCircleAnimator = null;
- }
- };
- private ValueAnimator.AnimatorUpdateListener mOffsetUpdateListener
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setOffset((float) animation.getAnimatedValue());
- }
- };
-
-
- public SearchPanelCircleView(Context context) {
- this(context, null);
- }
-
- public SearchPanelCircleView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SearchPanelCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public SearchPanelCircleView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- if (mCircleSize > 0.0f) {
- outline.setOval(mCircleRect);
- } else {
- outline.setEmpty();
- }
- outline.setAlpha(mOutlineAlpha);
- }
- });
- setWillNotDraw(false);
- mCircleMinSize = context.getResources().getDimensionPixelSize(
- R.dimen.search_panel_circle_size);
- mBaseMargin = context.getResources().getDimensionPixelSize(
- R.dimen.search_panel_circle_base_margin);
- mStaticOffset = context.getResources().getDimensionPixelSize(
- R.dimen.search_panel_circle_travel_distance);
- mMaxElevation = context.getResources().getDimensionPixelSize(
- R.dimen.search_panel_circle_elevation);
- mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.linear_out_slow_in);
- mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_slow_in);
- mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_linear_in);
- mBackgroundPaint.setAntiAlias(true);
- mBackgroundPaint.setColor(context.getColor(R.color.search_panel_circle_color));
- mRipplePaint.setColor(context.getColor(R.color.search_panel_ripple_color));
- mRipplePaint.setAntiAlias(true);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- drawBackground(canvas);
- drawRipples(canvas);
- }
-
- private void drawRipples(Canvas canvas) {
- for (int i = 0; i < mRipples.size(); i++) {
- Ripple ripple = mRipples.get(i);
- ripple.draw(canvas);
- }
- }
-
- private void drawBackground(Canvas canvas) {
- canvas.drawCircle(mCircleRect.centerX(), mCircleRect.centerY(), mCircleSize / 2,
- mBackgroundPaint);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mLogo = (ImageView) findViewById(R.id.search_logo);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- mLogo.layout(0, 0, mLogo.getMeasuredWidth(), mLogo.getMeasuredHeight());
- if (changed) {
- updateCircleRect(mStaticRect, mStaticOffset, true);
- }
- }
-
- public void setCircleSize(float circleSize) {
- setCircleSize(circleSize, false, null, 0, null);
- }
-
- public void setCircleSize(float circleSize, boolean animated, final Runnable endRunnable,
- int startDelay, Interpolator interpolator) {
- boolean isAnimating = mCircleAnimator != null;
- boolean animationPending = isAnimating && !mCircleAnimator.isRunning();
- boolean animatingOut = isAnimating && mCircleAnimationEndValue == 0;
- if (animated || animationPending || animatingOut) {
- if (isAnimating) {
- if (circleSize == mCircleAnimationEndValue) {
- return;
- }
- mCircleAnimator.cancel();
- }
- mCircleAnimator = ValueAnimator.ofFloat(mCircleSize, circleSize);
- mCircleAnimator.addUpdateListener(mCircleUpdateListener);
- mCircleAnimator.addListener(mClearAnimatorListener);
- mCircleAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (endRunnable != null) {
- endRunnable.run();
- }
- }
- });
- Interpolator desiredInterpolator = interpolator != null ? interpolator
- : circleSize == 0 ? mDisappearInterpolator : mAppearInterpolator;
- mCircleAnimator.setInterpolator(desiredInterpolator);
- mCircleAnimator.setDuration(300);
- mCircleAnimator.setStartDelay(startDelay);
- mCircleAnimator.start();
- mCircleAnimationEndValue = circleSize;
- } else {
- if (isAnimating) {
- float diff = circleSize - mCircleAnimationEndValue;
- PropertyValuesHolder[] values = mCircleAnimator.getValues();
- values[0].setFloatValues(diff, circleSize);
- mCircleAnimator.setCurrentPlayTime(mCircleAnimator.getCurrentPlayTime());
- mCircleAnimationEndValue = circleSize;
- } else {
- applyCircleSize(circleSize);
- updateElevation();
- }
- }
- }
-
- private void applyCircleSize(float circleSize) {
- mCircleSize = circleSize;
- updateLayout();
- }
-
- private void updateElevation() {
- float t = (mStaticOffset - mOffset) / (float) mStaticOffset;
- t = 1.0f - Math.max(t, 0.0f);
- float offset = t * mMaxElevation;
- setElevation(offset);
- }
-
- /**
- * Sets the offset to the edge of the screen. By default this not not animated.
- *
- * @param offset The offset to apply.
- */
- public void setOffset(float offset) {
- setOffset(offset, false, 0, null, null);
- }
-
- /**
- * Sets the offset to the edge of the screen.
- *
- * @param offset The offset to apply.
- * @param animate Whether an animation should be performed.
- * @param startDelay The desired start delay if animated.
- * @param interpolator The desired interpolator if animated. If null,
- * a default interpolator will be taken designed for appearing or
- * disappearing.
- * @param endRunnable The end runnable which should be executed when the animation is finished.
- */
- private void setOffset(float offset, boolean animate, int startDelay,
- Interpolator interpolator, final Runnable endRunnable) {
- if (!animate) {
- mOffset = offset;
- updateLayout();
- if (endRunnable != null) {
- endRunnable.run();
- }
- } else {
- if (mOffsetAnimator != null) {
- mOffsetAnimator.removeAllListeners();
- mOffsetAnimator.cancel();
- }
- mOffsetAnimator = ValueAnimator.ofFloat(mOffset, offset);
- mOffsetAnimator.addUpdateListener(mOffsetUpdateListener);
- mOffsetAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOffsetAnimator = null;
- if (endRunnable != null) {
- endRunnable.run();
- }
- }
- });
- Interpolator desiredInterpolator = interpolator != null ?
- interpolator : offset == 0 ? mDisappearInterpolator : mAppearInterpolator;
- mOffsetAnimator.setInterpolator(desiredInterpolator);
- mOffsetAnimator.setStartDelay(startDelay);
- mOffsetAnimator.setDuration(300);
- mOffsetAnimator.start();
- mOffsetAnimatingIn = offset != 0;
- }
- }
-
- private void updateLayout() {
- updateCircleRect();
- updateLogo();
- invalidateOutline();
- invalidate();
- updateClipping();
- }
-
- private void updateClipping() {
- boolean clip = mCircleSize < mCircleMinSize || !mRipples.isEmpty();
- if (clip != mClipToOutline) {
- setClipToOutline(clip);
- mClipToOutline = clip;
- }
- }
-
- private void updateLogo() {
- boolean exitAnimationRunning = mFadeOutAnimator != null;
- Rect rect = exitAnimationRunning ? mCircleRect : mStaticRect;
- float translationX = (rect.left + rect.right) / 2.0f - mLogo.getWidth() / 2.0f;
- float translationY = (rect.top + rect.bottom) / 2.0f - mLogo.getHeight() / 2.0f;
- float t = (mStaticOffset - mOffset) / (float) mStaticOffset;
- if (!exitAnimationRunning) {
- if (mHorizontal) {
- translationX += t * mStaticOffset * 0.3f;
- } else {
- translationY += t * mStaticOffset * 0.3f;
- }
- float alpha = 1.0f-t;
- alpha = Math.max((alpha - 0.5f) * 2.0f, 0);
- mLogo.setAlpha(alpha);
- } else {
- translationY += (mOffset - mStaticOffset) / 2;
- }
- mLogo.setTranslationX(translationX);
- mLogo.setTranslationY(translationY);
- }
-
- private void updateCircleRect() {
- updateCircleRect(mCircleRect, mOffset, false);
- }
-
- private void updateCircleRect(Rect rect, float offset, boolean useStaticSize) {
- int left, top;
- float circleSize = useStaticSize ? mCircleMinSize : mCircleSize;
- if (mHorizontal) {
- left = (int) (getWidth() - circleSize / 2 - mBaseMargin - offset);
- top = (int) ((getHeight() - circleSize) / 2);
- } else {
- left = (int) (getWidth() - circleSize) / 2;
- top = (int) (getHeight() - circleSize / 2 - mBaseMargin - offset);
- }
- rect.set(left, top, (int) (left + circleSize), (int) (top + circleSize));
- }
-
- public void setHorizontal(boolean horizontal) {
- mHorizontal = horizontal;
- updateCircleRect(mStaticRect, mStaticOffset, true);
- updateLayout();
- }
-
- public void setDragDistance(float distance) {
- if (!mAnimatingOut && (!mCircleHidden || mDraggedFarEnough)) {
- float circleSize = mCircleMinSize + rubberband(distance);
- setCircleSize(circleSize);
- }
-
- }
-
- private float rubberband(float diff) {
- return (float) Math.pow(Math.abs(diff), 0.6f);
- }
-
- public void startAbortAnimation(Runnable endRunnable) {
- if (mAnimatingOut) {
- if (endRunnable != null) {
- endRunnable.run();
- }
- return;
- }
- setCircleSize(0, true, null, 0, null);
- setOffset(0, true, 0, null, endRunnable);
- mCircleHidden = true;
- }
-
- public void startEnterAnimation() {
- if (mAnimatingOut) {
- return;
- }
- applyCircleSize(0);
- setOffset(0);
- setCircleSize(mCircleMinSize, true, null, 50, null);
- setOffset(mStaticOffset, true, 50, null, null);
- mCircleHidden = false;
- }
-
-
- public void startExitAnimation(final Runnable endRunnable) {
- if (!mHorizontal) {
- float offset = getHeight() / 2.0f;
- setOffset(offset - mBaseMargin, true, 50, mFastOutSlowInInterpolator, null);
- float xMax = getWidth() / 2;
- float yMax = getHeight() / 2;
- float maxRadius = (float) Math.ceil(Math.hypot(xMax, yMax) * 2);
- setCircleSize(maxRadius, true, null, 50, mFastOutSlowInInterpolator);
- performExitFadeOutAnimation(50, 300, endRunnable);
- } else {
-
- // when in landscape, we don't wan't the animation as it interferes with the general
- // rotation animation to the homescreen.
- endRunnable.run();
- }
- }
-
- private void performExitFadeOutAnimation(int startDelay, int duration,
- final Runnable endRunnable) {
- mFadeOutAnimator = ValueAnimator.ofFloat(mBackgroundPaint.getAlpha() / 255.0f, 0.0f);
-
- // Linear since we are animating multiple values
- mFadeOutAnimator.setInterpolator(new LinearInterpolator());
- mFadeOutAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float animatedFraction = animation.getAnimatedFraction();
- float logoValue = animatedFraction > 0.5f ? 1.0f : animatedFraction / 0.5f;
- logoValue = PhoneStatusBar.ALPHA_OUT.getInterpolation(1.0f - logoValue);
- float backgroundValue = animatedFraction < 0.2f ? 0.0f :
- PhoneStatusBar.ALPHA_OUT.getInterpolation((animatedFraction - 0.2f) / 0.8f);
- backgroundValue = 1.0f - backgroundValue;
- mBackgroundPaint.setAlpha((int) (backgroundValue * 255));
- mOutlineAlpha = backgroundValue;
- mLogo.setAlpha(logoValue);
- invalidateOutline();
- invalidate();
- }
- });
- mFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (endRunnable != null) {
- endRunnable.run();
- }
- mLogo.setAlpha(1.0f);
- mBackgroundPaint.setAlpha(255);
- mOutlineAlpha = 1.0f;
- mFadeOutAnimator = null;
- }
- });
- mFadeOutAnimator.setStartDelay(startDelay);
- mFadeOutAnimator.setDuration(duration);
- mFadeOutAnimator.start();
- }
-
- public void setDraggedFarEnough(boolean farEnough) {
- if (farEnough != mDraggedFarEnough) {
- if (farEnough) {
- if (mCircleHidden) {
- startEnterAnimation();
- }
- if (mOffsetAnimator == null) {
- addRipple();
- } else {
- postDelayed(new Runnable() {
- @Override
- public void run() {
- addRipple();
- }
- }, 100);
- }
- } else {
- startAbortAnimation(null);
- }
- mDraggedFarEnough = farEnough;
- }
-
- }
-
- private void addRipple() {
- if (mRipples.size() > 1) {
- // we only want 2 ripples at the time
- return;
- }
- float xInterpolation, yInterpolation;
- if (mHorizontal) {
- xInterpolation = 0.75f;
- yInterpolation = 0.5f;
- } else {
- xInterpolation = 0.5f;
- yInterpolation = 0.75f;
- }
- float circleCenterX = mStaticRect.left * (1.0f - xInterpolation)
- + mStaticRect.right * xInterpolation;
- float circleCenterY = mStaticRect.top * (1.0f - yInterpolation)
- + mStaticRect.bottom * yInterpolation;
- float radius = Math.max(mCircleSize, mCircleMinSize * 1.25f) * 0.75f;
- Ripple ripple = new Ripple(circleCenterX, circleCenterY, radius);
- ripple.start();
- }
-
- public void reset() {
- mDraggedFarEnough = false;
- mAnimatingOut = false;
- mCircleHidden = true;
- mClipToOutline = false;
- if (mFadeOutAnimator != null) {
- mFadeOutAnimator.cancel();
- }
- mBackgroundPaint.setAlpha(255);
- mOutlineAlpha = 1.0f;
- }
-
- /**
- * Check if an animation is currently running
- *
- * @param enterAnimation Is the animating queried the enter animation.
- */
- public boolean isAnimationRunning(boolean enterAnimation) {
- return mOffsetAnimator != null && (enterAnimation == mOffsetAnimatingIn);
- }
-
- public void performOnAnimationFinished(final Runnable runnable) {
- if (mOffsetAnimator != null) {
- mOffsetAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (runnable != null) {
- runnable.run();
- }
- }
- });
- } else {
- if (runnable != null) {
- runnable.run();
- }
- }
- }
-
- public void setAnimatingOut(boolean animatingOut) {
- mAnimatingOut = animatingOut;
- }
-
- /**
- * @return Whether the circle is currently launching to the search activity or aborting the
- * interaction
- */
- public boolean isAnimatingOut() {
- return mAnimatingOut;
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- // not really true but it's ok during an animation, as it's never permanent
- return false;
- }
-
- private class Ripple {
- float x;
- float y;
- float radius;
- float endRadius;
- float alpha;
-
- Ripple(float x, float y, float endRadius) {
- this.x = x;
- this.y = y;
- this.endRadius = endRadius;
- }
-
- void start() {
- ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
-
- // Linear since we are animating multiple values
- animator.setInterpolator(new LinearInterpolator());
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- alpha = 1.0f - animation.getAnimatedFraction();
- alpha = mDisappearInterpolator.getInterpolation(alpha);
- radius = mAppearInterpolator.getInterpolation(animation.getAnimatedFraction());
- radius *= endRadius;
- invalidate();
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRipples.remove(Ripple.this);
- updateClipping();
- }
-
- public void onAnimationStart(Animator animation) {
- mRipples.add(Ripple.this);
- updateClipping();
- }
- });
- animator.setDuration(400);
- animator.start();
- }
-
- public void draw(Canvas canvas) {
- mRipplePaint.setAlpha((int) (alpha * 255));
- canvas.drawCircle(x, y, radius, mRipplePaint);
- }
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
deleted file mode 100644
index c0689a4..0000000
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-import android.app.ActivityOptions;
-import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.media.AudioAttributes;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Slog;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.internal.app.IVoiceInteractionManagerService;
-import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.StatusBarPanel;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
-public class SearchPanelView extends FrameLayout implements StatusBarPanel {
-
- private static final String TAG = "SearchPanelView";
- private static final String ASSIST_ICON_METADATA_NAME =
- "com.android.systemui.action_assist_icon";
-
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
- private final Context mContext;
- private BaseStatusBar mBar;
-
- private SearchPanelCircleView mCircle;
- private ImageView mLogo;
- private View mScrim;
-
- private int mThreshold;
- private boolean mHorizontal;
-
- private boolean mLaunching;
- private boolean mDragging;
- private boolean mDraggedFarEnough;
- private float mStartTouch;
- private float mStartDrag;
- private boolean mLaunchPending;
-
- private IVoiceInteractionManagerService mVoiceInteractionManagerService;
-
- public SearchPanelView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SearchPanelView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mContext = context;
- mThreshold = context.getResources().getDimensionPixelSize(R.dimen.search_panel_threshold);
- }
-
- private void startAssist() {
- if (isVoiceInteractorActive()) {
- startVoiceInteractor();
- } else {
- startAssistActivity();
- }
- }
-
- private void startAssistActivity() {
- if (!mBar.isDeviceProvisioned()) return;
-
- // Close Recent Apps if needed
- mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
-
- final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
- if (intent == null) return;
-
- try {
- final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.search_launch_enter, R.anim.search_launch_exit);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- mContext.startActivityAsUser(intent, opts.toBundle(),
- new UserHandle(UserHandle.USER_CURRENT));
- }
- });
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "Activity not found for " + intent.getAction());
- }
- }
-
- private void startVoiceInteractor() {
- try {
- // TODO: Pass show callback
- mVoiceInteractionManagerService.showSessionForActiveService(null /* showCallback */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to call showSessionForActiveService", e);
- }
- }
-
- private boolean isVoiceInteractorActive() {
- try {
- return mVoiceInteractionManagerService.activeServiceSupportsAssistGesture();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to call isServiceActive", e);
- return false;
- }
- }
-
- private ComponentName getVoiceInteractorComponentName() {
- try {
- return mVoiceInteractionManagerService.getActiveServiceComponentName();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to call getActiveServiceComponentName", e);
- return null;
- }
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mCircle = (SearchPanelCircleView) findViewById(R.id.search_panel_circle);
- mLogo = (ImageView) findViewById(R.id.search_logo);
- mScrim = findViewById(R.id.search_panel_scrim);
- mVoiceInteractionManagerService = (IVoiceInteractionManagerService)
- IVoiceInteractionManagerService.Stub.asInterface(
- ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
- }
-
- private void maybeSwapSearchIcon() {
- Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
- ComponentName component = null;
- boolean isService = false;
- if (isVoiceInteractorActive()) {
- component = getVoiceInteractorComponentName();
- isService = true;
- } else if (intent != null) {
- component = intent.getComponent();
- }
- if (component != null) {
- replaceDrawable(mLogo, component, ASSIST_ICON_METADATA_NAME,
- isService);
- } else {
- mLogo.setImageDrawable(null);
- }
- }
-
- public void replaceDrawable(ImageView v, ComponentName component, String name,
- boolean isService) {
- if (component != null) {
- try {
- PackageManager packageManager = mContext.getPackageManager();
- // Look for the search icon specified in the activity meta-data
- Bundle metaData = isService
- ? packageManager.getServiceInfo(
- component, PackageManager.GET_META_DATA).metaData
- : packageManager.getActivityInfo(
- component, PackageManager.GET_META_DATA).metaData;
- if (metaData != null) {
- int iconResId = metaData.getInt(name);
- if (iconResId != 0) {
- Resources res = packageManager.getResourcesForApplication(
- component.getPackageName());
- v.setImageDrawable(res.getDrawable(iconResId));
- return;
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to swap drawable; "
- + component.flattenToShortString() + " not found", e);
- } catch (Resources.NotFoundException nfe) {
- Log.w(TAG, "Failed to swap drawable from "
- + component.flattenToShortString(), nfe);
- }
- }
- v.setImageDrawable(null);
- }
-
- @Override
- public boolean isInContentArea(int x, int y) {
- return true;
- }
-
- private void vibrate() {
- Context context = getContext();
- if (Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
- Resources res = context.getResources();
- Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration),
- VIBRATION_ATTRIBUTES);
- }
- }
-
- public void show(final boolean show, boolean animate) {
- if (show) {
- maybeSwapSearchIcon();
- if (getVisibility() != View.VISIBLE) {
- setVisibility(View.VISIBLE);
- vibrate();
- if (animate) {
- startEnterAnimation();
- } else {
- mScrim.setAlpha(1f);
- }
- }
- setFocusable(true);
- setFocusableInTouchMode(true);
- requestFocus();
- } else {
- if (animate) {
- startAbortAnimation();
- } else {
- setVisibility(View.INVISIBLE);
- }
- }
- }
-
- private void startEnterAnimation() {
- mCircle.startEnterAnimation();
- mScrim.setAlpha(0f);
- mScrim.animate()
- .alpha(1f)
- .setDuration(300)
- .setStartDelay(50)
- .setInterpolator(PhoneStatusBar.ALPHA_IN)
- .start();
-
- }
-
- private void startAbortAnimation() {
- mCircle.startAbortAnimation(new Runnable() {
- @Override
- public void run() {
- mCircle.setAnimatingOut(false);
- setVisibility(View.INVISIBLE);
- }
- });
- mCircle.setAnimatingOut(true);
- mScrim.animate()
- .alpha(0f)
- .setDuration(300)
- .setStartDelay(0)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT);
- }
-
- public void hide(boolean animate) {
- if (mBar != null) {
- // This will indirectly cause show(false, ...) to get called
- mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
- } else {
- if (animate) {
- startAbortAnimation();
- } else {
- setVisibility(View.INVISIBLE);
- }
- }
- }
-
- @Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- // Ignore hover events outside of this panel bounds since such events
- // generate spurious accessibility events with the panel content when
- // tapping outside of it, thus confusing the user.
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) {
- return super.dispatchHoverEvent(event);
- }
- return true;
- }
-
- /**
- * Whether the panel is showing, or, if it's animating, whether it will be
- * when the animation is done.
- */
- public boolean isShowing() {
- return getVisibility() == View.VISIBLE && !mCircle.isAnimatingOut();
- }
-
- public void setBar(BaseStatusBar bar) {
- mBar = bar;
- }
-
- public boolean isAssistantAvailable() {
- return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mLaunching || mLaunchPending) {
- return false;
- }
- int action = event.getActionMasked();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mStartTouch = mHorizontal ? event.getX() : event.getY();
- mDragging = false;
- mDraggedFarEnough = false;
- mCircle.reset();
- break;
- case MotionEvent.ACTION_MOVE:
- float currentTouch = mHorizontal ? event.getX() : event.getY();
- if (getVisibility() == View.VISIBLE && !mDragging &&
- (!mCircle.isAnimationRunning(true /* enterAnimation */)
- || Math.abs(mStartTouch - currentTouch) > mThreshold)) {
- mStartDrag = currentTouch;
- mDragging = true;
- }
- if (mDragging) {
- float offset = Math.max(mStartDrag - currentTouch, 0.0f);
- mCircle.setDragDistance(offset);
- mDraggedFarEnough = Math.abs(mStartTouch - currentTouch) > mThreshold;
- mCircle.setDraggedFarEnough(mDraggedFarEnough);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (mDraggedFarEnough) {
- if (mCircle.isAnimationRunning(true /* enterAnimation */)) {
- mLaunchPending = true;
- mCircle.setAnimatingOut(true);
- mCircle.performOnAnimationFinished(new Runnable() {
- @Override
- public void run() {
- startExitAnimation();
- }
- });
- } else {
- startExitAnimation();
- }
- } else {
- startAbortAnimation();
- }
- break;
- }
- return true;
- }
-
- private void startExitAnimation() {
- mLaunchPending = false;
- if (mLaunching || getVisibility() != View.VISIBLE) {
- return;
- }
- mLaunching = true;
- startAssist();
- vibrate();
- mCircle.setAnimatingOut(true);
- mCircle.startExitAnimation(new Runnable() {
- @Override
- public void run() {
- mLaunching = false;
- mCircle.setAnimatingOut(false);
- setVisibility(View.INVISIBLE);
- }
- });
- mScrim.animate()
- .alpha(0f)
- .setDuration(300)
- .setStartDelay(0)
- .setInterpolator(PhoneStatusBar.ALPHA_OUT);
- }
-
- public void setHorizontal(boolean horizontal) {
- mHorizontal = horizontal;
- mCircle.setHorizontal(horizontal);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java
new file mode 100644
index 0000000..36be355
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java
@@ -0,0 +1,292 @@
+package com.android.systemui.assist;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.media.AudioAttributes;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+/**
+ * Class to manage everything around the assist gesture.
+ */
+public class AssistGestureManager {
+
+ private static final String TAG = "AssistGestureManager";
+ private static final String ASSIST_ICON_METADATA_NAME =
+ "com.android.systemui.action_assist_icon";
+
+ private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .build();
+
+ private static final long TIMEOUT_SERVICE = 2500;
+ private static final long TIMEOUT_ACTIVITY = 1000;
+
+ private final Context mContext;
+ private final WindowManager mWindowManager;
+ private AssistOrbContainer mView;
+ private final PhoneStatusBar mBar;
+ private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
+
+ private IVoiceInteractionSessionShowCallback mShowCallback =
+ new IVoiceInteractionSessionShowCallback.Stub() {
+
+ @Override
+ public void onFailed() throws RemoteException {
+ mView.post(mHideRunnable);
+ }
+
+ @Override
+ public void onShown() throws RemoteException {
+ mView.post(mHideRunnable);
+ }
+ };
+
+ private Runnable mHideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mView.removeCallbacks(this);
+ mView.show(false /* show */, true /* animate */);
+ }
+ };
+
+ public AssistGestureManager(PhoneStatusBar bar, Context context) {
+ mContext = context;
+ mBar = bar;
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ }
+
+ public void onConfigurationChanged() {
+ boolean visible = false;
+ if (mView != null) {
+ visible = mView.isShowing();
+ mWindowManager.removeView(mView);
+ }
+
+ mView = (AssistOrbContainer) LayoutInflater.from(mContext).inflate(
+ R.layout.assist_orb, null);
+ mView.setVisibility(View.GONE);
+ mView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ WindowManager.LayoutParams lp = getLayoutParams();
+ mWindowManager.addView(mView, lp);
+ mBar.getNavigationBarView().setDelegateView(mView);
+ if (visible) {
+ mView.show(true /* show */, false /* animate */);
+ }
+ }
+
+ public void onGestureInvoked(boolean vibrate) {
+ boolean isVoiceInteractorActive = getVoiceInteractorSupportsAssistGesture();
+ if (!isVoiceInteractorActive && !isAssistantIntentAvailable()) {
+ return;
+ }
+ if (vibrate) {
+ vibrate();
+ }
+ if (!isVoiceInteractorActive || !isVoiceSessionRunning()) {
+ showOrb();
+ mView.postDelayed(mHideRunnable, isVoiceInteractorActive
+ ? TIMEOUT_SERVICE
+ : TIMEOUT_ACTIVITY);
+ }
+ startAssist();
+ }
+
+ private WindowManager.LayoutParams getLayoutParams() {
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ mContext.getResources().getDimensionPixelSize(R.dimen.assist_orb_scrim_height),
+ WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ if (ActivityManager.isHighEndGfx()) {
+ lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ }
+ lp.gravity = Gravity.BOTTOM | Gravity.START;
+ lp.setTitle("AssistPreviewPanel");
+ lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+ return lp;
+ }
+
+ private void showOrb() {
+ maybeSwapSearchIcon();
+ mView.show(true /* show */, true /* animate */);
+ }
+
+ private void startAssist() {
+ if (getVoiceInteractorSupportsAssistGesture()) {
+ startVoiceInteractor();
+ } else {
+ startAssistActivity();
+ }
+ }
+
+ private void startAssistActivity() {
+ if (!mBar.isDeviceProvisioned()) {
+ return;
+ }
+
+ // Close Recent Apps if needed
+ mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL |
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL);
+
+ final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
+ if (intent == null) {
+ return;
+ }
+
+ try {
+ final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.search_launch_enter, R.anim.search_launch_exit);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ mContext.startActivityAsUser(intent, opts.toBundle(),
+ new UserHandle(UserHandle.USER_CURRENT));
+ }
+ });
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity not found for " + intent.getAction());
+ }
+ }
+
+ private void startVoiceInteractor() {
+ try {
+ mVoiceInteractionManagerService.showSessionForActiveService(mShowCallback);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call showSessionForActiveService", e);
+ }
+ }
+
+ private boolean getVoiceInteractorSupportsAssistGesture() {
+ try {
+ return mVoiceInteractionManagerService.activeServiceSupportsAssistGesture();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call activeServiceSupportsAssistGesture", e);
+ return false;
+ }
+ }
+
+ private ComponentName getVoiceInteractorComponentName() {
+ try {
+ return mVoiceInteractionManagerService.getActiveServiceComponentName();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call getActiveServiceComponentName", e);
+ return null;
+ }
+ }
+
+ private boolean isVoiceSessionRunning() {
+ try {
+ return mVoiceInteractionManagerService.isSessionRunning();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to call isSessionRunning", e);
+ return false;
+ }
+ }
+
+ public void destroy() {
+ mWindowManager.removeViewImmediate(mView);
+ }
+
+ private void maybeSwapSearchIcon() {
+ Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, false, UserHandle.USER_CURRENT);
+ ComponentName component = null;
+ boolean isService = false;
+ if (getVoiceInteractorSupportsAssistGesture()) {
+ component = getVoiceInteractorComponentName();
+ isService = true;
+ } else if (intent != null) {
+ component = intent.getComponent();
+ }
+ if (component != null) {
+ replaceDrawable(mView.getOrb().getLogo(), component, ASSIST_ICON_METADATA_NAME,
+ isService);
+ } else {
+ mView.getOrb().getLogo().setImageDrawable(null);
+ }
+ }
+
+ public void replaceDrawable(ImageView v, ComponentName component, String name,
+ boolean isService) {
+ if (component != null) {
+ try {
+ PackageManager packageManager = mContext.getPackageManager();
+ // Look for the search icon specified in the activity meta-data
+ Bundle metaData = isService
+ ? packageManager.getServiceInfo(
+ component, PackageManager.GET_META_DATA).metaData
+ : packageManager.getActivityInfo(
+ component, PackageManager.GET_META_DATA).metaData;
+ if (metaData != null) {
+ int iconResId = metaData.getInt(name);
+ if (iconResId != 0) {
+ Resources res = packageManager.getResourcesForApplication(
+ component.getPackageName());
+ v.setImageDrawable(res.getDrawable(iconResId));
+ return;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to swap drawable; "
+ + component.flattenToShortString() + " not found", e);
+ } catch (Resources.NotFoundException nfe) {
+ Log.w(TAG, "Failed to swap drawable from "
+ + component.flattenToShortString(), nfe);
+ }
+ }
+ v.setImageDrawable(null);
+ }
+
+ private void vibrate() {
+ if (Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
+ Resources res = mContext.getResources();
+ Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration),
+ VIBRATION_ATTRIBUTES);
+ }
+ }
+
+ public boolean isAssistantIntentAvailable() {
+ return ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
new file mode 100644
index 0000000..67017db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 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.assist;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+public class AssistOrbContainer extends FrameLayout {
+
+ private static final long EXIT_START_DELAY = 150;
+
+ private final Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mFastOutLinearInInterpolator;
+
+ private View mScrim;
+ private View mNavbarScrim;
+ private AssistOrbView mOrb;
+
+ private boolean mAnimatingOut;
+
+ public AssistOrbContainer(Context context) {
+ this(context, null);
+ }
+
+ public AssistOrbContainer(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AssistOrbContainer(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ android.R.interpolator.linear_out_slow_in);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ android.R.interpolator.fast_out_slow_in);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mScrim = findViewById(R.id.assist_orb_scrim);
+ mNavbarScrim = findViewById(R.id.assist_orb_navbar_scrim);
+ mOrb = (AssistOrbView) findViewById(R.id.assist_orb);
+ }
+
+ public void show(final boolean show, boolean animate) {
+ if (show) {
+ if (getVisibility() != View.VISIBLE) {
+ setVisibility(View.VISIBLE);
+ if (animate) {
+ startEnterAnimation();
+ } else {
+ reset();
+ }
+ }
+ } else {
+ if (animate) {
+ startExitAnimation(new Runnable() {
+ @Override
+ public void run() {
+ mAnimatingOut = false;
+ setVisibility(View.GONE);
+ }
+ });
+ } else {
+ setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private void reset() {
+ mAnimatingOut = false;
+ mOrb.reset();
+ mScrim.setAlpha(1f);
+ mNavbarScrim.setAlpha(1f);
+ }
+
+ private void startEnterAnimation() {
+ if (mAnimatingOut) {
+ return;
+ }
+ mOrb.startEnterAnimation();
+ mScrim.setAlpha(0f);
+ mNavbarScrim.setAlpha(0f);
+ post(new Runnable() {
+ @Override
+ public void run() {
+ mScrim.animate()
+ .alpha(1f)
+ .setDuration(300)
+ .setStartDelay(0)
+ .setInterpolator(mLinearOutSlowInInterpolator);
+ mNavbarScrim.animate()
+ .alpha(1f)
+ .setDuration(300)
+ .setStartDelay(0)
+ .setInterpolator(mLinearOutSlowInInterpolator);
+ }
+ });
+ }
+
+ private void startExitAnimation(final Runnable endRunnable) {
+ if (mAnimatingOut) {
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
+ return;
+ }
+ mAnimatingOut = true;
+ mOrb.startExitAnimation(EXIT_START_DELAY);
+ mScrim.animate()
+ .alpha(0f)
+ .setDuration(250)
+ .setStartDelay(EXIT_START_DELAY)
+ .setInterpolator(mFastOutLinearInInterpolator);
+ mNavbarScrim.animate()
+ .alpha(0f)
+ .setDuration(250)
+ .setStartDelay(EXIT_START_DELAY)
+ .setInterpolator(mFastOutLinearInInterpolator)
+ .withEndAction(endRunnable);
+ }
+
+ /**
+ * Whether the panel is showing, or, if it's animating, whether it will be
+ * when the animation is done.
+ */
+ public boolean isShowing() {
+ return getVisibility() == View.VISIBLE && !mAnimatingOut;
+ }
+
+ public AssistOrbView getOrb() {
+ return mOrb;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
new file mode 100644
index 0000000..a3372a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
@@ -0,0 +1,285 @@
+/*
+ * 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.assist;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.OvershootInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class AssistOrbView extends FrameLayout {
+
+ private final int mCircleMinSize;
+ private final int mBaseMargin;
+ private final int mStaticOffset;
+ private final Paint mBackgroundPaint = new Paint();
+ private final Rect mCircleRect = new Rect();
+ private final Rect mStaticRect = new Rect();
+ private final Interpolator mAppearInterpolator;
+ private final Interpolator mDisappearInterpolator;
+ private final Interpolator mOvershootInterpolator = new OvershootInterpolator();
+
+ private boolean mClipToOutline;
+ private final int mMaxElevation;
+ private float mOutlineAlpha;
+ private float mOffset;
+ private float mCircleSize;
+ private ImageView mLogo;
+ private float mCircleAnimationEndValue;
+
+ private ValueAnimator mOffsetAnimator;
+ private ValueAnimator mCircleAnimator;
+
+ private ValueAnimator.AnimatorUpdateListener mCircleUpdateListener
+ = new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ applyCircleSize((float) animation.getAnimatedValue());
+ updateElevation();
+ }
+ };
+ private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCircleAnimator = null;
+ }
+ };
+ private ValueAnimator.AnimatorUpdateListener mOffsetUpdateListener
+ = new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mOffset = (float) animation.getAnimatedValue();
+ updateLayout();
+ }
+ };
+
+
+ public AssistOrbView(Context context) {
+ this(context, null);
+ }
+
+ public AssistOrbView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AssistOrbView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public AssistOrbView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ if (mCircleSize > 0.0f) {
+ outline.setOval(mCircleRect);
+ } else {
+ outline.setEmpty();
+ }
+ outline.setAlpha(mOutlineAlpha);
+ }
+ });
+ setWillNotDraw(false);
+ mCircleMinSize = context.getResources().getDimensionPixelSize(
+ R.dimen.assist_orb_size);
+ mBaseMargin = context.getResources().getDimensionPixelSize(
+ R.dimen.assist_orb_base_margin);
+ mStaticOffset = context.getResources().getDimensionPixelSize(
+ R.dimen.assist_orb_travel_distance);
+ mMaxElevation = context.getResources().getDimensionPixelSize(
+ R.dimen.assist_orb_elevation);
+ mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.linear_out_slow_in);
+ mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_linear_in);
+ mBackgroundPaint.setAntiAlias(true);
+ mBackgroundPaint.setColor(getResources().getColor(R.color.assist_orb_color));
+ }
+
+ public ImageView getLogo() {
+ return mLogo;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ drawBackground(canvas);
+ }
+
+ private void drawBackground(Canvas canvas) {
+ canvas.drawCircle(mCircleRect.centerX(), mCircleRect.centerY(), mCircleSize / 2,
+ mBackgroundPaint);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mLogo = (ImageView) findViewById(R.id.search_logo);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mLogo.layout(0, 0, mLogo.getMeasuredWidth(), mLogo.getMeasuredHeight());
+ if (changed) {
+ updateCircleRect(mStaticRect, mStaticOffset, true);
+ }
+ }
+
+ public void animateCircleSize(float circleSize, long duration,
+ long startDelay, Interpolator interpolator) {
+ if (circleSize == mCircleAnimationEndValue) {
+ return;
+ }
+ if (mCircleAnimator != null) {
+ mCircleAnimator.cancel();
+ }
+ mCircleAnimator = ValueAnimator.ofFloat(mCircleSize, circleSize);
+ mCircleAnimator.addUpdateListener(mCircleUpdateListener);
+ mCircleAnimator.addListener(mClearAnimatorListener);
+ mCircleAnimator.setInterpolator(interpolator);
+ mCircleAnimator.setDuration(duration);
+ mCircleAnimator.setStartDelay(startDelay);
+ mCircleAnimator.start();
+ mCircleAnimationEndValue = circleSize;
+ }
+
+ private void applyCircleSize(float circleSize) {
+ mCircleSize = circleSize;
+ updateLayout();
+ }
+
+ private void updateElevation() {
+ float t = (mStaticOffset - mOffset) / (float) mStaticOffset;
+ t = 1.0f - Math.max(t, 0.0f);
+ float offset = t * mMaxElevation;
+ setElevation(offset);
+ }
+
+ /**
+ * Animates the offset to the edge of the screen.
+ *
+ * @param offset The offset to apply.
+ * @param startDelay The desired start delay if animated.
+ *
+ * @param interpolator The desired interpolator if animated. If null,
+ * a default interpolator will be taken designed for appearing or
+ * disappearing.
+ */
+ private void animateOffset(float offset, long duration, long startDelay,
+ Interpolator interpolator) {
+ if (mOffsetAnimator != null) {
+ mOffsetAnimator.removeAllListeners();
+ mOffsetAnimator.cancel();
+ }
+ mOffsetAnimator = ValueAnimator.ofFloat(mOffset, offset);
+ mOffsetAnimator.addUpdateListener(mOffsetUpdateListener);
+ mOffsetAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOffsetAnimator = null;
+ }
+ });
+ mOffsetAnimator.setInterpolator(interpolator);
+ mOffsetAnimator.setStartDelay(startDelay);
+ mOffsetAnimator.setDuration(duration);
+ mOffsetAnimator.start();
+ }
+
+ private void updateLayout() {
+ updateCircleRect();
+ updateLogo();
+ invalidateOutline();
+ invalidate();
+ updateClipping();
+ }
+
+ private void updateClipping() {
+ boolean clip = mCircleSize < mCircleMinSize;
+ if (clip != mClipToOutline) {
+ setClipToOutline(clip);
+ mClipToOutline = clip;
+ }
+ }
+
+ private void updateLogo() {
+ float translationX = (mCircleRect.left + mCircleRect.right) / 2.0f - mLogo.getWidth() / 2.0f;
+ float translationY = (mCircleRect.top + mCircleRect.bottom) / 2.0f
+ - mLogo.getHeight() / 2.0f - mCircleMinSize / 7f;
+ float t = (mStaticOffset - mOffset) / (float) mStaticOffset;
+ translationY += t * mStaticOffset * 0.1f;
+ float alpha = 1.0f-t;
+ alpha = Math.max((alpha - 0.5f) * 2.0f, 0);
+ mLogo.setImageAlpha((int) (alpha * 255));
+ mLogo.setTranslationX(translationX);
+ mLogo.setTranslationY(translationY);
+ }
+
+ private void updateCircleRect() {
+ updateCircleRect(mCircleRect, mOffset, false);
+ }
+
+ private void updateCircleRect(Rect rect, float offset, boolean useStaticSize) {
+ int left, top;
+ float circleSize = useStaticSize ? mCircleMinSize : mCircleSize;
+ left = (int) (getWidth() - circleSize) / 2;
+ top = (int) (getHeight() - circleSize / 2 - mBaseMargin - offset);
+ rect.set(left, top, (int) (left + circleSize), (int) (top + circleSize));
+ }
+
+ public void startExitAnimation(long delay) {
+ animateCircleSize(0, 200, delay, mDisappearInterpolator);
+ animateOffset(0, 200, delay, mDisappearInterpolator);
+ }
+
+ public void startEnterAnimation() {
+ applyCircleSize(0);
+ post(new Runnable() {
+ @Override
+ public void run() {
+ animateCircleSize(mCircleMinSize, 300, 0 /* delay */, mOvershootInterpolator);
+ animateOffset(mStaticOffset, 400, 0 /* delay */, mAppearInterpolator);
+ }
+ });
+ }
+
+ public void reset() {
+ mClipToOutline = false;
+ mBackgroundPaint.setAlpha(255);
+ mOutlineAlpha = 1.0f;
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ // not really true but it's ok during an animation, as it's never permanent
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 55bdcac..f75dd73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -71,7 +71,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -79,7 +78,6 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
import android.widget.DateTimeView;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
@@ -90,7 +88,6 @@ import com.android.internal.util.NotificationColorUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
-import com.android.systemui.SearchPanelView;
import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.Recents;
@@ -132,7 +129,6 @@ public abstract class BaseStatusBar extends SystemUI implements
protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
- protected static final int MSG_CLOSE_SEARCH_PANEL = 1027;
protected static final int MSG_SHOW_HEADS_UP = 1028;
protected static final int MSG_HIDE_HEADS_UP = 1029;
protected static final int MSG_ESCALATE_HEADS_UP = 1030;
@@ -164,9 +160,6 @@ public abstract class BaseStatusBar extends SystemUI implements
protected HeadsUpNotificationView mHeadsUpNotificationView;
protected int mHeadsUpNotificationDecay;
- // Search panel
- protected SearchPanelView mSearchPanelView;
-
protected int mCurrentUserId = 0;
final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
@@ -1043,50 +1036,6 @@ public abstract class BaseStatusBar extends SystemUI implements
mHandler.sendEmptyMessage(msg);
}
- @Override
- public void showSearchPanel() {
- if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
- mSearchPanelView.show(true, true);
- }
- }
-
- @Override
- public void hideSearchPanel() {
- int msg = MSG_CLOSE_SEARCH_PANEL;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- protected abstract WindowManager.LayoutParams getSearchLayoutParams(
- LayoutParams layoutParams);
-
- protected void updateSearchPanel() {
- // Search Panel
- boolean visible = false;
- if (mSearchPanelView != null) {
- visible = mSearchPanelView.isShowing();
- mWindowManager.removeView(mSearchPanelView);
- }
-
- // Provide SearchPanel with a temporary parent to allow layout params to work.
- LinearLayout tmpRoot = new LinearLayout(mContext);
- mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_search_panel, tmpRoot, false);
- mSearchPanelView.setOnTouchListener(
- new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
- mSearchPanelView.setVisibility(View.GONE);
- boolean vertical = mNavigationBarView != null && mNavigationBarView.isVertical();
- mSearchPanelView.setHorizontal(vertical);
-
- WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
-
- mWindowManager.addView(mSearchPanelView, lp);
- mSearchPanelView.setBar(this);
- if (visible) {
- mSearchPanelView.show(true, false);
- }
- }
-
protected H createHandler() {
return new H();
}
@@ -1263,35 +1212,7 @@ public abstract class BaseStatusBar extends SystemUI implements
case MSG_SHOW_PREV_AFFILIATED_TASK:
showRecentsPreviousAffiliatedTask();
break;
- case MSG_CLOSE_SEARCH_PANEL:
- if (DEBUG) Log.d(TAG, "closing search panel");
- if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
- mSearchPanelView.show(false, true);
- }
- break;
- }
- }
- }
-
- public class TouchOutsideListener implements View.OnTouchListener {
- private int mMsg;
- private StatusBarPanel mPanel;
-
- public TouchOutsideListener(int msg, StatusBarPanel panel) {
- mMsg = msg;
- mPanel = panel;
- }
-
- public boolean onTouch(View v, MotionEvent ev) {
- final int action = ev.getAction();
- if (action == MotionEvent.ACTION_OUTSIDE
- || (action == MotionEvent.ACTION_DOWN
- && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
- mHandler.removeMessages(mMsg);
- mHandler.sendEmptyMessage(mMsg);
- return true;
}
- return false;
}
}
@@ -1935,7 +1856,7 @@ public abstract class BaseStatusBar extends SystemUI implements
protected abstract void setAreThereNotifications();
protected abstract void updateNotifications();
- protected abstract boolean shouldDisableNavbarGestures();
+ public abstract boolean shouldDisableNavbarGestures();
public abstract void addNotification(StatusBarNotification notification,
RankingMap ranking, Entry oldEntry);
@@ -2241,9 +2162,6 @@ public abstract class BaseStatusBar extends SystemUI implements
}
public void destroy() {
- if (mSearchPanelView != null) {
- mWindowManager.removeViewImmediate(mSearchPanelView);
- }
mContext.unregisterReceiver(mBroadcastReceiver);
try {
mNotificationListener.unregisterAsSystemService();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8f88e73..7aa9a90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -96,8 +96,6 @@ public class CommandQueue extends IStatusBar.Stub {
public void toggleRecentApps();
public void preloadRecentApps();
public void cancelPreloadRecentApps();
- public void showSearchPanel();
- public void hideSearchPanel();
public void setWindowState(int window, int state);
public void buzzBeepBlinked();
public void notificationLightOff();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 7ae6764..9e2207e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -22,11 +22,12 @@ import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.View;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
public class DelegateViewHelper {
private View mDelegateView;
private View mSourceView;
- private BaseStatusBar mBar;
+ private PhoneStatusBar mBar;
private int[] mTempPoint = new int[2];
private float[] mDownPoint = new float[2];
private float mTriggerThreshhold;
@@ -45,7 +46,7 @@ public class DelegateViewHelper {
mDelegateView = view;
}
- public void setBar(BaseStatusBar phoneStatusBar) {
+ public void setBar(PhoneStatusBar phoneStatusBar) {
mBar = phoneStatusBar;
}
@@ -79,7 +80,7 @@ public class DelegateViewHelper {
float y = k < historySize ? event.getHistoricalY(k) : event.getY();
final float distance = mSwapXY ? (mDownPoint[0] - x) : (mDownPoint[1] - y);
if (distance > mTriggerThreshhold) {
- mBar.showSearchPanel();
+ mBar.invokeAssistGesture(false /* vibrate */);
mPanelShowing = true;
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPanel.java
deleted file mode 100644
index 272c321..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPanel.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-public interface StatusBarPanel {
- public boolean isInContentArea(int x, int y);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 12ff399..c62ad66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -50,6 +50,8 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DelegateViewHelper;
import com.android.systemui.statusbar.policy.DeadZone;
+import com.android.systemui.statusbar.policy.KeyButtonView;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -195,7 +197,7 @@ public class NavigationBarView extends LinearLayout {
mDelegateHelper.setDelegateView(view);
}
- public void setBar(BaseStatusBar phoneStatusBar) {
+ public void setBar(PhoneStatusBar phoneStatusBar) {
mTaskSwitchHelper.setBar(phoneStatusBar);
mDelegateHelper.setBar(phoneStatusBar);
}
@@ -261,8 +263,8 @@ public class NavigationBarView extends LinearLayout {
return mCurrentView.findViewById(R.id.back);
}
- public View getHomeButton() {
- return mCurrentView.findViewById(R.id.home);
+ public KeyButtonView getHomeButton() {
+ return (KeyButtonView) mCurrentView.findViewById(R.id.home);
}
public View getImeSwitchButton() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 2c389fb..5deea77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -121,6 +121,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.assist.AssistGestureManager;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
@@ -323,6 +324,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private int mNavigationIconHints = 0;
private HandlerThread mHandlerThread;
+ private AssistGestureManager mAssistGestureManager;
+
// ensure quick settings is disabled until the current user makes it through the setup wizard
private boolean mUserSetup = false;
private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
@@ -642,8 +645,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
new NavigationBarView.OnVerticalChangedListener() {
@Override
public void onVerticalChanged(boolean isVertical) {
- if (mSearchPanelView != null) {
- mSearchPanelView.setHorizontal(isVertical);
+ if (mAssistGestureManager != null) {
+ mAssistGestureManager.onConfigurationChanged();
}
mNotificationPanel.setQsScrimEnabled(!isVertical);
}
@@ -834,6 +837,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
+ mAssistGestureManager = new AssistGestureManager(this, context);
+
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -953,60 +958,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return mStatusBarWindow;
}
- @Override
- protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
- boolean opaque = false;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
- if (ActivityManager.isHighEndGfx()) {
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
- lp.gravity = Gravity.BOTTOM | Gravity.START;
- lp.setTitle("SearchPanel");
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
- | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- return lp;
- }
-
- @Override
- protected void updateSearchPanel() {
- super.updateSearchPanel();
- if (mNavigationBarView != null) {
- mNavigationBarView.setDelegateView(mSearchPanelView);
- }
- }
-
- @Override
- public void showSearchPanel() {
- super.showSearchPanel();
- mHandler.removeCallbacks(mShowSearchPanel);
-
- // we want to freeze the sysui state wherever it is
- mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
-
- if (mNavigationBarView != null) {
- WindowManager.LayoutParams lp =
- (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
- lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWindowManager.updateViewLayout(mNavigationBarView, lp);
- }
- }
-
- @Override
- public void hideSearchPanel() {
- super.hideSearchPanel();
- if (mNavigationBarView != null) {
- WindowManager.LayoutParams lp =
- (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
- lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWindowManager.updateViewLayout(mNavigationBarView, lp);
- }
+ public void invokeAssistGesture(boolean vibrate) {
+ mHandler.removeCallbacks(mInvokeAssist);
+ mAssistGestureManager.onGestureInvoked(vibrate);
}
public int getStatusBarHeight() {
@@ -1036,30 +990,33 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
};
private int mShowSearchHoldoff = 0;
- private Runnable mShowSearchPanel = new Runnable() {
+ private Runnable mInvokeAssist = new Runnable() {
public void run() {
- showSearchPanel();
+ invokeAssistGesture(true /* vibrate */);
awakenDreams();
+ if (mNavigationBarView != null) {
+ mNavigationBarView.getHomeButton().abortCurrentGesture();
+ }
}
};
View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
- switch(event.getAction()) {
+ switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- if (!shouldDisableNavbarGestures()) {
- mHandler.removeCallbacks(mShowSearchPanel);
- mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
- }
- break;
+ if (!shouldDisableNavbarGestures()) {
+ mHandler.removeCallbacks(mInvokeAssist);
+ mHandler.postDelayed(mInvokeAssist, mShowSearchHoldoff);
+ }
+ break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mHandler.removeCallbacks(mShowSearchPanel);
- awakenDreams();
- break;
- }
- return false;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mHandler.removeCallbacks(mInvokeAssist);
+ awakenDreams();
+ break;
+ }
+ return false;
}
};
@@ -1083,7 +1040,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mNavigationBarView.getBackButton().setLongClickable(true);
mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
- updateSearchPanel();
+ mAssistGestureManager.onConfigurationChanged();
}
// For small-screen devices (read: phones) that lack hardware navigation buttons
@@ -2060,11 +2017,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
}
- if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
- mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
- mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
- }
-
if (mStatusBarWindow != null) {
// release focus immediately to kick off focus change transition
mStatusBarWindowManager.setStatusBarFocusable(false);
@@ -2989,7 +2941,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
};
@Override
- protected boolean shouldDisableNavbarGestures() {
+ public boolean shouldDisableNavbarGestures() {
return !isDeviceProvisioned()
|| mExpandedVisible
|| (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
@@ -3058,6 +3010,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mHandlerThread = null;
}
mContext.unregisterReceiver(mBroadcastReceiver);
+ mAssistGestureManager.destroy();
}
private boolean mDemoModeAllowed;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index a18daed..6bc51fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -48,6 +48,7 @@ public class KeyButtonView extends ImageView {
private int mTouchSlop;
private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
+ private boolean mGestureAborted;
private final Runnable mCheckLongPress = new Runnable() {
public void run() {
@@ -126,10 +127,15 @@ public class KeyButtonView extends ImageView {
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
int x, y;
+ if (action == MotionEvent.ACTION_DOWN) {
+ mGestureAborted = false;
+ }
+ if (mGestureAborted) {
+ return false;
+ }
switch (action) {
case MotionEvent.ACTION_DOWN:
- //Log.d("KeyButtonView", "press");
mDownTime = SystemClock.uptimeMillis();
setPressed(true);
if (mCode != 0) {
@@ -203,6 +209,11 @@ public class KeyButtonView extends ImageView {
InputManager.getInstance().injectInputEvent(ev,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
+
+ public void abortCurrentGesture() {
+ setPressed(false);
+ mGestureAborted = true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index d1e1b71..c272e48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -106,12 +106,6 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
- protected WindowManager.LayoutParams getSearchLayoutParams(
- LayoutParams layoutParams) {
- return null;
- }
-
- @Override
protected void setAreThereNotifications() {
}
@@ -120,7 +114,7 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
- protected boolean shouldDisableNavbarGestures() {
+ public boolean shouldDisableNavbarGestures() {
return true;
}