• 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 
35 import com.android.systemui.R;
36 import com.android.systemui.animation.Interpolators;
37 
38 /**
39  * Visually discloses that contextual data was provided to an assistant.
40  */
41 public class AssistDisclosure {
42     private final Context mContext;
43     private final WindowManager mWm;
44     private final Handler mHandler;
45 
46     private AssistDisclosureView mView;
47     private boolean mViewAdded;
48 
AssistDisclosure(Context context, Handler handler)49     public AssistDisclosure(Context context, Handler handler) {
50         mContext = context;
51         mHandler = handler;
52         mWm = mContext.getSystemService(WindowManager.class);
53     }
54 
postShow()55     public void postShow() {
56         mHandler.removeCallbacks(mShowRunnable);
57         mHandler.post(mShowRunnable);
58     }
59 
show()60     private void show() {
61         if (mView == null) {
62             mView = new AssistDisclosureView(mContext);
63         }
64         if (!mViewAdded) {
65             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
66                     WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
67                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
68                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
69                             | WindowManager.LayoutParams.FLAG_FULLSCREEN
70                             | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
71                     PixelFormat.TRANSLUCENT);
72             lp.setTitle("AssistDisclosure");
73             lp.setFitInsetsTypes(0 /* types */);
74 
75             mWm.addView(mView, lp);
76             mViewAdded = true;
77         }
78     }
79 
hide()80     private void hide() {
81         if (mViewAdded) {
82             mWm.removeView(mView);
83             mViewAdded = false;
84         }
85     }
86 
87     private Runnable mShowRunnable = new Runnable() {
88         @Override
89         public void run() {
90             show();
91         }
92     };
93 
94     private class AssistDisclosureView extends View
95             implements ValueAnimator.AnimatorUpdateListener {
96 
97         static final int FULL_ALPHA = 222; // 87%
98         static final int ALPHA_IN_ANIMATION_DURATION = 400;
99         static final int ALPHA_OUT_ANIMATION_DURATION = 300;
100 
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 mAlphaOutAnimator;
108         private final ValueAnimator mAlphaInAnimator;
109         private final AnimatorSet mAnimator;
110 
111         private int mAlpha = 0;
112 
AssistDisclosureView(Context context)113         public AssistDisclosureView(Context context) {
114             super(context);
115 
116             mAlphaInAnimator = ValueAnimator.ofInt(0, FULL_ALPHA)
117                     .setDuration(ALPHA_IN_ANIMATION_DURATION);
118             mAlphaInAnimator.addUpdateListener(this);
119             mAlphaInAnimator.setInterpolator(Interpolators.CUSTOM_40_40);
120             mAlphaOutAnimator = ValueAnimator.ofInt(FULL_ALPHA, 0).setDuration(
121                     ALPHA_OUT_ANIMATION_DURATION);
122             mAlphaOutAnimator.addUpdateListener(this);
123             mAlphaOutAnimator.setInterpolator(Interpolators.CUSTOM_40_40);
124             mAnimator = new AnimatorSet();
125             mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator);
126             mAnimator.addListener(new AnimatorListenerAdapter() {
127                 boolean mCancelled;
128 
129                 @Override
130                 public void onAnimationStart(Animator animation) {
131                     mCancelled = false;
132                 }
133 
134                 @Override
135                 public void onAnimationCancel(Animator animation) {
136                     mCancelled = true;
137                 }
138 
139                 @Override
140                 public void onAnimationEnd(Animator animation) {
141                     if (!mCancelled) {
142                         hide();
143                     }
144                 }
145             });
146 
147             PorterDuffXfermode srcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
148             mPaint.setColor(Color.WHITE);
149             mPaint.setXfermode(srcMode);
150             mShadowPaint.setColor(Color.DKGRAY);
151             mShadowPaint.setXfermode(srcMode);
152 
153             mThickness = getResources().getDimension(R.dimen.assist_disclosure_thickness);
154             mShadowThickness = getResources().getDimension(
155                     R.dimen.assist_disclosure_shadow_thickness);
156         }
157 
158         @Override
onAttachedToWindow()159         protected void onAttachedToWindow() {
160             super.onAttachedToWindow();
161 
162             startAnimation();
163             sendAccessibilityEvent(AccessibilityEvent.TYPE_ASSIST_READING_CONTEXT);
164         }
165 
166         @Override
onDetachedFromWindow()167         protected void onDetachedFromWindow() {
168             super.onDetachedFromWindow();
169 
170             mAnimator.cancel();
171             mAlpha = 0;
172         }
173 
startAnimation()174         private void startAnimation() {
175             mAnimator.cancel();
176             mAnimator.start();
177         }
178 
179         @Override
onDraw(Canvas canvas)180         protected void onDraw(Canvas canvas) {
181             mPaint.setAlpha(mAlpha);
182             mShadowPaint.setAlpha(mAlpha / 4);
183 
184             drawGeometry(canvas, mShadowPaint, mShadowThickness);
185             drawGeometry(canvas, mPaint, 0);
186         }
187 
drawGeometry(Canvas canvas, Paint paint, float padding)188         private void drawGeometry(Canvas canvas, Paint paint, float padding) {
189             final int width = getWidth();
190             final int height = getHeight();
191             float thickness = mThickness;
192 
193             // bottom
194             drawBeam(canvas,
195                     0,
196                     height - thickness,
197                     width,
198                     height, paint, padding);
199 
200             // sides
201             drawBeam(canvas,
202                     0,
203                     0,
204                     thickness,
205                     height - thickness, paint, padding);
206             drawBeam(canvas,
207                     width - thickness,
208                     0,
209                     width,
210                     height - thickness, paint, padding);
211 
212             // top
213             drawBeam(canvas,
214                     thickness,
215                     0,
216                     width - thickness,
217                     thickness, paint, padding);
218         }
219 
drawBeam(Canvas canvas, float left, float top, float right, float bottom, Paint paint, float padding)220         private void drawBeam(Canvas canvas, float left, float top, float right, float bottom,
221                 Paint paint, float padding) {
222             canvas.drawRect(left - padding,
223                     top - padding,
224                     right + padding,
225                     bottom + padding,
226                     paint);
227         }
228 
229         @Override
onAnimationUpdate(ValueAnimator animation)230         public void onAnimationUpdate(ValueAnimator animation) {
231             if (animation == mAlphaOutAnimator) {
232                 mAlpha = (int) mAlphaOutAnimator.getAnimatedValue();
233             } else if (animation == mAlphaInAnimator) {
234                 mAlpha = (int) mAlphaInAnimator.getAnimatedValue();
235             }
236             invalidate();
237         }
238     }
239 }
240