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: Iafffcc7d138b0f502116a5e557f0c3f17e9d0b73main
parent
575340c674
commit
da2486fd63
|
@ -40,5 +40,8 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<receiver
|
||||||
|
android:name=".NotificationBroadcastReceiver"
|
||||||
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
<string name="menu_softinput_visible" translatable="false">Keyboard Visible</string>
|
<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] -->
|
<!-- 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>
|
<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] -->
|
<!-- 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>
|
<string name="custom_action_label">Custom</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -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_NAVIGATE_OFF = 3;
|
||||||
private static final int MENU_SOFTINPUT_VISIBLE = 4;
|
private static final int MENU_SOFTINPUT_VISIBLE = 4;
|
||||||
private static final int MENU_SOFTINPUT_HIDDEN = 5;
|
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_THEME = "theme";
|
||||||
private static final String PREF_NAVIGATE = "navigate";
|
private static final String PREF_NAVIGATE = "navigate";
|
||||||
private static final String PREF_SOFTINPUT = "softinput";
|
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_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_SOFTINPUT_HIDDEN, 3, getString(R.string.menu_softinput_hidden));
|
||||||
menu.add(Menu.NONE, MENU_CHANGE_THEME, 4, R.string.menu_change_theme);
|
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 {
|
try {
|
||||||
final PackageInfo pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
|
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))
|
getString(R.string.menu_version, pinfo.versionName))
|
||||||
.setEnabled(false);
|
.setEnabled(false);
|
||||||
} catch (NameNotFoundException e) {
|
} 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) {
|
} else if (itemId == MENU_SOFTINPUT_VISIBLE || itemId == MENU_SOFTINPUT_HIDDEN) {
|
||||||
saveSoftInputMode(itemId == MENU_SOFTINPUT_VISIBLE);
|
saveSoftInputMode(itemId == MENU_SOFTINPUT_VISIBLE);
|
||||||
restartActivity();
|
restartActivity();
|
||||||
|
} else if (itemId == MENU_DIRECT_REPLY) {
|
||||||
|
NotificationUtils.sendDirectReplyNotification(this);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue