Merge "Let rule could decide dispatching excess when detecting combination key" into tm-dev am: 569b52c826

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17062395

Change-Id: I4d378e6db75feb745a66fe0abbc119cbb98bf8bd
This commit is contained in:
Arthur Hung
2022-03-07 06:18:43 +00:00
committed by Automerger Merge Worker
3 changed files with 80 additions and 31 deletions

View File

@@ -48,7 +48,7 @@ public class KeyCombinationManager {
// The rule has been triggered by current keys. // The rule has been triggered by current keys.
@GuardedBy("mLock") @GuardedBy("mLock")
private TwoKeysCombinationRule mTriggeredRule; private TwoKeysCombinationRule mTriggeredRule;
private final Handler mHandler = new Handler(); private final Handler mHandler;
// Keys in a key combination must be pressed within this interval of each other. // Keys in a key combination must be pressed within this interval of each other.
private static final long COMBINE_KEY_DELAY_MILLIS = 150; private static final long COMBINE_KEY_DELAY_MILLIS = 150;
@@ -93,6 +93,11 @@ public class KeyCombinationManager {
return false; return false;
} }
// The excessive delay before it dispatching to client.
long getKeyInterceptDelayMs() {
return COMBINE_KEY_DELAY_MILLIS;
}
abstract void execute(); abstract void execute();
abstract void cancel(); abstract void cancel();
@@ -103,7 +108,8 @@ public class KeyCombinationManager {
} }
} }
public KeyCombinationManager() { public KeyCombinationManager(Handler handler) {
mHandler = handler;
} }
void addRule(TwoKeysCombinationRule rule) { void addRule(TwoKeysCombinationRule rule) {
@@ -195,11 +201,19 @@ public class KeyCombinationManager {
*/ */
long getKeyInterceptTimeout(int keyCode) { long getKeyInterceptTimeout(int keyCode) {
synchronized (mLock) { synchronized (mLock) {
if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) { if (mDownTimes.get(keyCode) == 0) {
return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
}
return 0; return 0;
} }
long delayMs = 0;
for (final TwoKeysCombinationRule rule : mActiveRules) {
if (rule.shouldInterceptKey(keyCode)) {
delayMs = Math.max(delayMs, rule.getKeyInterceptDelayMs());
}
}
// Make sure the delay is less than COMBINE_KEY_DELAY_MILLIS.
delayMs = Math.min(delayMs, COMBINE_KEY_DELAY_MILLIS);
return mDownTimes.get(keyCode) + delayMs;
}
} }
/** /**

View File

@@ -2124,7 +2124,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} }
private void initKeyCombinationRules() { private void initKeyCombinationRules() {
mKeyCombinationManager = new KeyCombinationManager(); mKeyCombinationManager = new KeyCombinationManager(mHandler);
final boolean screenshotChordEnabled = mContext.getResources().getBoolean( final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableScreenshotChord); com.android.internal.R.bool.config_enableScreenshotChord);
@@ -2215,11 +2215,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mBackKeyHandled = true; mBackKeyHandled = true;
interceptAccessibilityGestureTv(); interceptAccessibilityGestureTv();
} }
@Override @Override
void cancel() { void cancel() {
cancelAccessibilityGestureTv(); cancelAccessibilityGestureTv();
} }
@Override
long getKeyInterceptDelayMs() {
// Use a timeout of 0 to prevent additional latency in processing of
// this key. This will potentially cause some unwanted UI actions if the
// user does end up triggering the key combination later, but in most
// cases, the user will simply hit a single key, and this will allow us
// to process it without first waiting to see if the combination is
// going to be triggered.
return 0;
}
}); });
mKeyCombinationManager.addRule( mKeyCombinationManager.addRule(
@@ -2229,11 +2238,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mBackKeyHandled = true; mBackKeyHandled = true;
interceptBugreportGestureTv(); interceptBugreportGestureTv();
} }
@Override @Override
void cancel() { void cancel() {
cancelBugreportGestureTv(); cancelBugreportGestureTv();
} }
@Override
long getKeyInterceptDelayMs() {
return 0;
}
}); });
} }
} }

View File

@@ -36,6 +36,9 @@ import androidx.test.filters.SmallTest;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** /**
* Test class for {@link KeyCombinationManager}. * Test class for {@link KeyCombinationManager}.
* *
@@ -47,16 +50,18 @@ import org.junit.Test;
public class KeyCombinationTests { public class KeyCombinationTests {
private KeyCombinationManager mKeyCombinationManager; private KeyCombinationManager mKeyCombinationManager;
private boolean mAction1Triggered = false; private final CountDownLatch mAction1Triggered = new CountDownLatch(1);
private boolean mAction2Triggered = false; private final CountDownLatch mAction2Triggered = new CountDownLatch(1);
private boolean mAction3Triggered = false; private final CountDownLatch mAction3Triggered = new CountDownLatch(1);
private boolean mPreCondition = true; private boolean mPreCondition = true;
private static final long SCHEDULE_TIME = 300; private static final long SCHEDULE_TIME = 300;
private Handler mHandler;
@Before @Before
public void setUp() { public void setUp() {
mKeyCombinationManager = new KeyCombinationManager(); mHandler = new Handler(Looper.getMainLooper());
mKeyCombinationManager = new KeyCombinationManager(mHandler);
initKeyCombinationRules(); initKeyCombinationRules();
} }
@@ -67,7 +72,7 @@ public class KeyCombinationTests {
KEYCODE_POWER) { KEYCODE_POWER) {
@Override @Override
void execute() { void execute() {
mAction1Triggered = true; mAction1Triggered.countDown();
} }
@Override @Override
@@ -86,12 +91,17 @@ public class KeyCombinationTests {
@Override @Override
void execute() { void execute() {
mAction2Triggered = true; mAction2Triggered.countDown();
} }
@Override @Override
void cancel() { void cancel() {
} }
@Override
long getKeyInterceptDelayMs() {
return 0;
}
}); });
// Rule 3 : power + volume_up schedule and trigger action after timeout. // Rule 3 : power + volume_up schedule and trigger action after timeout.
@@ -100,10 +110,9 @@ public class KeyCombinationTests {
final Runnable mAction = new Runnable() { final Runnable mAction = new Runnable() {
@Override @Override
public void run() { public void run() {
mAction3Triggered = true; mAction3Triggered.countDown();
} }
}; };
final Handler mHandler = new Handler(Looper.getMainLooper());
@Override @Override
void execute() { void execute() {
@@ -149,60 +158,74 @@ public class KeyCombinationTests {
} }
@Test @Test
public void testTriggerRule() { public void testTriggerRule() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis(); final long eventTime = SystemClock.uptimeMillis();
pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN); pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
assertTrue(mAction1Triggered); assertTrue(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN); pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN);
assertTrue(mAction2Triggered); assertTrue(mAction2Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP, SCHEDULE_TIME + 50); pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP, SCHEDULE_TIME + 50);
assertTrue(mAction3Triggered); assertTrue(mAction3Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
} }
/** /**
* Nothing should happen if there is no definition. * Nothing should happen if there is no definition.
*/ */
@Test @Test
public void testNotTrigger_NoRule() { public void testNotTrigger_NoRule() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis(); final long eventTime = SystemClock.uptimeMillis();
pressKeys(eventTime, KEYCODE_BACK, eventTime, KEYCODE_VOLUME_DOWN); pressKeys(eventTime, KEYCODE_BACK, eventTime, KEYCODE_VOLUME_DOWN);
assertFalse(mAction1Triggered); assertFalse(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
assertFalse(mAction2Triggered); assertFalse(mAction2Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
assertFalse(mAction3Triggered); assertFalse(mAction3Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
} }
/** /**
* Nothing should happen if the interval of press time is too long. * Nothing should happen if the interval of press time is too long.
*/ */
@Test @Test
public void testNotTrigger_Interval() { public void testNotTrigger_Interval() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis(); final long eventTime = SystemClock.uptimeMillis();
final long earlyEventTime = eventTime - 200; // COMBINE_KEY_DELAY_MILLIS = 150; final long earlyEventTime = eventTime - 200; // COMBINE_KEY_DELAY_MILLIS = 150;
pressKeys(earlyEventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN); pressKeys(earlyEventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
assertFalse(mAction1Triggered); assertFalse(mAction1Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
} }
/** /**
* Nothing should happen if the condition is false. * Nothing should happen if the condition is false.
*/ */
@Test @Test
public void testNotTrigger_Condition() { public void testNotTrigger_Condition() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis(); final long eventTime = SystemClock.uptimeMillis();
// we won't trigger action 2 because the condition is false. // we won't trigger action 2 because the condition is false.
mPreCondition = false; mPreCondition = false;
pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN); pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN);
assertFalse(mAction2Triggered); assertFalse(mAction2Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
} }
/** /**
* Nothing should happen if the keys released too early. * Nothing should happen if the keys released too early.
*/ */
@Test @Test
public void testNotTrigger_EarlyRelease() { public void testNotTrigger_EarlyRelease() throws InterruptedException {
final long eventTime = SystemClock.uptimeMillis(); final long eventTime = SystemClock.uptimeMillis();
pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP); pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP);
assertFalse(mAction3Triggered); assertFalse(mAction3Triggered.await(SCHEDULE_TIME, TimeUnit.MILLISECONDS));
}
/**
* The KeyInterceptTimeout should return the max timeout value.
*/
@Test
public void testKeyInterceptTimeout() {
final long eventTime = SystemClock.uptimeMillis();
final KeyEvent firstKeyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
KEYCODE_VOLUME_UP, 0 /* repeat */, 0 /* metaState */);
// Press KEYCODE_VOLUME_UP would activate rule2 and rule3,
// and rule2's intercept delay is 0.
mKeyCombinationManager.interceptKey(firstKeyDown, true);
assertTrue(mKeyCombinationManager.getKeyInterceptTimeout(KEYCODE_VOLUME_UP) > eventTime);
} }
} }