1 /* 2 * Copyright (C) 2017 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.inputmethodservice.cts; 18 19 import static android.inputmethodservice.cts.common.DeviceEventConstants.ACTION_DEVICE_EVENT; 20 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TIME; 21 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TYPE; 22 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_SENDER; 23 import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_CLASS; 24 import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_PACKAGE; 25 26 import android.content.ContentValues; 27 import android.content.Intent; 28 import android.database.Cursor; 29 import android.inputmethodservice.cts.common.DeviceEventConstants; 30 import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType; 31 import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants; 32 import android.inputmethodservice.cts.common.test.TestInfo; 33 import android.inputmethodservice.cts.db.Entity; 34 import android.inputmethodservice.cts.db.Field; 35 import android.inputmethodservice.cts.db.Table; 36 import android.os.SystemClock; 37 import androidx.annotation.NonNull; 38 import android.util.Log; 39 40 import java.util.function.Predicate; 41 import java.util.stream.Stream; 42 43 /** 44 * Device event object. 45 * <p>Device event is one of IME event and Test event, and is used to test behaviors of Input Method 46 * Framework.</p> 47 */ 48 public final class DeviceEvent { 49 50 private static final boolean DEBUG_STREAM = false; 51 52 public static final Table<DeviceEvent> TABLE = new DeviceEventTable(EventTableConstants.NAME); 53 54 /** 55 * Create an intent to send a device event. 56 * @param sender an event sender. 57 * @param type an event type defined at {@link DeviceEventType}. 58 * @return an intent that has event {@code sender}, {@code type}, time from 59 * {@link SystemClock#uptimeMillis()}, and target component of event receiver. 60 */ newDeviceEventIntent(@onNull final String sender, @NonNull final DeviceEventType type)61 public static Intent newDeviceEventIntent(@NonNull final String sender, 62 @NonNull final DeviceEventType type) { 63 return new Intent() 64 .setAction(ACTION_DEVICE_EVENT) 65 .setClassName(RECEIVER_PACKAGE, RECEIVER_CLASS) 66 .putExtra(EXTRA_EVENT_SENDER, sender) 67 .putExtra(EXTRA_EVENT_TYPE, type.name()) 68 .putExtra(EXTRA_EVENT_TIME, SystemClock.uptimeMillis()); 69 } 70 71 /** 72 * Create an {@link DeviceEvent} object from an intent. 73 * @param intent a device event intent defined at {@link DeviceEventConstants}. 74 * @return {@link DeviceEvent} object that has event sender, type, and time form an 75 * {@code intent}. 76 */ newEvent(final Intent intent)77 public static DeviceEvent newEvent(final Intent intent) { 78 final String sender = intent.getStringExtra(EXTRA_EVENT_SENDER); 79 if (sender == null) { 80 throw new IllegalArgumentException( 81 "Intent must have " + EXTRA_EVENT_SENDER + ": " + intent); 82 } 83 84 final String typeString = intent.getStringExtra(EXTRA_EVENT_TYPE); 85 if (typeString == null) { 86 throw new IllegalArgumentException( 87 "Intent must have " + EXTRA_EVENT_TYPE + ": " + intent); 88 } 89 final DeviceEventType type = DeviceEventType.valueOf(typeString); 90 91 if (!intent.hasExtra(EXTRA_EVENT_TIME)) { 92 throw new IllegalArgumentException( 93 "Intent must have " + EXTRA_EVENT_TIME + ": " + intent); 94 } 95 96 return new DeviceEvent(sender, type, intent.getLongExtra(EXTRA_EVENT_TIME, 0L)); 97 } 98 99 /** 100 * Build {@link ContentValues} object from {@link DeviceEvent} object. 101 * @param event a {@link DeviceEvent} object to be converted. 102 * @return a converted {@link ContentValues} object. 103 */ buildContentValues(final DeviceEvent event)104 public static ContentValues buildContentValues(final DeviceEvent event) { 105 return TABLE.buildContentValues(event); 106 } 107 108 /** 109 * Build {@link Stream<DeviceEvent>} object from {@link Cursor} comes from Content Provider. 110 * @param cursor a {@link Cursor} object to be converted. 111 * @return a converted {@link Stream<DeviceEvent>} object. 112 */ buildStream(final Cursor cursor)113 public static Stream<DeviceEvent> buildStream(final Cursor cursor) { 114 return TABLE.buildStream(cursor); 115 } 116 117 /** 118 * Build {@link Predicate<DeviceEvent>} whether a device event comes from {@code sender} 119 * 120 * @param sender event sender. 121 * @return {@link Predicate<DeviceEvent>} object. 122 */ isFrom(final String sender)123 public static Predicate<DeviceEvent> isFrom(final String sender) { 124 return e -> e.sender.equals(sender); 125 } 126 127 /** 128 * Build {@link Predicate<DeviceEvent>} whether a device event has an event {@code type}. 129 * 130 * @param type a event type defined in {@link DeviceEventType}. 131 * @return {@link Predicate<DeviceEvent>} object. 132 */ isType(final DeviceEventType type)133 public static Predicate<DeviceEvent> isType(final DeviceEventType type) { 134 return e -> e.type == type; 135 } 136 137 /** 138 * Build {@link Predicate<DeviceEvent>} whether a device event is newer than or equals to 139 * {@code time}. 140 * 141 * @param time a time to compare against. 142 * @return {@link Predicate<DeviceEvent>} object. 143 */ isNewerThan(final long time)144 public static Predicate<DeviceEvent> isNewerThan(final long time) { 145 return e -> e.time >= time; 146 } 147 148 /** 149 * Event source, either Input Method class name or {@link TestInfo#getTestName()}. 150 */ 151 @NonNull 152 public final String sender; 153 154 /** 155 * Event type, either IME event or Test event. 156 */ 157 @NonNull 158 public final DeviceEventType type; 159 160 /** 161 * Event time, value is from {@link SystemClock#uptimeMillis()}. 162 */ 163 public final long time; 164 DeviceEvent(final String sender, final DeviceEventType type, final long time)165 private DeviceEvent(final String sender, final DeviceEventType type, final long time) { 166 this.sender = sender; 167 this.type = type; 168 this.time = time; 169 } 170 171 @Override toString()172 public String toString() { 173 return "Event{ time:" + time + " type:" + type + " sender:" + sender + " }"; 174 } 175 176 /** 177 * Abstraction of device event table in database. 178 */ 179 private static final class DeviceEventTable extends Table<DeviceEvent> { 180 181 private static final String LOG_TAG = DeviceEventTable.class.getSimpleName(); 182 183 private final Field SENDER; 184 private final Field TYPE; 185 private final Field TIME; 186 DeviceEventTable(final String name)187 private DeviceEventTable(final String name) { 188 super(name, new Entity.Builder<DeviceEvent>() 189 .addField(EventTableConstants.SENDER, Cursor.FIELD_TYPE_STRING) 190 .addField(EventTableConstants.TYPE, Cursor.FIELD_TYPE_STRING) 191 .addField(EventTableConstants.TIME, Cursor.FIELD_TYPE_INTEGER) 192 .build()); 193 SENDER = getField(EventTableConstants.SENDER); 194 TYPE = getField(EventTableConstants.TYPE); 195 TIME = getField(EventTableConstants.TIME); 196 } 197 198 @Override buildContentValues(final DeviceEvent event)199 public ContentValues buildContentValues(final DeviceEvent event) { 200 final ContentValues values = new ContentValues(); 201 SENDER.putString(values, event.sender); 202 TYPE.putString(values, event.type.name()); 203 TIME.putLong(values, event.time); 204 return values; 205 } 206 207 @Override buildStream(Cursor cursor)208 public Stream<DeviceEvent> buildStream(Cursor cursor) { 209 if (DEBUG_STREAM) { 210 Log.d(LOG_TAG, "buildStream:"); 211 } 212 final Stream.Builder<DeviceEvent> builder = Stream.builder(); 213 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { 214 final DeviceEvent event = new DeviceEvent( 215 SENDER.getString(cursor), 216 DeviceEventType.valueOf(TYPE.getString(cursor)), 217 TIME.getLong(cursor)); 218 builder.accept(event); 219 if (DEBUG_STREAM) { 220 Log.d(LOG_TAG, " event=" +event); 221 } 222 } 223 return builder.build(); 224 } 225 } 226 } 227