• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui.assist;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ValueAnimator;
23 import android.content.Context;
24 import android.graphics.Canvas;
25 import android.graphics.Color;
26 import android.graphics.Paint;
27 import android.graphics.PixelFormat;
28 import android.graphics.PorterDuff;
29 import android.graphics.PorterDuffXfermode;
30 import android.os.Handler;
31 import android.view.View;
32 import android.view.WindowManager;
33 import android.view.accessibility.AccessibilityEvent;
34 import android.view.animation.AnimationUtils;
35 
36 import com.android.systemui.Interpolators;
37 import com.android.systemui.R;
38 
39 /**
40  * Visually discloses that contextual data was provided to an assistant.
41  */
42 public class AssistDisclosure {
43     private final Context mContext;
44     private final WindowManager mWm;
45     private final Handler mHandler;
46 
47     private AssistDisclosureView mView;
48     private boolean mViewAdded;
49 
AssistDisclosure(Context context, Handler handler)50     public AssistDisclosure(Context context, Handler handler) {
51         mContext = context;
52         mHandler = handler;
53         mWm = mContext.getSystemService(WindowManager.class);
54     }
55 
postShow()56     public void postShow() {
57         mHandler.removeCallbacks(mShowRunnable);
58         mHandler.post(mShowRunnable);
59     }
60 
show()61     private void show() {
62         if (mView == null) {
63             mView = new AssistDisclosureView(mContext);
64         }
65         if (!mViewAdded) {
66             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
67                     WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
68                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
69                             | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
70                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
71                             | WindowManager.LayoutParams.FLAG_FULLSCREEN
72                             | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
73                     PixelFormat.TRANSLUCENT);
74             lp.setTitle("AssistDisclosure");
75 
76             mWm.addView(mView, lp);
77             mViewAdded = true;
78         }
79     }
80 
hide()81     private void hide() {
82         if (mViewAdded) {
83             mWm.removeView(mView);
84             mViewAdded = false;
85         }
86     }
87 
88     private Runnable mShowRunnable = new Runnable() {
89         @Override
90         public void run() {
91             show();
92         }
93     };
94 
95     private class AssistDisclosureView extends View
96             implements ValueAnimator.AnimatorUpdateListener {
97 
98         public static final int TRACING_ANIMATION_DURATION = 600;
99         public static final int ALPHA_IN_ANIMATION_DURATION = 450;
100         public static final int ALPHA_OUT_ANIMATION_DURATION = 400;
101 
102         private float mThickness;
103         private float mShadowThickness;
104         private final Paint mPaint = new Paint();
105         private final Paint mShadowPaint = new Paint();
106 
107         private final ValueAnimator mTracingAnimator;
108         private final ValueAnimator mAlphaOutAnimator;
109         private final ValueAnimator mAlphaInAnimator;
110         private final AnimatorSet mAnimator;
111 
112         private float mTracingProgress = 0;
113         private int mAlpha = 0;
114 
AssistDisclosureView(Context context)115         public AssistDisclosureView(Context context) {
116             super(context);
117 
118             mTracingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(TRACING_ANIMATION_DURATION);
119             mTracingAnimator.addUpdateListener(this);
120             mTracingAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
121                     R.interpolator.assist_disclosure_trace));
122             mAlphaInAnimator = ValueAnimator.ofInt(0, 255).setDuration(ALPHA_IN_ANIMATION_DURATION);
123             mAlphaInAnimator.addUpdateListener(this);
124             mAlphaInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
125             mAlphaOutAnimator = ValueAnimator.ofInt(255, 0).setDuration(
126                     ALPHA_OUT_ANIMATION_DURATION);
127             mAlphaOutAnimator.addUpdateListener(this);
128             mAlphaOutAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
129             mAnimator = new AnimatorSet();
130             mAnimator.play(mAlphaInAnimator).with(mTracingAnimator);
131             mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator);
132             mAnimator.addListener(new AnimatorListenerAdapter() {
133                 boolean mCancelled;
134 
135                 @Override
136                 public void onAnimationStart(Animator animation) {
137                     mCancelled = false;
138                 }
139 
140                 @Override
141                 public void onAnimationCancel(Animator animation) {
142                     mCancelled = true;
143                 }
144 
145                 @Override
146                 public void onAnimationEnd(Animator animation) {
147                     if (!mCancelled) {
148                         hide();
149                     }
150                 }
151             });
152 
153             PorterDuffXfermode srcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
154             mPaint.setColor(Color.WHITE);
155             mPaint.setXfermode(srcMode);
156             mShadowPaint.setColor(Color.DKGRAY);
157             mShadowPaint.setXfermode(srcMode);
158 
159             mThickness = getResources().getDimension(R.dimen.assist_disclosure_thickness);
160             mShadowThickness = getResources().getDimension(
161                     R.dimen.assist_disclosure_shadow_thickness);
162         }
163 
164         @Override
onAttachedToWindow()165         protected void onAttachedToWindow() {
166             super.onAttachedToWindow();
167 
168             startAnimation();
169             sendAccessibilityEvent(AccessibilityEvent.TYPE_ASSIST_READING_CONTEXT);
170         }
171 
172         @Override
onDetachedFromWindow()173         protected void onDetachedFromWindow() {
174             super.onDetachedFromWindow();
175 
176             mAnimator.cancel();
177 
178             mTracingProgress = 0;
179             mAlpha = 0;
180         }
181 
startAnimation()182         private void startAnimation() {
183             mAnimator.cancel();
184             mAnimator.start();
185         }
186 
187         @Override
onDraw(Canvas canvas)188         protected void onDraw(Canvas canvas) {
189             mPaint.setAlpha(mAlpha);
190             mShadowPaint.setAlpha(mAlpha / 4);
191 
192             drawGeometry(canvas, mShadowPaint, mShadowThickness);
193             drawGeometry(canvas, mPaint, 0);
194         }
195 
drawGeometry(Canvas canvas, Paint paint, float padding)196         private void drawGeometry(Canvas canvas, Paint paint, float padding) {
197             final int width = getWidth();
198             final int height = getHeight();
199             float thickness = mThickness;
200             final float pixelProgress = mTracingProgress * (width + height - 2 * thickness);
201 
202             float bottomProgress = Math.min(pixelProgress, width / 2f);
203             if (bottomProgress > 0) {
204                 drawBeam(canvas,
205                         width / 2f - bottomProgress,
206                         height - thickness,
207                         width / 2f + bottomProgress,
208                         height, paint, padding);
209             }
210 
211             float sideProgress = Math.min(pixelProgress - bottomProgress, height - thickness);
212             if (sideProgress > 0) {
213                 drawBeam(canvas,
214                         0,
215                         (height - thickness) - sideProgress,
216                         thickness,
217                         height - thickness, paint, padding);
218                 drawBeam(canvas,
219                         width - thickness,
220                         (height - thickness) - sideProgress,
221                         width,
222                         height - thickness, paint, padding);
223             }
224 
225             float topProgress = Math.min(pixelProgress - bottomProgress - sideProgress,
226                     width / 2 - thickness);
227             if (sideProgress > 0 && topProgress > 0) {
228                 drawBeam(canvas,
229                         thickness,
230                         0,
231                         thickness + topProgress,
232                         thickness, paint, padding);
233                 drawBeam(canvas,
234                         (width - thickness) - topProgress,
235                         0,
236                         width - thickness,
237                         thickness, paint, padding);
238             }
239         }
240 
drawBeam(Canvas canvas, float left, float top, float right, float bottom, Paint paint, float padding)241         private void drawBeam(Canvas canvas, float left, float top, float right, float bottom,
242                 Paint paint, float padding) {
243             canvas.drawRect(left - padding,
244                     top - padding,
245                     right + padding,
246                     bottom + padding,
247                     paint);
248         }
249 
250         @Override
onAnimationUpdate(ValueAnimator animation)251         public void onAnimationUpdate(ValueAnimator animation) {
252             if (animation == mAlphaOutAnimator) {
253                 mAlpha = (int) mAlphaOutAnimator.getAnimatedValue();
254             } else if (animation == mAlphaInAnimator) {
255                 mAlpha = (int) mAlphaInAnimator.getAnimatedValue();
256             } else if (animation == mTracingAnimator) {
257                 mTracingProgress = (float) mTracingAnimator.getAnimatedValue();
258             }
259             invalidate();
260         }
261     }
262 }
263