• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.shade;
18 
19 import static android.os.Trace.TRACE_TAG_APP;
20 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
21 
22 import static com.android.systemui.Flags.enableViewCaptureTracing;
23 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
24 
25 import android.annotation.ColorInt;
26 import android.annotation.DrawableRes;
27 import android.annotation.LayoutRes;
28 import android.annotation.Nullable;
29 import android.content.Context;
30 import android.content.res.Configuration;
31 import android.graphics.Canvas;
32 import android.graphics.Paint;
33 import android.graphics.Rect;
34 import android.graphics.drawable.Drawable;
35 import android.media.permission.SafeCloseable;
36 import android.net.Uri;
37 import android.os.Bundle;
38 import android.os.Trace;
39 import android.util.AttributeSet;
40 import android.view.ActionMode;
41 import android.view.InputQueue;
42 import android.view.KeyEvent;
43 import android.view.LayoutInflater;
44 import android.view.Menu;
45 import android.view.MenuItem;
46 import android.view.MotionEvent;
47 import android.view.SurfaceHolder;
48 import android.view.View;
49 import android.view.ViewGroup;
50 import android.view.ViewTreeObserver;
51 import android.view.Window;
52 import android.view.WindowInsetsController;
53 import android.view.accessibility.AccessibilityEvent;
54 
55 import com.android.app.viewcapture.ViewCaptureFactory;
56 import com.android.internal.view.FloatingActionMode;
57 import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
58 import com.android.systemui.Flags;
59 import com.android.systemui.scene.ui.view.WindowRootView;
60 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
61 import com.android.systemui.statusbar.phone.ConfigurationForwarder;
62 
63 /**
64  * Combined keyguard and notification panel view. Also holding backdrop and scrims. This view can
65  * serve as the root view of the main SysUI window, but because other views can also serve that
66  * purpose, users of this class cannot assume it is the root.
67  */
68 public class NotificationShadeWindowView extends WindowRootView {
69     public static final String TAG = "NotificationShadeWindowView";
70 
71     // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by
72     // DecorView, but since this is a special window we have to roll our own.
73     private View mFloatingActionModeOriginatingView;
74     private ActionMode mFloatingActionMode;
75     private FloatingToolbar mFloatingToolbar;
76     private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
77     @Nullable private ConfigurationForwarder mConfigurationForwarder;
78 
79     private InteractionEventHandler mInteractionEventHandler;
80 
81     private SafeCloseable mViewCaptureCloseable;
82 
83     private boolean mAnimatingContentLaunch = false;
84 
NotificationShadeWindowView(Context context, AttributeSet attrs)85     public NotificationShadeWindowView(Context context, AttributeSet attrs) {
86         super(context, attrs);
87         setMotionEventSplittingEnabled(false);
88     }
89 
90     @Override
onAttachedToWindow()91     protected void onAttachedToWindow() {
92         super.onAttachedToWindow();
93         setWillNotDraw(!DEBUG);
94         if (enableViewCaptureTracing()) {
95             mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
96                 .startCapture(getRootView(), ".NotificationShadeWindowView");
97         }
98     }
99 
100     @Override
onDetachedFromWindow()101     protected void onDetachedFromWindow() {
102         super.onDetachedFromWindow();
103         if (mViewCaptureCloseable != null) {
104             mViewCaptureCloseable.close();
105         }
106     }
107 
setInteractionEventHandler(InteractionEventHandler listener)108     protected void setInteractionEventHandler(InteractionEventHandler listener) {
109         mInteractionEventHandler = listener;
110     }
111 
112     @Override
dispatchTouchEvent(MotionEvent ev)113     public boolean dispatchTouchEvent(MotionEvent ev) {
114         Boolean result = mInteractionEventHandler.handleDispatchTouchEvent(ev);
115 
116         result = result != null ? result : super.dispatchTouchEvent(ev);
117 
118         TouchLogger.logDispatchTouch(TAG, ev, result);
119 
120         mInteractionEventHandler.dispatchTouchEventComplete();
121 
122         return result;
123     }
124 
125     @Override
onInterceptTouchEvent(MotionEvent ev)126     public boolean onInterceptTouchEvent(MotionEvent ev) {
127         boolean intercept = mInteractionEventHandler.shouldInterceptTouchEvent(ev);
128         if (!intercept) {
129             intercept = super.onInterceptTouchEvent(ev);
130         }
131         if (intercept) {
132             mInteractionEventHandler.didIntercept(ev);
133         }
134 
135         return intercept;
136     }
137 
138     @Override
onTouchEvent(MotionEvent ev)139     public boolean onTouchEvent(MotionEvent ev) {
140         boolean handled = mInteractionEventHandler.handleTouchEvent(ev);
141 
142         if (!handled) {
143             handled = super.onTouchEvent(ev);
144         }
145 
146         if (!handled) {
147             mInteractionEventHandler.didNotHandleTouchEvent(ev);
148         }
149 
150         return handled;
151     }
152 
153     @Override
onMovedToDisplay(int displayId, Configuration config)154     public void onMovedToDisplay(int displayId, Configuration config) {
155         super.onMovedToDisplay(displayId, config);
156         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
157         ShadeTraceLogger.logOnMovedToDisplay(displayId, config);
158         if (mConfigurationForwarder != null) {
159             mConfigurationForwarder.dispatchOnMovedToDisplay(displayId, config);
160         }
161         // When the window is moved we're only receiving a call to this method instead of the
162         // onConfigurationChange itself. Let's just trigegr a normal config change.
163         onConfigurationChanged(config);
164     }
165 
166     @Override
onConfigurationChanged(Configuration newConfig)167     protected void onConfigurationChanged(Configuration newConfig) {
168         super.onConfigurationChanged(newConfig);
169         ShadeTraceLogger.logOnConfigChanged(newConfig);
170     }
171 
172     @Override
requestSendAccessibilityEvent(View child, AccessibilityEvent event)173     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
174         if (Flags.shadeLaunchAccessibility() && mAnimatingContentLaunch
175                 && event.getEventType() == TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
176             // Block accessibility focus events during launch animations to avoid stray TalkBack
177             // announcements.
178             return false;
179         }
180 
181         return super.requestSendAccessibilityEvent(child, event);
182     }
183 
setAnimatingContentLaunch(boolean animating)184     public void setAnimatingContentLaunch(boolean animating) {
185         mAnimatingContentLaunch = animating;
186     }
187 
setConfigurationForwarder(ConfigurationForwarder configurationForwarder)188     public void setConfigurationForwarder(ConfigurationForwarder configurationForwarder) {
189         ShadeWindowGoesAround.isUnexpectedlyInLegacyMode();
190         mConfigurationForwarder = configurationForwarder;
191     }
192 
193     @Override
onDraw(Canvas canvas)194     public void onDraw(Canvas canvas) {
195         super.onDraw(canvas);
196         if (DEBUG) {
197             Paint pt = new Paint();
198             pt.setColor(0x80FFFF00);
199             pt.setStrokeWidth(12.0f);
200             pt.setStyle(Paint.Style.STROKE);
201             canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), pt);
202         }
203     }
204 
205     @Override
startActionModeForChild(View originalView, ActionMode.Callback callback, int type)206     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback,
207             int type) {
208         if (type == ActionMode.TYPE_FLOATING) {
209             return startActionMode(originalView, callback);
210         }
211         return super.startActionModeForChild(originalView, callback, type);
212     }
213 
createFloatingActionMode( View originatingView, ActionMode.Callback2 callback)214     private ActionMode createFloatingActionMode(
215             View originatingView, ActionMode.Callback2 callback) {
216         if (mFloatingActionMode != null) {
217             mFloatingActionMode.finish();
218         }
219         cleanupFloatingActionModeViews();
220         mFloatingToolbar = new FloatingToolbar(mFakeWindow);
221         final FloatingActionMode mode =
222                 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
223         mFloatingActionModeOriginatingView = originatingView;
224         mFloatingToolbarPreDrawListener = () -> {
225             mode.updateViewLocationInWindow();
226             return true;
227         };
228         return mode;
229     }
230 
setHandledFloatingActionMode(ActionMode mode)231     private void setHandledFloatingActionMode(ActionMode mode) {
232         mFloatingActionMode = mode;
233         mFloatingActionMode.invalidate();  // Will show the floating toolbar if necessary.
234         mFloatingActionModeOriginatingView.getViewTreeObserver()
235                 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
236     }
237 
cleanupFloatingActionModeViews()238     private void cleanupFloatingActionModeViews() {
239         if (mFloatingToolbar != null) {
240             mFloatingToolbar.dismiss();
241             mFloatingToolbar = null;
242         }
243         if (mFloatingActionModeOriginatingView != null) {
244             if (mFloatingToolbarPreDrawListener != null) {
245                 mFloatingActionModeOriginatingView.getViewTreeObserver()
246                         .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
247                 mFloatingToolbarPreDrawListener = null;
248             }
249             mFloatingActionModeOriginatingView = null;
250         }
251     }
252 
startActionMode( View originatingView, ActionMode.Callback callback)253     private ActionMode startActionMode(
254             View originatingView, ActionMode.Callback callback) {
255         ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
256         ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback);
257         if (wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
258             setHandledFloatingActionMode(mode);
259         } else {
260             mode = null;
261         }
262         return mode;
263     }
264 
265     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)266     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
267         Trace.beginSection("NotificationShadeWindowView#onMeasure");
268         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
269         Trace.endSection();
270     }
271 
272     @Override
requestLayout()273     public void requestLayout() {
274         Trace.instant(TRACE_TAG_APP, "NotificationShadeWindowView#requestLayout");
275         super.requestLayout();
276     }
277 
278     private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
279         private final ActionMode.Callback mWrapped;
280 
ActionModeCallback2Wrapper(ActionMode.Callback wrapped)281         ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
282             mWrapped = wrapped;
283         }
284 
onCreateActionMode(ActionMode mode, Menu menu)285         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
286             return mWrapped.onCreateActionMode(mode, menu);
287         }
288 
onPrepareActionMode(ActionMode mode, Menu menu)289         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
290             requestFitSystemWindows();
291             return mWrapped.onPrepareActionMode(mode, menu);
292         }
293 
onActionItemClicked(ActionMode mode, MenuItem item)294         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
295             return mWrapped.onActionItemClicked(mode, item);
296         }
297 
onDestroyActionMode(ActionMode mode)298         public void onDestroyActionMode(ActionMode mode) {
299             mWrapped.onDestroyActionMode(mode);
300             if (mode == mFloatingActionMode) {
301                 cleanupFloatingActionModeViews();
302                 mFloatingActionMode = null;
303             }
304             requestFitSystemWindows();
305         }
306 
307         @Override
onGetContentRect(ActionMode mode, View view, Rect outRect)308         public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
309             if (mWrapped instanceof ActionMode.Callback2) {
310                 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
311             } else {
312                 super.onGetContentRect(mode, view, outRect);
313             }
314         }
315     }
316 
317     interface InteractionEventHandler {
318         /**
319          * Returns a result for {@link ViewGroup#dispatchTouchEvent(MotionEvent)} or null to defer
320          * to the super method.
321          */
handleDispatchTouchEvent(MotionEvent ev)322         Boolean handleDispatchTouchEvent(MotionEvent ev);
323 
324         /**
325          * Called after all dispatching is done.
326          */
327 
dispatchTouchEventComplete()328         void dispatchTouchEventComplete();
329 
330         /**
331          * Returns if the view should intercept the touch event.
332          *
333          * The touch event may still be interecepted if
334          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)} decides to do so.
335          */
shouldInterceptTouchEvent(MotionEvent ev)336         boolean shouldInterceptTouchEvent(MotionEvent ev);
337 
338         /**
339          * Called when the view decides to intercept the touch event.
340          */
didIntercept(MotionEvent ev)341         void didIntercept(MotionEvent ev);
342 
handleTouchEvent(MotionEvent ev)343         boolean handleTouchEvent(MotionEvent ev);
344 
didNotHandleTouchEvent(MotionEvent ev)345         void didNotHandleTouchEvent(MotionEvent ev);
346     }
347 
348     /**
349      * Minimal window to satisfy FloatingToolbar.
350      */
351     private final Window mFakeWindow = new Window(mContext) {
352         @Override
353         public void takeSurface(SurfaceHolder.Callback2 callback) {
354         }
355 
356         @Override
357         public void takeInputQueue(InputQueue.Callback callback) {
358         }
359 
360         @Override
361         public boolean isFloating() {
362             return false;
363         }
364 
365         @Override
366         public void alwaysReadCloseOnTouchAttr() {
367         }
368 
369         @Override
370         public void setContentView(@LayoutRes int layoutResID) {
371         }
372 
373         @Override
374         public void setContentView(View view) {
375         }
376 
377         @Override
378         public void setContentView(View view, ViewGroup.LayoutParams params) {
379         }
380 
381         @Override
382         public void addContentView(View view, ViewGroup.LayoutParams params) {
383         }
384 
385         @Override
386         public void clearContentView() {
387         }
388 
389         @Override
390         public View getCurrentFocus() {
391             return null;
392         }
393 
394         @Override
395         public LayoutInflater getLayoutInflater() {
396             return null;
397         }
398 
399         @Override
400         public void setTitle(CharSequence title) {
401         }
402 
403         @Override
404         public void setTitleColor(@ColorInt int textColor) {
405         }
406 
407         @Override
408         public void openPanel(int featureId, KeyEvent event) {
409         }
410 
411         @Override
412         public void closePanel(int featureId) {
413         }
414 
415         @Override
416         public void togglePanel(int featureId, KeyEvent event) {
417         }
418 
419         @Override
420         public void invalidatePanelMenu(int featureId) {
421         }
422 
423         @Override
424         public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
425             return false;
426         }
427 
428         @Override
429         public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
430             return false;
431         }
432 
433         @Override
434         public void closeAllPanels() {
435         }
436 
437         @Override
438         public boolean performContextMenuIdentifierAction(int id, int flags) {
439             return false;
440         }
441 
442         @Override
443         public void onConfigurationChanged(Configuration newConfig) {
444         }
445 
446         @Override
447         public void setBackgroundDrawable(Drawable drawable) {
448         }
449 
450         @Override
451         public void setFeatureDrawableResource(int featureId, @DrawableRes int resId) {
452         }
453 
454         @Override
455         public void setFeatureDrawableUri(int featureId, Uri uri) {
456         }
457 
458         @Override
459         public void setFeatureDrawable(int featureId, Drawable drawable) {
460         }
461 
462         @Override
463         public void setFeatureDrawableAlpha(int featureId, int alpha) {
464         }
465 
466         @Override
467         public void setFeatureInt(int featureId, int value) {
468         }
469 
470         @Override
471         public void takeKeyEvents(boolean get) {
472         }
473 
474         @Override
475         public boolean superDispatchKeyEvent(KeyEvent event) {
476             return false;
477         }
478 
479         @Override
480         public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
481             return false;
482         }
483 
484         @Override
485         public boolean superDispatchTouchEvent(MotionEvent event) {
486             return false;
487         }
488 
489         @Override
490         public boolean superDispatchTrackballEvent(MotionEvent event) {
491             return false;
492         }
493 
494         @Override
495         public boolean superDispatchGenericMotionEvent(MotionEvent event) {
496             return false;
497         }
498 
499         @Override
500         public View getDecorView() {
501             return NotificationShadeWindowView.this;
502         }
503 
504         @Override
505         public View peekDecorView() {
506             return null;
507         }
508 
509         @Override
510         public Bundle saveHierarchyState() {
511             return null;
512         }
513 
514         @Override
515         public void restoreHierarchyState(Bundle savedInstanceState) {
516         }
517 
518         @Override
519         protected void onActive() {
520         }
521 
522         @Override
523         public void setChildDrawable(int featureId, Drawable drawable) {
524         }
525 
526         @Override
527         public void setChildInt(int featureId, int value) {
528         }
529 
530         @Override
531         public boolean isShortcutKey(int keyCode, KeyEvent event) {
532             return false;
533         }
534 
535         @Override
536         public void setVolumeControlStream(int streamType) {
537         }
538 
539         @Override
540         public int getVolumeControlStream() {
541             return 0;
542         }
543 
544         @Override
545         public int getStatusBarColor() {
546             return 0;
547         }
548 
549         @Override
550         public void setStatusBarColor(@ColorInt int color) {
551         }
552 
553         @Override
554         public int getNavigationBarColor() {
555             return 0;
556         }
557 
558         @Override
559         public void setNavigationBarColor(@ColorInt int color) {
560         }
561 
562         @Override
563         public void setDecorCaptionShade(int decorCaptionShade) {
564         }
565 
566         @Override
567         public void setResizingCaptionDrawable(Drawable drawable) {
568         }
569 
570         @Override
571         public void onMultiWindowModeChanged() {
572         }
573 
574         @Override
575         public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
576         }
577 
578         @Override
579         public WindowInsetsController getInsetsController() {
580             return null;
581         }
582     };
583 
584 }
585 
586