• 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         static final int FULL_ALPHA = 222; // 87%
99         static final int ALPHA_IN_ANIMATION_DURATION = 400;
100         static final int ALPHA_OUT_ANIMATION_DURATION = 300;
101 
102 
103         private float mThickness;
104         private float mShadowThickness;
105         private final Paint mPaint = new Paint();
106         private final Paint mShadowPaint = new Paint();
107 
108         private final ValueAnimator mAlphaOutAnimator;
109         private final ValueAnimator mAlphaInAnimator;
110         private final AnimatorSet mAnimator;
111 
112         private int mAlpha = 0;
113 
AssistDisclosureView(Context context)114         public AssistDisclosureView(Context context) {
115             super(context);
116 
117             mAlphaInAnimator = ValueAnimator.ofInt(0, FULL_ALPHA)
118                     .setDuration(ALPHA_IN_ANIMATION_DURATION);
119             mAlphaInAnimator.addUpdateListener(this);
120             mAlphaInAnimator.setInterpolator(Interpolators.CUSTOM_40_40);
121             mAlphaOutAnimator = ValueAnimator.ofInt(FULL_ALPHA, 0).setDuration(
122                     ALPHA_OUT_ANIMATION_DURATION);
123             mAlphaOutAnimator.addUpdateListener(this);
124             mAlphaOutAnimator.setInterpolator(Interpolators.CUSTOM_40_40);
125             mAnimator = new AnimatorSet();
126             mAnimator.play(mAlphaInAnimator).before(mAlphaOutAnimator);
127             mAnimator.addListener(new AnimatorListenerAdapter() {
128                 boolean mCancelled;
129 
130                 @Override
131                 public void onAnimationStart(Animator animation) {
132                     mCancelled = false;
133                 }
134 
135                 @Override
136                 public void onAnimationCancel(Animator animation) {
137                     mCancelled = true;
138                 }
139 
140                 @Override
141                 public void onAnimationEnd(Animator animation) {
142                     if (!mCancelled) {
143                         hide();
144                     }
145                 }
146             });
147 
148             PorterDuffXfermode srcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
149             mPaint.setColor(Color.WHITE);
150             mPaint.setXfermode(srcMode);
151             mShadowPaint.setColor(Color.DKGRAY);
152             mShadowPaint.setXfermode(srcMode);
153 
154             mThickness = getResources().getDimension(R.dimen.assist_disclosure_thickness);
155             mShadowThickness = getResources().getDimension(
156                     R.dimen.assist_disclosure_shadow_thickness);
157         }
158 
159         @Override
onAttachedToWindow()160         protected void onAttachedToWindow() {
161             super.onAttachedToWindow();
162 
163             startAnimation();
164             sendAccessibilityEvent(AccessibilityEvent.TYPE_ASSIST_READING_CONTEXT);
165         }
166 
167         @Override
onDetachedFromWindow()168         protected void onDetachedFromWindow() {
169             super.onDetachedFromWindow();
170 
171             mAnimator.cancel();
172             mAlpha = 0;
173         }
174 
startAnimation()175         private void startAnimation() {
176             mAnimator.cancel();
177             mAnimator.start();
178         }
179 
180         @Override
onDraw(Canvas canvas)181         protected void onDraw(Canvas canvas) {
182             mPaint.setAlpha(mAlpha);
183             mShadowPaint.setAlpha(mAlpha / 4);
184 
185             drawGeometry(canvas, mShadowPaint, mShadowThickness);
186             drawGeometry(canvas, mPaint, 0);
187         }
188 
drawGeometry(Canvas canvas, Paint paint, float padding)189         private void drawGeometry(Canvas canvas, Paint paint, float padding) {
190             final int width = getWidth();
191             final int height = getHeight();
192             float thickness = mThickness;
193 
194             // bottom
195             drawBeam(canvas,
196                     0,
197                     height - thickness,
198                     width,
199                     height, paint, padding);
200 
201             // sides
202             drawBeam(canvas,
203                     0,
204                     0,
205                     thickness,
206                     height - thickness, paint, padding);
207             drawBeam(canvas,
208                     width - thickness,
209                     0,
210                     width,
211                     height - thickness, paint, padding);
212 
213             // top
214             drawBeam(canvas,
215                     thickness,
216                     0,
217                     width - thickness,
218                     thickness, paint, padding);
219         }
220 
drawBeam(Canvas canvas, float left, float top, float right, float bottom, Paint paint, float padding)221         private void drawBeam(Canvas canvas, float left, float top, float right, float bottom,
222                 Paint paint, float padding) {
223             canvas.drawRect(left - padding,
224                     top - padding,
225                     right + padding,
226                     bottom + padding,
227                     paint);
228         }
229 
230         @Override
onAnimationUpdate(ValueAnimator animation)231         public void onAnimationUpdate(ValueAnimator animation) {
232             if (animation == mAlphaOutAnimator) {
233                 mAlpha = (int) mAlphaOutAnimator.getAnimatedValue();
234             } else if (animation == mAlphaInAnimator) {
235                 mAlpha = (int) mAlphaInAnimator.getAnimatedValue();
236             }
237             invalidate();
238         }
239     }
240 }
241