1 /*
2  * Copyright (C) 2018 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 package com.android.tv.ui.hideable;
17 
18 import android.content.Context;
19 import android.os.Looper;
20 import android.os.Message;
21 import android.support.annotation.NonNull;
22 import android.support.annotation.UiThread;
23 import android.support.annotation.VisibleForTesting;
24 import android.view.accessibility.AccessibilityManager;
25 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
26 import com.android.tv.common.WeakHandler;
27 
28 /**
29  * Schedules a view element to be hidden after a delay.
30  *
31  * <p>When accessibility is turned on elements are not automatically hidden.
32  *
33  * <p>Users of this class must pass it to {@link
34  * AccessibilityManager#addAccessibilityStateChangeListener(AccessibilityStateChangeListener)} and
35  * {@link
36  * AccessibilityManager#removeAccessibilityStateChangeListener(AccessibilityStateChangeListener)}
37  * during the appropriate live cycle event, or handle calling {@link
38  * #onAccessibilityStateChanged(boolean)}.
39  */
40 @UiThread
41 public final class AutoHideScheduler implements AccessibilityStateChangeListener {
42     private static final int MSG_HIDE = 1;
43 
44     private final HideHandler mHandler;
45     private final Runnable mRunnable;
46 
AutoHideScheduler(Context context, Runnable runnable)47     public AutoHideScheduler(Context context, Runnable runnable) {
48         this(
49                 runnable,
50                 context.getSystemService(AccessibilityManager.class),
51                 Looper.getMainLooper());
52     }
53 
54     @VisibleForTesting
AutoHideScheduler(Runnable runnable, AccessibilityManager accessibilityManager, Looper looper)55     AutoHideScheduler(Runnable runnable, AccessibilityManager accessibilityManager, Looper looper) {
56         // Keep a reference here because HideHandler only has a weak reference to it.
57         mRunnable = runnable;
58         mHandler = new HideHandler(looper, mRunnable);
59         mHandler.setAllowAutoHide(!accessibilityManager.isEnabled());
60     }
61 
cancel()62     public void cancel() {
63         mHandler.removeMessages(MSG_HIDE);
64     }
65 
schedule(long delayMs)66     public void schedule(long delayMs) {
67         cancel();
68         if (mHandler.mAllowAutoHide) {
69             mHandler.sendEmptyMessageDelayed(MSG_HIDE, delayMs);
70         }
71     }
72 
73     @Override
onAccessibilityStateChanged(boolean enabled)74     public void onAccessibilityStateChanged(boolean enabled) {
75         mHandler.onAccessibilityStateChanged(enabled);
76     }
77 
isScheduled()78     public boolean isScheduled() {
79         return mHandler.hasMessages(MSG_HIDE);
80     }
81 
82     private static class HideHandler extends WeakHandler<Runnable>
83             implements AccessibilityStateChangeListener {
84 
85         private boolean mAllowAutoHide;
86 
HideHandler(Looper looper, Runnable hideRunner)87         public HideHandler(Looper looper, Runnable hideRunner) {
88             super(looper, hideRunner);
89         }
90 
91         @Override
handleMessage(Message msg, @NonNull Runnable runnable)92         protected void handleMessage(Message msg, @NonNull Runnable runnable) {
93             switch (msg.what) {
94                 case MSG_HIDE:
95                     if (mAllowAutoHide) {
96                         runnable.run();
97                     }
98                     break;
99                 default:
100                     // do nothing
101             }
102         }
103 
setAllowAutoHide(boolean mAllowAutoHide)104         public void setAllowAutoHide(boolean mAllowAutoHide) {
105             this.mAllowAutoHide = mAllowAutoHide;
106         }
107 
108         @Override
onAccessibilityStateChanged(boolean enabled)109         public void onAccessibilityStateChanged(boolean enabled) {
110             mAllowAutoHide = !enabled;
111         }
112     }
113 }
114