1 /* 2 * Copyright 2020 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.server.media; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.PendingIntent; 23 import android.content.Context; 24 import android.media.session.ISessionManager; 25 import android.media.session.MediaSession; 26 import android.os.Binder; 27 import android.view.KeyEvent; 28 import android.view.ViewConfiguration; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.util.HashMap; 33 import java.util.Map; 34 35 /** 36 * Provides a way to customize behavior for media key events. 37 * <p> 38 * In order to override the implementation of the single/double/triple tap or long press, 39 * {@link #setOverriddenKeyEvents(int, int)} should be called for each key code with the 40 * overridden {@link KeyEventType} bit value set, and the corresponding method, 41 * {@link #onSingleTap(KeyEvent)}, {@link #onDoubleTap(KeyEvent)}, 42 * {@link #onTripleTap(KeyEvent)}, {@link #onLongPress(KeyEvent)} should be implemented. 43 * <p> 44 * Note: When instantiating this class, {@link MediaSessionService} will only use the constructor 45 * without any parameters. 46 */ 47 public abstract class MediaKeyDispatcher { 48 @IntDef(flag = true, value = { 49 KEY_EVENT_SINGLE_TAP, 50 KEY_EVENT_DOUBLE_TAP, 51 KEY_EVENT_TRIPLE_TAP, 52 KEY_EVENT_LONG_PRESS 53 }) 54 @Retention(RetentionPolicy.SOURCE) 55 @interface KeyEventType {} 56 static final int KEY_EVENT_SINGLE_TAP = 1 << 0; 57 static final int KEY_EVENT_DOUBLE_TAP = 1 << 1; 58 static final int KEY_EVENT_TRIPLE_TAP = 1 << 2; 59 static final int KEY_EVENT_LONG_PRESS = 1 << 3; 60 61 private Map<Integer, Integer> mOverriddenKeyEvents; 62 MediaKeyDispatcher(Context context)63 public MediaKeyDispatcher(Context context) { 64 // Constructor used for reflection 65 mOverriddenKeyEvents = new HashMap<>(); 66 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY, 0); 67 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PAUSE, 0); 68 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0); 69 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MUTE, 0); 70 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_HEADSETHOOK, 0); 71 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_STOP, 0); 72 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_NEXT, 0); 73 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, 0); 74 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_DOWN, 0); 75 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_UP, 0); 76 mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_MUTE, 0); 77 } 78 79 // TODO: Move this method into MediaSessionPolicyProvider.java for better readability. 80 /** 81 * Implement this to customize the logic for which MediaSession should consume which key event. 82 * 83 * Note: This session will have greater priority over the {@link PendingIntent} returned from 84 * {@link #getMediaButtonReceiver(KeyEvent, int, boolean)}. 85 * 86 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons. 87 * @param uid the uid value retrieved by calling {@link Binder#getCallingUid()} from 88 * {@link ISessionManager#dispatchMediaKeyEvent(String, boolean, KeyEvent, boolean)} 89 * @param asSystemService {@code true} if the event came from the system service via hardware 90 * devices. {@code false} if the event came from the app process through key injection. 91 * @return a {@link MediaSession.Token} instance that should consume the given key event. 92 */ 93 @Nullable getMediaSession(@onNull KeyEvent keyEvent, int uid, boolean asSystemService)94 MediaSession.Token getMediaSession(@NonNull KeyEvent keyEvent, int uid, 95 boolean asSystemService) { 96 return null; 97 } 98 99 /** 100 * Implement this to customize the logic for which MediaButtonReceiver should consume a 101 * dispatched key event. 102 * <p> 103 * This pending intent will have lower priority over the {@link MediaSession.Token} 104 * returned from {@link #getMediaSession(KeyEvent, int, boolean)}. 105 * <p> 106 * Use a pending intent with an explicit intent; setting a pending intent with an implicit 107 * intent that cannot be resolved to a certain component name will fail. 108 * 109 * @return a {@link PendingIntent} instance that should receive the dispatched key event. 110 */ 111 @Nullable getMediaButtonReceiver(@onNull KeyEvent keyEvent, int uid, boolean asSystemService)112 PendingIntent getMediaButtonReceiver(@NonNull KeyEvent keyEvent, int uid, 113 boolean asSystemService) { 114 return null; 115 } 116 117 /** 118 * Gets the map of key code -> {@link KeyEventType} that have been overridden. 119 * 120 * <p>For the list of relevant key codes, see {@link KeyEvent#isMediaSessionKey(int)}. 121 */ 122 @KeyEventType getOverriddenKeyEvents()123 Map<Integer, Integer> getOverriddenKeyEvents() { 124 return mOverriddenKeyEvents; 125 } 126 isSingleTapOverridden(@eyEventType int overriddenKeyEvents)127 static boolean isSingleTapOverridden(@KeyEventType int overriddenKeyEvents) { 128 return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_SINGLE_TAP) != 0; 129 } 130 isDoubleTapOverridden(@eyEventType int overriddenKeyEvents)131 static boolean isDoubleTapOverridden(@KeyEventType int overriddenKeyEvents) { 132 return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_DOUBLE_TAP) != 0; 133 } 134 isTripleTapOverridden(@eyEventType int overriddenKeyEvents)135 static boolean isTripleTapOverridden(@KeyEventType int overriddenKeyEvents) { 136 return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_TRIPLE_TAP) != 0; 137 } 138 isLongPressOverridden(@eyEventType int overriddenKeyEvents)139 static boolean isLongPressOverridden(@KeyEventType int overriddenKeyEvents) { 140 return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_LONG_PRESS) != 0; 141 } 142 143 /** 144 * Sets the value of the given key event type flagged with overridden {@link KeyEventType} to 145 * the given key code. If called multiple times for the same key code, will be overwritten to 146 * the most recently called {@link KeyEventType} value. 147 * <p> 148 * The list of valid key codes are the following: 149 * <ul> 150 * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY} 151 * <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE} 152 * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} 153 * <li> {@link KeyEvent#KEYCODE_MUTE} 154 * <li> {@link KeyEvent#KEYCODE_HEADSETHOOK} 155 * <li> {@link KeyEvent#KEYCODE_MEDIA_STOP} 156 * <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT} 157 * <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS} 158 * <li> {@link KeyEvent#KEYCODE_VOLUME_DOWN} 159 * <li> {@link KeyEvent#KEYCODE_VOLUME_UP} 160 * <li> {@link KeyEvent#KEYCODE_VOLUME_MUTE} 161 * </ul> 162 * @see {@link KeyEvent#isMediaSessionKey(int)} 163 * @param keyCode 164 */ setOverriddenKeyEvents(int keyCode, @KeyEventType int keyEventType)165 void setOverriddenKeyEvents(int keyCode, @KeyEventType int keyEventType) { 166 mOverriddenKeyEvents.put(keyCode, keyEventType); 167 } 168 169 /** 170 * Customized implementation for single tap event. Will be run if 171 * {@link #KEY_EVENT_SINGLE_TAP} flag is on for the corresponding key code from 172 * {@link #getOverriddenKeyEvents()}. 173 * 174 * It is considered a single tap if only one {@link KeyEvent} with the same 175 * {@link KeyEvent#getKeyCode()} is dispatched within 176 * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds. Change the 177 * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval. 178 * 179 * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent. 180 * 181 * @param keyEvent 182 */ onSingleTap(KeyEvent keyEvent)183 void onSingleTap(KeyEvent keyEvent) { 184 } 185 186 /** 187 * Customized implementation for double tap event. Will be run if 188 * {@link #KEY_EVENT_DOUBLE_TAP} flag is on for the corresponding key code from 189 * {@link #getOverriddenKeyEvents()}. 190 * 191 * It is considered a double tap if two {@link KeyEvent}s with the same 192 * {@link KeyEvent#getKeyCode()} are dispatched within 193 * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the 194 * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval. 195 * 196 * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent. 197 * 198 * @param keyEvent 199 */ onDoubleTap(KeyEvent keyEvent)200 void onDoubleTap(KeyEvent keyEvent) { 201 } 202 203 /** 204 * Customized implementation for triple tap event. Will be run if 205 * {@link #KEY_EVENT_TRIPLE_TAP} flag is on for the corresponding key code from 206 * {@link #getOverriddenKeyEvents()}. 207 * 208 * It is considered a triple tap if three {@link KeyEvent}s with the same 209 * {@link KeyEvent#getKeyCode()} are dispatched within 210 * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the 211 * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval. 212 * 213 * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent. 214 * 215 * @param keyEvent 216 */ onTripleTap(KeyEvent keyEvent)217 void onTripleTap(KeyEvent keyEvent) { 218 } 219 220 /** 221 * Customized implementation for long press event. Will be run if 222 * {@link #KEY_EVENT_LONG_PRESS} flag is on for the corresponding key code from 223 * {@link #getOverriddenKeyEvents()}. 224 * 225 * It is considered a long press if an {@link KeyEvent#ACTION_DOWN} key event is followed by 226 * another {@link KeyEvent#ACTION_DOWN} key event with {@link KeyEvent#FLAG_LONG_PRESS} 227 * enabled, and an {@link KeyEvent#getRepeatCount()} that is equal to 1. 228 * 229 * Note: This will be called for the following key events: 230 * <ul> 231 * <li>A {@link KeyEvent#ACTION_DOWN} KeyEvent with {@link KeyEvent#FLAG_LONG_PRESS} and 232 * {@link KeyEvent#getRepeatCount()} equal to 1</li> 233 * <li>Multiple {@link KeyEvent#ACTION_DOWN} KeyEvents with increasing 234 * {@link KeyEvent#getRepeatCount()}</li> 235 * <li>A {@link KeyEvent#ACTION_UP} KeyEvent</li> 236 * </ul> 237 * 238 * @param keyEvent 239 */ onLongPress(KeyEvent keyEvent)240 void onLongPress(KeyEvent keyEvent) { 241 } 242 } 243