Home | History | Annotate | Download | only in bubbles
      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 
     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)
     61     Bubble(Context context, NotificationEntry e) {
     62         this (context, e, null);
     63     }
     64 
     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 
     90     public String getKey() {
     91         return mKey;
     92     }
     93 
     94     public String getGroupId() {
     95         return mGroupId;
     96     }
     97 
     98     public String getPackageName() {
     99         return entry.notification.getPackageName();
    100     }
    101 
    102     public String getAppName() {
    103         return mAppName;
    104     }
    105 
    106     boolean isInflated() {
    107         return mInflated;
    108     }
    109 
    110     public void updateDotVisibility() {
    111         if (iconView != null) {
    112             iconView.updateDotVisibility(true /* animate */);
    113         }
    114     }
    115 
    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 
    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 
    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      */
    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      */
    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      */
    166     public long getLastAccessTime() {
    167         return mLastAccessed;
    168     }
    169 
    170     /**
    171      * Should be invoked whenever a Bubble is accessed (selected while expanded).
    172      */
    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      */
    181     public boolean isOngoing() {
    182         return entry.isForegroundService();
    183     }
    184 
    185     @Override
    186     public String toString() {
    187         return "Bubble{" + mKey + '}';
    188     }
    189 
    190     @Override
    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
    199     public int hashCode() {
    200         return Objects.hash(mKey);
    201     }
    202 }
    203