• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.server.nearby.fastpair.notification;
18 
19 import static com.android.server.nearby.fastpair.Constant.TAG;
20 
21 import android.annotation.LayoutRes;
22 import android.annotation.Nullable;
23 import android.app.Notification;
24 import android.content.Context;
25 import android.content.res.Resources;
26 import android.graphics.Bitmap;
27 import android.util.Log;
28 import android.view.View;
29 import android.widget.LinearLayout;
30 import android.widget.RemoteViews;
31 
32 import com.android.nearby.halfsheet.R;
33 
34 /** Wrapper class for creating larger heads up notifications. */
35 public class LargeHeadsUpNotificationBuilder extends FastPairNotificationBuilder {
36     private final boolean mLargeIcon;
37     private final RemoteViews mNotification;
38     private final RemoteViews mNotificationCollapsed;
39 
40     @Nullable private Runnable mLargeIconAction;
41     @Nullable private Runnable mProgressAction;
42 
LargeHeadsUpNotificationBuilder(Context context, String channelId, boolean largeIcon)43     public LargeHeadsUpNotificationBuilder(Context context, String channelId, boolean largeIcon) {
44         super(context, channelId);
45 
46         this.mLargeIcon = largeIcon;
47         this.mNotification = getRemoteViews(R.layout.fast_pair_heads_up_notification);
48         this.mNotificationCollapsed = getRemoteViews(R.layout.fast_pair_heads_up_notification);
49 
50         if (mNotification != null) {
51             mNotificationCollapsed.setViewVisibility(android.R.id.secondaryProgress, View.GONE);
52         }
53     }
54 
55     /**
56      * Create a new RemoteViews object that will display the views contained
57      * fast_pair_heads_up_notification layout.
58      */
59     @Nullable
getRemoteViews(@ayoutRes int layoutId)60     public RemoteViews getRemoteViews(@LayoutRes int layoutId) {
61         return new RemoteViews(mPackageName, layoutId);
62     }
63 
64     @Override
setContentTitle(@ullable CharSequence title)65     public Notification.Builder setContentTitle(@Nullable CharSequence title) {
66         if (mNotification != null) {
67             mNotification.setTextViewText(android.R.id.title, title);
68         }
69         if (mNotificationCollapsed != null) {
70             mNotificationCollapsed.setTextViewText(android.R.id.title, title);
71             // Collapsed mode does not need additional lines.
72             mNotificationCollapsed.setInt(android.R.id.title, "setMaxLines", 1);
73         }
74         return super.setContentTitle(title);
75     }
76 
77     @Override
setContentText(@ullable CharSequence text)78     public Notification.Builder setContentText(@Nullable CharSequence text) {
79         if (mNotification != null) {
80             mNotification.setTextViewText(android.R.id.text1, text);
81         }
82         if (mNotificationCollapsed != null) {
83             mNotificationCollapsed.setTextViewText(android.R.id.text1, text);
84             // Collapsed mode does not need additional lines.
85             mNotificationCollapsed.setInt(android.R.id.text1, "setMaxLines", 1);
86         }
87 
88         return super.setContentText(text);
89     }
90 
91     @Override
setSubText(@ullable CharSequence subText)92     public Notification.Builder setSubText(@Nullable CharSequence subText) {
93         if (mNotification != null) {
94             mNotification.setTextViewText(android.R.id.text2, subText);
95         }
96         if (mNotificationCollapsed != null) {
97             mNotificationCollapsed.setTextViewText(android.R.id.text2, subText);
98         }
99         return super.setSubText(subText);
100     }
101 
102     @Override
setLargeIcon(@ullable Bitmap bitmap)103     public Notification.Builder setLargeIcon(@Nullable Bitmap bitmap) {
104         RemoteViews image =
105                 getRemoteViews(
106                         useLargeIcon()
107                                 ? R.layout.fast_pair_heads_up_notification_large_image
108                                 : R.layout.fast_pair_heads_up_notification_small_image);
109         if (image == null) {
110             return super.setLargeIcon(bitmap);
111         }
112         image.setImageViewBitmap(android.R.id.icon, bitmap);
113 
114         if (mNotification != null) {
115             mNotification.removeAllViews(android.R.id.icon1);
116             mNotification.addView(android.R.id.icon1, image);
117         }
118         if (mNotificationCollapsed != null) {
119             mNotificationCollapsed.removeAllViews(android.R.id.icon1);
120             mNotificationCollapsed.addView(android.R.id.icon1, image);
121             // In Android S, if super.setLargeIcon() is called, there will be an extra icon on
122             // top-right.
123             // But we still need to store this setting for the default UI when something wrong.
124             mLargeIconAction = () -> super.setLargeIcon(bitmap);
125             return this;
126         }
127         return super.setLargeIcon(bitmap);
128     }
129 
130     @Override
setProgress(int max, int progress, boolean indeterminate)131     public Notification.Builder setProgress(int max, int progress, boolean indeterminate) {
132         if (mNotification != null) {
133             mNotification.setViewVisibility(android.R.id.secondaryProgress, View.VISIBLE);
134             mNotification.setProgressBar(android.R.id.progress, max, progress, indeterminate);
135         }
136         if (mNotificationCollapsed != null) {
137             mNotificationCollapsed.setViewVisibility(android.R.id.secondaryProgress, View.VISIBLE);
138             mNotificationCollapsed.setProgressBar(android.R.id.progress, max, progress,
139                     indeterminate);
140             // In Android S, if super.setProgress() is called, there will be an extra progress bar.
141             // But we still need to store this setting for the default UI when something wrong.
142             mProgressAction = () -> super.setProgress(max, progress, indeterminate);
143             return this;
144         }
145         return super.setProgress(max, progress, indeterminate);
146     }
147 
148     @Override
build()149     public Notification build() {
150         if (mNotification != null) {
151             boolean buildSuccess = false;
152             try {
153                 // Attempt to apply the remote views. This verifies that all of the resources are
154                 // correctly available.
155                 // If it fails, fall back to a non-custom notification.
156                 mNotification.apply(mContext, new LinearLayout(mContext));
157                 if (mNotificationCollapsed != null) {
158                     mNotificationCollapsed.apply(mContext, new LinearLayout(mContext));
159                 }
160                 buildSuccess = true;
161             } catch (Resources.NotFoundException e) {
162                 Log.w(TAG, "Failed to build notification, not setting custom view.", e);
163             }
164 
165             if (buildSuccess) {
166                 if (mNotificationCollapsed != null) {
167                     setStyle(new Notification.DecoratedCustomViewStyle());
168                     setCustomContentView(mNotificationCollapsed);
169                     setCustomBigContentView(mNotification);
170                 } else {
171                     setCustomHeadsUpContentView(mNotification);
172                 }
173             } else {
174                 if (mLargeIconAction != null) {
175                     mLargeIconAction.run();
176                 }
177                 if (mProgressAction != null) {
178                     mProgressAction.run();
179                 }
180             }
181         }
182         return super.build();
183     }
184 
useLargeIcon()185     private boolean useLargeIcon() {
186         return mLargeIcon;
187     }
188 }
189