1 /*
2  * Copyright (C) 2023 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.statusbar;
18 
19 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
20 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
21 import static android.app.StatusBarManager.DISABLE_BACK;
22 import static android.app.StatusBarManager.DISABLE_HOME;
23 import static android.app.StatusBarManager.DISABLE_RECENT;
24 import static android.view.Display.DEFAULT_DISPLAY;
25 import static android.view.ViewRootImpl.CLIENT_IMMERSIVE_CONFIRMATION;
26 import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
27 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
28 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
29 
30 import android.animation.ArgbEvaluator;
31 import android.animation.ValueAnimator;
32 import android.annotation.NonNull;
33 import android.annotation.Nullable;
34 import android.app.ActivityManager;
35 import android.content.BroadcastReceiver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.content.res.ColorStateList;
40 import android.content.res.Resources;
41 import android.content.res.TypedArray;
42 import android.database.ContentObserver;
43 import android.graphics.Insets;
44 import android.graphics.PixelFormat;
45 import android.graphics.drawable.ColorDrawable;
46 import android.os.Binder;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.IBinder;
50 import android.os.Looper;
51 import android.os.Message;
52 import android.os.RemoteException;
53 import android.os.ServiceManager;
54 import android.os.UserHandle;
55 import android.os.UserManager;
56 import android.provider.Settings;
57 import android.service.vr.IVrManager;
58 import android.service.vr.IVrStateCallbacks;
59 import android.util.DisplayMetrics;
60 import android.util.Log;
61 import android.view.Display;
62 import android.view.Gravity;
63 import android.view.MotionEvent;
64 import android.view.View;
65 import android.view.ViewGroup;
66 import android.view.ViewTreeObserver;
67 import android.view.WindowInsets;
68 import android.view.WindowInsets.Type;
69 import android.view.WindowManager;
70 import android.view.animation.AnimationUtils;
71 import android.view.animation.Interpolator;
72 import android.widget.Button;
73 import android.widget.FrameLayout;
74 import android.widget.ImageView;
75 
76 import com.android.systemui.CoreStartable;
77 import com.android.systemui.R;
78 import com.android.systemui.shared.system.TaskStackChangeListener;
79 import com.android.systemui.shared.system.TaskStackChangeListeners;
80 import com.android.systemui.util.settings.SecureSettings;
81 
82 import javax.inject.Inject;
83 
84 /**
85  *  Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden
86  *  entering immersive mode.
87  */
88 public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Callbacks,
89         TaskStackChangeListener {
90     private static final String TAG = "ImmersiveModeConfirm";
91     private static final boolean DEBUG = false;
92     private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution
93     private static final String CONFIRMED = "confirmed";
94     private static final int IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE =
95             WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
96 
97     private static boolean sConfirmed;
98     private final SecureSettings mSecureSettings;
99 
100     private Context mDisplayContext;
101     private final Context mSysUiContext;
102     private final Handler mHandler = new H(Looper.getMainLooper());
103     private long mShowDelayMs = 0L;
104     private final IBinder mWindowToken = new Binder();
105     private final CommandQueue mCommandQueue;
106 
107     private ClingWindowView mClingWindow;
108     /** The last {@link WindowManager} that is used to add the confirmation window. */
109     @Nullable
110     private WindowManager mWindowManager;
111     /**
112      * The WindowContext that is registered with {@link #mWindowManager} with options to specify the
113      * {@link RootDisplayArea} to attach the confirmation window.
114      */
115     @Nullable
116     private Context mWindowContext;
117     /**
118      * The root display area feature id that the {@link #mWindowContext} is attaching to.
119      */
120     private int mWindowContextRootDisplayAreaId = FEATURE_UNDEFINED;
121     // Local copy of vr mode enabled state, to avoid calling into VrManager with
122     // the lock held.
123     private boolean mVrModeEnabled = false;
124     private boolean mCanSystemBarsBeShownByUser = true;
125     private int mLockTaskState = LOCK_TASK_MODE_NONE;
126     private boolean mNavBarEmpty;
127 
128     private ContentObserver mContentObserver;
129 
130     @Inject
ImmersiveModeConfirmation(Context context, CommandQueue commandQueue, SecureSettings secureSettings)131     public ImmersiveModeConfirmation(Context context, CommandQueue commandQueue,
132                                      SecureSettings secureSettings) {
133         mSysUiContext = context;
134         final Display display = mSysUiContext.getDisplay();
135         mDisplayContext = display.getDisplayId() == DEFAULT_DISPLAY
136                 ? mSysUiContext : mSysUiContext.createDisplayContext(display);
137         mCommandQueue = commandQueue;
138         mSecureSettings = secureSettings;
139     }
140 
loadSetting(int currentUserId)141     boolean loadSetting(int currentUserId) {
142         final boolean wasConfirmed = sConfirmed;
143         sConfirmed = false;
144         if (DEBUG) Log.d(TAG, String.format("loadSetting() currentUserId=%d", currentUserId));
145         String value = null;
146         try {
147             value = mSecureSettings.getStringForUser(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
148                     UserHandle.USER_CURRENT);
149             sConfirmed = CONFIRMED.equals(value);
150             if (DEBUG) Log.d(TAG, "Loaded sConfirmed=" + sConfirmed);
151         } catch (Throwable t) {
152             Log.w(TAG, "Error loading confirmations, value=" + value, t);
153         }
154         return sConfirmed != wasConfirmed;
155     }
156 
saveSetting(Context context)157     private static void saveSetting(Context context) {
158         if (DEBUG) Log.d(TAG, "saveSetting()");
159         try {
160             final String value = sConfirmed ? CONFIRMED : null;
161             Settings.Secure.putStringForUser(context.getContentResolver(),
162                     Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
163                     value,
164                     UserHandle.USER_CURRENT);
165             if (DEBUG) Log.d(TAG, "Saved value=" + value);
166         } catch (Throwable t) {
167             Log.w(TAG, "Error saving confirmations, sConfirmed=" + sConfirmed, t);
168         }
169     }
170 
171     @Override
onDisplayRemoved(int displayId)172     public void onDisplayRemoved(int displayId) {
173         if (displayId != mSysUiContext.getDisplayId()) {
174             return;
175         }
176         mHandler.removeMessages(H.SHOW);
177         mHandler.removeMessages(H.HIDE);
178         IVrManager vrManager = IVrManager.Stub.asInterface(
179                 ServiceManager.getService(Context.VR_SERVICE));
180         if (vrManager != null) {
181             try {
182                 vrManager.unregisterListener(mVrStateCallbacks);
183             } catch (RemoteException ex) {
184             }
185         }
186         mCommandQueue.removeCallback(this);
187     }
188 
onSettingChanged(int currentUserId)189     private void onSettingChanged(int currentUserId) {
190         final boolean changed = loadSetting(currentUserId);
191         // Remove the window if the setting changes to be confirmed.
192         if (changed && sConfirmed) {
193             mHandler.sendEmptyMessage(H.HIDE);
194         }
195     }
196 
197     @Override
immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode)198     public void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {
199         mHandler.removeMessages(H.SHOW);
200         if (isImmersiveMode) {
201             if (DEBUG) Log.d(TAG, "immersiveModeChanged() sConfirmed=" +  sConfirmed);
202             boolean userSetupComplete = (mSecureSettings.getIntForUser(
203                     Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0);
204 
205             if ((DEBUG_SHOW_EVERY_TIME || !sConfirmed)
206                     && userSetupComplete
207                     && !mVrModeEnabled
208                     && mCanSystemBarsBeShownByUser
209                     && !mNavBarEmpty
210                     && !UserManager.isDeviceInDemoMode(mDisplayContext)
211                     && (mLockTaskState != LOCK_TASK_MODE_LOCKED)) {
212                 final Message msg = mHandler.obtainMessage(
213                         H.SHOW);
214                 msg.arg1 = rootDisplayAreaId;
215                 mHandler.sendMessageDelayed(msg, mShowDelayMs);
216             }
217         } else {
218             mHandler.sendEmptyMessage(H.HIDE);
219         }
220     }
221 
222     @Override
disable(int displayId, int disableFlag, int disableFlag2, boolean animate)223     public void disable(int displayId, int disableFlag, int disableFlag2, boolean animate) {
224         if (mSysUiContext.getDisplayId() != displayId) {
225             return;
226         }
227         final int disableNavigationBar = (DISABLE_HOME | DISABLE_BACK | DISABLE_RECENT);
228         mNavBarEmpty = (disableFlag & disableNavigationBar) == disableNavigationBar;
229     }
230 
231     @Override
confirmImmersivePrompt()232     public void confirmImmersivePrompt() {
233         if (mClingWindow != null) {
234             if (DEBUG) Log.d(TAG, "confirmImmersivePrompt()");
235             mHandler.post(mConfirm);
236         }
237     }
238 
handleHide()239     private void handleHide() {
240         if (mClingWindow != null) {
241             if (DEBUG) Log.d(TAG, "Hiding immersive mode confirmation");
242             if (mWindowManager != null) {
243                 try {
244                     mWindowManager.removeView(mClingWindow);
245                 } catch (WindowManager.InvalidDisplayException e) {
246                     Log.w(TAG, "Fail to hide the immersive confirmation window because of "
247                             + e);
248                 }
249                 mWindowManager = null;
250                 mWindowContext = null;
251             }
252             mClingWindow = null;
253         }
254     }
255 
getClingWindowLayoutParams()256     private WindowManager.LayoutParams getClingWindowLayoutParams() {
257         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
258                 ViewGroup.LayoutParams.MATCH_PARENT,
259                 ViewGroup.LayoutParams.MATCH_PARENT,
260                 IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE,
261                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
262                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
263                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
264                 PixelFormat.TRANSLUCENT);
265         lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
266         // Trusted overlay so touches outside the touchable area are allowed to pass through
267         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
268                 | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
269                 | WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW;
270         lp.setTitle("ImmersiveModeConfirmation");
271         lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
272         lp.token = getWindowToken();
273         return lp;
274     }
275 
getBubbleLayoutParams()276     private FrameLayout.LayoutParams getBubbleLayoutParams() {
277         return new FrameLayout.LayoutParams(
278                 mSysUiContext.getResources().getDimensionPixelSize(
279                         R.dimen.immersive_mode_cling_width),
280                 ViewGroup.LayoutParams.WRAP_CONTENT,
281                 Gravity.CENTER_HORIZONTAL | Gravity.TOP);
282     }
283 
284     /**
285      * @return the window token that's used by all ImmersiveModeConfirmation windows.
286      */
getWindowToken()287     IBinder getWindowToken() {
288         return mWindowToken;
289     }
290 
291     @Override
start()292     public void start() {
293         if (CLIENT_TRANSIENT || CLIENT_IMMERSIVE_CONFIRMATION) {
294             mCommandQueue.addCallback(this);
295 
296             final Resources r = mSysUiContext.getResources();
297             mShowDelayMs = r.getInteger(R.integer.dock_enter_exit_duration) * 3L;
298             mCanSystemBarsBeShownByUser = !r.getBoolean(
299                     R.bool.config_remoteInsetsControllerControlsSystemBars) || r.getBoolean(
300                     R.bool.config_remoteInsetsControllerSystemBarsCanBeShownByUserAction);
301             IVrManager vrManager = IVrManager.Stub.asInterface(
302                     ServiceManager.getService(Context.VR_SERVICE));
303             if (vrManager != null) {
304                 try {
305                     mVrModeEnabled = vrManager.getVrModeState();
306                     vrManager.registerListener(mVrStateCallbacks);
307                     mVrStateCallbacks.onVrStateChanged(mVrModeEnabled);
308                 } catch (RemoteException e) {
309                     // Ignore, we cannot do anything if we failed to access vr manager.
310                 }
311             }
312             TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
313             mContentObserver = new ContentObserver(mHandler) {
314                 @Override
315                 public void onChange(boolean selfChange) {
316                     onSettingChanged(mSysUiContext.getUserId());
317                 }
318             };
319 
320             // Register to listen for changes in Settings.Secure settings.
321             mSecureSettings.registerContentObserverForUser(
322                     Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, mContentObserver,
323                     UserHandle.USER_CURRENT);
324             mSecureSettings.registerContentObserverForUser(
325                     Settings.Secure.USER_SETUP_COMPLETE, mContentObserver,
326                     UserHandle.USER_CURRENT);
327         }
328     }
329 
330     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
331         @Override
332         public void onVrStateChanged(boolean enabled) {
333             mVrModeEnabled = enabled;
334             if (mVrModeEnabled) {
335                 mHandler.removeMessages(H.SHOW);
336                 mHandler.sendEmptyMessage(H.HIDE);
337             }
338         }
339     };
340 
341     private class ClingWindowView extends FrameLayout {
342         private static final int BGCOLOR = 0x80000000;
343         private static final int OFFSET_DP = 96;
344         private static final int ANIMATION_DURATION = 250;
345 
346         private final Runnable mConfirm;
347         private final ColorDrawable mColor = new ColorDrawable(0);
348         private final Interpolator mInterpolator;
349         private ValueAnimator mColorAnim;
350         private ViewGroup mClingLayout;
351 
352         private Runnable mUpdateLayoutRunnable = new Runnable() {
353             @Override
354             public void run() {
355                 if (mClingLayout != null && mClingLayout.getParent() != null) {
356                     mClingLayout.setLayoutParams(getBubbleLayoutParams());
357                 }
358             }
359         };
360 
361         private ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener =
362                 new ViewTreeObserver.OnComputeInternalInsetsListener() {
363                     private final int[] mTmpInt2 = new int[2];
364 
365                     @Override
366                     public void onComputeInternalInsets(
367                             ViewTreeObserver.InternalInsetsInfo inoutInfo) {
368                         // Set touchable region to cover the cling layout.
369                         mClingLayout.getLocationInWindow(mTmpInt2);
370                         inoutInfo.setTouchableInsets(
371                                 ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
372                         inoutInfo.touchableRegion.set(
373                                 mTmpInt2[0],
374                                 mTmpInt2[1],
375                                 mTmpInt2[0] + mClingLayout.getWidth(),
376                                 mTmpInt2[1] + mClingLayout.getHeight());
377                     }
378                 };
379 
380         private BroadcastReceiver mReceiver = new BroadcastReceiver() {
381             @Override
382             public void onReceive(Context context, Intent intent) {
383                 if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
384                     post(mUpdateLayoutRunnable);
385                 }
386             }
387         };
388 
ClingWindowView(Context context, Runnable confirm)389         ClingWindowView(Context context, Runnable confirm) {
390             super(context);
391             mConfirm = confirm;
392             setBackground(mColor);
393             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
394             mInterpolator = AnimationUtils
395                     .loadInterpolator(mContext, android.R.interpolator.linear_out_slow_in);
396         }
397 
398         @Override
onAttachedToWindow()399         public void onAttachedToWindow() {
400             super.onAttachedToWindow();
401 
402             DisplayMetrics metrics = new DisplayMetrics();
403             mContext.getDisplay().getMetrics(metrics);
404             float density = metrics.density;
405 
406             getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
407 
408             // create the confirmation cling
409             mClingLayout = (ViewGroup)
410                     View.inflate(mSysUiContext, R.layout.immersive_mode_cling, null);
411 
412             TypedArray ta = mDisplayContext.obtainStyledAttributes(
413                     new int[]{android.R.attr.colorAccent});
414             int colorAccent = ta.getColor(0, 0);
415             ta.recycle();
416             mClingLayout.setBackgroundColor(colorAccent);
417             ImageView expandMore = mClingLayout.findViewById(R.id.immersive_cling_ic_expand_more);
418             if (expandMore != null) {
419                 expandMore.setImageTintList(ColorStateList.valueOf(colorAccent));
420             }
421             ImageView lightBgCirc = mClingLayout.findViewById(R.id.immersive_cling_back_bg_light);
422             if (lightBgCirc != null) {
423                 // Set transparency to 50%
424                 lightBgCirc.setImageAlpha(128);
425             }
426 
427             final Button ok = mClingLayout.findViewById(R.id.ok);
428             ok.setOnClickListener(new OnClickListener() {
429                 @Override
430                 public void onClick(View v) {
431                     mConfirm.run();
432                 }
433             });
434             addView(mClingLayout, getBubbleLayoutParams());
435 
436             if (ActivityManager.isHighEndGfx()) {
437                 final View cling = mClingLayout;
438                 cling.setAlpha(0f);
439                 cling.setTranslationY(-OFFSET_DP * density);
440 
441                 postOnAnimation(new Runnable() {
442                     @Override
443                     public void run() {
444                         cling.animate()
445                                 .alpha(1f)
446                                 .translationY(0)
447                                 .setDuration(ANIMATION_DURATION)
448                                 .setInterpolator(mInterpolator)
449                                 .withLayer()
450                                 .start();
451 
452                         mColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), 0, BGCOLOR);
453                         mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
454                             @Override
455                             public void onAnimationUpdate(ValueAnimator animation) {
456                                 final int c = (Integer) animation.getAnimatedValue();
457                                 mColor.setColor(c);
458                             }
459                         });
460                         mColorAnim.setDuration(ANIMATION_DURATION);
461                         mColorAnim.setInterpolator(mInterpolator);
462                         mColorAnim.start();
463                     }
464                 });
465             } else {
466                 mColor.setColor(BGCOLOR);
467             }
468 
469             mContext.registerReceiver(mReceiver,
470                     new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
471         }
472 
473         @Override
onDetachedFromWindow()474         public void onDetachedFromWindow() {
475             mContext.unregisterReceiver(mReceiver);
476         }
477 
478         @Override
onTouchEvent(MotionEvent motion)479         public boolean onTouchEvent(MotionEvent motion) {
480             return true;
481         }
482 
483         @Override
onApplyWindowInsets(WindowInsets insets)484         public WindowInsets onApplyWindowInsets(WindowInsets insets) {
485             // we will be hiding the nav bar, so layout as if it's already hidden
486             return new WindowInsets.Builder(insets).setInsets(
487                     Type.systemBars(), Insets.NONE).build();
488         }
489     }
490 
491     /**
492      * To get window manager for the display.
493      *
494      * @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the
495      *         confirmation window.
496      */
497     @NonNull
createWindowManager(int rootDisplayAreaId)498     private WindowManager createWindowManager(int rootDisplayAreaId) {
499         if (mWindowManager != null) {
500             throw new IllegalStateException(
501                     "Must not create a new WindowManager while there is an existing one");
502         }
503         // Create window context to specify the RootDisplayArea
504         final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
505         mWindowContextRootDisplayAreaId = rootDisplayAreaId;
506         mWindowContext = mDisplayContext.createWindowContext(
507                 IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
508         mWindowManager = mWindowContext.getSystemService(WindowManager.class);
509         return mWindowManager;
510     }
511 
512     /**
513      * Returns options that specify the {@link RootDisplayArea} to attach the confirmation window.
514      *         {@code null} if the {@code rootDisplayAreaId} is {@link FEATURE_UNDEFINED}.
515      */
516     @Nullable
getOptionsForWindowContext(int rootDisplayAreaId)517     private Bundle getOptionsForWindowContext(int rootDisplayAreaId) {
518         // In case we don't care which root display area the window manager is specifying.
519         if (rootDisplayAreaId == FEATURE_UNDEFINED) {
520             return null;
521         }
522 
523         final Bundle options = new Bundle();
524         options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
525         return options;
526     }
527 
handleShow(int rootDisplayAreaId)528     private void handleShow(int rootDisplayAreaId) {
529         if (mClingWindow != null) {
530             if (rootDisplayAreaId == mWindowContextRootDisplayAreaId) {
531                 if (DEBUG) Log.d(TAG, "Immersive mode confirmation has already been shown");
532                 return;
533             } else {
534                 // Hide the existing confirmation before show a new one in the new root.
535                 if (DEBUG) Log.d(TAG, "Immersive mode confirmation was shown in a different root");
536                 handleHide();
537             }
538         }
539         if (DEBUG) Log.d(TAG, "Showing immersive mode confirmation");
540         mClingWindow = new ClingWindowView(mDisplayContext, mConfirm);
541         // show the confirmation
542         final WindowManager.LayoutParams lp = getClingWindowLayoutParams();
543         try {
544             createWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
545         } catch (WindowManager.InvalidDisplayException e) {
546             Log.w(TAG, "Fail to show the immersive confirmation window because of " + e);
547         }
548     }
549 
550     private final Runnable mConfirm = new Runnable() {
551         @Override
552         public void run() {
553             if (DEBUG) Log.d(TAG, "mConfirm.run()");
554             if (!sConfirmed) {
555                 sConfirmed = true;
556                 saveSetting(mDisplayContext);
557             }
558             handleHide();
559         }
560     };
561 
562     private final class H extends Handler {
563         private static final int SHOW = 1;
564         private static final int HIDE = 2;
565 
H(Looper looper)566         H(Looper looper) {
567             super(looper);
568         }
569 
570         @Override
handleMessage(Message msg)571         public void handleMessage(Message msg) {
572             if (!CLIENT_TRANSIENT && !CLIENT_IMMERSIVE_CONFIRMATION) {
573                 return;
574             }
575             switch(msg.what) {
576                 case SHOW:
577                     handleShow(msg.arg1);
578                     break;
579                 case HIDE:
580                     handleHide();
581                     break;
582             }
583         }
584     }
585 
586     @Override
onLockTaskModeChanged(int lockTaskState)587     public void onLockTaskModeChanged(int lockTaskState) {
588         mLockTaskState = lockTaskState;
589     }
590 }
591