1 /* 2 * Copyright (C) 2015 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; 17 18 import android.os.Message; 19 import android.support.annotation.NonNull; 20 import androidx.leanback.widget.VerticalGridView; 21 import android.util.Log; 22 import android.view.KeyEvent; 23 import android.view.View; 24 import com.android.tv.common.WeakHandler; 25 26 /** Listener to make focus change faster over time. */ 27 public class OnRepeatedKeyInterceptListener implements VerticalGridView.OnKeyInterceptListener { 28 private static final String TAG = "OnRepeatedKeyListener"; 29 private static final boolean DEBUG = false; 30 31 private static final int[] THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS = {2000, 5000}; 32 private static final int[] MAX_SKIPPED_VIEW_COUNT = {1, 4}; 33 private static final int MSG_MOVE_FOCUS = 1000; 34 35 private final VerticalGridView mView; 36 private final MyHandler mHandler = new MyHandler(this); 37 private int mDirection; 38 private boolean mFocusAccelerated; 39 private long mRepeatedKeyInterval; 40 OnRepeatedKeyInterceptListener(VerticalGridView view)41 public OnRepeatedKeyInterceptListener(VerticalGridView view) { 42 mView = view; 43 } 44 isFocusAccelerated()45 public boolean isFocusAccelerated() { 46 return mFocusAccelerated; 47 } 48 49 @Override onInterceptKeyEvent(KeyEvent event)50 public boolean onInterceptKeyEvent(KeyEvent event) { 51 mHandler.removeMessages(MSG_MOVE_FOCUS); 52 if (event.getKeyCode() != KeyEvent.KEYCODE_DPAD_UP 53 && event.getKeyCode() != KeyEvent.KEYCODE_DPAD_DOWN) { 54 return false; 55 } 56 57 long duration = event.getEventTime() - event.getDownTime(); 58 if (duration < THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS[0] || event.isCanceled()) { 59 mFocusAccelerated = false; 60 return false; 61 } 62 mDirection = 63 event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP ? View.FOCUS_UP : View.FOCUS_DOWN; 64 int skippedViewCount = MAX_SKIPPED_VIEW_COUNT[0]; 65 for (int i = 1; i < THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS.length; ++i) { 66 if (THRESHOLD_FAST_FOCUS_CHANGE_TIME_MS[i] < duration) { 67 skippedViewCount = MAX_SKIPPED_VIEW_COUNT[i]; 68 } else { 69 break; 70 } 71 } 72 if (event.getAction() == KeyEvent.ACTION_DOWN) { 73 mRepeatedKeyInterval = duration / event.getRepeatCount(); 74 mFocusAccelerated = true; 75 } else { 76 // HACK: we move focus skippedViewCount times more even after ACTION_UP. Without this 77 // hack, a focused view's position doesn't reach to the desired position 78 // in ProgramGrid. 79 mFocusAccelerated = false; 80 } 81 for (int i = 0; i < skippedViewCount; ++i) { 82 mHandler.sendEmptyMessageDelayed( 83 MSG_MOVE_FOCUS, mRepeatedKeyInterval * i / (skippedViewCount + 1)); 84 } 85 if (DEBUG) Log.d(TAG, "onInterceptKeyEvent: focused view " + mView.findFocus()); 86 return false; 87 } 88 89 private static class MyHandler extends WeakHandler<OnRepeatedKeyInterceptListener> { MyHandler(OnRepeatedKeyInterceptListener listener)90 private MyHandler(OnRepeatedKeyInterceptListener listener) { 91 super(listener); 92 } 93 94 @Override handleMessage(Message msg, @NonNull OnRepeatedKeyInterceptListener listener)95 public void handleMessage(Message msg, @NonNull OnRepeatedKeyInterceptListener listener) { 96 if (msg.what == MSG_MOVE_FOCUS) { 97 View focused = listener.mView.findFocus(); 98 if (DEBUG) Log.d(TAG, "MSG_MOVE_FOCUS: focused view " + focused); 99 if (focused != null) { 100 View v = focused.focusSearch(listener.mDirection); 101 if (v != null && v != focused) { 102 v.requestFocus(listener.mDirection); 103 } 104 } 105 } 106 } 107 } 108 } 109