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