From da2486fd63d7d46e982d0e2fb2eba3dac07aff32 Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Wed, 16 Jan 2019 10:28:07 -0800 Subject: [PATCH] Support testing Direct Reply with EditTextVariations With this CL, EditTextVariations is able to send Direct Repply notification so that we can easily test IME behaviors there. You don't need to set up a chat application then let it receive a message from someone else any more just to test IME behaviors on Direct Reply. Fix: 122957841 Test: manually done as follows. 1. tapas EditTextVariations 2. make -j 3. adb install -r \ $ANDROID_TARGET_OUT_TESTCASES/EditTextVariations/EditTextVariations.apk 4. Open EditTextVariations 5. Tap the menu icon. 6. Select "Direct Reply" 7. Make sure that there is a Direct Reply notification. Change-Id: Iafffcc7d138b0f502116a5e557f0c3f17e9d0b73 --- tools/EditTextVariations/AndroidManifest.xml | 3 + .../EditTextVariations/res/values/strings.xml | 2 + .../EditTextVariations.java | 8 +- .../NotificationBroadcastReceiver.java | 31 +++++ .../edittextvariations/NotificationUtils.java | 119 ++++++++++++++++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationBroadcastReceiver.java create mode 100644 tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationUtils.java diff --git a/tools/EditTextVariations/AndroidManifest.xml b/tools/EditTextVariations/AndroidManifest.xml index 7694f4db9..96c244b32 100644 --- a/tools/EditTextVariations/AndroidManifest.xml +++ b/tools/EditTextVariations/AndroidManifest.xml @@ -40,5 +40,8 @@ + diff --git a/tools/EditTextVariations/res/values/strings.xml b/tools/EditTextVariations/res/values/strings.xml index 02387f2ff..cb896e8b6 100644 --- a/tools/EditTextVariations/res/values/strings.xml +++ b/tools/EditTextVariations/res/values/strings.xml @@ -33,6 +33,8 @@ Keyboard Visible Keyboard Hidden + + Direct Reply Custom diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EditTextVariations.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EditTextVariations.java index 44e0a4d55..6eb85a528 100644 --- a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EditTextVariations.java +++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EditTextVariations.java @@ -60,6 +60,7 @@ public final class EditTextVariations extends Activity implements TextView.OnEdi private static final int MENU_NAVIGATE_OFF = 3; private static final int MENU_SOFTINPUT_VISIBLE = 4; private static final int MENU_SOFTINPUT_HIDDEN = 5; + private static final int MENU_DIRECT_REPLY = 6; private static final String PREF_THEME = "theme"; private static final String PREF_NAVIGATE = "navigate"; private static final String PREF_SOFTINPUT = "softinput"; @@ -162,9 +163,12 @@ public final class EditTextVariations extends Activity implements TextView.OnEdi menu.add(Menu.NONE, MENU_SOFTINPUT_VISIBLE, 2, getString(R.string.menu_softinput_visible)); menu.add(Menu.NONE, MENU_SOFTINPUT_HIDDEN, 3, getString(R.string.menu_softinput_hidden)); menu.add(Menu.NONE, MENU_CHANGE_THEME, 4, R.string.menu_change_theme); + if (NotificationUtils.DIRECT_REPLY_SUPPORTED) { + menu.add(Menu.NONE, MENU_DIRECT_REPLY, 5, R.string.menu_direct_reply); + } try { final PackageInfo pinfo = getPackageManager().getPackageInfo(getPackageName(), 0); - menu.add(Menu.NONE, MENU_VERSION, 5, + menu.add(Menu.NONE, MENU_VERSION, 6, getString(R.string.menu_version, pinfo.versionName)) .setEnabled(false); } catch (NameNotFoundException e) { @@ -194,6 +198,8 @@ public final class EditTextVariations extends Activity implements TextView.OnEdi } else if (itemId == MENU_SOFTINPUT_VISIBLE || itemId == MENU_SOFTINPUT_HIDDEN) { saveSoftInputMode(itemId == MENU_SOFTINPUT_VISIBLE); restartActivity(); + } else if (itemId == MENU_DIRECT_REPLY) { + NotificationUtils.sendDirectReplyNotification(this); } return true; } diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationBroadcastReceiver.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationBroadcastReceiver.java new file mode 100644 index 000000000..97db49b15 --- /dev/null +++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationBroadcastReceiver.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.tools.edittextvariations; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * A non-exported {@link BroadcastReceiver} to receive {@link Intent} from notifications. + */ +public final class NotificationBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + NotificationUtils.onReceiveDirectReply(context, intent); + } +} diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationUtils.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationUtils.java new file mode 100644 index 000000000..64480bbf0 --- /dev/null +++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/NotificationUtils.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.tools.edittextvariations; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.RemoteInput; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.os.Process; +import android.os.UserHandle; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +final class NotificationUtils { + private static final String REPLY_ACTION = "REPLY_ACTION"; + private static final String KEY_REPLY = "KEY_REPLY"; + private static final String KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"; + private static final String CHANNEL_NAME = "Channel Name"; + private static final String CHANNEL_DESCRIPTION = "Channel Description"; + private static final String CHANNEL_ID = "Channel ID"; + private static final AtomicBoolean sNotificationChannelInitialized = new AtomicBoolean(); + private static final AtomicInteger sNextNotificationId = new AtomicInteger(1); + + static final boolean DIRECT_REPLY_SUPPORTED = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; + + static void ensureNotificationChannel(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + // NotificationChannel is not implemented. No need to set up notification channel. + return; + } + if (!sNotificationChannelInitialized.compareAndSet(false, true)) { + // Already initialized. + return; + } + + // Create the NotificationChannel + final NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, + NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(CHANNEL_DESCRIPTION); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + context.getSystemService(NotificationManager.class).createNotificationChannel(channel); + } + + static void sendDirectReplyNotification(Context context) { + if (!DIRECT_REPLY_SUPPORTED) { + // DirectReply is not supported. + return; + } + + ensureNotificationChannel(context); + + RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY) + .setLabel("Reply Label") + .build(); + + final int notificationId = sNextNotificationId.getAndIncrement(); + final PendingIntent pendingIntent = getReplyPendingIntent(context, notificationId); + final Notification.Action action = + new Notification.Action.Builder(null, "Direct Reply Test", pendingIntent) + .addRemoteInput(remoteInput) + .build(); + final Notification notification = new Notification.Builder(context, CHANNEL_ID) + .setContentText("Content Title") + .setSmallIcon(R.drawable.ic_launcher) + .setContentText("Message from " + UserHandle.getUserHandleForUid(Process.myUid())) + .setShowWhen(true) + .addAction(action) + .build(); + context.getSystemService(NotificationManager.class).notify(notificationId, notification); + } + + static void onReceiveDirectReply(Context context, Intent intent) { + final Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + if (remoteInput == null) { + return; + } + final CharSequence reply = remoteInput.getCharSequence(KEY_REPLY); + final int notificationId = intent.getIntExtra(KEY_NOTIFICATION_ID, 0); + final Notification.Builder notificationBuilder = + new Notification.Builder(context, CHANNEL_ID); + notificationBuilder.setContentText("Content Title") + .setSmallIcon(R.drawable.ic_launcher) + .setContentText(String.format("Sent \"%s\" to %s", reply, + UserHandle.getUserHandleForUid(Process.myUid()))); + context.getSystemService(NotificationManager.class) + .notify(notificationId, notificationBuilder.build()); + } + + private static PendingIntent getReplyPendingIntent(Context context, int notificationId) { + final Intent intent = new Intent(context, NotificationBroadcastReceiver.class); + intent.setAction(REPLY_ACTION); + intent.putExtra(KEY_NOTIFICATION_ID, notificationId); + // Pass notificationId as the result code to get a new PendingIntent rather than an existing + // one. + return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, + PendingIntent.FLAG_ONE_SHOT); + } +}