• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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