/* * Copyright (C) 2021 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.systemui.statusbar; import android.annotation.NonNull; import android.app.Notification; import android.app.RemoteInputHistoryItem; import android.content.Context; import android.net.Uri; import android.os.Parcelable; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.Arrays; import java.util.stream.Stream; import javax.inject.Inject; /** * A helper class which will augment the notifications using arguments and other information * accessible to the entry in order to provide intermediate remote input states. */ @SysUISingleton public class RemoteInputNotificationRebuilder { private final Context mContext; @Inject RemoteInputNotificationRebuilder(Context context) { mContext = context; } /** * When a smart reply is sent off to the app, we insert the text into the remote input history, * and show a spinner to indicate that the app has yet to respond. */ @NonNull public StatusBarNotification rebuildForSendingSmartReply(NotificationEntry entry, CharSequence reply) { return rebuildWithRemoteInputInserted(entry, reply, true /* showSpinner */, null /* mimeType */, null /* uri */); } /** * When the app cancels a notification in response to a smart reply, we remove the spinner * and leave the previously-added reply. This is the lifetime-extended appearance of the * notification. */ @NonNull public StatusBarNotification rebuildForCanceledSmartReplies( NotificationEntry entry) { return rebuildWithRemoteInputInserted(entry, null /* remoteInputTest */, false /* showSpinner */, null /* mimeType */, null /* uri */); } /** * When the app cancels a notification in response to a remote input reply, we update the * notification with the reply text and/or attachment. This is the lifetime-extended * appearance of the notification. */ @NonNull public StatusBarNotification rebuildForRemoteInputReply(NotificationEntry entry) { CharSequence remoteInputText = entry.remoteInputText; if (TextUtils.isEmpty(remoteInputText)) { remoteInputText = entry.remoteInputTextWhenReset; } String remoteInputMimeType = entry.remoteInputMimeType; Uri remoteInputUri = entry.remoteInputUri; StatusBarNotification newSbn = rebuildWithRemoteInputInserted(entry, remoteInputText, false /* showSpinner */, remoteInputMimeType, remoteInputUri); return newSbn; } /** Inner method for generating the SBN */ @VisibleForTesting @NonNull StatusBarNotification rebuildWithRemoteInputInserted(NotificationEntry entry, CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) { StatusBarNotification sbn = entry.getSbn(); Notification.Builder b = Notification.Builder .recoverBuilder(mContext, sbn.getNotification().clone()); if (remoteInputText != null || uri != null) { RemoteInputHistoryItem newItem = uri != null ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText) : new RemoteInputHistoryItem(remoteInputText); Parcelable[] oldHistoryItems = sbn.getNotification().extras .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null ? Stream.concat( Stream.of(newItem), Arrays.stream(oldHistoryItems).map(p -> (RemoteInputHistoryItem) p)) .toArray(RemoteInputHistoryItem[]::new) : new RemoteInputHistoryItem[] { newItem }; b.setRemoteInputHistory(newHistoryItems); } b.setShowRemoteInputSpinner(showSpinner); b.setHideSmartReplies(true); Notification newNotification = b.build(); // Undo any compatibility view inflation newNotification.contentView = sbn.getNotification().contentView; newNotification.bigContentView = sbn.getNotification().bigContentView; newNotification.headsUpContentView = sbn.getNotification().headsUpContentView; return new StatusBarNotification( sbn.getPackageName(), sbn.getOpPkg(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); } }