blob: bc80a1a5137d007d2fa7bbbf05d67a9a177e9237 [file] [log] [blame]
wilsonshihe8321942019-10-18 18:39:46 +08001/*
2 * Copyright (C) 2020 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.statusbar.phone;
18
Tiger Huang52724442020-01-20 21:38:42 +080019import static android.view.WindowInsets.Type.systemBars;
20
wilsonshihe8321942019-10-18 18:39:46 +080021import android.annotation.ColorInt;
22import android.annotation.DrawableRes;
23import android.annotation.LayoutRes;
24import android.content.Context;
25import android.content.res.Configuration;
26import android.content.res.TypedArray;
27import android.graphics.Canvas;
28import android.graphics.Insets;
29import android.graphics.Paint;
30import android.graphics.Rect;
31import android.graphics.drawable.Drawable;
32import android.net.Uri;
33import android.os.Bundle;
34import android.util.AttributeSet;
35import android.view.ActionMode;
36import android.view.DisplayCutout;
37import android.view.InputQueue;
38import android.view.KeyEvent;
39import android.view.LayoutInflater;
40import android.view.Menu;
41import android.view.MenuItem;
42import android.view.MotionEvent;
43import android.view.SurfaceHolder;
44import android.view.View;
45import android.view.ViewGroup;
46import android.view.ViewTreeObserver;
47import android.view.Window;
48import android.view.WindowInsets;
49import android.view.WindowInsetsController;
50import android.widget.FrameLayout;
51
52import com.android.internal.view.FloatingActionMode;
53import com.android.internal.widget.FloatingToolbar;
54import com.android.systemui.R;
55
56/**
57 * Combined keyguard and notification panel view. Also holding backdrop and scrims.
58 */
59public class NotificationShadeWindowView extends FrameLayout {
60 public static final String TAG = "NotificationShadeWindowView";
61 public static final boolean DEBUG = StatusBar.DEBUG;
62
63 private int mRightInset = 0;
64 private int mLeftInset = 0;
65
66 // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by
67 // DecorView, but since this is a special window we have to roll our own.
68 private View mFloatingActionModeOriginatingView;
69 private ActionMode mFloatingActionMode;
70 private FloatingToolbar mFloatingToolbar;
71 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
72
73 private InteractionEventHandler mInteractionEventHandler;
74
75 public NotificationShadeWindowView(Context context, AttributeSet attrs) {
76 super(context, attrs);
77 setMotionEventSplittingEnabled(false);
78 }
79
80 public NotificationPanelView getNotificationPanelView() {
81 return findViewById(R.id.notification_panel);
82 }
83
84 @Override
85 public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
Tiger Huang52724442020-01-20 21:38:42 +080086 final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
wilsonshihe8321942019-10-18 18:39:46 +080087 if (getFitsSystemWindows()) {
88 boolean paddingChanged = insets.top != getPaddingTop()
89 || insets.bottom != getPaddingBottom();
90
wilsonshihe8321942019-10-18 18:39:46 +080091 // Drop top inset, and pass through bottom inset.
92 if (paddingChanged) {
93 setPadding(0, 0, 0, 0);
94 }
95 } else {
wilsonshihe8321942019-10-18 18:39:46 +080096 boolean changed = getPaddingLeft() != 0
97 || getPaddingRight() != 0
98 || getPaddingTop() != 0
99 || getPaddingBottom() != 0;
100 if (changed) {
101 setPadding(0, 0, 0, 0);
102 }
103 }
shawnlinbd6d0ad2020-01-29 18:38:49 +0800104
105 mLeftInset = 0;
106 mRightInset = 0;
107 DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
108 if (displayCutout != null) {
109 mLeftInset = displayCutout.getSafeInsetLeft();
110 mRightInset = displayCutout.getSafeInsetRight();
111 }
112 mLeftInset = Math.max(insets.left, mLeftInset);
113 mRightInset = Math.max(insets.right, mRightInset);
114 applyMargins();
wilsonshihe8321942019-10-18 18:39:46 +0800115 return windowInsets;
116 }
117
118 private void applyMargins() {
119 final int count = getChildCount();
120 for (int i = 0; i < count; i++) {
121 View child = getChildAt(i);
122 if (child.getLayoutParams() instanceof LayoutParams) {
123 LayoutParams lp = (LayoutParams) child.getLayoutParams();
124 if (!lp.ignoreRightInset
125 && (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset)) {
126 lp.rightMargin = mRightInset;
127 lp.leftMargin = mLeftInset;
128 child.requestLayout();
129 }
130 }
131 }
132 }
133
134 @Override
135 public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
136 return new LayoutParams(getContext(), attrs);
137 }
138
139 @Override
140 protected FrameLayout.LayoutParams generateDefaultLayoutParams() {
141 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
142 }
143
144 @Override
145 protected void onAttachedToWindow() {
146 super.onAttachedToWindow();
147 setWillNotDraw(!DEBUG);
148 }
149
150 @Override
151 public boolean dispatchKeyEvent(KeyEvent event) {
152 if (mInteractionEventHandler.interceptMediaKey(event)) {
153 return true;
154 }
155
156 if (super.dispatchKeyEvent(event)) {
157 return true;
158 }
159
160 return mInteractionEventHandler.dispatchKeyEvent(event);
161 }
162
163 protected void setInteractionEventHandler(InteractionEventHandler listener) {
164 mInteractionEventHandler = listener;
165 }
166
167 @Override
168 public boolean dispatchTouchEvent(MotionEvent ev) {
169 Boolean result = mInteractionEventHandler.handleDispatchTouchEvent(ev);
170
171 return result != null ? result : super.dispatchTouchEvent(ev);
172 }
173
174 @Override
175 public boolean onInterceptTouchEvent(MotionEvent ev) {
176 boolean intercept = mInteractionEventHandler.shouldInterceptTouchEvent(ev);
177 if (!intercept) {
178 intercept = super.onInterceptTouchEvent(ev);
179 }
180 if (intercept) {
181 mInteractionEventHandler.didIntercept(ev);
182 }
183
184 return intercept;
185 }
186
187 @Override
188 public boolean onTouchEvent(MotionEvent ev) {
189 boolean handled = mInteractionEventHandler.handleTouchEvent(ev);
190
191 if (!handled) {
192 handled = super.onTouchEvent(ev);
193 }
194
195 if (!handled) {
196 mInteractionEventHandler.didNotHandleTouchEvent(ev);
197 }
198
199 return handled;
200 }
201
202 @Override
203 public void onDraw(Canvas canvas) {
204 super.onDraw(canvas);
205 if (DEBUG) {
206 Paint pt = new Paint();
207 pt.setColor(0x80FFFF00);
208 pt.setStrokeWidth(12.0f);
209 pt.setStyle(Paint.Style.STROKE);
210 canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), pt);
211 }
212 }
213
214 class LayoutParams extends FrameLayout.LayoutParams {
215
216 public boolean ignoreRightInset;
217
218 LayoutParams(int width, int height) {
219 super(width, height);
220 }
221
222 LayoutParams(Context c, AttributeSet attrs) {
223 super(c, attrs);
224
225 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StatusBarWindowView_Layout);
226 ignoreRightInset = a.getBoolean(
227 R.styleable.StatusBarWindowView_Layout_ignoreRightInset, false);
228 a.recycle();
229 }
230 }
231
232 @Override
233 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback,
234 int type) {
235 if (type == ActionMode.TYPE_FLOATING) {
236 return startActionMode(originalView, callback, type);
237 }
238 return super.startActionModeForChild(originalView, callback, type);
239 }
240
241 private ActionMode createFloatingActionMode(
242 View originatingView, ActionMode.Callback2 callback) {
243 if (mFloatingActionMode != null) {
244 mFloatingActionMode.finish();
245 }
246 cleanupFloatingActionModeViews();
247 mFloatingToolbar = new FloatingToolbar(mFakeWindow);
248 final FloatingActionMode mode =
249 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
250 mFloatingActionModeOriginatingView = originatingView;
251 mFloatingToolbarPreDrawListener =
252 new ViewTreeObserver.OnPreDrawListener() {
253 @Override
254 public boolean onPreDraw() {
255 mode.updateViewLocationInWindow();
256 return true;
257 }
258 };
259 return mode;
260 }
261
262 private void setHandledFloatingActionMode(ActionMode mode) {
263 mFloatingActionMode = mode;
264 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
265 mFloatingActionModeOriginatingView.getViewTreeObserver()
266 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
267 }
268
269 private void cleanupFloatingActionModeViews() {
270 if (mFloatingToolbar != null) {
271 mFloatingToolbar.dismiss();
272 mFloatingToolbar = null;
273 }
274 if (mFloatingActionModeOriginatingView != null) {
275 if (mFloatingToolbarPreDrawListener != null) {
276 mFloatingActionModeOriginatingView.getViewTreeObserver()
277 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
278 mFloatingToolbarPreDrawListener = null;
279 }
280 mFloatingActionModeOriginatingView = null;
281 }
282 }
283
284 private ActionMode startActionMode(
285 View originatingView, ActionMode.Callback callback, int type) {
286 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
287 ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback);
288 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
289 setHandledFloatingActionMode(mode);
290 } else {
291 mode = null;
292 }
293 return mode;
294 }
295
296 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
297 private final ActionMode.Callback mWrapped;
298
299 ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
300 mWrapped = wrapped;
301 }
302
303 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
304 return mWrapped.onCreateActionMode(mode, menu);
305 }
306
307 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
308 requestFitSystemWindows();
309 return mWrapped.onPrepareActionMode(mode, menu);
310 }
311
312 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
313 return mWrapped.onActionItemClicked(mode, item);
314 }
315
316 public void onDestroyActionMode(ActionMode mode) {
317 mWrapped.onDestroyActionMode(mode);
318 if (mode == mFloatingActionMode) {
319 cleanupFloatingActionModeViews();
320 mFloatingActionMode = null;
321 }
322 requestFitSystemWindows();
323 }
324
325 @Override
326 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
327 if (mWrapped instanceof ActionMode.Callback2) {
328 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
329 } else {
330 super.onGetContentRect(mode, view, outRect);
331 }
332 }
333 }
334
335 interface InteractionEventHandler {
336 /**
337 * Returns a result for {@link ViewGroup#dispatchTouchEvent(MotionEvent)} or null to defer
338 * to the super method.
339 */
340 Boolean handleDispatchTouchEvent(MotionEvent ev);
341
342 /**
343 * Returns if the view should intercept the touch event.
344 *
345 * The touch event may still be interecepted if
346 * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)} decides to do so.
347 */
348 boolean shouldInterceptTouchEvent(MotionEvent ev);
349
350 /**
351 * Called when the view decides to intercept the touch event.
352 */
353 void didIntercept(MotionEvent ev);
354
355 boolean handleTouchEvent(MotionEvent ev);
356
357 void didNotHandleTouchEvent(MotionEvent ev);
358
359 boolean interceptMediaKey(KeyEvent event);
360
361 boolean dispatchKeyEvent(KeyEvent event);
362 }
363
364 /**
365 * Minimal window to satisfy FloatingToolbar.
366 */
367 private Window mFakeWindow = new Window(mContext) {
368 @Override
369 public void takeSurface(SurfaceHolder.Callback2 callback) {
370 }
371
372 @Override
373 public void takeInputQueue(InputQueue.Callback callback) {
374 }
375
376 @Override
377 public boolean isFloating() {
378 return false;
379 }
380
381 @Override
382 public void alwaysReadCloseOnTouchAttr() {
383 }
384
385 @Override
386 public void setContentView(@LayoutRes int layoutResID) {
387 }
388
389 @Override
390 public void setContentView(View view) {
391 }
392
393 @Override
394 public void setContentView(View view, ViewGroup.LayoutParams params) {
395 }
396
397 @Override
398 public void addContentView(View view, ViewGroup.LayoutParams params) {
399 }
400
401 @Override
402 public void clearContentView() {
403 }
404
405 @Override
406 public View getCurrentFocus() {
407 return null;
408 }
409
410 @Override
411 public LayoutInflater getLayoutInflater() {
412 return null;
413 }
414
415 @Override
416 public void setTitle(CharSequence title) {
417 }
418
419 @Override
420 public void setTitleColor(@ColorInt int textColor) {
421 }
422
423 @Override
424 public void openPanel(int featureId, KeyEvent event) {
425 }
426
427 @Override
428 public void closePanel(int featureId) {
429 }
430
431 @Override
432 public void togglePanel(int featureId, KeyEvent event) {
433 }
434
435 @Override
436 public void invalidatePanelMenu(int featureId) {
437 }
438
439 @Override
440 public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
441 return false;
442 }
443
444 @Override
445 public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
446 return false;
447 }
448
449 @Override
450 public void closeAllPanels() {
451 }
452
453 @Override
454 public boolean performContextMenuIdentifierAction(int id, int flags) {
455 return false;
456 }
457
458 @Override
459 public void onConfigurationChanged(Configuration newConfig) {
460 }
461
462 @Override
463 public void setBackgroundDrawable(Drawable drawable) {
464 }
465
466 @Override
467 public void setFeatureDrawableResource(int featureId, @DrawableRes int resId) {
468 }
469
470 @Override
471 public void setFeatureDrawableUri(int featureId, Uri uri) {
472 }
473
474 @Override
475 public void setFeatureDrawable(int featureId, Drawable drawable) {
476 }
477
478 @Override
479 public void setFeatureDrawableAlpha(int featureId, int alpha) {
480 }
481
482 @Override
483 public void setFeatureInt(int featureId, int value) {
484 }
485
486 @Override
487 public void takeKeyEvents(boolean get) {
488 }
489
490 @Override
491 public boolean superDispatchKeyEvent(KeyEvent event) {
492 return false;
493 }
494
495 @Override
496 public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
497 return false;
498 }
499
500 @Override
501 public boolean superDispatchTouchEvent(MotionEvent event) {
502 return false;
503 }
504
505 @Override
506 public boolean superDispatchTrackballEvent(MotionEvent event) {
507 return false;
508 }
509
510 @Override
511 public boolean superDispatchGenericMotionEvent(MotionEvent event) {
512 return false;
513 }
514
515 @Override
516 public View getDecorView() {
517 return NotificationShadeWindowView.this;
518 }
519
520 @Override
521 public View peekDecorView() {
522 return null;
523 }
524
525 @Override
526 public Bundle saveHierarchyState() {
527 return null;
528 }
529
530 @Override
531 public void restoreHierarchyState(Bundle savedInstanceState) {
532 }
533
534 @Override
535 protected void onActive() {
536 }
537
538 @Override
539 public void setChildDrawable(int featureId, Drawable drawable) {
540 }
541
542 @Override
543 public void setChildInt(int featureId, int value) {
544 }
545
546 @Override
547 public boolean isShortcutKey(int keyCode, KeyEvent event) {
548 return false;
549 }
550
551 @Override
552 public void setVolumeControlStream(int streamType) {
553 }
554
555 @Override
556 public int getVolumeControlStream() {
557 return 0;
558 }
559
560 @Override
561 public int getStatusBarColor() {
562 return 0;
563 }
564
565 @Override
566 public void setStatusBarColor(@ColorInt int color) {
567 }
568
569 @Override
570 public int getNavigationBarColor() {
571 return 0;
572 }
573
574 @Override
575 public void setNavigationBarColor(@ColorInt int color) {
576 }
577
578 @Override
579 public void setDecorCaptionShade(int decorCaptionShade) {
580 }
581
582 @Override
583 public void setResizingCaptionDrawable(Drawable drawable) {
584 }
585
586 @Override
587 public void onMultiWindowModeChanged() {
588 }
589
590 @Override
591 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
592 }
593
594 @Override
595 public void reportActivityRelaunched() {
596 }
597
598 @Override
599 public WindowInsetsController getInsetsController() {
600 return null;
601 }
602 };
603
604}
605