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