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
This commit is contained in:
Yohei Yukawa 2019-01-16 10:28:07 -08:00
parent 575340c674
commit da2486fd63
5 changed files with 162 additions and 1 deletions

View File

@ -40,5 +40,8 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".NotificationBroadcastReceiver"
android:exported="false" />
</application>
</manifest>

View File

@ -33,6 +33,8 @@
<string name="menu_softinput_visible" translatable="false">Keyboard Visible</string>
<!-- The menu title to stay hidden software keyboard when the application is launched. [CHAR LIMIT=20] -->
<string name="menu_softinput_hidden" translatable="false">Keyboard Hidden</string>
<!-- The menu title to send a notification to test direct reply. [CHAR LIMIT=20] -->
<string name="menu_direct_reply">Direct Reply</string>
<!-- The example of custom action key label. Must be short to fit on key. 5 chars or less is preferable. [CHAR LIMIT=7] -->
<string name="custom_action_label">Custom</string>
</resources>

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}