blob: 67b18fdfc858d6054819782e7c53731060b254e2 [file] [log] [blame]
Mady Mellordea7ecf2018-12-10 15:47:40 -08001/*
2 * Copyright (C) 2018 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
17package com.android.systemui.bubbles;
18
19import android.annotation.Nullable;
Mady Mellor9801e852019-01-22 14:50:28 -080020import android.app.Notification;
21import android.app.PendingIntent;
Mady Mellordea7ecf2018-12-10 15:47:40 -080022import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080023import android.content.Intent;
24import android.content.pm.ApplicationInfo;
25import android.content.pm.PackageManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080026import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080027import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080028import android.graphics.Color;
29import android.graphics.drawable.ShapeDrawable;
Mady Mellor9801e852019-01-22 14:50:28 -080030import android.provider.Settings;
Mady Mellor3f2efdb2018-11-21 11:30:45 -080031import android.text.TextUtils;
Mady Mellordea7ecf2018-12-10 15:47:40 -080032import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080033import android.util.Log;
Mady Mellordea7ecf2018-12-10 15:47:40 -080034import android.view.View;
Mady Mellor9801e852019-01-22 14:50:28 -080035import android.widget.ImageButton;
Mady Mellordea7ecf2018-12-10 15:47:40 -080036import android.widget.LinearLayout;
Mark Renouf89b1a4a2018-12-04 14:59:45 -050037import android.widget.TextView;
Mady Mellordea7ecf2018-12-10 15:47:40 -080038
39import com.android.systemui.R;
40import com.android.systemui.recents.TriangleShape;
Mady Mellor9801e852019-01-22 14:50:28 -080041import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellordea7ecf2018-12-10 15:47:40 -080042
43/**
44 * Container for the expanded bubble view, handles rendering the caret and header of the view.
45 */
Mady Mellor9801e852019-01-22 14:50:28 -080046public class BubbleExpandedViewContainer extends LinearLayout implements View.OnClickListener {
47 private static final String TAG = "BubbleExpandedView";
Mady Mellordea7ecf2018-12-10 15:47:40 -080048
49 // The triangle pointing to the expanded view
50 private View mPointerView;
Mark Renouf89b1a4a2018-12-04 14:59:45 -050051 // The view displayed between the pointer and the expanded view
52 private TextView mHeaderView;
Mady Mellor9801e852019-01-22 14:50:28 -080053 // Tappable header icon deeplinking into the app
54 private ImageButton mDeepLinkIcon;
55 // Tappable header icon deeplinking into notification settings
56 private ImageButton mSettingsIcon;
Mady Mellordea7ecf2018-12-10 15:47:40 -080057 // The view that is being displayed for the expanded state
58 private View mExpandedView;
59
Mady Mellor9801e852019-01-22 14:50:28 -080060 private NotificationEntry mEntry;
61 private PackageManager mPm;
62 private String mAppName;
63
64 // Need reference to let it know to collapse when new task is launched
65 private BubbleStackView mStackView;
66
Mady Mellordea7ecf2018-12-10 15:47:40 -080067 public BubbleExpandedViewContainer(Context context) {
68 this(context, null);
69 }
70
71 public BubbleExpandedViewContainer(Context context, AttributeSet attrs) {
72 this(context, attrs, 0);
73 }
74
75 public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
76 this(context, attrs, defStyleAttr, 0);
77 }
78
79 public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr,
80 int defStyleRes) {
81 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellor9801e852019-01-22 14:50:28 -080082 mPm = context.getPackageManager();
Mady Mellordea7ecf2018-12-10 15:47:40 -080083 }
84
85 @Override
86 protected void onFinishInflate() {
87 super.onFinishInflate();
88
89 Resources res = getResources();
90 mPointerView = findViewById(R.id.pointer_view);
91 int width = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
92 int height = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -080093
94 TypedArray ta = getContext().obtainStyledAttributes(
95 new int[] {android.R.attr.colorBackgroundFloating});
96 int bgColor = ta.getColor(0, Color.WHITE);
97 ta.recycle();
98
Mady Mellordea7ecf2018-12-10 15:47:40 -080099 ShapeDrawable triangleDrawable = new ShapeDrawable(
100 TriangleShape.create(width, height, true /* pointUp */));
Mady Mellordd497052019-01-30 17:23:48 -0800101 triangleDrawable.setTint(bgColor);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800102 mPointerView.setBackground(triangleDrawable);
Mady Mellor9801e852019-01-22 14:50:28 -0800103
104 mHeaderView = findViewById(R.id.header_text);
105 mDeepLinkIcon = findViewById(R.id.deep_link_button);
106 mSettingsIcon = findViewById(R.id.settings_button);
107 mDeepLinkIcon.setOnClickListener(this);
108 mSettingsIcon.setOnClickListener(this);
109 }
110
111 /**
112 * Sets the notification entry used to populate this view.
113 */
114 public void setEntry(NotificationEntry entry, BubbleStackView stackView) {
115 mStackView = stackView;
116 mEntry = entry;
117
118 ApplicationInfo info;
119 try {
120 info = mPm.getApplicationInfo(
121 entry.notification.getPackageName(),
122 PackageManager.MATCH_UNINSTALLED_PACKAGES
123 | PackageManager.MATCH_DISABLED_COMPONENTS
124 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
125 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
126 if (info != null) {
127 mAppName = String.valueOf(mPm.getApplicationLabel(info));
128 }
129 } catch (PackageManager.NameNotFoundException e) {
130 // Ahh... just use package name
131 mAppName = entry.notification.getPackageName();
132 }
133
134 updateHeaderView();
135 }
136
137 private void updateHeaderView() {
138 mSettingsIcon.setContentDescription(getResources().getString(
139 R.string.bubbles_settings_button_description, mAppName));
140 mDeepLinkIcon.setContentDescription(getResources().getString(
141 R.string.bubbles_deep_link_button_description, mAppName));
142 if (mEntry != null && mEntry.getBubbleMetadata() != null) {
143 setHeaderText(mEntry.getBubbleMetadata().getTitle());
144 } else {
145 // This should only happen if we're auto-bubbling notification content that isn't
146 // explicitly a bubble
147 setHeaderText(mAppName);
148 }
149 }
150
151 @Override
152 public void onClick(View view) {
153 if (mEntry == null) {
154 return;
155 }
156 Notification n = mEntry.notification.getNotification();
157 int id = view.getId();
158 if (id == R.id.deep_link_button) {
159 mStackView.collapseStack(() -> {
160 try {
161 n.contentIntent.send();
162 } catch (PendingIntent.CanceledException e) {
163 Log.w(TAG, "Failed to send intent for bubble with key: "
164 + (mEntry != null ? mEntry.key : " null entry"));
165 }
166 });
167 } else if (id == R.id.settings_button) {
168 Intent intent = getSettingsIntent(mEntry.notification.getPackageName(),
169 mEntry.notification.getUid());
170 mStackView.collapseStack(() -> mContext.startActivity(intent));
171 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800172 }
173
174 /**
175 * Set the x position that the tip of the triangle should point to.
176 */
177 public void setPointerPosition(int x) {
178 // Adjust for the pointer size
179 x -= (mPointerView.getWidth() / 2);
180 mPointerView.setTranslationX(x);
181 }
182
183 /**
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500184 * Set the text displayed within the header.
185 */
Mady Mellor9801e852019-01-22 14:50:28 -0800186 private void setHeaderText(CharSequence text) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500187 mHeaderView.setText(text);
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800188 mHeaderView.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500189 }
190
191 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800192 * Set the view to display for the expanded state. Passing null will clear the view.
193 */
194 public void setExpandedView(View view) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500195 if (mExpandedView == view) {
196 return;
197 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800198 if (mExpandedView != null) {
199 removeView(mExpandedView);
200 }
201 mExpandedView = view;
202 if (mExpandedView != null) {
203 addView(mExpandedView);
204 }
205 }
206
207 /**
208 * @return the view containing the expanded content, can be null.
209 */
210 @Nullable
211 public View getExpandedView() {
212 return mExpandedView;
213 }
Mady Mellor9801e852019-01-22 14:50:28 -0800214
215 private Intent getSettingsIntent(String packageName, final int appUid) {
216 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
217 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
218 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
219 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
220 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
221 return intent;
222 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800223}