1 /*
2  * Copyright (C) 2021 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 com.android.systemui.statusbar;
18 
19 import android.annotation.NonNull;
20 import android.app.Notification;
21 import android.app.RemoteInputHistoryItem;
22 import android.content.Context;
23 import android.net.Uri;
24 import android.os.Parcelable;
25 import android.service.notification.StatusBarNotification;
26 import android.text.TextUtils;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.systemui.dagger.SysUISingleton;
30 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
31 
32 import java.util.Arrays;
33 import java.util.stream.Stream;
34 
35 import javax.inject.Inject;
36 
37 /**
38  * A helper class which will augment the notifications using arguments and other information
39  * accessible to the entry in order to provide intermediate remote input states.
40  */
41 @SysUISingleton
42 public class RemoteInputNotificationRebuilder {
43 
44     private final Context mContext;
45 
46     @Inject
RemoteInputNotificationRebuilder(Context context)47     RemoteInputNotificationRebuilder(Context context) {
48         mContext = context;
49     }
50 
51     /**
52      * When a smart reply is sent off to the app, we insert the text into the remote input history,
53      * and show a spinner to indicate that the app has yet to respond.
54      */
55     @NonNull
rebuildForSendingSmartReply(NotificationEntry entry, CharSequence reply)56     public StatusBarNotification rebuildForSendingSmartReply(NotificationEntry entry,
57             CharSequence reply) {
58         return rebuildWithRemoteInputInserted(entry, reply,
59                 true /* showSpinner */,
60                 null /* mimeType */, null /* uri */);
61     }
62 
63     /**
64      * When the app cancels a notification in response to a smart reply, we remove the spinner
65      * and leave the previously-added reply.  This is the lifetime-extended appearance of the
66      * notification.
67      */
68     @NonNull
rebuildForCanceledSmartReplies( NotificationEntry entry)69     public StatusBarNotification rebuildForCanceledSmartReplies(
70             NotificationEntry entry) {
71         return rebuildWithRemoteInputInserted(entry, null /* remoteInputTest */,
72                 false /* showSpinner */, null /* mimeType */, null /* uri */);
73     }
74 
75     /**
76      * When the app cancels a notification in response to a remote input reply, we update the
77      * notification with the reply text and/or attachment. This is the lifetime-extended
78      * appearance of the notification.
79      */
80     @NonNull
rebuildForRemoteInputReply(NotificationEntry entry)81     public StatusBarNotification rebuildForRemoteInputReply(NotificationEntry entry) {
82         CharSequence remoteInputText = entry.remoteInputText;
83         if (TextUtils.isEmpty(remoteInputText)) {
84             remoteInputText = entry.remoteInputTextWhenReset;
85         }
86         String remoteInputMimeType = entry.remoteInputMimeType;
87         Uri remoteInputUri = entry.remoteInputUri;
88         StatusBarNotification newSbn = rebuildWithRemoteInputInserted(entry,
89                 remoteInputText, false /* showSpinner */, remoteInputMimeType,
90                 remoteInputUri);
91         return newSbn;
92     }
93 
94     /** Inner method for generating the SBN */
95     @VisibleForTesting
96     @NonNull
rebuildWithRemoteInputInserted(NotificationEntry entry, CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri)97     StatusBarNotification rebuildWithRemoteInputInserted(NotificationEntry entry,
98             CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) {
99         StatusBarNotification sbn = entry.getSbn();
100 
101         Notification.Builder b = Notification.Builder
102                 .recoverBuilder(mContext, sbn.getNotification().clone());
103         if (remoteInputText != null || uri != null) {
104             RemoteInputHistoryItem newItem = uri != null
105                     ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText)
106                     : new RemoteInputHistoryItem(remoteInputText);
107             Parcelable[] oldHistoryItems = sbn.getNotification().extras
108                     .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
109             RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null
110                     ? Stream.concat(
111                     Stream.of(newItem),
112                     Arrays.stream(oldHistoryItems).map(p -> (RemoteInputHistoryItem) p))
113                     .toArray(RemoteInputHistoryItem[]::new)
114                     : new RemoteInputHistoryItem[] { newItem };
115             b.setRemoteInputHistory(newHistoryItems);
116         }
117         b.setShowRemoteInputSpinner(showSpinner);
118         b.setHideSmartReplies(true);
119 
120         Notification newNotification = b.build();
121 
122         // Undo any compatibility view inflation
123         newNotification.contentView = sbn.getNotification().contentView;
124         newNotification.bigContentView = sbn.getNotification().bigContentView;
125         newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
126 
127         return new StatusBarNotification(
128                 sbn.getPackageName(),
129                 sbn.getOpPkg(),
130                 sbn.getId(),
131                 sbn.getTag(),
132                 sbn.getUid(),
133                 sbn.getInitialPid(),
134                 newNotification,
135                 sbn.getUser(),
136                 sbn.getOverrideGroupKey(),
137                 sbn.getPostTime());
138     }
139 
140 
141 }
142