1 /* 2 * Copyright (C) 2019 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 package com.android.systemui.bubbles; 17 18 19 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; 20 21 import android.content.Context; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageManager; 24 import android.os.UserHandle; 25 import android.view.LayoutInflater; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.systemui.R; 29 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 30 31 import java.util.Objects; 32 33 /** 34 * Encapsulates the data and UI elements of a bubble. 35 */ 36 class Bubble { 37 38 private static final boolean DEBUG = false; 39 private static final String TAG = "Bubble"; 40 41 private final String mKey; 42 private final String mGroupId; 43 private String mAppName; 44 private final BubbleExpandedView.OnBubbleBlockedListener mListener; 45 46 private boolean mInflated; 47 public NotificationEntry entry; 48 BubbleView iconView; 49 BubbleExpandedView expandedView; 50 private long mLastUpdated; 51 private long mLastAccessed; 52 private PackageManager mPm; 53 groupId(NotificationEntry entry)54 public static String groupId(NotificationEntry entry) { 55 UserHandle user = entry.notification.getUser(); 56 return user.getIdentifier() + "|" + entry.notification.getPackageName(); 57 } 58 59 /** Used in tests when no UI is required. */ 60 @VisibleForTesting(visibility = PRIVATE) Bubble(Context context, NotificationEntry e)61 Bubble(Context context, NotificationEntry e) { 62 this (context, e, null); 63 } 64 Bubble(Context context, NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener)65 Bubble(Context context, NotificationEntry e, 66 BubbleExpandedView.OnBubbleBlockedListener listener) { 67 entry = e; 68 mKey = e.key; 69 mLastUpdated = e.notification.getPostTime(); 70 mGroupId = groupId(e); 71 mListener = listener; 72 73 mPm = context.getPackageManager(); 74 ApplicationInfo info; 75 try { 76 info = mPm.getApplicationInfo( 77 entry.notification.getPackageName(), 78 PackageManager.MATCH_UNINSTALLED_PACKAGES 79 | PackageManager.MATCH_DISABLED_COMPONENTS 80 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 81 | PackageManager.MATCH_DIRECT_BOOT_AWARE); 82 if (info != null) { 83 mAppName = String.valueOf(mPm.getApplicationLabel(info)); 84 } 85 } catch (PackageManager.NameNotFoundException unused) { 86 mAppName = entry.notification.getPackageName(); 87 } 88 } 89 getKey()90 public String getKey() { 91 return mKey; 92 } 93 getGroupId()94 public String getGroupId() { 95 return mGroupId; 96 } 97 getPackageName()98 public String getPackageName() { 99 return entry.notification.getPackageName(); 100 } 101 getAppName()102 public String getAppName() { 103 return mAppName; 104 } 105 isInflated()106 boolean isInflated() { 107 return mInflated; 108 } 109 updateDotVisibility()110 public void updateDotVisibility() { 111 if (iconView != null) { 112 iconView.updateDotVisibility(true /* animate */); 113 } 114 } 115 inflate(LayoutInflater inflater, BubbleStackView stackView)116 void inflate(LayoutInflater inflater, BubbleStackView stackView) { 117 if (mInflated) { 118 return; 119 } 120 iconView = (BubbleView) inflater.inflate( 121 R.layout.bubble_view, stackView, false /* attachToRoot */); 122 iconView.setNotif(entry); 123 124 expandedView = (BubbleExpandedView) inflater.inflate( 125 R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); 126 expandedView.setEntry(entry, stackView, mAppName); 127 expandedView.setOnBlockedListener(mListener); 128 129 mInflated = true; 130 } 131 setDismissed()132 void setDismissed() { 133 entry.setBubbleDismissed(true); 134 // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker 135 if (expandedView != null) { 136 expandedView.cleanUpExpandedState(); 137 } 138 } 139 setEntry(NotificationEntry entry)140 void setEntry(NotificationEntry entry) { 141 this.entry = entry; 142 mLastUpdated = entry.notification.getPostTime(); 143 if (mInflated) { 144 iconView.update(entry); 145 expandedView.update(entry); 146 } 147 } 148 149 /** 150 * @return the newer of {@link #getLastUpdateTime()} and {@link #getLastAccessTime()} 151 */ getLastActivity()152 public long getLastActivity() { 153 return Math.max(mLastUpdated, mLastAccessed); 154 } 155 156 /** 157 * @return the timestamp in milliseconds of the most recent notification entry for this bubble 158 */ getLastUpdateTime()159 public long getLastUpdateTime() { 160 return mLastUpdated; 161 } 162 163 /** 164 * @return the timestamp in milliseconds when this bubble was last displayed in expanded state 165 */ getLastAccessTime()166 public long getLastAccessTime() { 167 return mLastAccessed; 168 } 169 170 /** 171 * Should be invoked whenever a Bubble is accessed (selected while expanded). 172 */ markAsAccessedAt(long lastAccessedMillis)173 void markAsAccessedAt(long lastAccessedMillis) { 174 mLastAccessed = lastAccessedMillis; 175 entry.setShowInShadeWhenBubble(false); 176 } 177 178 /** 179 * @return whether bubble is from a notification associated with a foreground service. 180 */ isOngoing()181 public boolean isOngoing() { 182 return entry.isForegroundService(); 183 } 184 185 @Override toString()186 public String toString() { 187 return "Bubble{" + mKey + '}'; 188 } 189 190 @Override equals(Object o)191 public boolean equals(Object o) { 192 if (this == o) return true; 193 if (!(o instanceof Bubble)) return false; 194 Bubble bubble = (Bubble) o; 195 return Objects.equals(mKey, bubble.mKey); 196 } 197 198 @Override hashCode()199 public int hashCode() { 200 return Objects.hash(mKey); 201 } 202 } 203