Merge "Add the Manage menu!" into rvc-dev am: c670ebf25e
Change-Id: I9164da8d34a32a96cf9594209889c4898cf24233
This commit is contained in:
21
packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
Normal file
21
packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true">
|
||||||
|
<ripple android:color="#99999999" />
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
99
packages/SystemUI/res/layout/bubble_manage_menu.xml
Normal file
99
packages/SystemUI/res/layout/bubble_manage_menu.xml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/rounded_bg_full"
|
||||||
|
android:elevation="@dimen/bubble_manage_menu_elevation"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bubble_manage_menu_dismiss_container"
|
||||||
|
android:background="@drawable/bubble_manage_menu_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_remove_no_shadow"
|
||||||
|
android:tint="@color/global_actions_text"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
|
||||||
|
android:text="@string/bubble_dismiss_text" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bubble_manage_menu_dont_bubble_container"
|
||||||
|
android:background="@drawable/bubble_manage_menu_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_stop_bubble"
|
||||||
|
android:tint="@color/global_actions_text"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
|
||||||
|
android:text="@string/bubbles_dont_bubble_conversation" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bubble_manage_menu_settings_container"
|
||||||
|
android:background="@drawable/bubble_manage_menu_row"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/bubble_manage_menu_settings_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/ic_remove_no_shadow"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bubble_manage_menu_settings_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -1200,6 +1200,7 @@
|
|||||||
snap to the dismiss target. -->
|
snap to the dismiss target. -->
|
||||||
<dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
|
<dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
|
||||||
<dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
|
<dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
|
||||||
|
<dimen name="bubble_manage_menu_elevation">4dp</dimen>
|
||||||
|
|
||||||
<dimen name="dismiss_circle_size">52dp</dimen>
|
<dimen name="dismiss_circle_size">52dp</dimen>
|
||||||
<dimen name="dismiss_target_x_size">24dp</dimen>
|
<dimen name="dismiss_target_x_size">24dp</dimen>
|
||||||
|
|||||||
@@ -2629,6 +2629,8 @@
|
|||||||
<string name="bubbles_user_education_manage">Tap Manage to turn off bubbles from this app</string>
|
<string name="bubbles_user_education_manage">Tap Manage to turn off bubbles from this app</string>
|
||||||
<!-- Button text for dismissing the bubble "manage" button tool tip [CHAR LIMIT=20]-->
|
<!-- Button text for dismissing the bubble "manage" button tool tip [CHAR LIMIT=20]-->
|
||||||
<string name="bubbles_user_education_got_it">Got it</string>
|
<string name="bubbles_user_education_got_it">Got it</string>
|
||||||
|
<!-- Label for the button that takes the user to the notification settings for the given app. -->
|
||||||
|
<string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
|
||||||
|
|
||||||
<!-- Notification content text when the system navigation mode changes as a result of changing the default launcher [CHAR LIMIT=NONE] -->
|
<!-- Notification content text when the system navigation mode changes as a result of changing the default launcher [CHAR LIMIT=NONE] -->
|
||||||
<string name="notification_content_system_nav_changed">System navigation updated. To make changes, go to Settings.</string>
|
<string name="notification_content_system_nav_changed">System navigation updated. To make changes, go to Settings.</string>
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ class Bubble implements BubbleViewProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private FlyoutMessage mFlyoutMessage;
|
private FlyoutMessage mFlyoutMessage;
|
||||||
|
private Drawable mBadgedAppIcon;
|
||||||
private Bitmap mBadgedImage;
|
private Bitmap mBadgedImage;
|
||||||
private int mDotColor;
|
private int mDotColor;
|
||||||
private Path mDotPath;
|
private Path mDotPath;
|
||||||
@@ -133,6 +134,10 @@ class Bubble implements BubbleViewProvider {
|
|||||||
return mBadgedImage;
|
return mBadgedImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Drawable getBadgedAppIcon() {
|
||||||
|
return mBadgedAppIcon;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDotColor() {
|
public int getDotColor() {
|
||||||
return mDotColor;
|
return mDotColor;
|
||||||
@@ -239,6 +244,7 @@ class Bubble implements BubbleViewProvider {
|
|||||||
mAppName = info.appName;
|
mAppName = info.appName;
|
||||||
mFlyoutMessage = info.flyoutMessage;
|
mFlyoutMessage = info.flyoutMessage;
|
||||||
|
|
||||||
|
mBadgedAppIcon = info.badgedAppIcon;
|
||||||
mBadgedImage = info.badgedBubbleImage;
|
mBadgedImage = info.badgedBubbleImage;
|
||||||
mDotColor = info.dotColor;
|
mDotColor = info.dotColor;
|
||||||
mDotPath = info.dotPath;
|
mDotPath = info.dotPath;
|
||||||
|
|||||||
@@ -605,6 +605,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
|||||||
if (mExpandListener != null) {
|
if (mExpandListener != null) {
|
||||||
mStackView.setExpandListener(mExpandListener);
|
mStackView.setExpandListener(mExpandListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mStackView.setUnbubbleConversationCallback(notificationEntry ->
|
||||||
|
onUserChangedBubble(notificationEntry, false /* shouldBubble */));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import android.graphics.Point;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.ShapeDrawable;
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.service.notification.StatusBarNotification;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -55,14 +54,13 @@ import com.android.internal.policy.ScreenDecorationsUtils;
|
|||||||
import com.android.systemui.Dependency;
|
import com.android.systemui.Dependency;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.recents.TriangleShape;
|
import com.android.systemui.recents.TriangleShape;
|
||||||
import com.android.systemui.shared.system.SysUiStatsLog;
|
|
||||||
import com.android.systemui.statusbar.AlphaOptimizedButton;
|
import com.android.systemui.statusbar.AlphaOptimizedButton;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for the expanded bubble view, handles rendering the caret and settings icon.
|
* Container for the expanded bubble view, handles rendering the caret and settings icon.
|
||||||
*/
|
*/
|
||||||
public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
|
public class BubbleExpandedView extends LinearLayout {
|
||||||
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
|
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
|
||||||
|
|
||||||
private enum ActivityViewStatus {
|
private enum ActivityViewStatus {
|
||||||
@@ -100,9 +98,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
private int mPointerWidth;
|
private int mPointerWidth;
|
||||||
private int mPointerHeight;
|
private int mPointerHeight;
|
||||||
private ShapeDrawable mPointerDrawable;
|
private ShapeDrawable mPointerDrawable;
|
||||||
private Rect mTempRect = new Rect();
|
|
||||||
private int[] mTempLoc = new int[2];
|
|
||||||
private int mExpandedViewTouchSlop;
|
|
||||||
|
|
||||||
@Nullable private Bubble mBubble;
|
@Nullable private Bubble mBubble;
|
||||||
|
|
||||||
@@ -224,7 +219,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
|
mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
|
||||||
mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
|
mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
|
||||||
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
|
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
|
||||||
mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -239,7 +233,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
|
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
|
||||||
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
|
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
|
||||||
|
|
||||||
|
|
||||||
mPointerDrawable = new ShapeDrawable(TriangleShape.create(
|
mPointerDrawable = new ShapeDrawable(TriangleShape.create(
|
||||||
mPointerWidth, mPointerHeight, true /* pointUp */));
|
mPointerWidth, mPointerHeight, true /* pointUp */));
|
||||||
mPointerView.setBackground(mPointerDrawable);
|
mPointerView.setBackground(mPointerDrawable);
|
||||||
@@ -248,7 +241,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
|
mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
|
||||||
R.dimen.bubble_manage_button_height);
|
R.dimen.bubble_manage_button_height);
|
||||||
mSettingsIcon = findViewById(R.id.settings_button);
|
mSettingsIcon = findViewById(R.id.settings_button);
|
||||||
mSettingsIcon.setOnClickListener(this);
|
|
||||||
|
|
||||||
mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
|
mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
|
||||||
true /* singleTaskInstance */);
|
true /* singleTaskInstance */);
|
||||||
@@ -289,6 +281,19 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
return mBubble != null ? mBubble.getEntry() : null;
|
return mBubble != null ? mBubble.getEntry() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setManageClickListener(OnClickListener manageClickListener) {
|
||||||
|
findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ActivityView's obscured touchable region. This calls onLocationChanged, which
|
||||||
|
* results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is useful
|
||||||
|
* if a view has been added or removed from on top of the ActivityView, such as the manage menu.
|
||||||
|
*/
|
||||||
|
void updateObscuredTouchableRegion() {
|
||||||
|
mActivityView.onLocationChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void applyThemeAttrs() {
|
void applyThemeAttrs() {
|
||||||
final TypedArray ta = mContext.obtainStyledAttributes(
|
final TypedArray ta = mContext.obtainStyledAttributes(
|
||||||
new int[] {
|
new int[] {
|
||||||
@@ -472,51 +477,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
- mPointerMargin - bottomInset;
|
- mPointerMargin - bottomInset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the provided x, y values (in raw coordinates) are in a touchable area of the
|
|
||||||
* expanded view.
|
|
||||||
*
|
|
||||||
* The touchable areas are the ActivityView (plus some slop around it) and the manage button.
|
|
||||||
*/
|
|
||||||
boolean intersectingTouchableContent(int rawX, int rawY) {
|
|
||||||
mTempRect.setEmpty();
|
|
||||||
if (mActivityView != null) {
|
|
||||||
mTempLoc = mActivityView.getLocationOnScreen();
|
|
||||||
mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
|
|
||||||
mTempLoc[1] - mExpandedViewTouchSlop,
|
|
||||||
mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
|
|
||||||
mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
|
|
||||||
}
|
|
||||||
if (mTempRect.contains(rawX, rawY)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
mTempLoc = mSettingsIcon.getLocationOnScreen();
|
|
||||||
mTempRect.set(mTempLoc[0],
|
|
||||||
mTempLoc[1],
|
|
||||||
mTempLoc[0] + mSettingsIcon.getWidth(),
|
|
||||||
mTempLoc[1] + mSettingsIcon.getHeight());
|
|
||||||
if (mTempRect.contains(rawX, rawY)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
if (mBubble == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int id = view.getId();
|
|
||||||
if (id == R.id.settings_button) {
|
|
||||||
Intent intent = mBubble.getSettingsIntent();
|
|
||||||
mStackView.collapseStack(() -> {
|
|
||||||
mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser());
|
|
||||||
logBubbleClickEvent(mBubble,
|
|
||||||
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update appearance of the expanded view being displayed.
|
* Update appearance of the expanded view being displayed.
|
||||||
*/
|
*/
|
||||||
@@ -547,10 +507,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
* Position of the manage button displayed in the expanded view. Used for placing user
|
* Position of the manage button displayed in the expanded view. Used for placing user
|
||||||
* education about the manage button.
|
* education about the manage button.
|
||||||
*/
|
*/
|
||||||
public Rect getManageButtonLocationOnScreen() {
|
public void getManageButtonBoundsOnScreen(Rect rect) {
|
||||||
mTempLoc = mSettingsIcon.getLocationOnScreen();
|
mSettingsIcon.getBoundsOnScreen(rect);
|
||||||
return new Rect(mTempLoc[0], mTempLoc[1], mTempLoc[0] + mSettingsIcon.getWidth(),
|
|
||||||
mTempLoc[1] + mSettingsIcon.getHeight());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -611,26 +569,4 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
|
|||||||
}
|
}
|
||||||
return INVALID_DISPLAY;
|
return INVALID_DISPLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs bubble UI click event.
|
|
||||||
*
|
|
||||||
* @param bubble the bubble notification entry that user is interacting with.
|
|
||||||
* @param action the user interaction enum.
|
|
||||||
*/
|
|
||||||
private void logBubbleClickEvent(Bubble bubble, int action) {
|
|
||||||
StatusBarNotification notification = bubble.getEntry().getSbn();
|
|
||||||
SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
|
|
||||||
notification.getPackageName(),
|
|
||||||
notification.getNotification().getChannelId(),
|
|
||||||
notification.getId(),
|
|
||||||
mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
|
|
||||||
mStackView.getBubbleCount(),
|
|
||||||
action,
|
|
||||||
mStackView.getNormalizedXPosition(),
|
|
||||||
mStackView.getNormalizedYPosition(),
|
|
||||||
bubble.showInShade(),
|
|
||||||
bubble.isOngoing(),
|
|
||||||
false /* isAppForeground (unused) */);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,14 @@ import android.animation.ValueAnimator;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.ColorMatrix;
|
import android.graphics.ColorMatrix;
|
||||||
import android.graphics.ColorMatrixColorFilter;
|
import android.graphics.ColorMatrixColorFilter;
|
||||||
|
import android.graphics.Outline;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
@@ -47,6 +49,7 @@ import android.graphics.RectF;
|
|||||||
import android.graphics.Region;
|
import android.graphics.Region;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
import android.view.DisplayCutout;
|
import android.view.DisplayCutout;
|
||||||
@@ -55,6 +58,7 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewOutlineProvider;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -62,6 +66,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
|||||||
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
import androidx.annotation.MainThread;
|
||||||
@@ -83,6 +88,7 @@ import com.android.systemui.bubbles.animation.StackAnimationController;
|
|||||||
import com.android.systemui.model.SysUiState;
|
import com.android.systemui.model.SysUiState;
|
||||||
import com.android.systemui.shared.system.QuickStepContract;
|
import com.android.systemui.shared.system.QuickStepContract;
|
||||||
import com.android.systemui.shared.system.SysUiStatsLog;
|
import com.android.systemui.shared.system.SysUiStatsLog;
|
||||||
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
|
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
|
||||||
import com.android.systemui.util.DismissCircleView;
|
import com.android.systemui.util.DismissCircleView;
|
||||||
import com.android.systemui.util.FloatingContentCoordinator;
|
import com.android.systemui.util.FloatingContentCoordinator;
|
||||||
@@ -97,6 +103,7 @@ import java.math.RoundingMode;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders bubbles in a stack and handles animating expanded and collapsed states.
|
* Renders bubbles in a stack and handles animating expanded and collapsed states.
|
||||||
@@ -224,7 +231,7 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
private int mPointerHeight;
|
private int mPointerHeight;
|
||||||
private int mStatusBarHeight;
|
private int mStatusBarHeight;
|
||||||
private int mImeOffset;
|
private int mImeOffset;
|
||||||
private BubbleViewProvider mExpandedBubble;
|
@Nullable private BubbleViewProvider mExpandedBubble;
|
||||||
private boolean mIsExpanded;
|
private boolean mIsExpanded;
|
||||||
|
|
||||||
/** Whether the stack is currently on the left side of the screen, or animating there. */
|
/** Whether the stack is currently on the left side of the screen, or animating there. */
|
||||||
@@ -244,6 +251,10 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BubbleController.BubbleExpandListener mExpandListener;
|
private BubbleController.BubbleExpandListener mExpandListener;
|
||||||
|
|
||||||
|
/** Callback to run when we want to unbubble the given notification's conversation. */
|
||||||
|
private Consumer<NotificationEntry> mUnbubbleConversationCallback;
|
||||||
|
|
||||||
private SysUiState mSysUiState;
|
private SysUiState mSysUiState;
|
||||||
|
|
||||||
private boolean mViewUpdatedRequested = false;
|
private boolean mViewUpdatedRequested = false;
|
||||||
@@ -255,9 +266,7 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
|
|
||||||
private LayoutInflater mInflater;
|
private LayoutInflater mInflater;
|
||||||
|
|
||||||
// Used for determining view / touch intersection
|
private Rect mTempRect = new Rect();
|
||||||
int[] mTempLoc = new int[2];
|
|
||||||
RectF mTempRect = new RectF();
|
|
||||||
|
|
||||||
private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
|
private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
|
||||||
|
|
||||||
@@ -471,6 +480,11 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the manage menu is visible, just hide it.
|
||||||
|
if (mShowingManage) {
|
||||||
|
showManageMenu(false /* show */);
|
||||||
|
}
|
||||||
|
|
||||||
if (mBubbleData.isExpanded()) {
|
if (mBubbleData.isExpanded()) {
|
||||||
maybeShowManageEducation(false /* show */);
|
maybeShowManageEducation(false /* show */);
|
||||||
|
|
||||||
@@ -627,6 +641,13 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
private BubbleManageEducationView mManageEducationView;
|
private BubbleManageEducationView mManageEducationView;
|
||||||
private boolean mAnimatingManageEducationAway;
|
private boolean mAnimatingManageEducationAway;
|
||||||
|
|
||||||
|
private ViewGroup mManageMenu;
|
||||||
|
private ImageView mManageSettingsIcon;
|
||||||
|
private TextView mManageSettingsText;
|
||||||
|
private boolean mShowingManage = false;
|
||||||
|
private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
|
||||||
|
SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
public BubbleStackView(Context context, BubbleData data,
|
public BubbleStackView(Context context, BubbleData data,
|
||||||
@Nullable SurfaceSynchronizer synchronizer,
|
@Nullable SurfaceSynchronizer synchronizer,
|
||||||
@@ -689,6 +710,8 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
mExpandedViewContainer.setClipChildren(false);
|
mExpandedViewContainer.setClipChildren(false);
|
||||||
addView(mExpandedViewContainer);
|
addView(mExpandedViewContainer);
|
||||||
|
|
||||||
|
setUpManageMenu();
|
||||||
|
|
||||||
setUpFlyout();
|
setUpFlyout();
|
||||||
mFlyoutTransitionSpring.setSpring(new SpringForce()
|
mFlyoutTransitionSpring.setSpring(new SpringForce()
|
||||||
.setStiffness(SpringForce.STIFFNESS_LOW)
|
.setStiffness(SpringForce.STIFFNESS_LOW)
|
||||||
@@ -838,7 +861,9 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
// ActivityViews, etc.) were touched. Collapse the stack if it's expanded.
|
// ActivityViews, etc.) were touched. Collapse the stack if it's expanded.
|
||||||
setOnTouchListener((view, ev) -> {
|
setOnTouchListener((view, ev) -> {
|
||||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
if (mBubbleData.isExpanded()) {
|
if (mShowingManage) {
|
||||||
|
showManageMenu(false /* show */);
|
||||||
|
} else if (mBubbleData.isExpanded()) {
|
||||||
mBubbleData.setExpanded(false);
|
mBubbleData.setExpanded(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -847,6 +872,66 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setUpManageMenu() {
|
||||||
|
if (mManageMenu != null) {
|
||||||
|
removeView(mManageMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
mManageMenu = (ViewGroup) LayoutInflater.from(getContext()).inflate(
|
||||||
|
R.layout.bubble_manage_menu, this, false);
|
||||||
|
mManageMenu.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
|
PhysicsAnimator.getInstance(mManageMenu).setDefaultSpringConfig(mManageSpringConfig);
|
||||||
|
|
||||||
|
final TypedArray ta = mContext.obtainStyledAttributes(
|
||||||
|
new int[] {android.R.attr.dialogCornerRadius});
|
||||||
|
final int menuCornerRadius = ta.getDimensionPixelSize(0, 0);
|
||||||
|
ta.recycle();
|
||||||
|
|
||||||
|
mManageMenu.setOutlineProvider(new ViewOutlineProvider() {
|
||||||
|
@Override
|
||||||
|
public void getOutline(View view, Outline outline) {
|
||||||
|
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), menuCornerRadius);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mManageMenu.setClipToOutline(true);
|
||||||
|
|
||||||
|
mManageMenu.findViewById(R.id.bubble_manage_menu_dismiss_container).setOnClickListener(
|
||||||
|
view -> {
|
||||||
|
showManageMenu(false /* show */);
|
||||||
|
dismissBubbleIfExists(mBubbleData.getSelectedBubble());
|
||||||
|
});
|
||||||
|
|
||||||
|
mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener(
|
||||||
|
view -> {
|
||||||
|
showManageMenu(false /* show */);
|
||||||
|
final Bubble bubble = mBubbleData.getSelectedBubble();
|
||||||
|
if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
|
||||||
|
mUnbubbleConversationCallback.accept(bubble.getEntry());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener(
|
||||||
|
view -> {
|
||||||
|
showManageMenu(false /* show */);
|
||||||
|
final Bubble bubble = mBubbleData.getSelectedBubble();
|
||||||
|
if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
|
||||||
|
final Intent intent = bubble.getSettingsIntent();
|
||||||
|
collapseStack(() -> {
|
||||||
|
mContext.startActivityAsUser(
|
||||||
|
intent, bubble.getEntry().getSbn().getUser());
|
||||||
|
logBubbleClickEvent(
|
||||||
|
bubble,
|
||||||
|
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon);
|
||||||
|
mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name);
|
||||||
|
addView(mManageMenu);
|
||||||
|
}
|
||||||
|
|
||||||
private void setUpUserEducation() {
|
private void setUpUserEducation() {
|
||||||
if (mUserEducationView != null) {
|
if (mUserEducationView != null) {
|
||||||
removeView(mUserEducationView);
|
removeView(mUserEducationView);
|
||||||
@@ -934,6 +1019,7 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
setUpFlyout();
|
setUpFlyout();
|
||||||
setUpOverflow();
|
setUpOverflow();
|
||||||
setUpUserEducation();
|
setUpUserEducation();
|
||||||
|
setUpManageMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
|
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
|
||||||
@@ -960,6 +1046,9 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation));
|
Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation));
|
||||||
addOnLayoutChangeListener(mOrientationChangedListener);
|
addOnLayoutChangeListener(mOrientationChangedListener);
|
||||||
hideFlyoutImmediate();
|
hideFlyoutImmediate();
|
||||||
|
|
||||||
|
mManageMenu.setVisibility(View.INVISIBLE);
|
||||||
|
mShowingManage = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1100,6 +1189,12 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
mExpandListener = listener;
|
mExpandListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets the function to call to un-bubble the given conversation. */
|
||||||
|
public void setUnbubbleConversationCallback(
|
||||||
|
Consumer<NotificationEntry> unbubbleConversationCallback) {
|
||||||
|
mUnbubbleConversationCallback = unbubbleConversationCallback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the stack of bubbles is expanded or not.
|
* Whether the stack of bubbles is expanded or not.
|
||||||
*/
|
*/
|
||||||
@@ -1361,15 +1456,14 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
mManageEducationView.setAlpha(0);
|
mManageEducationView.setAlpha(0);
|
||||||
mManageEducationView.setVisibility(VISIBLE);
|
mManageEducationView.setVisibility(VISIBLE);
|
||||||
mManageEducationView.post(() -> {
|
mManageEducationView.post(() -> {
|
||||||
final Rect position =
|
mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
|
||||||
mExpandedBubble.getExpandedView().getManageButtonLocationOnScreen();
|
|
||||||
final int viewHeight = mManageEducationView.getManageViewHeight();
|
final int viewHeight = mManageEducationView.getManageViewHeight();
|
||||||
final int inset = getResources().getDimensionPixelSize(
|
final int inset = getResources().getDimensionPixelSize(
|
||||||
R.dimen.bubbles_manage_education_top_inset);
|
R.dimen.bubbles_manage_education_top_inset);
|
||||||
mManageEducationView.bringToFront();
|
mManageEducationView.bringToFront();
|
||||||
mManageEducationView.setManageViewPosition(position.left,
|
mManageEducationView.setManageViewPosition(mTempRect.left,
|
||||||
position.top - viewHeight + inset);
|
mTempRect.top - viewHeight + inset);
|
||||||
mManageEducationView.setPointerPosition(position.centerX() - position.left);
|
mManageEducationView.setPointerPosition(mTempRect.centerX() - mTempRect.left);
|
||||||
mManageEducationView.animate()
|
mManageEducationView.animate()
|
||||||
.setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
|
.setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
|
||||||
.setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
|
.setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
|
||||||
@@ -1443,6 +1537,9 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void animateCollapse() {
|
private void animateCollapse() {
|
||||||
|
// Hide the menu if it's visible.
|
||||||
|
showManageMenu(false);
|
||||||
|
|
||||||
mIsExpanded = false;
|
mIsExpanded = false;
|
||||||
final BubbleViewProvider previouslySelected = mExpandedBubble;
|
final BubbleViewProvider previouslySelected = mExpandedBubble;
|
||||||
beforeExpandedViewAnimation();
|
beforeExpandedViewAnimation();
|
||||||
@@ -1570,9 +1667,9 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
|
public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
|
||||||
// If the notification shade is expanded, we shouldn't let the ActivityView steal any touch
|
// If the notification shade is expanded, or the manage menu is open, we shouldn't let the
|
||||||
// events from any location.
|
// ActivityView steal any touch events from any location.
|
||||||
if (mNotificationShadeWindowController.getPanelExpanded()) {
|
if (mNotificationShadeWindowController.getPanelExpanded() || mShowingManage) {
|
||||||
touchableRegion.setEmpty();
|
touchableRegion.setEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1658,17 +1755,20 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
private void dismissMagnetizedObject() {
|
private void dismissMagnetizedObject() {
|
||||||
if (mIsExpanded) {
|
if (mIsExpanded) {
|
||||||
final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
|
final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
|
||||||
final Bubble draggedOutBubble = mBubbleData.getBubbleWithView(draggedOutBubbleView);
|
dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView));
|
||||||
|
|
||||||
if (mBubbleData.hasBubbleWithKey(draggedOutBubble.getKey())) {
|
|
||||||
mBubbleData.notificationEntryRemoved(
|
|
||||||
draggedOutBubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
|
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dismissBubbleIfExists(@Nullable Bubble bubble) {
|
||||||
|
if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
|
||||||
|
mBubbleData.notificationEntryRemoved(
|
||||||
|
bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Prepares and starts the desaturate/darken animation on the bubble stack. */
|
/** Prepares and starts the desaturate/darken animation on the bubble stack. */
|
||||||
private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
|
private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
|
||||||
mDesaturateAndDarkenTargetView = targetView;
|
mDesaturateAndDarkenTargetView = targetView;
|
||||||
@@ -1912,6 +2012,63 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showManageMenu(boolean show) {
|
||||||
|
mShowingManage = show;
|
||||||
|
|
||||||
|
// This should not happen, since the manage menu is only visible when there's an expanded
|
||||||
|
// bubble. If we end up in this state, just hide the menu immediately.
|
||||||
|
if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
|
||||||
|
mManageMenu.setVisibility(View.INVISIBLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If available, update the manage menu's settings option with the expanded bubble's app
|
||||||
|
// name and icon.
|
||||||
|
if (show && mBubbleData.hasBubbleWithKey(mExpandedBubble.getKey())) {
|
||||||
|
final Bubble bubble = mBubbleData.getBubbleWithKey(mExpandedBubble.getKey());
|
||||||
|
mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon());
|
||||||
|
mManageSettingsText.setText(getResources().getString(
|
||||||
|
R.string.bubbles_app_settings, bubble.getAppName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
|
||||||
|
|
||||||
|
// When the menu is open, it should be at these coordinates. This will make the menu's
|
||||||
|
// bottom left corner match up with the button's bottom left corner.
|
||||||
|
final float targetX = mTempRect.left;
|
||||||
|
final float targetY = mTempRect.bottom - mManageMenu.getHeight();
|
||||||
|
|
||||||
|
if (show) {
|
||||||
|
mManageMenu.setScaleX(0.5f);
|
||||||
|
mManageMenu.setScaleY(0.5f);
|
||||||
|
mManageMenu.setTranslationX(targetX - mManageMenu.getWidth() / 4);
|
||||||
|
mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4);
|
||||||
|
mManageMenu.setAlpha(0f);
|
||||||
|
|
||||||
|
PhysicsAnimator.getInstance(mManageMenu)
|
||||||
|
.spring(DynamicAnimation.ALPHA, 1f)
|
||||||
|
.spring(DynamicAnimation.SCALE_X, 1f)
|
||||||
|
.spring(DynamicAnimation.SCALE_Y, 1f)
|
||||||
|
.spring(DynamicAnimation.TRANSLATION_X, targetX)
|
||||||
|
.spring(DynamicAnimation.TRANSLATION_Y, targetY)
|
||||||
|
.start();
|
||||||
|
|
||||||
|
mManageMenu.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
PhysicsAnimator.getInstance(mManageMenu)
|
||||||
|
.spring(DynamicAnimation.ALPHA, 0f)
|
||||||
|
.spring(DynamicAnimation.SCALE_X, 0.5f)
|
||||||
|
.spring(DynamicAnimation.SCALE_Y, 0.5f)
|
||||||
|
.spring(DynamicAnimation.TRANSLATION_X, targetX - mManageMenu.getWidth() / 4)
|
||||||
|
.spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4)
|
||||||
|
.withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE))
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the AV's obscured touchable region for the new menu visibility state.
|
||||||
|
mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateExpandedBubble() {
|
private void updateExpandedBubble() {
|
||||||
if (DEBUG_BUBBLE_STACK_VIEW) {
|
if (DEBUG_BUBBLE_STACK_VIEW) {
|
||||||
Log.d(TAG, "updateExpandedBubble()");
|
Log.d(TAG, "updateExpandedBubble()");
|
||||||
@@ -1921,6 +2078,7 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
&& mExpandedBubble.getExpandedView() != null) {
|
&& mExpandedBubble.getExpandedView() != null) {
|
||||||
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
|
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
|
||||||
mExpandedViewContainer.addView(bev);
|
mExpandedViewContainer.addView(bev);
|
||||||
|
bev.setManageClickListener((view) -> showManageMenu(!mShowingManage));
|
||||||
bev.populateExpandedView();
|
bev.populateExpandedView();
|
||||||
mExpandedViewContainer.setVisibility(VISIBLE);
|
mExpandedViewContainer.setVisibility(VISIBLE);
|
||||||
mExpandedViewContainer.setAlpha(1.0f);
|
mExpandedViewContainer.setAlpha(1.0f);
|
||||||
@@ -2089,4 +2247,26 @@ public class BubbleStackView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
return bubbles;
|
return bubbles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs bubble UI click event.
|
||||||
|
*
|
||||||
|
* @param bubble the bubble notification entry that user is interacting with.
|
||||||
|
* @param action the user interaction enum.
|
||||||
|
*/
|
||||||
|
private void logBubbleClickEvent(Bubble bubble, int action) {
|
||||||
|
StatusBarNotification notification = bubble.getEntry().getSbn();
|
||||||
|
SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
|
||||||
|
notification.getPackageName(),
|
||||||
|
notification.getNotification().getChannelId(),
|
||||||
|
notification.getId(),
|
||||||
|
getBubbleIndex(getExpandedBubble()),
|
||||||
|
getBubbleCount(),
|
||||||
|
action,
|
||||||
|
getNormalizedXPosition(),
|
||||||
|
getNormalizedYPosition(),
|
||||||
|
bubble.showInShade(),
|
||||||
|
bubble.isOngoing(),
|
||||||
|
false /* isAppForeground (unused) */);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
|
|||||||
ShortcutInfo shortcutInfo;
|
ShortcutInfo shortcutInfo;
|
||||||
String appName;
|
String appName;
|
||||||
Bitmap badgedBubbleImage;
|
Bitmap badgedBubbleImage;
|
||||||
|
Drawable badgedAppIcon;
|
||||||
int dotColor;
|
int dotColor;
|
||||||
Path dotPath;
|
Path dotPath;
|
||||||
Bubble.FlyoutMessage flyoutMessage;
|
Bubble.FlyoutMessage flyoutMessage;
|
||||||
@@ -176,6 +177,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
|
|||||||
}
|
}
|
||||||
|
|
||||||
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon);
|
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon);
|
||||||
|
info.badgedAppIcon = badgedIcon;
|
||||||
info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
|
info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
|
||||||
badgeBitmapInfo).icon;
|
badgeBitmapInfo).icon;
|
||||||
|
|
||||||
|
|||||||
@@ -20,15 +20,17 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
|
* Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
|
||||||
*/
|
*/
|
||||||
interface BubbleViewProvider {
|
interface BubbleViewProvider {
|
||||||
BubbleExpandedView getExpandedView();
|
@Nullable BubbleExpandedView getExpandedView();
|
||||||
|
|
||||||
void setContentVisibility(boolean visible);
|
void setContentVisibility(boolean visible);
|
||||||
|
|
||||||
View getIconView();
|
@Nullable View getIconView();
|
||||||
|
|
||||||
void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
|
void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
|
||||||
|
|
||||||
|
|||||||
@@ -61,14 +61,17 @@ internal val animators = WeakHashMap<Any, PhysicsAnimator<*>>()
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Default spring configuration to use for animations where stiffness and/or damping ratio
|
* Default spring configuration to use for animations where stiffness and/or damping ratio
|
||||||
* were not provided.
|
* were not provided, and a default spring was not set via [PhysicsAnimator.setDefaultSpringConfig].
|
||||||
*/
|
*/
|
||||||
private val defaultSpring = PhysicsAnimator.SpringConfig(
|
private val globalDefaultSpring = PhysicsAnimator.SpringConfig(
|
||||||
SpringForce.STIFFNESS_MEDIUM,
|
SpringForce.STIFFNESS_MEDIUM,
|
||||||
SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
|
SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
|
||||||
|
|
||||||
/** Default fling configuration to use for animations where friction was not provided. */
|
/**
|
||||||
private val defaultFling = PhysicsAnimator.FlingConfig(
|
* Default fling configuration to use for animations where friction was not provided, and a default
|
||||||
|
* fling config was not set via [PhysicsAnimator.setDefaultFlingConfig].
|
||||||
|
*/
|
||||||
|
private val globalDefaultFling = PhysicsAnimator.FlingConfig(
|
||||||
friction = 1f, min = -Float.MAX_VALUE, max = Float.MAX_VALUE)
|
friction = 1f, min = -Float.MAX_VALUE, max = Float.MAX_VALUE)
|
||||||
|
|
||||||
/** Whether to log helpful debug information about animations. */
|
/** Whether to log helpful debug information about animations. */
|
||||||
@@ -111,6 +114,12 @@ class PhysicsAnimator<T> private constructor (val target: T) {
|
|||||||
/** End actions to run when all animations have completed. */
|
/** End actions to run when all animations have completed. */
|
||||||
private val endActions = ArrayList<EndAction>()
|
private val endActions = ArrayList<EndAction>()
|
||||||
|
|
||||||
|
/** SpringConfig to use by default for properties whose springs were not provided. */
|
||||||
|
private var defaultSpring: SpringConfig = globalDefaultSpring
|
||||||
|
|
||||||
|
/** FlingConfig to use by default for properties whose fling configs were not provided. */
|
||||||
|
private var defaultFling: FlingConfig = globalDefaultFling
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
|
* Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
|
||||||
* the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
|
* the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
|
||||||
@@ -203,6 +212,19 @@ class PhysicsAnimator<T> private constructor (val target: T) {
|
|||||||
return spring(property, toPosition, 0f, config)
|
return spring(property, toPosition, 0f, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Springs a property to a given value using the provided configuration options, and a start
|
||||||
|
* velocity of 0f.
|
||||||
|
*
|
||||||
|
* @see spring
|
||||||
|
*/
|
||||||
|
fun spring(
|
||||||
|
property: FloatPropertyCompat<in T>,
|
||||||
|
toPosition: Float
|
||||||
|
): PhysicsAnimator<T> {
|
||||||
|
return spring(property, toPosition, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flings a property using the given start velocity, using a [FlingAnimation] configured using
|
* Flings a property using the given start velocity, using a [FlingAnimation] configured using
|
||||||
* the provided configuration settings.
|
* the provided configuration settings.
|
||||||
@@ -392,6 +414,14 @@ class PhysicsAnimator<T> private constructor (val target: T) {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setDefaultSpringConfig(defaultSpring: SpringConfig) {
|
||||||
|
this.defaultSpring = defaultSpring
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDefaultFlingConfig(defaultFling: FlingConfig) {
|
||||||
|
this.defaultFling = defaultFling
|
||||||
|
}
|
||||||
|
|
||||||
/** Starts the animations! */
|
/** Starts the animations! */
|
||||||
fun start() {
|
fun start() {
|
||||||
startAction()
|
startAction()
|
||||||
@@ -752,7 +782,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
constructor() :
|
constructor() :
|
||||||
this(defaultSpring.stiffness, defaultSpring.dampingRatio)
|
this(globalDefaultSpring.stiffness, globalDefaultSpring.dampingRatio)
|
||||||
|
|
||||||
constructor(stiffness: Float, dampingRatio: Float) :
|
constructor(stiffness: Float, dampingRatio: Float) :
|
||||||
this(stiffness = stiffness, dampingRatio = dampingRatio, startVelocity = 0f)
|
this(stiffness = stiffness, dampingRatio = dampingRatio, startVelocity = 0f)
|
||||||
@@ -782,10 +812,10 @@ class PhysicsAnimator<T> private constructor (val target: T) {
|
|||||||
internal var startVelocity: Float
|
internal var startVelocity: Float
|
||||||
) {
|
) {
|
||||||
|
|
||||||
constructor() : this(defaultFling.friction)
|
constructor() : this(globalDefaultFling.friction)
|
||||||
|
|
||||||
constructor(friction: Float) :
|
constructor(friction: Float) :
|
||||||
this(friction, defaultFling.min, defaultFling.max)
|
this(friction, globalDefaultFling.min, globalDefaultFling.max)
|
||||||
|
|
||||||
constructor(friction: Float, min: Float, max: Float) :
|
constructor(friction: Float, min: Float, max: Float) :
|
||||||
this(friction, min, max, startVelocity = 0f)
|
this(friction, min, max, startVelocity = 0f)
|
||||||
|
|||||||
Reference in New Issue
Block a user