blob: 234a6992f49eb74e933d8b6639312531ae5de7b9 [file] [log] [blame]
Adrian Roos4f43dc02015-06-17 16:43:38 -07001/*
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
17package com.android.systemui.assist;
18
19import com.android.systemui.R;
20
21import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.AnimatorSet;
24import android.animation.ValueAnimator;
25import android.content.Context;
26import android.graphics.Canvas;
27import android.graphics.Color;
28import android.graphics.Paint;
29import android.graphics.PixelFormat;
30import android.graphics.PorterDuff;
31import android.graphics.PorterDuffXfermode;
32import android.os.Handler;
33import android.view.View;
34import android.view.WindowManager;
35import android.view.animation.AnimationUtils;
36
37/**
38 * Visually discloses that contextual data was provided to an assistant.
39 */
40public class AssistDisclosure {
41 private final Context mContext;
42 private final WindowManager mWm;
43 private final Handler mHandler;
44
45 private AssistDisclosureView mView;
46 private boolean mViewAdded;
47
48 public AssistDisclosure(Context context, Handler handler) {
49 mContext = context;
50 mHandler = handler;
51 mWm = mContext.getSystemService(WindowManager.class);
52 }
53
54 public void postShow() {
55 mHandler.removeCallbacks(mShowRunnable);
56 mHandler.post(mShowRunnable);
57 }
58
59 private void show() {
60 if (mView == null) {
61 mView = new AssistDisclosureView(mContext);
62 }
63 if (!mViewAdded) {
64 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
65 WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
66 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
67 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
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
74 mWm.addView(mView, lp);
75 mViewAdded = true;
76 }
77 }
78
79 private void hide() {
80 if (mViewAdded) {
81 mWm.removeView(mView);
82 mViewAdded = false;
83 }
84 }
85
86 private Runnable mShowRunnable = new Runnable() {
87 @Override
88 public void run() {
89 show();
90 }
91 };
92
93 private class AssistDisclosureView extends View
94 implements ValueAnimator.AnimatorUpdateListener {
95
96 public static final int TRACING_ANIMATION_DURATION = 300;
97 public static final int ALPHA_ANIMATION_DURATION = 200;
98
99 private float mThickness;
100 private float mShadowThickness;
101 private final Paint mPaint = new Paint();
102 private final Paint mShadowPaint = new Paint();
103
104 private final ValueAnimator mTracingAnimator;
105 private final ValueAnimator mAlphaAnimator;
106 private final AnimatorSet mAnimator;
107
108 private float mTracingProgress = 0;
109 private int mAlpha = 255;
110
111 public AssistDisclosureView(Context context) {
112 super(context);
113
114 mTracingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(TRACING_ANIMATION_DURATION);
115 mTracingAnimator.addUpdateListener(this);
116 mTracingAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
117 android.R.interpolator.fast_out_slow_in));
118 mAlphaAnimator = ValueAnimator.ofInt(255, 0).setDuration(ALPHA_ANIMATION_DURATION);
119 mAlphaAnimator.addUpdateListener(this);
120 mAlphaAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext,
121 android.R.interpolator.fast_out_linear_in));
122 mAnimator = new AnimatorSet();
123 mAnimator.play(mTracingAnimator).before(mAlphaAnimator);
124 mAnimator.addListener(new AnimatorListenerAdapter() {
125 boolean mCancelled;
126
127 @Override
128 public void onAnimationStart(Animator animation) {
129 mCancelled = false;
130 }
131
132 @Override
133 public void onAnimationCancel(Animator animation) {
134 mCancelled = true;
135 }
136
137 @Override
138 public void onAnimationEnd(Animator animation) {
139 if (!mCancelled) {
140 hide();
141 }
142 }
143 });
144
145 PorterDuffXfermode srcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
146 mPaint.setColor(Color.WHITE);
147 mPaint.setXfermode(srcMode);
148 mShadowPaint.setColor(Color.DKGRAY);
149 mShadowPaint.setXfermode(srcMode);
150
151 mThickness = getResources().getDimension(R.dimen.assist_disclosure_thickness);
152 mShadowThickness = getResources().getDimension(
153 R.dimen.assist_disclosure_shadow_thickness);
154 }
155
156 @Override
157 protected void onAttachedToWindow() {
158 super.onAttachedToWindow();
159
160 startAnimation();
161 }
162
163 @Override
164 protected void onDetachedFromWindow() {
165 super.onDetachedFromWindow();
166
167 mAnimator.cancel();
168
169 mTracingProgress = 0;
170 mAlpha = 255;
171 }
172
173 private void startAnimation() {
174 mAnimator.cancel();
175 mAnimator.start();
176 }
177
178 @Override
179 protected void onDraw(Canvas canvas) {
180 mPaint.setAlpha(mAlpha);
181 mShadowPaint.setAlpha(mAlpha / 4);
182
183 drawGeometry(canvas, mShadowPaint, mShadowThickness);
184 drawGeometry(canvas, mPaint, 0);
185 }
186
187 private void drawGeometry(Canvas canvas, Paint paint, float padding) {
188 final int width = getWidth();
189 final int height = getHeight();
190 float thickness = mThickness;
191 final float pixelProgress = mTracingProgress * (width + height - 2 * thickness);
192
193 float bottomProgress = Math.min(pixelProgress, width / 2f);
194 if (bottomProgress > 0) {
195 drawBeam(canvas,
196 width / 2f - bottomProgress,
197 height - thickness,
198 width / 2f + bottomProgress,
199 height, paint, padding);
200 }
201
202 float sideProgress = Math.min(pixelProgress - bottomProgress, height - thickness);
203 if (sideProgress > 0) {
204 drawBeam(canvas,
205 0,
206 (height - thickness) - sideProgress,
207 thickness,
208 height - thickness, paint, padding);
209 drawBeam(canvas,
210 width - thickness,
211 (height - thickness) - sideProgress,
212 width,
213 height - thickness, paint, padding);
214 }
215
216 float topProgress = Math.min(pixelProgress - bottomProgress - sideProgress,
217 width / 2 - thickness);
218 if (sideProgress > 0 && topProgress > 0) {
219 drawBeam(canvas,
220 thickness,
221 0,
222 thickness + topProgress,
223 thickness, paint, padding);
224 drawBeam(canvas,
225 (width - thickness) - topProgress,
226 0,
227 width - thickness,
228 thickness, paint, padding);
229 }
230 }
231
232 private void drawBeam(Canvas canvas, float left, float top, float right, float bottom,
233 Paint paint, float padding) {
234 canvas.drawRect(left - padding,
235 top - padding,
236 right + padding,
237 bottom + padding,
238 paint);
239 }
240
241 @Override
242 public void onAnimationUpdate(ValueAnimator animation) {
243 if (animation == mAlphaAnimator) {
244 mAlpha = (int) mAlphaAnimator.getAnimatedValue();
245 } else if (animation == mTracingAnimator) {
246 mTracingProgress = (float) mTracingAnimator.getAnimatedValue();
247 }
248 invalidate();
249 }
250 }
251}