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