diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 5069110ba7b..1f9c6504894 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1193,6 +1193,15 @@ android:value="com.android.settings.notification.history.NotificationStation" /> + + + + + + + diff --git a/res/drawable/ic_clear.xml b/res/drawable/ic_clear.xml new file mode 100644 index 00000000000..224425fa27d --- /dev/null +++ b/res/drawable/ic_clear.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/res/drawable/ic_snooze.xml b/res/drawable/ic_snooze.xml new file mode 100644 index 00000000000..d7bbbaf42a7 --- /dev/null +++ b/res/drawable/ic_snooze.xml @@ -0,0 +1,24 @@ + + + + diff --git a/res/drawable/ic_today.xml b/res/drawable/ic_today.xml new file mode 100644 index 00000000000..1d396104ce8 --- /dev/null +++ b/res/drawable/ic_today.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/res/drawable/rounded_bg.xml b/res/drawable/rounded_bg.xml new file mode 100644 index 00000000000..fcdd7614e64 --- /dev/null +++ b/res/drawable/rounded_bg.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/res/layout/notification_history.xml b/res/layout/notification_history.xml new file mode 100644 index 00000000000..91a59a8e8d0 --- /dev/null +++ b/res/layout/notification_history.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/notification_history_app_layout.xml b/res/layout/notification_history_app_layout.xml new file mode 100644 index 00000000000..6f26789e947 --- /dev/null +++ b/res/layout/notification_history_app_layout.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/notification_history_log_row.xml b/res/layout/notification_history_log_row.xml new file mode 100644 index 00000000000..4c38167faf0 --- /dev/null +++ b/res/layout/notification_history_log_row.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/notification_sbn_log_row.xml b/res/layout/notification_sbn_log_row.xml new file mode 100644 index 00000000000..3c51abd8e6f --- /dev/null +++ b/res/layout/notification_sbn_log_row.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 28f7a1956f4..f79874c216d 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -81,6 +81,8 @@ 16sp 14dp + 26dp + 16dp 7dp 17dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 871aa82ae59..35a76a45e1f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6163,6 +6163,11 @@ More details Notification log + Notification history + Today + Snoozed + Recently dismissed + Call ringtone & vibrate diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java index a8396a14929..f749764a162 100644 --- a/src/com/android/settings/notification/NotificationBackend.java +++ b/src/com/android/settings/notification/NotificationBackend.java @@ -21,6 +21,7 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import android.app.INotificationManager; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.app.NotificationHistory; import android.app.role.RoleManager; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; @@ -367,6 +368,15 @@ public class NotificationBackend { return false; } + public NotificationHistory getNotificationHistory(String pkg) { + try { + return sINM.getNotificationHistory(pkg); + } catch (Exception e) { + Log.w(TAG, "Error calling NoMan", e); + } + return new NotificationHistory(); + } + protected void recordAggregatedUsageEvents(Context context, AppRow appRow) { long now = System.currentTimeMillis(); long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK); diff --git a/src/com/android/settings/notification/history/HistoryLoader.java b/src/com/android/settings/notification/history/HistoryLoader.java new file mode 100644 index 00000000000..046b5efeb4d --- /dev/null +++ b/src/com/android/settings/notification/history/HistoryLoader.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2020 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.settings.notification.history; + +import android.app.NotificationHistory; +import android.app.NotificationHistory.HistoricalNotification; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.UserHandle; + +import com.android.settings.notification.NotificationBackend; +import com.android.settingslib.utils.ThreadUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HistoryLoader { + private final Context mContext; + private final NotificationBackend mBackend; + private final PackageManager mPm; + + public HistoryLoader(Context context, NotificationBackend backend, PackageManager pm) { + mContext = context; + mBackend = backend; + mPm = pm; + } + + public void load(OnHistoryLoaderListener listener) { + ThreadUtils.postOnBackgroundThread(() -> { + Map historicalNotifications = new HashMap<>(); + NotificationHistory history = + mBackend.getNotificationHistory(mContext.getPackageName()); + + while (history.hasNextNotification()) { + HistoricalNotification hn = history.getNextNotification(); + + String key = hn.getPackage() + "|" + hn.getUid(); + NotificationHistoryPackage hnsForPackage = historicalNotifications.getOrDefault( + key, + new NotificationHistoryPackage(hn.getPackage(), hn.getUid())); + hnsForPackage.notifications.add(hn); + historicalNotifications.put(key, hnsForPackage); + } + List packages = + new ArrayList<>(historicalNotifications.values()); + Collections.sort(packages, + (o1, o2) -> -1 * Long.compare(o1.getMostRecent(), o2.getMostRecent())); + for (NotificationHistoryPackage nhp : packages) { + ApplicationInfo info; + try { + info = mPm.getApplicationInfoAsUser( + nhp.pkgName, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE, + UserHandle.getUserId(nhp.uid)); + if (info != null) { + nhp.label = String.valueOf(mPm.getApplicationLabel(info)); + nhp.icon = mPm.getUserBadgedIcon(mPm.getApplicationIcon(info), + UserHandle.of(UserHandle.getUserId(nhp.uid))); + } + } catch (PackageManager.NameNotFoundException e) { + // app is gone, just show package name and generic icon + nhp.icon = mPm.getDefaultActivityIcon(); + } + } + ThreadUtils.postOnMainThread(() -> listener.onHistoryLoaded(packages)); + }); + } + + interface OnHistoryLoaderListener { + void onHistoryLoaded(List notificationsByPackage); + } +} diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java new file mode 100644 index 00000000000..173508b4bec --- /dev/null +++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2020 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.settings.notification.history; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.INotificationManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.util.Slog; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; +import com.android.settings.notification.NotificationBackend; + +import java.util.Arrays; + +public class NotificationHistoryActivity extends Activity { + + private static String TAG = "NotifHistory"; + + private ViewGroup mTodayView; + private ViewGroup mSnoozeView; + private ViewGroup mDismissView; + private HistoryLoader mHistoryLoader; + private INotificationManager mNm; + private PackageManager mPm; + + private HistoryLoader.OnHistoryLoaderListener mOnHistoryLoaderListener = notifications -> { + // for each package, new header and recycler view + for (NotificationHistoryPackage nhp : notifications) { + View viewForPackage = LayoutInflater.from(this) + .inflate(R.layout.notification_history_app_layout, null); + + final View container = viewForPackage.findViewById(R.id.list_container); + container.setVisibility(View.GONE); + ImageButton expand = viewForPackage.findViewById(R.id.expand); + expand.setOnClickListener(v -> { + container.setVisibility(container.getVisibility() == View.VISIBLE + ? View.GONE : View.VISIBLE); + expand.setImageResource(container.getVisibility() == View.VISIBLE + ? R.drawable.ic_expand_less + : com.android.internal.R.drawable.ic_expand_more); + }); + + TextView label = viewForPackage.findViewById(R.id.label); + label.setText(nhp.label != null ? nhp.label : nhp.pkgName); + ImageView icon = viewForPackage.findViewById(R.id.icon); + icon.setImageDrawable(nhp.icon); + + RecyclerView rv = viewForPackage.findViewById(R.id.notification_list); + rv.setLayoutManager(new LinearLayoutManager(this)); + rv.setAdapter(new NotificationHistoryAdapter()); + ((NotificationHistoryAdapter) rv.getAdapter()).onRebuildComplete(nhp.notifications); + mTodayView.addView(viewForPackage); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.notification_history); + mTodayView = findViewById(R.id.apps); + mSnoozeView = findViewById(R.id.snoozed_list); + mDismissView = findViewById(R.id.recently_dismissed_list); + } + + @Override + protected void onResume() { + super.onResume(); + + mPm = getPackageManager(); + + mHistoryLoader = new HistoryLoader(this, new NotificationBackend(), mPm); + mHistoryLoader.load(mOnHistoryLoaderListener); + + mNm = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + try { + mListener.registerAsSystemService(this, new ComponentName(getPackageName(), + this.getClass().getCanonicalName()), ActivityManager.getCurrentUser()); + } catch (RemoteException e) { + Log.e(TAG, "Cannot register listener", e); + } + } + + @Override + public void onPause() { + try { + mListener.unregisterAsSystemService(); + } catch (RemoteException e) { + Log.e(TAG, "Cannot unregister listener", e); + } + super.onPause(); + } + + private final NotificationListenerService mListener = new NotificationListenerService() { + + @Override + public void onListenerConnected() { + StatusBarNotification[] snoozed = getSnoozedNotifications(); + if (snoozed == null || snoozed.length == 0) { + mSnoozeView.setVisibility(View.GONE); + } else { + RecyclerView rv = mSnoozeView.findViewById(R.id.notification_list); + rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this)); + rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm)); + ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete( + Arrays.asList(snoozed)); + } + + try { + StatusBarNotification[] dismissed = mNm.getHistoricalNotifications( + NotificationHistoryActivity.this.getPackageName(), 10); + RecyclerView rv = mDismissView.findViewById(R.id.notification_list); + rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this)); + rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm)); + ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete( + Arrays.asList(dismissed)); + mDismissView.setVisibility(View.VISIBLE); + } catch (Exception e) { + Slog.e(TAG, "Cannot load recently dismissed", e); + mDismissView.setVisibility(View.GONE); + } + } + }; +} diff --git a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java new file mode 100644 index 00000000000..f87bb20373e --- /dev/null +++ b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 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.settings.notification.history; + +import android.app.NotificationHistory; +import android.app.NotificationHistory.HistoricalNotification; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; +import com.android.settings.notification.NotificationBackend; +import com.android.settingslib.utils.ThreadUtils; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class NotificationHistoryAdapter extends + RecyclerView.Adapter { + + private List mValues; + + public NotificationHistoryAdapter() { + mValues = new ArrayList<>(); + setHasStableIds(true); + } + + @Override + public NotificationHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.notification_history_log_row, parent, false); + return new NotificationHistoryViewHolder(view); + } + + @Override + public void onBindViewHolder(final @NonNull NotificationHistoryViewHolder holder, + int position) { + final HistoricalNotification hn = mValues.get(position); + holder.setTitle(hn.getTitle()); + holder.setSummary(hn.getText()); + holder.setPostedTime(hn.getPostedTimeMs()); + holder.addOnClick(hn.getPackage(), hn.getUserId(), hn.getChannelId()); + } + + @Override + public int getItemCount() { + return mValues.size(); + } + + public void onRebuildComplete(List notifications) { + mValues = notifications; + notifyDataSetChanged(); + } +} diff --git a/src/com/android/settings/notification/history/NotificationHistoryPackage.java b/src/com/android/settings/notification/history/NotificationHistoryPackage.java new file mode 100644 index 00000000000..5170b75d7fd --- /dev/null +++ b/src/com/android/settings/notification/history/NotificationHistoryPackage.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 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.settings.notification.history; + +import android.app.NotificationHistory; +import android.graphics.drawable.Drawable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class NotificationHistoryPackage { + String pkgName; + int uid; + List notifications; + CharSequence label; + Drawable icon; + + public NotificationHistoryPackage(String pkgName, int uid) { + this.pkgName = pkgName; + this.uid = uid; + notifications = new ArrayList<>(); + } + + public long getMostRecent() { + if (notifications.isEmpty()) { + return 0; + } + return notifications.get(0).getPostedTimeMs(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NotificationHistoryPackage that = (NotificationHistoryPackage) o; + return uid == that.uid && + Objects.equals(pkgName, that.pkgName) && + Objects.equals(notifications, that.notifications) && + Objects.equals(label, that.label) && + Objects.equals(icon, that.icon); + } + + @Override + public int hashCode() { + return Objects.hash(pkgName, uid, notifications, label, icon); + } +} diff --git a/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java new file mode 100644 index 00000000000..35f5615f046 --- /dev/null +++ b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 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.settings.notification.history; + +import static android.provider.Settings.EXTRA_APP_PACKAGE; +import static android.provider.Settings.EXTRA_CHANNEL_ID; + +import android.content.Intent; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.View; +import android.widget.DateTimeView; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; + +public class NotificationHistoryViewHolder extends RecyclerView.ViewHolder { + + private final DateTimeView mTime; + private final TextView mTitle; + private final TextView mSummary; + + NotificationHistoryViewHolder(View itemView) { + super(itemView); + mTime = itemView.findViewById(R.id.timestamp); + mTime.setShowRelativeTime(true); + mTitle = itemView.findViewById(R.id.title); + mSummary = itemView.findViewById(R.id.text); + } + + void setSummary(CharSequence summary) { + mSummary.setText(summary); + mSummary.setVisibility(summary != null ? View.VISIBLE : View.GONE); + } + + void setTitle(CharSequence title) { + mTitle.setText(title); + mTitle.setVisibility(title != null ? View.VISIBLE : View.GONE); + } + + void setPostedTime(long postedTime) { + mTime.setTime(postedTime); + } + + void addOnClick(String pkg, int userId, String channelId) { + itemView.setOnClickListener(v -> { + Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) + .putExtra(EXTRA_APP_PACKAGE, pkg) + .putExtra(EXTRA_CHANNEL_ID, channelId); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + itemView.getContext().startActivityAsUser(intent, UserHandle.of(userId)); + }); + } +} diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java new file mode 100644 index 00000000000..80ba278e257 --- /dev/null +++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 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.settings.notification.history; + +import static android.content.pm.PackageManager.*; + +import android.app.Notification; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class NotificationSbnAdapter extends + RecyclerView.Adapter { + + private static final String TAG = "SbnAdapter"; + private List mValues; + private Map mUserBadgeCache; + private final Context mContext; + private PackageManager mPm; + + public NotificationSbnAdapter(Context context, PackageManager pm) { + mContext = context; + mPm = pm; + mUserBadgeCache = new HashMap<>(); + mValues = new ArrayList<>(); + setHasStableIds(true); + } + + @Override + public NotificationSbnViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.notification_sbn_log_row, parent, false); + return new NotificationSbnViewHolder(view); + } + + @Override + public void onBindViewHolder(final @NonNull NotificationSbnViewHolder holder, + int position) { + final StatusBarNotification sbn = mValues.get(position); + holder.setIcon(loadIcon(sbn)); + holder.setPackageName(loadPackageName(sbn.getPackageName()).toString()); + holder.setTitle(getTitleString(sbn.getNotification())); + holder.setSummary(getTextString(mContext, sbn.getNotification())); + holder.setPostedTime(sbn.getPostTime()); + if (!mUserBadgeCache.containsKey(sbn.getUserId())) { + Drawable profile = mContext.getPackageManager().getUserBadgeForDensity( + UserHandle.of(sbn.getUserId()), -1); + mUserBadgeCache.put(sbn.getUserId(), profile); + } + holder.setProfileBadge(mUserBadgeCache.get(sbn.getUserId())); + } + + @Override + public int getItemCount() { + return mValues.size(); + } + + public void onRebuildComplete(List notifications) { + mValues = notifications; + notifyDataSetChanged(); + } + + private @NonNull CharSequence loadPackageName(String pkg) { + try { + ApplicationInfo info = mPm.getApplicationInfo(pkg, + MATCH_ANY_USER); + if (info != null) return mPm.getApplicationLabel(info); + } catch (NameNotFoundException e) { + Log.e(TAG, "Cannot load package name", e); + } + return pkg; + } + + private static String getTitleString(Notification n) { + CharSequence title = null; + if (n.extras != null) { + title = n.extras.getCharSequence(Notification.EXTRA_TITLE); + } + return title == null? null : String.valueOf(title); + } + + /** + * Returns the appropriate substring for this notification based on the style of notification. + */ + private static String getTextString(Context appContext, Notification n) { + CharSequence text = null; + if (n.extras != null) { + text = n.extras.getCharSequence(Notification.EXTRA_TEXT); + + Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n); + + if (nb.getStyle() instanceof Notification.BigTextStyle) { + text = ((Notification.BigTextStyle) nb.getStyle()).getBigText(); + } else if (nb.getStyle() instanceof Notification.MessagingStyle) { + Notification.MessagingStyle ms = (Notification.MessagingStyle) nb.getStyle(); + final List messages = ms.getMessages(); + if (messages != null && messages.size() > 0) { + text = messages.get(messages.size() - 1).getText(); + } + } + + if (TextUtils.isEmpty(text)) { + text = n.extras.getCharSequence(Notification.EXTRA_TEXT); + } + } + return text == null ? null : String.valueOf(text); + } + + private Drawable loadIcon(StatusBarNotification sbn) { + Drawable draw = sbn.getNotification().getSmallIcon().loadDrawableAsUser( + sbn.getPackageContext(mContext), sbn.getUserId()); + if (draw == null) { + return null; + } + draw.mutate(); + draw.setColorFilter(sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP); + return draw; + } +} diff --git a/src/com/android/settings/notification/history/NotificationSbnViewHolder.java b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java new file mode 100644 index 00000000000..79cda7fe267 --- /dev/null +++ b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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.settings.notification.history; + +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.text.TextUtils; +import android.view.View; +import android.widget.DateTimeView; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; + +public class NotificationSbnViewHolder extends RecyclerView.ViewHolder { + + private final TextView mPkgName; + private final ImageView mIcon; + private final DateTimeView mTime; + private final TextView mTitle; + private final TextView mSummary; + private final ImageView mProfileBadge; + + NotificationSbnViewHolder(View itemView) { + super(itemView); + mPkgName = itemView.findViewById(R.id.pkgname); + mIcon = itemView.findViewById(R.id.icon); + mTime = itemView.findViewById(R.id.timestamp); + mTitle = itemView.findViewById(R.id.title); + mSummary = itemView.findViewById(R.id.text); + mProfileBadge = itemView.findViewById(R.id.profile_badge); + } + + void setSummary(CharSequence summary) { + mSummary.setVisibility(TextUtils.isEmpty(summary) ? View.GONE : View.VISIBLE); + mSummary.setText(summary); + } + + void setTitle(CharSequence title) { + if (title == null) { + return; + } + mTitle.setText(title); + } + + void setIcon(Drawable icon) { + mIcon.setImageDrawable(icon); + } + + void setPackageName(String pkg) { + mPkgName.setText(pkg); + } + + void setPostedTime(long postedTime) { + mTime.setTime(postedTime); + } + + void setProfileBadge(Drawable badge) { + mProfileBadge.setImageDrawable(badge); + } +}