• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 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 android.hardware.input;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.view.KeyEvent;
22 
23 import java.util.Objects;
24 
25 /**
26  * Data class to store input gesture data.
27  *
28  * <p>
29  * All input gestures are of type Trigger -> Action(Key gesture type, app data). And currently types
30  * of triggers supported are:
31  * - KeyTrigger (Keycode + modifierState)
32  * - TODO(b/365064144): Add Touchpad gesture based trigger
33  * </p>
34  * @hide
35  */
36 public final class InputGestureData {
37 
38     public static final int TOUCHPAD_GESTURE_TYPE_UNKNOWN = 0;
39     public static final int TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP = 1;
40 
41     @NonNull
42     private final AidlInputGestureData mInputGestureData;
43 
InputGestureData(@onNull AidlInputGestureData inputGestureData)44     public InputGestureData(@NonNull AidlInputGestureData inputGestureData) {
45         this.mInputGestureData = inputGestureData;
46         validate();
47     }
48 
49     /** Returns the trigger information for this input gesture */
getTrigger()50     public Trigger getTrigger() {
51         return createTriggerFromAidlTrigger(mInputGestureData.trigger);
52     }
53 
54     /** Returns the action to perform for this input gesture */
getAction()55     public Action getAction() {
56         return new Action(mInputGestureData.gestureType, getAppLaunchData());
57     }
58 
validate()59     private void validate() {
60         Trigger trigger = getTrigger();
61         Action action = getAction();
62         if (trigger == null) {
63             throw new IllegalArgumentException("No trigger found");
64         }
65         if (action.keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
66             throw new IllegalArgumentException("No system action found");
67         }
68         if (action.keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
69                 && action.appLaunchData == null) {
70             throw new IllegalArgumentException(
71                     "No app launch data for system action launch application");
72         }
73     }
74 
getAidlData()75     public AidlInputGestureData getAidlData() {
76         return mInputGestureData;
77     }
78 
79     @Nullable
getAppLaunchData()80     private AppLaunchData getAppLaunchData() {
81         if (mInputGestureData.gestureType != KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
82             return null;
83         }
84         return AppLaunchData.createLaunchData(mInputGestureData.appLaunchCategory,
85                 mInputGestureData.appLaunchRole, mInputGestureData.appLaunchPackageName,
86                 mInputGestureData.appLaunchClassName);
87     }
88 
89     /** Builder class for creating {@link InputGestureData} */
90     public static class Builder {
91         @Nullable
92         private Trigger mTrigger = null;
93         private int mKeyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
94         @Nullable
95         private AppLaunchData mAppLaunchData = null;
96 
97         /** Set input gesture trigger data for key based gestures */
setTrigger(Trigger trigger)98         public Builder setTrigger(Trigger trigger) {
99             mTrigger = trigger;
100             return this;
101         }
102 
103         /** Set input gesture system action */
setKeyGestureType(@eyGestureEvent.KeyGestureType int keyGestureType)104         public Builder setKeyGestureType(@KeyGestureEvent.KeyGestureType int keyGestureType) {
105             mKeyGestureType = keyGestureType;
106             return this;
107         }
108 
109         /** Set input gesture system action as launching a target app */
setAppLaunchData(@onNull AppLaunchData appLaunchData)110         public Builder setAppLaunchData(@NonNull AppLaunchData appLaunchData) {
111             mKeyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION;
112             mAppLaunchData = appLaunchData;
113             return this;
114         }
115 
116         /** Creates {@link android.hardware.input.InputGestureData} based on data provided */
build()117         public InputGestureData build() throws IllegalArgumentException {
118             if (mTrigger == null) {
119                 throw new IllegalArgumentException("No trigger found");
120             }
121             if (mKeyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
122                 throw new IllegalArgumentException("No system action found");
123             }
124             if (mKeyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
125                     && mAppLaunchData == null) {
126                 throw new IllegalArgumentException(
127                         "No app launch data for system action launch application");
128             }
129             AidlInputGestureData data = new AidlInputGestureData();
130             data.trigger = mTrigger.getAidlTrigger();
131             data.gestureType = mKeyGestureType;
132             if (mAppLaunchData != null) {
133                 if (mAppLaunchData instanceof AppLaunchData.CategoryData categoryData) {
134                     data.appLaunchCategory = categoryData.getCategory();
135                 } else if (mAppLaunchData instanceof AppLaunchData.RoleData roleData) {
136                     data.appLaunchRole = roleData.getRole();
137                 } else if (mAppLaunchData instanceof AppLaunchData.ComponentData componentData) {
138                     data.appLaunchPackageName = componentData.getPackageName();
139                     data.appLaunchClassName = componentData.getClassName();
140                 } else {
141                     throw new IllegalArgumentException("AppLaunchData type is invalid!");
142                 }
143             }
144             return new InputGestureData(data);
145         }
146     }
147 
148     @Override
toString()149     public String toString() {
150         return "InputGestureData { "
151                 + "trigger = " + getTrigger()
152                 + ", action = " + getAction()
153                 + " }";
154     }
155 
156     @Override
equals(Object o)157     public boolean equals(Object o) {
158         if (this == o) return true;
159         if (o == null || getClass() != o.getClass()) return false;
160         InputGestureData that = (InputGestureData) o;
161         return Objects.equals(mInputGestureData, that.mInputGestureData);
162     }
163 
164     @Override
hashCode()165     public int hashCode() {
166         return mInputGestureData.hashCode();
167     }
168 
169     public interface Trigger {
getAidlTrigger()170         AidlInputGestureData.Trigger getAidlTrigger();
171     }
172 
173     /** Creates a input gesture trigger based on a key press */
createKeyTrigger(int keycode, int modifierState)174     public static Trigger createKeyTrigger(int keycode, int modifierState) {
175         return new KeyTrigger(keycode, modifierState);
176     }
177 
178     /** Creates a input gesture trigger based on a touchpad gesture */
createTouchpadTrigger(int touchpadGestureType)179     public static Trigger createTouchpadTrigger(int touchpadGestureType) {
180         return new TouchpadTrigger(touchpadGestureType);
181     }
182 
createTriggerFromAidlTrigger(AidlInputGestureData.Trigger aidlTrigger)183     public static Trigger createTriggerFromAidlTrigger(AidlInputGestureData.Trigger aidlTrigger) {
184         switch (aidlTrigger.getTag()) {
185             case AidlInputGestureData.Trigger.Tag.key: {
186                 AidlInputGestureData.KeyTrigger trigger = aidlTrigger.getKey();
187                 if (trigger == null) {
188                     throw new RuntimeException("aidlTrigger is corrupted, null key trigger!");
189                 }
190                 return new KeyTrigger(trigger);
191             }
192             case AidlInputGestureData.Trigger.Tag.touchpadGesture: {
193                 AidlInputGestureData.TouchpadGestureTrigger trigger =
194                         aidlTrigger.getTouchpadGesture();
195                 if (trigger == null) {
196                     throw new RuntimeException(
197                             "aidlTrigger is corrupted, null touchpad trigger!");
198                 }
199                 return new TouchpadTrigger(trigger);
200             }
201             default:
202                 throw new RuntimeException("aidlTrigger is corrupted, invalid trigger type!");
203 
204         }
205     }
206 
207     /** Key based input gesture trigger */
208     public static class KeyTrigger implements Trigger {
209 
210         AidlInputGestureData.KeyTrigger mAidlKeyTrigger;
211 
KeyTrigger(@onNull AidlInputGestureData.KeyTrigger aidlKeyTrigger)212         private KeyTrigger(@NonNull AidlInputGestureData.KeyTrigger aidlKeyTrigger) {
213             mAidlKeyTrigger = aidlKeyTrigger;
214         }
215 
KeyTrigger(int keycode, int modifierState)216         private KeyTrigger(int keycode, int modifierState) {
217             if (keycode <= KeyEvent.KEYCODE_UNKNOWN || keycode > KeyEvent.getMaxKeyCode()) {
218                 throw new IllegalArgumentException("Invalid keycode = " + keycode);
219             }
220             mAidlKeyTrigger = new AidlInputGestureData.KeyTrigger();
221             mAidlKeyTrigger.keycode = keycode;
222             mAidlKeyTrigger.modifierState = modifierState;
223         }
224 
getKeycode()225         public int getKeycode() {
226             return mAidlKeyTrigger.keycode;
227         }
228 
getModifierState()229         public int getModifierState() {
230             return mAidlKeyTrigger.modifierState;
231         }
232 
getAidlTrigger()233         public AidlInputGestureData.Trigger getAidlTrigger() {
234             AidlInputGestureData.Trigger trigger = new AidlInputGestureData.Trigger();
235             trigger.setKey(mAidlKeyTrigger);
236             return trigger;
237         }
238 
239         @Override
equals(Object o)240         public boolean equals(Object o) {
241             if (this == o) return true;
242             if (!(o instanceof KeyTrigger that)) return false;
243             return Objects.equals(mAidlKeyTrigger, that.mAidlKeyTrigger);
244         }
245 
246         @Override
hashCode()247         public int hashCode() {
248             return mAidlKeyTrigger.hashCode();
249         }
250 
251         @Override
toString()252         public String toString() {
253             return "KeyTrigger{" +
254                     "mKeycode=" + KeyEvent.keyCodeToString(mAidlKeyTrigger.keycode) +
255                     ", mModifierState=" + mAidlKeyTrigger.modifierState +
256                     '}';
257         }
258     }
259 
260     /** Touchpad based input gesture trigger */
261     public static class TouchpadTrigger implements Trigger {
262         AidlInputGestureData.TouchpadGestureTrigger mAidlTouchpadTrigger;
263 
TouchpadTrigger( @onNull AidlInputGestureData.TouchpadGestureTrigger aidlTouchpadTrigger)264         private TouchpadTrigger(
265                 @NonNull AidlInputGestureData.TouchpadGestureTrigger aidlTouchpadTrigger) {
266             mAidlTouchpadTrigger = aidlTouchpadTrigger;
267         }
268 
TouchpadTrigger(int touchpadGestureType)269         private TouchpadTrigger(int touchpadGestureType) {
270             if (touchpadGestureType != TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP) {
271                 throw new IllegalArgumentException(
272                         "Invalid touchpadGestureType = " + touchpadGestureType);
273             }
274             mAidlTouchpadTrigger = new AidlInputGestureData.TouchpadGestureTrigger();
275             mAidlTouchpadTrigger.gestureType = touchpadGestureType;
276         }
277 
getTouchpadGestureType()278         public int getTouchpadGestureType() {
279             return mAidlTouchpadTrigger.gestureType;
280         }
281 
getAidlTrigger()282         public AidlInputGestureData.Trigger getAidlTrigger() {
283             AidlInputGestureData.Trigger trigger = new AidlInputGestureData.Trigger();
284             trigger.setTouchpadGesture(mAidlTouchpadTrigger);
285             return trigger;
286         }
287 
288         @Override
toString()289         public String toString() {
290             return "TouchpadTrigger{" +
291                     "mTouchpadGestureType=" + mAidlTouchpadTrigger.gestureType +
292                     '}';
293         }
294 
295         @Override
equals(Object o)296         public boolean equals(Object o) {
297             if (this == o) return true;
298             if (!(o instanceof TouchpadTrigger that)) return false;
299             return Objects.equals(mAidlTouchpadTrigger, that.mAidlTouchpadTrigger);
300         }
301 
302         @Override
hashCode()303         public int hashCode() {
304             return mAidlTouchpadTrigger.hashCode();
305         }
306     }
307 
308     /** Data for action to perform when input gesture is triggered */
Action(@eyGestureEvent.KeyGestureType int keyGestureType, @Nullable AppLaunchData appLaunchData)309     public record Action(@KeyGestureEvent.KeyGestureType int keyGestureType,
310                          @Nullable AppLaunchData appLaunchData) {
311     }
312 
313     /** Filter definition for InputGestureData */
314     public enum Filter {
315         KEY(AidlInputGestureData.Trigger.Tag.key),
316         TOUCHPAD(AidlInputGestureData.Trigger.Tag.touchpadGesture);
317 
318         @AidlInputGestureData.Trigger.Tag
319         private final int mTag;
320 
Filter(@idlInputGestureData.Trigger.Tag int tag)321         Filter(@AidlInputGestureData.Trigger.Tag int tag) {
322             mTag = tag;
323         }
324 
325         @Nullable
of(@idlInputGestureData.Trigger.Tag int tag)326         public static Filter of(@AidlInputGestureData.Trigger.Tag int tag) {
327             return switch (tag) {
328                 case AidlInputGestureData.Trigger.Tag.key -> KEY;
329                 case AidlInputGestureData.Trigger.Tag.touchpadGesture -> TOUCHPAD;
330                 default -> null;
331             };
332         }
333 
334         @AidlInputGestureData.Trigger.Tag
getTag()335         public int getTag() {
336             return mTag;
337         }
338 
matches(@onNull InputGestureData inputGestureData)339         public boolean matches(@NonNull InputGestureData inputGestureData) {
340             return mTag == inputGestureData.mInputGestureData.trigger.getTag();
341         }
342     }
343 }
344