summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java97
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java95
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java72
-rw-r--r--packages/SystemUI/res/layout/notification_menu_row.xml22
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml8
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java637
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java367
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java9
14 files changed, 851 insertions, 762 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
new file mode 100644
index 000000000000..8dde35782be5
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package com.android.systemui.plugins.statusbar;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+
+@ProvidesInterface(action = NotificationMenuRowPlugin.ACTION,
+ version = NotificationMenuRowPlugin.VERSION)
+@DependsOn(target = OnMenuEventListener.class)
+@DependsOn(target = MenuItem.class)
+@DependsOn(target = NotificationSwipeActionHelper.class)
+@DependsOn(target = SnoozeOption.class)
+public interface NotificationMenuRowPlugin extends Plugin {
+
+ public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_MENU_ROW";
+ public static final int VERSION = 1;
+
+ @ProvidesInterface(version = OnMenuEventListener.VERSION)
+ public interface OnMenuEventListener {
+ public static final int VERSION = 1;
+ public void onMenuClicked(View row, int x, int y, MenuItem menu);
+
+ public void onMenuReset(View row);
+
+ public void onMenuShown(View row);
+ }
+
+ @ProvidesInterface(version = MenuItem.VERSION)
+ public interface MenuItem {
+ public static final int VERSION = 1;
+ public View getMenuView();
+
+ public View getGutsView();
+
+ public String getContentDescription();
+ }
+
+ /**
+ * @return a list of items to populate the menu 'behind' a notification.
+ */
+ public ArrayList<MenuItem> getMenuItems(Context context);
+
+ /**
+ * @return the {@link MenuItem} to display when a notification is long pressed.
+ */
+ public MenuItem getLongpressMenuItem(Context context);
+
+ public void setMenuItems(ArrayList<MenuItem> items);
+
+ public void setMenuClickListener(OnMenuEventListener listener);
+
+ public void setSwipeActionHelper(NotificationSwipeActionHelper listener);
+
+ public void setAppName(String appName);
+
+ public void createMenu(ViewGroup parent);
+
+ public View getMenuView();
+
+ public boolean isMenuVisible();
+
+ public void resetMenu();
+
+ public void onTranslationUpdate(float translation);
+
+ public void onHeightUpdate();
+
+ public boolean onTouchEvent(View view, MotionEvent ev, float velocity);
+
+ public default boolean useDefaultMenuItems() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java
deleted file mode 100644
index 529c42154098..000000000000
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java
+++ /dev/null
@@ -1,95 +0,0 @@
-
-package com.android.systemui.plugins.statusbar;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.view.View;
-
-import java.util.ArrayList;
-
-import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-@ProvidesInterface(action = NotificationMenuRowProvider.ACTION,
- version = NotificationMenuRowProvider.VERSION)
-public interface NotificationMenuRowProvider extends Plugin {
-
- public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_MENU_ROW";
-
- public static final int VERSION = 1;
-
- /**
- * Returns a list of items to populate the menu 'behind' a notification.
- */
- public ArrayList<MenuItem> getMenuItems(Context context);
-
- public interface OnMenuClickListener {
- public void onMenuClicked(View row, int x, int y, MenuItem menu);
-
- public void onMenuReset(View row);
- }
-
- public interface GutsInteractionListener {
- public void onInteraction(View view);
-
- public void closeGuts(View view);
- }
-
- public interface GutsContent {
- public void setInteractionListener(GutsInteractionListener listener);
-
- public View getContentView();
-
- public boolean handleCloseControls(boolean save);
-
- public boolean willBeRemoved();
- }
-
- public interface SnoozeGutsContent extends GutsContent {
- public void setSnoozeListener(SnoozeListener listener);
-
- public void setStatusBarNotification(StatusBarNotification sbn);
- }
-
- public interface SnoozeListener {
- public void snoozeNotification(StatusBarNotification sbn, SnoozeOption snoozeOption);
- }
-
- public static class MenuItem {
- public Drawable icon;
- public String menuDescription;
- public View menuView;
- public GutsContent gutsContent;
-
- public MenuItem(Drawable i, String s, GutsContent content) {
- icon = i;
- menuDescription = s;
- gutsContent = content;
- }
-
- public View getGutsView() {
- return gutsContent.getContentView();
- }
-
- public boolean onTouch(View v, int x, int y) {
- return false;
- }
- }
-
- public static class SnoozeOption {
- public SnoozeCriterion criterion;
- public int snoozeForMinutes;
- public CharSequence description;
- public CharSequence confirmation;
-
- public SnoozeOption(SnoozeCriterion crit, int minsToSnoozeFor, CharSequence desc,
- CharSequence confirm) {
- criterion = crit;
- snoozeForMinutes = minsToSnoozeFor;
- description = desc;
- confirmation = confirm;
- }
- }
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
new file mode 100644
index 000000000000..4ce1e36bde3a
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package com.android.systemui.plugins.statusbar;
+
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.view.MotionEvent;
+import android.view.View;
+
+@ProvidesInterface(version = NotificationSwipeActionHelper.VERSION)
+@DependsOn(target = SnoozeOption.class)
+public interface NotificationSwipeActionHelper {
+ public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_SWIPE_ACTION";
+
+ public static final int VERSION = 1;
+
+ /**
+ * Call this to dismiss a notification.
+ */
+ public void dismiss(View animView, float velocity);
+
+ /**
+ * Call this to snap a notification to provided {@code targetLeft}.
+ */
+ public void snap(View animView, float velocity, float targetLeft);
+
+ /**
+ * Call this to snooze a notification based on the provided {@link SnoozeOption}.
+ */
+ public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption);
+
+ public float getMinDismissVelocity();
+
+ public boolean isDismissGesture(MotionEvent ev);
+
+ public boolean swipedFarEnough(float translation, float viewSize);
+
+ public boolean swipedFastEnough(float translation, float velocity);
+
+ @ProvidesInterface(version = SnoozeOption.VERSION)
+ public static class SnoozeOption {
+ public static final int VERSION = 1;
+ public int snoozeForMinutes;
+ public SnoozeCriterion criterion;
+ public CharSequence description;
+ public CharSequence confirmation;
+
+ public SnoozeOption(SnoozeCriterion crit, int minsToSnoozeFor, CharSequence desc,
+ CharSequence confirm) {
+ criterion = crit;
+ snoozeForMinutes = minsToSnoozeFor;
+ description = desc;
+ confirmation = confirm;
+ }
+ }
+}
diff --git a/packages/SystemUI/res/layout/notification_menu_row.xml b/packages/SystemUI/res/layout/notification_menu_row.xml
deleted file mode 100644
index 12bcf8176561..000000000000
--- a/packages/SystemUI/res/layout/notification_menu_row.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2016, 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.
--->
-<com.android.systemui.statusbar.NotificationMenuRow
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="invisible"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index d62cc184889c..7f3708788d95 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -23,13 +23,7 @@
android:clickable="true"
>
- <ViewStub
- android:layout="@layout/notification_menu_row"
- android:id="@+id/menu_row_stub"
- android:inflatedId="@+id/notification_menu_row"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
+ <!-- Menu displayed behind notification added here programmatically -->
<com.android.systemui.statusbar.NotificationBackgroundView
android:id="@+id/backgroundNormal"
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index a95713fb017e..5a04108df807 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -32,9 +32,10 @@ import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.NotificationMenuRow;
import java.util.HashMap;
@@ -267,7 +268,7 @@ public class SwipeHelper implements Gefingerpoken {
mCurrView = mCallback.getChildAtPosition(ev);
if (mCurrView != null) {
- onDownUpdate(mCurrView);
+ onDownUpdate(mCurrView, ev);
mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
mVelocityTracker.addMovement(ev);
mInitialTouchPos = getPos(ev);
@@ -285,8 +286,12 @@ public class SwipeHelper implements Gefingerpoken {
mCurrView.getLocationOnScreen(mTmpPos);
final int x = (int) ev.getRawX() - mTmpPos[0];
final int y = (int) ev.getRawY() - mTmpPos[1];
- mLongPressListener.onLongPress(mCurrView, x, y,
- NotificationMenuRow.getLongpressMenuItem(mContext));
+ MenuItem menuItem = null;
+ if (mCurrView instanceof ExpandableNotificationRow) {
+ menuItem = ((ExpandableNotificationRow) mCurrView)
+ .getProvider().getLongpressMenuItem(mContext);
+ }
+ mLongPressListener.onLongPress(mCurrView, x, y, menuItem);
}
}
};
@@ -479,14 +484,14 @@ public class SwipeHelper implements Gefingerpoken {
/**
* Called when there's a down event.
*/
- public void onDownUpdate(View currView) {
+ public void onDownUpdate(View currView, MotionEvent ev) {
// Do nothing
}
/**
* Called on a move event.
*/
- protected void onMoveUpdate(View view, float totalTranslation, float delta) {
+ protected void onMoveUpdate(View view, MotionEvent ev, float totalTranslation, float delta) {
// Do nothing
}
@@ -580,7 +585,7 @@ public class SwipeHelper implements Gefingerpoken {
setTranslation(mCurrView, mTranslation + delta);
updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed);
- onMoveUpdate(mCurrView, mTranslation + delta, delta);
+ onMoveUpdate(mCurrView, ev, mTranslation + delta, delta);
}
break;
case MotionEvent.ACTION_UP:
@@ -635,7 +640,7 @@ public class SwipeHelper implements Gefingerpoken {
return DISMISS_IF_SWIPED_FAR_ENOUGH && Math.abs(translation) > 0.4 * getSize(mCurrView);
}
- protected boolean isDismissGesture(MotionEvent ev) {
+ public boolean isDismissGesture(MotionEvent ev) {
boolean falsingDetected = mCallback.isAntiFalsingNeeded();
if (mFalsingManager.isClassiferEnabled()) {
falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 00968ee8d0e4..9176f57c35cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -43,6 +43,7 @@ import android.view.ViewStub;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Chronometer;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -50,10 +51,15 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.NotificationColorUtil;
import com.android.internal.widget.CachingIconView;
+import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.statusbar.NotificationGuts.GutsContent;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationInflater;
@@ -71,10 +77,13 @@ import com.android.systemui.statusbar.stack.StackScrollState;
import java.util.ArrayList;
import java.util.List;
-public class ExpandableNotificationRow extends ActivatableNotificationView {
+public class ExpandableNotificationRow extends ActivatableNotificationView
+ implements PluginListener<NotificationMenuRowPlugin> {
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
private static final int COLORED_DIVIDER_ALPHA = 0x7B;
+ private static final int MENU_VIEW_INDEX = 0;
+
private final NotificationInflater mNotificationInflater;
private int mIconTransformContentShift;
private int mIconTransformContentShiftNoIcon;
@@ -129,7 +138,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private int mNotificationColor;
private ExpansionLogger mLogger;
private String mLoggingKey;
- private NotificationMenuRow mMenuRow;
private NotificationGuts mGuts;
private NotificationData.Entry mEntry;
private StatusBarNotification mStatusBarNotification;
@@ -141,7 +149,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private boolean mChildrenExpanded;
private boolean mIsSummaryWithChildren;
private NotificationChildrenContainer mChildrenContainer;
- private ViewStub mMenuRowStub;
+ private NotificationMenuRowPlugin mMenuRow;
private ViewStub mGutsStub;
private boolean mIsSystemChildExpanded;
private boolean mIsPinned;
@@ -422,7 +430,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void setAppName(String appName) {
mAppName = appName;
- if (mMenuRow != null) {
+ if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.setAppName(mAppName);
}
}
@@ -496,7 +504,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
protected boolean handleSlideBack() {
- if (mMenuRow != null && mMenuRow.isVisible()) {
+ if (mMenuRow != null && mMenuRow.isMenuVisible()) {
animateTranslateNotification(0 /* targetLeft */);
return true;
}
@@ -725,12 +733,65 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
public void setGutsView(MenuItem item) {
- if (mGuts != null) {
- item.gutsContent.setInteractionListener(mGuts);
- mGuts.setGutsContent(item.gutsContent);
+ if (mGuts != null && item.getGutsView() instanceof GutsContent) {
+ ((GutsContent) item.getGutsView()).setGutsParent(mGuts);
+ mGuts.setGutsContent((GutsContent) item.getGutsView());
}
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ Dependency.get(PluginManager.class).addPluginListener(this,
+ NotificationMenuRowPlugin.class, false /* Allow multiple */);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ Dependency.get(PluginManager.class).removePluginListener(this);
+ }
+
+ @Override
+ public void onPluginConnected(NotificationMenuRowPlugin plugin, Context pluginContext) {
+ boolean existed = mMenuRow.getMenuView() != null;
+ if (existed) {
+ removeView(mMenuRow.getMenuView());
+ }
+ mMenuRow = plugin;
+ if (mMenuRow.useDefaultMenuItems()) {
+ mMenuRow.setMenuItems(NotificationMenuRow.getDefaultMenuItems(mContext));
+ }
+ if (existed) {
+ createMenu();
+ }
+ }
+
+ @Override
+ public void onPluginDisconnected(NotificationMenuRowPlugin plugin) {
+ boolean existed = mMenuRow.getMenuView() != null;
+ mMenuRow = new NotificationMenuRow(mContext); // Back to default
+ if (existed) {
+ createMenu();
+ }
+ }
+
+ public NotificationMenuRowPlugin createMenu() {
+ if (mMenuRow.getMenuView() == null) {
+ mMenuRow.createMenu(this);
+ mMenuRow.setAppName(mAppName);
+ FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ addView(mMenuRow.getMenuView(), MENU_VIEW_INDEX, lp);
+ }
+ return mMenuRow;
+ }
+
+
+ public NotificationMenuRowPlugin getProvider() {
+ return mMenuRow;
+ }
+
public void onDensityOrFontScaleChanged() {
initDimens();
if (mIsSummaryWithChildren) {
@@ -747,17 +808,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mGuts.setVisibility(oldGuts.getVisibility());
addView(mGuts, index);
}
- if (mMenuRow != null) {
- View oldMenu = mMenuRow;
+ View oldMenu = mMenuRow.getMenuView();
+ if (oldMenu != null) {
int menuIndex = indexOfChild(oldMenu);
removeView(oldMenu);
- mMenuRow = (NotificationMenuRow) LayoutInflater.from(mContext).inflate(
- R.layout.notification_menu_row, this, false);
- mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this);
+ mMenuRow.createMenu(ExpandableNotificationRow.this);
mMenuRow.setAppName(mAppName);
- mMenuRow.setVisibility(oldMenu.getVisibility());
- addView(mMenuRow, menuIndex);
-
+ addView(mMenuRow.getMenuView(), menuIndex);
}
for (NotificationContentView l : mLayouts) {
l.reInflateViews();
@@ -1061,6 +1118,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
super(context, attrs);
mFalsingManager = FalsingManager.getInstance(context);
mNotificationInflater = new NotificationInflater(this);
+ mMenuRow = new NotificationMenuRow(mContext);
initDimens();
}
@@ -1113,15 +1171,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
l.setExpandClickListener(mExpandClickListener);
l.setContainingNotification(this);
}
- mMenuRowStub = (ViewStub) findViewById(R.id.menu_row_stub);
- mMenuRowStub.setOnInflateListener(new ViewStub.OnInflateListener() {
- @Override
- public void onInflate(ViewStub stub, View inflated) {
- mMenuRow = (NotificationMenuRow) inflated;
- mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this);
- mMenuRow.setAppName(mAppName);
- }
- });
mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
@@ -1145,13 +1194,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
});
- // Add the views that we translate to reveal the gear
+ // Add the views that we translate to reveal the menu
mTranslateableViews = new ArrayList<View>();
for (int i = 0; i < getChildCount(); i++) {
mTranslateableViews.add(getChildAt(i));
}
// Remove views that don't translate
- mTranslateableViews.remove(mMenuRowStub);
mTranslateableViews.remove(mChildrenContainerStub);
mTranslateableViews.remove(mGutsStub);
}
@@ -1166,9 +1214,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
}
invalidateOutline();
- if (mMenuRow != null) {
- mMenuRow.resetState(true /* notify */);
- }
+ mMenuRow.resetMenu();
}
public void animateTranslateNotification(final float leftTarget) {
@@ -1194,8 +1240,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
}
invalidateOutline();
- if (mMenuRow != null) {
- mMenuRow.updateMenuAlpha(translationX, getMeasuredWidth());
+ if (mMenuRow.getMenuView() != null) {
+ mMenuRow.onTranslationUpdate(translationX);
}
}
@@ -1232,8 +1278,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
public void onAnimationEnd(Animator anim) {
- if (!cancelled && mMenuRow != null && leftTarget == 0) {
- mMenuRow.resetState(true /* notify */);
+ if (!cancelled && leftTarget == 0) {
+ mMenuRow.resetMenu();
mTranslateAnim = null;
}
}
@@ -1242,20 +1288,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return translateAnim;
}
- public float getSpaceForGear() {
- if (mMenuRow != null) {
- return mMenuRow.getSpaceForMenu();
- }
- return 0;
- }
-
- public NotificationMenuRow getSettingsRow() {
- if (mMenuRow == null) {
- mMenuRowStub.inflate();
- }
- return mMenuRow;
- }
-
public void inflateGuts() {
if (mGuts == null) {
mGutsStub.inflate();
@@ -1531,8 +1563,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
updateMaxHeights();
- if (mMenuRow != null) {
- mMenuRow.updateVerticalLocation();
+ if (mMenuRow.getMenuView() != null) {
+ mMenuRow.onHeightUpdate();
}
updateContentShiftHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index fd1317e61436..2713f58272d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -51,8 +51,8 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsContent;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.util.Set;
@@ -60,8 +60,7 @@ import java.util.Set;
/**
* The guts of a notification revealed when performing a long press.
*/
-public class NotificationGuts extends FrameLayout
- implements NotificationMenuRowProvider.GutsInteractionListener {
+public class NotificationGuts extends FrameLayout {
private static final String TAG = "NotificationGuts";
private static final long CLOSE_GUTS_DELAY = 8000;
@@ -78,10 +77,35 @@ public class NotificationGuts extends FrameLayout
private GutsContent mGutsContent;
+ public interface GutsContent {
+
+ public void setGutsParent(NotificationGuts listener);
+
+ /**
+ * @return the view to be shown in the notification guts.
+ */
+ public View getContentView();
+
+ /**
+ * Called when the guts view have been told to close, typically after an outside
+ * interaction. Returning {@code true} here will prevent the guts view to close.
+ */
+ public boolean handleCloseControls(boolean save);
+
+ /**
+ * @return whether the notification associated with these guts is set to be removed.
+ */
+ public boolean willBeRemoved();
+ }
+
public interface OnGutsClosedListener {
public void onGutsClosed(NotificationGuts guts);
}
+ interface OnSettingsClickListener {
+ void onClick(View v, int appUid);
+ }
+
public NotificationGuts(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
@@ -163,10 +187,6 @@ public class NotificationGuts extends FrameLayout
}
}
- interface OnSettingsClickListener {
- void onClick(View v, int appUid);
- }
-
public void closeControls(int x, int y, boolean save) {
if (getWindowToken() == null) {
if (mListener != null) {
@@ -251,14 +271,4 @@ public class NotificationGuts extends FrameLayout
public boolean isExposed() {
return mExposed;
}
-
- @Override
- public void onInteraction(View view) {
- resetFalsingCheck();
- }
-
- @Override
- public void closeGuts(View view) {
- closeControls(-1 /* x */, -1 /* y */, true /* notify */);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index a9043e4c83bb..8d432c4767f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -53,8 +53,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsContent;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener;
import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -65,7 +63,7 @@ import java.util.Set;
/**
* The guts of a notification revealed when performing a long press.
*/
-public class NotificationInfo extends LinearLayout implements GutsContent {
+public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
private static final String TAG = "InfoGuts";
private INotificationManager mINotificationManager;
@@ -79,7 +77,7 @@ public class NotificationInfo extends LinearLayout implements GutsContent {
private View mChannelDisabledView;
private Switch mChannelEnabledSwitch;
- private GutsInteractionListener mGutsInteractionListener;
+ private NotificationGuts mGutsContainer;
public NotificationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -274,8 +272,8 @@ public class NotificationInfo extends LinearLayout implements GutsContent {
// Callback when checked.
mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (mGutsInteractionListener != null) {
- mGutsInteractionListener.onInteraction(NotificationInfo.this);
+ if (mGutsContainer != null) {
+ mGutsContainer.resetFalsingCheck();
}
updateSecondaryText();
});
@@ -300,8 +298,8 @@ public class NotificationInfo extends LinearLayout implements GutsContent {
}
@Override
- public void setInteractionListener(GutsInteractionListener listener) {
- mGutsInteractionListener = listener;
+ public void setGutsParent(NotificationGuts guts) {
+ mGutsContainer = guts;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index 534a71936b5a..5055dda0d863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -16,276 +16,449 @@
package com.android.systemui.statusbar;
+import java.util.ArrayList;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
+import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
-import java.util.ArrayList;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.OnMenuClickListener;
-
-public class NotificationMenuRow extends FrameLayout
- implements PluginListener<NotificationMenuRowProvider>, View.OnClickListener {
+public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener {
private static final int ICON_ALPHA_ANIM_DURATION = 200;
+ private static final long SHOW_MENU_DELAY = 60;
+ private static final long SWIPE_MENU_TIMING = 200;
+
+ private static final int NOTIFICATION_INFO_INDEX = 1;
private ExpandableNotificationRow mParent;
- private OnMenuClickListener mListener;
- private NotificationMenuRowProvider mMenuProvider;
- private ArrayList<MenuItem> mMenuItems = new ArrayList<>();
+
+ private Context mContext;
+ private FrameLayout mMenuContainer;
+ private ArrayList<MenuItem> mMenuItems;
+ private OnMenuEventListener mMenuListener;
private ValueAnimator mFadeAnimator;
- private boolean mMenuFadedIn = false;
- private boolean mAnimating = false;
- private boolean mOnLeft = true;
- private boolean mDismissing = false;
- private boolean mSnapping = false;
- private boolean mIconsPlaced = false;
+ private boolean mAnimating;
+ private boolean mMenuFadedIn;
+
+ private boolean mOnLeft;
+ private boolean mIconsPlaced;
+
+ private boolean mDismissing;
+ private boolean mSnapping;
+ private float mTranslation;
private int[] mIconLocation = new int[2];
private int[] mParentLocation = new int[2];
private float mHorizSpaceForIcon;
private int mVertSpaceForIcons;
-
private int mIconPadding;
- private int mIconTint;
private float mAlpha = 0f;
- public NotificationMenuRow(Context context) {
- this(context, null);
- }
+ private CheckForDrag mCheckForDrag;
+ private Handler mHandler;
- public NotificationMenuRow(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
+ private boolean mMenuSnappedTo;
+ private boolean mMenuSnappedOnLeft;
+ private boolean mShouldShowMenu;
- public NotificationMenuRow(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
+ private NotificationSwipeActionHelper mSwipeHelper;
- public NotificationMenuRow(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs);
- mMenuItems.addAll(getDefaultNotificationMenuItems());
+ public NotificationMenuRow(Context context) {
+ mContext = context;
+ final Resources res = context.getResources();
+ mShouldShowMenu = res.getBoolean(R.bool.config_showNotificationGear);
+ mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
+ mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
+ mIconPadding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
+ mHandler = new Handler();
+ mMenuItems = getDefaultMenuItems(context);
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- Dependency.get(PluginManager.class).addPluginListener(
- this, NotificationMenuRowProvider.class, false /* Allow multiple */);
+ public ArrayList<MenuItem> getMenuItems(Context context) {
+ return mMenuItems;
}
@Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(PluginManager.class).removePluginListener(this);
+ public MenuItem getLongpressMenuItem(Context context) {
+ return mMenuItems.get(NOTIFICATION_INFO_INDEX);
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- final Resources res = getResources();
- mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
- mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
- mIconPadding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
- mIconTint = res.getColor(R.color.notification_gear_color);
- updateMenu(false /* notify */);
+ public void setSwipeActionHelper(NotificationSwipeActionHelper helper) {
+ mSwipeHelper = helper;
}
- public static MenuItem getLongpressMenuItem(Context context) {
- Resources res = context.getResources();
- Drawable settingsIcon = res.getDrawable(R.drawable.ic_settings);
- String settingsDescription = res.getString(R.string.notification_menu_gear_description);
- NotificationInfo settingsContent = (NotificationInfo) LayoutInflater.from(context).inflate(
- R.layout.notification_info, null, false);
- MenuItem settings = new MenuItem(settingsIcon, settingsDescription, settingsContent);
- return settings;
+ @Override
+ public void setMenuClickListener(OnMenuEventListener listener) {
+ mMenuListener = listener;
}
- public ArrayList<MenuItem> getDefaultNotificationMenuItems() {
- ArrayList<MenuItem> items = new ArrayList<MenuItem>();
- Resources res = getResources();
-
- Drawable snoozeIcon = res.getDrawable(R.drawable.ic_snooze);
- NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(mContext)
- .inflate(R.layout.notification_snooze, null, false);
- String snoozeDescription = res.getString(R.string.notification_menu_snooze_description);
- MenuItem snooze = new MenuItem(snoozeIcon, snoozeDescription, content);
- items.add(snooze);
+ @Override
+ public void createMenu(ViewGroup parent) {
+ mParent = (ExpandableNotificationRow) parent;
+ mMenuContainer = new FrameLayout(mContext);
+ for (int i = 0; i < mMenuItems.size(); i++) {
+ addMenuView(mMenuItems.get(i), mMenuContainer);
+ }
+ resetState(false);
+ }
- Drawable settingsIcon = res.getDrawable(R.drawable.ic_settings);
- String settingsDescription = res.getString(R.string.notification_menu_gear_description);
- NotificationInfo settingsContent = (NotificationInfo) LayoutInflater.from(mContext).inflate(
- R.layout.notification_info, null, false);
- MenuItem settings = new MenuItem(settingsIcon, settingsDescription, settingsContent);
- items.add(settings);
- return items;
+ @Override
+ public boolean isMenuVisible() {
+ return mAlpha > 0;
}
- private void updateMenu(boolean notify) {
- removeAllViews();
- mMenuItems.clear();
- if (mMenuProvider != null) {
- mMenuItems.addAll(mMenuProvider.getMenuItems(getContext()));
- }
- mMenuItems.addAll(getDefaultNotificationMenuItems());
- for (int i = 0; i < mMenuItems.size(); i++) {
- final View v = createMenuView(mMenuItems.get(i));
- mMenuItems.get(i).menuView = v;
- }
- resetState(notify);
+ @Override
+ public View getMenuView() {
+ return mMenuContainer;
}
- private View createMenuView(MenuItem item) {
- AlphaOptimizedImageView iv = new AlphaOptimizedImageView(getContext());
- addView(iv);
- iv.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
- iv.setImageDrawable(item.icon);
- iv.setOnClickListener(this);
- iv.setColorFilter(mIconTint);
- iv.setAlpha(mAlpha);
- FrameLayout.LayoutParams lp = (LayoutParams) iv.getLayoutParams();
- lp.width = (int) mHorizSpaceForIcon;
- lp.height = (int) mHorizSpaceForIcon;
- return iv;
+ @Override
+ public void resetMenu() {
+ resetState(true);
}
- public void resetState(boolean notify) {
+ private void resetState(boolean notify) {
setMenuAlpha(0f);
mIconsPlaced = false;
mMenuFadedIn = false;
mAnimating = false;
mSnapping = false;
mDismissing = false;
- setMenuLocation(mOnLeft ? 1 : -1 /* on left */);
- if (mListener != null && notify) {
- mListener.onMenuReset(mParent);
+ mMenuSnappedTo = false;
+ setMenuLocation();
+ if (mMenuListener != null && notify) {
+ mMenuListener.onMenuReset(mParent);
}
}
- public void setMenuClickListener(OnMenuClickListener listener) {
- mListener = listener;
- }
+ @Override
+ public boolean onTouchEvent(View view, MotionEvent ev, float velocity) {
+ final int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mSnapping = false;
+ if (mFadeAnimator != null) {
+ mFadeAnimator.cancel();
+ }
+ mHandler.removeCallbacks(mCheckForDrag);
+ mCheckForDrag = null;
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ mSnapping = false;
+ // If the menu is visible and the movement is towards it it's not a location change.
+ boolean locationChange = isTowardsMenu(mTranslation)
+ ? false : isMenuLocationChange();
+ if (locationChange) {
+ // Don't consider it "snapped" if location has changed.
+ mMenuSnappedTo = false;
+
+ // Changed directions, make sure we check to fade in icon again.
+ if (!mHandler.hasCallbacks(mCheckForDrag)) {
+ // No check scheduled, set null to schedule a new one.
+ mCheckForDrag = null;
+ } else {
+ // Check scheduled, reset alpha and update location; check will fade it in
+ setMenuAlpha(0f);
+ setMenuLocation();
+ }
+ }
+ if (mShouldShowMenu
+ && !NotificationStackScrollLayout.isPinnedHeadsUp(view)
+ && !mParent.areGutsExposed()
+ && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
+ // Only show the menu if we're not a heads up view and guts aren't exposed.
+ mCheckForDrag = new CheckForDrag();
+ mHandler.postDelayed(mCheckForDrag, SHOW_MENU_DELAY);
+ }
+ break;
- public void setNotificationRowParent(ExpandableNotificationRow parent) {
- mParent = parent;
- setMenuLocation(mOnLeft ? 1 : -1);
+ case MotionEvent.ACTION_UP:
+ return handleUpEvent(ev, view, velocity);
+ }
+ return false;
}
- public void setAppName(String appName) {
- Resources res = getResources();
- final int count = mMenuItems.size();
- for (int i = 0; i < count; i++) {
- MenuItem item = mMenuItems.get(i);
- String description = String.format(
- res.getString(R.string.notification_menu_accessibility),
- appName, item.menuDescription);
- item.menuView.setContentDescription(description);
+ private boolean handleUpEvent(MotionEvent ev, View animView, float velocity) {
+ // If the menu should not be shown, then there is no need to check if the a swipe
+ // should result in a snapping to the menu. As a result, just check if the swipe
+ // was enough to dismiss the notification.
+ if (!mShouldShowMenu) {
+ if (mSwipeHelper.isDismissGesture(ev)) {
+ dismiss(animView, velocity);
+ } else {
+ snapBack(animView, velocity);
+ }
+ return true;
+ }
+
+ final boolean gestureTowardsMenu = isTowardsMenu(velocity);
+ final boolean gestureFastEnough =
+ mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity);
+ final boolean gestureFarEnough =
+ mSwipeHelper.swipedFarEnough(mTranslation, mParent.getWidth());
+ final double timeForGesture = ev.getEventTime() - ev.getDownTime();
+ final boolean showMenuForSlowOnGoing = !mParent.canViewBeDismissed()
+ && timeForGesture >= SWIPE_MENU_TIMING;
+
+ final float targetLeft = mOnLeft ? getSpaceForMenu() : -getSpaceForMenu();
+ if (mMenuSnappedTo && isMenuVisible()) {
+ if (mMenuSnappedOnLeft == mOnLeft) {
+ boolean coveringMenu = Math.abs(mTranslation) <= getSpaceForMenu() * 0.6f;
+ if (gestureTowardsMenu || coveringMenu) {
+ // Gesture is towards or covering the menu or a dismiss
+ snapBack(animView, 0);
+ } else if (mSwipeHelper.isDismissGesture(ev)) {
+ dismiss(animView, velocity);
+ } else {
+ // Didn't move enough to dismiss or cover, snap to the menu
+ showMenu(animView, targetLeft, velocity);
+ }
+ } else if ((!gestureFastEnough && swipedEnoughToShowMenu())
+ || (gestureTowardsMenu && !gestureFarEnough)) {
+ // The menu has been snapped to previously, however, the menu is now on the
+ // other side. If gesture is towards menu and not too far snap to the menu.
+ showMenu(animView, targetLeft, velocity);
+ } else if (mSwipeHelper.isDismissGesture(ev)) {
+ dismiss(animView, velocity);
+ } else {
+ snapBack(animView, velocity);
+ }
+ } else if (((!gestureFastEnough || showMenuForSlowOnGoing)
+ && swipedEnoughToShowMenu())
+ || gestureTowardsMenu) {
+ // Menu has not been snapped to previously and this is menu revealing gesture
+ showMenu(animView, targetLeft, velocity);
+ } else if (mSwipeHelper.isDismissGesture(ev)) {
+ dismiss(animView, velocity);
+ } else {
+ snapBack(animView, velocity);
}
+ return true;
}
- public ExpandableNotificationRow getNotificationParent() {
- return mParent;
+ private void showMenu(View animView, float targetLeft, float velocity) {
+ mMenuSnappedTo = true;
+ mMenuSnappedOnLeft = mOnLeft;
+ mMenuListener.onMenuShown(animView);
+ mSwipeHelper.snap(animView, targetLeft, velocity);
}
- public void setMenuAlpha(float alpha) {
- mAlpha = alpha;
- if (alpha == 0) {
- mMenuFadedIn = false; // Can fade in again once it's gone.
- setVisibility(View.INVISIBLE);
- } else {
- setVisibility(View.VISIBLE);
- }
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- getChildAt(i).setAlpha(mAlpha);
- }
+ private void snapBack(View animView, float velocity) {
+ mMenuSnappedTo = false;
+ mSnapping = true;
+ mSwipeHelper.snap(animView, 0 /* leftTarget */, velocity);
}
- /**
- * Returns whether the menu is displayed on the left side of the view or not.
- */
- public boolean isMenuOnLeft() {
- return mOnLeft;
+ private void dismiss(View animView, float velocity) {
+ mMenuSnappedTo = false;
+ mDismissing = true;
+ mSwipeHelper.dismiss(animView, velocity);
}
- /**
- * Returns the horizontal space in pixels required to display the menu.
- */
- public float getSpaceForMenu() {
- return mHorizSpaceForIcon * getChildCount();
+ private boolean swipedEnoughToShowMenu() {
+ // If the notification can't be dismissed then how far it can move is
+ // restricted -- reduce the distance it needs to move in this case.
+ final float multiplier = mParent.canViewBeDismissed() ? 0.4f : 0.2f;
+ final float snapBackThreshold = getSpaceForMenu() * multiplier;
+ return !mSwipeHelper.swipedFarEnough(0, 0) && isMenuVisible() && (mOnLeft
+ ? mTranslation > snapBackThreshold
+ : mTranslation < -snapBackThreshold);
}
/**
- * Indicates whether the menu is visible at 1 alpha. Does not indicate if entire view is
- * visible.
+ * Returns whether the gesture is towards the menu location or not.
*/
- public boolean isVisible() {
- return mAlpha > 0;
+ private boolean isTowardsMenu(float movement) {
+ return isMenuVisible()
+ && ((mOnLeft && movement <= 0)
+ || (!mOnLeft && movement >= 0));
+ }
+
+ @Override
+ public void setAppName(String appName) {
+ if (appName == null) {
+ return;
+ }
+ Resources res = mContext.getResources();
+ final int count = mMenuItems.size();
+ for (int i = 0; i < count; i++) {
+ MenuItem item = mMenuItems.get(i);
+ String description = String.format(
+ res.getString(R.string.notification_menu_accessibility),
+ appName, item.getContentDescription());
+ View menuView = item.getMenuView();
+ if (menuView != null) {
+ menuView.setContentDescription(description);
+ }
+ }
}
- public void cancelFadeAnimator() {
- if (mFadeAnimator != null) {
- mFadeAnimator.cancel();
+ @Override
+ public void onHeightUpdate() {
+ if (mParent == null || mMenuItems.size() == 0) {
+ return;
+ }
+ int parentHeight = mParent.getCollapsedHeight();
+ float translationY;
+ if (parentHeight < mVertSpaceForIcons) {
+ translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
+ } else {
+ translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2;
}
+ mMenuContainer.setTranslationY(translationY);
}
- public void updateMenuAlpha(final float transX, final float size) {
+ @Override
+ public void onTranslationUpdate(float translation) {
+ mTranslation = translation;
if (mAnimating || !mMenuFadedIn) {
// Don't adjust when animating, or if the menu hasn't been shown yet.
return;
}
-
- final float fadeThreshold = size * 0.3f;
- final float absTrans = Math.abs(transX);
+ final float fadeThreshold = mParent.getWidth() * 0.3f;
+ final float absTrans = Math.abs(translation);
float desiredAlpha = 0;
-
if (absTrans == 0) {
desiredAlpha = 0;
} else if (absTrans <= fadeThreshold) {
desiredAlpha = 1;
} else {
- desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold));
+ desiredAlpha = 1 - ((absTrans - fadeThreshold) / (mParent.getWidth() - fadeThreshold));
}
setMenuAlpha(desiredAlpha);
}
- public void fadeInMenu(final boolean fromLeft, final float transX,
- final float notiThreshold) {
+ @Override
+ public void onClick(View v) {
+ if (mMenuListener == null) {
+ // Nothing to do
+ return;
+ }
+ v.getLocationOnScreen(mIconLocation);
+ mParent.getLocationOnScreen(mParentLocation);
+ final int centerX = (int) (mHorizSpaceForIcon / 2);
+ final int centerY = v.getHeight() / 2;
+ final int x = mIconLocation[0] - mParentLocation[0] + centerX;
+ final int y = mIconLocation[1] - mParentLocation[1] + centerY;
+ final int index = mMenuContainer.indexOfChild(v);
+ mMenuListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
+ }
+
+ private boolean isMenuLocationChange() {
+ boolean onLeft = mTranslation > mIconPadding;
+ boolean onRight = mTranslation < -mIconPadding;
+ if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void setMenuLocation() {
+ boolean showOnLeft = mTranslation > 0;
+ if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping || mParent == null) {
+ // Do nothing
+ return;
+ }
+ final boolean isRtl = mParent.isLayoutRtl();
+ final int count = mMenuContainer.getChildCount();
+ final int width = mParent.getWidth();
+ for (int i = 0; i < count; i++) {
+ final View v = mMenuContainer.getChildAt(i);
+ final float left = isRtl
+ ? -(width - mHorizSpaceForIcon * (i + 1))
+ : i * mHorizSpaceForIcon;
+ final float right = isRtl
+ ? -i * mHorizSpaceForIcon
+ : width - (mHorizSpaceForIcon * (i + 1));
+ v.setTranslationX(showOnLeft ? left : right);
+ }
+ mOnLeft = showOnLeft;
+ mIconsPlaced = true;
+ }
+
+ private void setMenuAlpha(float alpha) {
+ mAlpha = alpha;
+ if (mMenuContainer == null) {
+ return;
+ }
+ if (alpha == 0) {
+ mMenuFadedIn = false; // Can fade in again once it's gone.
+ mMenuContainer.setVisibility(View.INVISIBLE);
+ } else {
+ mMenuContainer.setVisibility(View.VISIBLE);
+ }
+ final int count = mMenuContainer.getChildCount();
+ for (int i = 0; i < count; i++) {
+ mMenuContainer.getChildAt(i).setAlpha(mAlpha);
+ }
+ }
+
+ /**
+ * Returns the horizontal space in pixels required to display the menu.
+ */
+ private float getSpaceForMenu() {
+ return mHorizSpaceForIcon * mMenuContainer.getChildCount();
+ }
+
+ private final class CheckForDrag implements Runnable {
+ @Override
+ public void run() {
+ final float absTransX = Math.abs(mTranslation);
+ final float bounceBackToMenuWidth = getSpaceForMenu();
+ final float notiThreshold = mParent.getWidth() * 0.4f;
+ if ((!isMenuVisible() || isMenuLocationChange())
+ && absTransX >= bounceBackToMenuWidth * 0.4
+ && absTransX < notiThreshold) {
+ fadeInMenu(notiThreshold);
+ }
+ }
+ }
+
+ private void fadeInMenu(final float notiThreshold) {
if (mDismissing || mAnimating) {
return;
}
- if (isMenuLocationChange(transX)) {
+ if (isMenuLocationChange()) {
setMenuAlpha(0f);
}
- setMenuLocation((int) transX);
+ final float transX = mTranslation;
+ final boolean fromLeft = mTranslation > 0;
+ setMenuLocation();
mFadeAnimator = ValueAnimator.ofFloat(mAlpha, 1);
mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final float absTrans = Math.abs(transX);
- boolean pastGear = (fromLeft && transX <= notiThreshold)
+ boolean pastMenu = (fromLeft && transX <= notiThreshold)
|| (!fromLeft && absTrans <= notiThreshold);
- if (pastGear && !mMenuFadedIn) {
+ if (pastMenu && !mMenuFadedIn) {
setMenuAlpha((float) animation.getAnimatedValue());
}
}
@@ -313,91 +486,77 @@ public class NotificationMenuRow extends FrameLayout
mFadeAnimator.start();
}
- public void updateVerticalLocation() {
- if (mParent == null || mMenuItems.size() == 0) {
- return;
- }
- int parentHeight = mParent.getCollapsedHeight();
- float translationY;
- if (parentHeight < mVertSpaceForIcons) {
- translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2);
- } else {
- translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2;
- }
- setTranslationY(translationY);
- }
-
@Override
- public void onRtlPropertiesChanged(int layoutDirection) {
- mIconsPlaced = false;
- setMenuLocation(mOnLeft ? 1 : -1);
+ public void setMenuItems(ArrayList<MenuItem> items) {
+ // Do nothing we use our own for now.
+ // TODO -- handle / allow custom menu items!
}
- public void setMenuLocation(int translation) {
- boolean onLeft = translation > 0;
- if ((mIconsPlaced && onLeft == mOnLeft) || mSnapping || mParent == null) {
- // Do nothing
- return;
- }
- final boolean isRtl = mParent.isLayoutRtl();
- final int count = getChildCount();
- final int width = getWidth();
- for (int i = 0; i < count; i++) {
- final View v = getChildAt(i);
- final float left = isRtl
- ? -(width - mHorizSpaceForIcon * (i + 1))
- : i * mHorizSpaceForIcon;
- final float right = isRtl
- ? -i * mHorizSpaceForIcon
- : width - (mHorizSpaceForIcon * (i + 1));
- v.setTranslationX(onLeft ? left : right);
- }
- mOnLeft = onLeft;
- mIconsPlaced = true;
- }
+ public static ArrayList<MenuItem> getDefaultMenuItems(Context context) {
+ ArrayList<MenuItem> items = new ArrayList<MenuItem>();
+ Resources res = context.getResources();
- public boolean isMenuLocationChange(float translation) {
- boolean onLeft = translation > mIconPadding;
- boolean onRight = translation < -mIconPadding;
- if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
- return true;
- }
- return false;
- }
+ NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(context)
+ .inflate(R.layout.notification_snooze, null, false);
+ String snoozeDescription = res.getString(R.string.notification_menu_snooze_description);
+ MenuItem snooze = new NotificationMenuItem(context, snoozeDescription, content,
+ R.drawable.ic_snooze);
+ items.add(snooze);
- public void setDismissing() {
- mDismissing = true;
+ String settingsDescription = res.getString(R.string.notification_menu_gear_description);
+ NotificationInfo settingsContent = (NotificationInfo) LayoutInflater.from(context).inflate(
+ R.layout.notification_info, null, false);
+ MenuItem settings = new NotificationMenuItem(context, settingsDescription, settingsContent,
+ R.drawable.ic_settings);
+ items.add(settings);
+ return items;
}
- public void setSnapping(boolean snapping) {
- mSnapping = snapping;
+ private void addMenuView(MenuItem item, ViewGroup parent) {
+ View menuView = item.getMenuView();
+ if (menuView != null) {
+ parent.addView(menuView);
+ menuView.setOnClickListener(this);
+ FrameLayout.LayoutParams lp = (LayoutParams) menuView.getLayoutParams();
+ lp.width = (int) mHorizSpaceForIcon;
+ lp.height = (int) mHorizSpaceForIcon;
+ menuView.setLayoutParams(lp);
+ }
}
- @Override
- public void onClick(View v) {
- if (mListener == null) {
- // Nothing to do
- return;
+ public static class NotificationMenuItem implements MenuItem {
+ View mMenuView;
+ GutsContent mGutsContent;
+ String mContentDescription;
+
+ public NotificationMenuItem(Context context, String s, GutsContent content, int iconResId) {
+ Resources res = context.getResources();
+ int padding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
+ int tint = res.getColor(R.color.notification_gear_color);
+ AlphaOptimizedImageView iv = new AlphaOptimizedImageView(context);
+ iv.setPadding(padding, padding, padding, padding);
+ Drawable icon = context.getResources().getDrawable(iconResId);
+ iv.setImageDrawable(icon);
+ iv.setColorFilter(tint);
+ iv.setAlpha(1f);
+ mMenuView = iv;
+ mContentDescription = s;
+ mGutsContent = content;
}
- v.getLocationOnScreen(mIconLocation);
- mParent.getLocationOnScreen(mParentLocation);
- final int centerX = (int) (mHorizSpaceForIcon / 2);
- final int centerY = (int) (v.getTranslationY() * 2 + v.getHeight()) / 2;
- final int x = mIconLocation[0] - mParentLocation[0] + centerX;
- final int y = mIconLocation[1] - mParentLocation[1] + centerY;
- final int index = indexOfChild(v);
- mListener.onMenuClicked(mParent, x, y, mMenuItems.get(index));
- }
- @Override
- public void onPluginConnected(NotificationMenuRowProvider plugin, Context pluginContext) {
- mMenuProvider = plugin;
- updateMenu(false /* notify */);
- }
+ @Override
+ public View getMenuView() {
+ return mMenuView;
+ }
- @Override
- public void onPluginDisconnected(NotificationMenuRowProvider plugin) {
- mMenuProvider = null;
- updateMenu(false /* notify */);
+ @Override
+ public View getGutsView() {
+ return mGutsContent.getContentView();
+ }
+
+ @Override
+ public String getContentDescription() {
+ return mContentDescription;
+ }
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 6b1e62d9ad71..0de3e0244fa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -16,14 +16,10 @@ package com.android.systemui.statusbar;
*/
import java.util.ArrayList;
-import java.util.Calendar;
import java.util.List;
-import java.util.concurrent.TimeUnit;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeOption;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import android.content.Context;
import android.content.res.Resources;
@@ -33,23 +29,18 @@ import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.RadioGroup;
-import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.TextView;
-import android.widget.Toast;
import com.android.systemui.R;
public class NotificationSnooze extends LinearLayout
- implements NotificationMenuRowProvider.SnoozeGutsContent, View.OnClickListener {
+ implements NotificationGuts.GutsContent, View.OnClickListener {
private static final int MAX_ASSISTANT_SUGGESTIONS = 2;
- private GutsInteractionListener mGutsInteractionListener;
- private SnoozeListener mSnoozeListener;
+ private NotificationGuts mGutsContainer;
+ private NotificationSwipeActionHelper mSnoozeListener;
private StatusBarNotification mSbn;
private TextView mSelectedOptionText;
@@ -155,8 +146,8 @@ public class NotificationSnooze extends LinearLayout
@Override
public void onClick(View v) {
- if (mGutsInteractionListener != null) {
- mGutsInteractionListener.onInteraction(this);
+ if (mGutsContainer != null) {
+ mGutsContainer.resetFalsingCheck();
}
final int id = v.getId();
final SnoozeOption tag = (SnoozeOption) v.getTag();
@@ -172,7 +163,7 @@ public class NotificationSnooze extends LinearLayout
private void undoSnooze() {
mSelectedOption = null;
- mGutsInteractionListener.closeGuts(this);
+ mGutsContainer.closeControls(-1 /* x */, -1 /* y */, true /* notify */);
}
@Override
@@ -185,18 +176,16 @@ public class NotificationSnooze extends LinearLayout
return this;
}
- @Override
public void setStatusBarNotification(StatusBarNotification sbn) {
mSbn = sbn;
}
@Override
- public void setInteractionListener(GutsInteractionListener listener) {
- mGutsInteractionListener = listener;
+ public void setGutsParent(NotificationGuts guts) {
+ mGutsContainer = guts;
}
- @Override
- public void setSnoozeListener(SnoozeListener listener) {
+ public void setSnoozeListener(NotificationSwipeActionHelper listener) {
mSnoozeListener = listener;
}
@@ -206,7 +195,7 @@ public class NotificationSnooze extends LinearLayout
// then we commit the snooze action.
if (mSnoozeListener != null && mSelectedOption != null) {
mSnoozing = true;
- mSnoozeListener.snoozeNotification(mSbn, mSelectedOption);
+ mSnoozeListener.snooze(mSbn, mSelectedOption);
return true;
} else {
// Reset the view once it's closed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 101aee4e9130..b82b113f3f8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -140,8 +140,7 @@ import com.android.systemui.fragments.PluginFragmentListener;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeOption;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTileHost;
@@ -241,8 +240,8 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeGutsContent;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -255,8 +254,8 @@ import java.util.Stack;
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
- OnHeadsUpChangedListener, VisualStabilityManager.Callback, SnoozeListener,
- CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
+ OnHeadsUpChangedListener, VisualStabilityManager.Callback, CommandQueue.Callbacks,
+ ActivatableNotificationView.OnActivatedListener,
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
ExpandableNotificationRow.OnExpandClickListener {
public static final boolean MULTIUSER_DEBUG = false;
@@ -5069,15 +5068,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
- public SnoozeListener getSnoozeListener() {
- return this;
- }
-
- @Override
- public void snoozeNotification(StatusBarNotification sbn, SnoozeOption snoozeOption) {
- setNotificationSnoozed(sbn, snoozeOption);
- }
-
// Begin Extra BaseStatusBar methods.
protected CommandQueue mCommandQueue;
@@ -5745,7 +5735,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}, false /* afterKeyguardGone */);
}
- protected void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
+ public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
if (snoozeOption.criterion != null) {
mNotificationListener.snoozeNotification(sbn.getKey(), snoozeOption.criterion.getId());
} else {
@@ -5768,20 +5758,22 @@ public class StatusBar extends SystemUI implements DemoMode,
mGutsMenuItem = null;
});
- if (item.gutsContent instanceof SnoozeGutsContent) {
- ((SnoozeGutsContent) item.gutsContent).setSnoozeListener(getSnoozeListener());
- ((SnoozeGutsContent) item.gutsContent).setStatusBarNotification(sbn);
- ((NotificationSnooze) item.gutsContent).setSnoozeOptions(row.getEntry().snoozeCriteria);
+ View gutsView = item.getGutsView();
+ if (gutsView instanceof NotificationSnooze) {
+ NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
+ snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
+ snoozeGuts.setStatusBarNotification(sbn);
+ snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
}
- if (item.gutsContent instanceof NotificationInfo) {
+ if (gutsView instanceof NotificationInfo) {
final UserHandle userHandle = sbn.getUser();
PackageManager pmUser = getPackageManagerForUser(mContext,
userHandle.getIdentifier());
final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
final String pkg = sbn.getPackageName();
- NotificationInfo info = (NotificationInfo) item.gutsContent;
+ NotificationInfo info = (NotificationInfo) gutsView;
final NotificationInfo.OnSettingsClickListener onSettingsClick = (View v,
NotificationChannel channel, int appUid) -> {
mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO);
@@ -5890,7 +5882,7 @@ public class StatusBar extends SystemUI implements DemoMode,
+ "window");
return;
}
- dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
+ dismissPopups(-1 /* x */, -1 /* y */, false /* resetMenu */,
false /* animate */);
guts.setVisibility(View.VISIBLE);
final double horz = Math.max(guts.getWidth() - x, x);
@@ -5904,7 +5896,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- // Move the notification view back over the gear
+ // Move the notification view back over the menu
row.resetTranslation();
}
});
@@ -5930,19 +5922,19 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public void dismissPopups() {
- dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
+ dismissPopups(-1 /* x */, -1 /* y */, true /* resetMenu */, false /* animate */);
}
private void dismissPopups(int x, int y) {
- dismissPopups(x, y, true /* resetGear */, false /* animate */);
+ dismissPopups(x, y, true /* resetMenu */, false /* animate */);
}
- public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
+ public void dismissPopups(int x, int y, boolean resetMenu, boolean animate) {
if (mNotificationGutsExposed != null) {
mNotificationGutsExposed.closeControls(x, y, true /* save */);
}
- if (resetGear) {
- mStackScroller.resetExposedGearView(animate, true /* force */);
+ if (resetMenu) {
+ mStackScroller.resetExposedMenuView(animate, true /* force */);
}
}
@@ -6299,8 +6291,8 @@ public class StatusBar extends SystemUI implements DemoMode,
return;
}
- // Check if the notification is displaying the gear, if so slide notification back
- if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) {
+ // Check if the notification is displaying the menu, if so slide notification back
+ if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
row.animateTranslateNotification(0);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index d3b336bef629..7d2d0df3f0d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -37,6 +37,7 @@ import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
+import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
@@ -63,15 +64,15 @@ import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationGuts;
-import com.android.systemui.statusbar.NotificationMenuRow;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
@@ -96,7 +97,7 @@ import java.util.List;
public class NotificationStackScrollLayout extends ViewGroup
implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter,
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener,
- NotificationMenuRowProvider.OnMenuClickListener, ScrollContainer,
+ NotificationMenuRowPlugin.OnMenuEventListener, ScrollContainer,
VisibilityLocationProvider {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
@@ -228,10 +229,9 @@ public class NotificationStackScrollLayout extends ViewGroup
private int mMaxScrollAfterExpand;
private SwipeHelper.LongPressListener mLongPressListener;
- private NotificationMenuRow mCurrIconRow;
+ private NotificationMenuRowPlugin mCurrMenuRow;
private View mTranslatingParentView;
- private View mGearExposedView;
- private boolean mShouldShowGear;
+ private View mMenuExposedView;
/**
* Should in this touch motion only be scrolling allowed? It's true when the scroller was
@@ -397,7 +397,6 @@ public class NotificationStackScrollLayout extends ViewGroup
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
initView(context);
mFalsingManager = FalsingManager.getInstance(context);
- mShouldShowGear = res.getBoolean(R.bool.config_showNotificationGear);
mShouldDrawNotificationBackground =
res.getBoolean(R.bool.config_drawNotificationBackground);
@@ -410,6 +409,10 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
+ public NotificationSwipeActionHelper getSwipeActionHelper() {
+ return mSwipeHelper;
+ }
+
@Override
public void onMenuClicked(View view, int x, int y, MenuItem item) {
if (mLongPressListener == null) {
@@ -426,13 +429,22 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public void onMenuReset(View row) {
if (mTranslatingParentView != null && row == mTranslatingParentView) {
- mSwipeHelper.setSnappedToGear(false);
- mGearExposedView = null;
+ mMenuExposedView = null;
mTranslatingParentView = null;
}
}
@Override
+ public void onMenuShown(View row) {
+ mMenuExposedView = mTranslatingParentView;
+ if (row instanceof ExpandableNotificationRow) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
+ ((ExpandableNotificationRow) row).getStatusBarNotification()
+ .getPackageName());
+ }
+ mSwipeHelper.onMenuShown(row);
+ }
+
protected void onDraw(Canvas canvas) {
if (mShouldDrawNotificationBackground && mCurrentBounds.top < mCurrentBounds.bottom) {
canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom,
@@ -936,9 +948,9 @@ public class NotificationStackScrollLayout extends ViewGroup
// We start the swipe and snap back in the same frame, we don't want any animation
mDragAnimPendingChildren.remove(animView);
}
- if (mCurrIconRow != null && targetLeft == 0) {
- mCurrIconRow.resetState(true /* notify */);
- mCurrIconRow = null;
+ if (mCurrMenuRow != null && targetLeft == 0) {
+ mCurrMenuRow.resetMenu();
+ mCurrMenuRow = null;
}
}
@@ -999,10 +1011,10 @@ public class NotificationStackScrollLayout extends ViewGroup
ExpandableNotificationRow parent = row.getNotificationParent();
if (parent != null && parent.areChildrenExpanded()
&& (parent.areGutsExposed()
- || mGearExposedView == parent
+ || mMenuExposedView == parent
|| (parent.getNotificationChildren().size() == 1
&& parent.isClearable()))) {
- // In this case the group is expanded and showing the gear for the
+ // In this case the group is expanded and showing the menu for the
// group, further interaction should apply to the group, not any
// child notifications so we use the parent of the child. We also do the same
// if we only have a single child.
@@ -1261,8 +1273,8 @@ public class NotificationStackScrollLayout extends ViewGroup
public void snapViewIfNeeded(ExpandableNotificationRow child) {
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
- // If the child is showing the gear to go to settings, snap to that
- float targetLeft = child.getSettingsRow().isVisible() ? child.getTranslation() : 0;
+ // If the child is showing the notification menu snap to that
+ float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
mSwipeHelper.snapChildIfNeeded(child, animate, targetLeft);
}
@@ -4196,15 +4208,11 @@ public class NotificationStackScrollLayout extends ViewGroup
void flingTopOverscroll(float velocity, boolean open);
}
- private class NotificationSwipeHelper extends SwipeHelper {
- private static final long SHOW_GEAR_DELAY = 60;
- private static final long COVER_GEAR_DELAY = 4000;
- private static final long SWIPE_GEAR_TIMING = 200;
- private CheckForDrag mCheckForDrag;
+ private class NotificationSwipeHelper extends SwipeHelper
+ implements NotificationSwipeActionHelper {
+ private static final long COVER_MENU_DELAY = 4000;
private Runnable mFalsingCheck;
private Handler mHandler;
- private boolean mGearSnappedTo;
- private boolean mGearSnappedOnLeft;
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
super(swipeDirection, callback, context);
@@ -4212,69 +4220,46 @@ public class NotificationStackScrollLayout extends ViewGroup
mFalsingCheck = new Runnable() {
@Override
public void run() {
- resetExposedGearView(true /* animate */, true /* force */);
+ resetExposedMenuView(true /* animate */, true /* force */);
}
};
}
@Override
- public void onDownUpdate(View currView) {
- // Set the active view
+ public void onDownUpdate(View currView, MotionEvent ev) {
mTranslatingParentView = currView;
-
- // Reset check for drag gesture
- cancelCheckForDrag();
- if (mCurrIconRow != null) {
- mCurrIconRow.setSnapping(false);
+ mCurrMenuRow = null;
+ if (mCurrMenuRow != null) {
+ mCurrMenuRow.onTouchEvent(currView, ev, 0 /* velocity */);
}
- mCheckForDrag = null;
- mCurrIconRow = null;
mHandler.removeCallbacks(mFalsingCheck);
- // Slide back any notifications that might be showing a gear
- resetExposedGearView(true /* animate */, false /* force */);
+ // Slide back any notifications that might be showing a menu
+ resetExposedMenuView(true /* animate */, false /* force */);
if (currView instanceof ExpandableNotificationRow) {
- // Set the listener for the current row's gear
- mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
- mCurrIconRow.setMenuClickListener(NotificationStackScrollLayout.this);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) currView;
+ mCurrMenuRow = row.createMenu();
+ mCurrMenuRow.setSwipeActionHelper(NotificationSwipeHelper.this);
+ mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this);
}
}
@Override
- public void onMoveUpdate(View view, float translation, float delta) {
+ public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) {
mHandler.removeCallbacks(mFalsingCheck);
-
- if (mCurrIconRow != null) {
- mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
-
- // If the gear is visible and the movement is towards it it's not a location change.
- boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isMenuOnLeft();
- boolean locationChange = isTowardsGear(translation, onLeft)
- ? false : mCurrIconRow.isMenuLocationChange(translation);
- if (locationChange) {
- // Don't consider it "snapped" if location has changed.
- setSnappedToGear(false);
-
- // Changed directions, make sure we check to fade in icon again.
- if (!mHandler.hasCallbacks(mCheckForDrag)) {
- // No check scheduled, set null to schedule a new one.
- mCheckForDrag = null;
- } else {
- // Check scheduled, reset alpha and update location; check will fade it in
- mCurrIconRow.setMenuAlpha(0f);
- mCurrIconRow.setMenuLocation((int) translation);
- }
- }
+ if (mCurrMenuRow != null) {
+ mCurrMenuRow.onTouchEvent(view, ev, 0 /* velocity */);
}
+ }
- final boolean gutsExposed = (view instanceof ExpandableNotificationRow)
- && ((ExpandableNotificationRow) view).areGutsExposed();
-
- if (mShouldShowGear && !isPinnedHeadsUp(view) && !gutsExposed) {
- // Only show the gear if we're not a heads up view and guts aren't exposed.
- checkForDrag();
+ @Override
+ public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
+ float translation) {
+ if (mCurrMenuRow != null) {
+ return mCurrMenuRow.onTouchEvent(animView, ev, velocity);
}
+ return false;
}
@Override
@@ -4286,7 +4271,7 @@ public class NotificationStackScrollLayout extends ViewGroup
// of the panel early.
handleChildDismissed(view);
}
- handleGearCoveredOrDismissed();
+ handleMenuCoveredOrDismissed();
}
@Override
@@ -4294,121 +4279,21 @@ public class NotificationStackScrollLayout extends ViewGroup
super.snapChild(animView, targetLeft, velocity);
onDragCancelled(animView);
if (targetLeft == 0) {
- handleGearCoveredOrDismissed();
- }
- }
-
- private void handleGearCoveredOrDismissed() {
- cancelCheckForDrag();
- setSnappedToGear(false);
- if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
- mGearExposedView = null;
+ handleMenuCoveredOrDismissed();
}
}
@Override
- public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
- float translation) {
- if (mCurrIconRow == null) {
- cancelCheckForDrag();
- return false; // Let SwipeHelper handle it.
- }
-
- // If the gear icon should not be shown, then there is no need to check if the a swipe
- // should result in a snapping to the gear icon. As a result, just check if the swipe
- // was enough to dismiss the notification.
- if (!mShouldShowGear) {
- dismissOrSnapBack(animView, velocity, ev);
- return true;
- }
-
- boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isMenuOnLeft());
- boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity();
- final double timeForGesture = ev.getEventTime() - ev.getDownTime();
- final boolean showGearForSlowOnGoing = !canChildBeDismissed(animView)
- && timeForGesture >= SWIPE_GEAR_TIMING;
-
- if (mGearSnappedTo && mCurrIconRow.isVisible()) {
- if (mGearSnappedOnLeft == mCurrIconRow.isMenuOnLeft()) {
- boolean coveringGear =
- Math.abs(getTranslation(animView)) <= getSpaceForGear(animView) * 0.6f;
- if (gestureTowardsGear || coveringGear) {
- // Gesture is towards or covering the gear
- snapChild(animView, 0 /* leftTarget */, velocity);
- } else if (isDismissGesture(ev)) {
- // Gesture is a dismiss that's not towards the gear
- dismissChild(animView, velocity,
- !swipedFastEnough() /* useAccelerateInterpolator */);
- } else {
- // Didn't move enough to dismiss or cover, snap to the gear
- snapToGear(animView, velocity);
- }
- } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
- || (gestureTowardsGear && !swipedFarEnough())) {
- // The gear has been snapped to previously, however, the gear is now on the
- // other side. If gesture is towards gear and not too far snap to the gear.
- snapToGear(animView, velocity);
- } else {
- dismissOrSnapBack(animView, velocity, ev);
- }
- } else if (((!gestureFastEnough || showGearForSlowOnGoing)
- && swipedEnoughToShowGear(animView))
- || gestureTowardsGear) {
- // Gear has not been snapped to previously and this is gear revealing gesture
- snapToGear(animView, velocity);
- } else {
- dismissOrSnapBack(animView, velocity, ev);
- }
- return true;
+ public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) {
+ mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
}
- private void dismissOrSnapBack(View animView, float velocity, MotionEvent ev) {
- if (isDismissGesture(ev)) {
- dismissChild(animView, velocity,
- !swipedFastEnough() /* useAccelerateInterpolator */);
- } else {
- snapChild(animView, 0 /* leftTarget */, velocity);
+ private void handleMenuCoveredOrDismissed() {
+ if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) {
+ mMenuExposedView = null;
}
}
- private void snapToGear(View animView, float velocity) {
- final float snapBackThreshold = getSpaceForGear(animView);
- final float target = mCurrIconRow.isMenuOnLeft() ? snapBackThreshold
- : -snapBackThreshold;
- mGearExposedView = mTranslatingParentView;
- if (animView instanceof ExpandableNotificationRow) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
- ((ExpandableNotificationRow) animView).getStatusBarNotification()
- .getPackageName());
- }
- if (mCurrIconRow != null) {
- mCurrIconRow.setSnapping(true);
- setSnappedToGear(true);
- }
- onDragCancelled(animView);
-
- // If we're on the lockscreen we want to false this.
- if (isAntiFalsingNeeded()) {
- mHandler.removeCallbacks(mFalsingCheck);
- mHandler.postDelayed(mFalsingCheck, COVER_GEAR_DELAY);
- }
- super.snapChild(animView, target, velocity);
- }
-
- private boolean swipedEnoughToShowGear(View animView) {
- if (mTranslatingParentView == null) {
- return false;
- }
- // If the notification can't be dismissed then how far it can move is
- // restricted -- reduce the distance it needs to move in this case.
- final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
- final float snapBackThreshold = getSpaceForGear(animView) * multiplier;
- final float translation = getTranslation(animView);
- return !swipedFarEnough() && mCurrIconRow.isVisible() && (mCurrIconRow.isMenuOnLeft()
- ? translation > snapBackThreshold
- : translation < -snapBackThreshold);
- }
-
@Override
public Animator getViewTranslationAnimator(View v, float target,
AnimatorUpdateListener listener) {
@@ -4429,6 +4314,42 @@ public class NotificationStackScrollLayout extends ViewGroup
return ((ExpandableView) v).getTranslation();
}
+ @Override
+ public void dismiss(View animView, float velocity) {
+ dismissChild(animView, velocity,
+ !swipedFastEnough(0, 0) /* useAccelerateInterpolator */);
+ }
+
+ @Override
+ public void snap(View animView, float targetLeft, float velocity) {
+ snapChild(animView, targetLeft, velocity);
+ }
+
+ @Override
+ public boolean swipedFarEnough(float translation, float viewSize) {
+ return swipedFarEnough();
+ }
+
+ @Override
+ public boolean swipedFastEnough(float translation, float velocity) {
+ return swipedFastEnough();
+ }
+
+ @Override
+ public float getMinDismissVelocity() {
+ return getEscapeVelocity();
+ }
+
+ public void onMenuShown(View animView) {
+ onDragCancelled(animView);
+
+ // If we're on the lockscreen we want to false this.
+ if (isAntiFalsingNeeded()) {
+ mHandler.removeCallbacks(mFalsingCheck);
+ mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY);
+ }
+ }
+
public void closeControlsIfOutsideTouch(MotionEvent ev) {
NotificationGuts guts = mStatusBar.getExposedGuts();
View view = null;
@@ -4437,9 +4358,9 @@ public class NotificationStackScrollLayout extends ViewGroup
// Checking guts
view = guts;
height = guts.getActualHeight();
- } else if (mCurrIconRow != null && mCurrIconRow.isVisible()
+ } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible()
&& mTranslatingParentView != null) {
- // Checking gear
+ // Checking menu
view = mTranslatingParentView;
height = ((ExpandableView) mTranslatingParentView).getActualHeight();
}
@@ -4452,95 +4373,29 @@ public class NotificationStackScrollLayout extends ViewGroup
final int y = mTempInt2[1];
Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
if (!rect.contains(rx, ry)) {
- // Touch was outside visible guts / gear notification, close what's visible
- mStatusBar.dismissPopups(-1, -1, true /* resetGear */, true /* animate */);
- }
- }
- }
-
- /**
- * Returns whether the gesture is towards the gear location or not.
- */
- private boolean isTowardsGear(float velocity, boolean onLeft) {
- if (mCurrIconRow == null) {
- return false;
- }
- return mCurrIconRow.isVisible()
- && ((onLeft && velocity <= 0) || (!onLeft && velocity >= 0));
- }
-
- /**
- * Indicates the the gear has been snapped to.
- */
- private void setSnappedToGear(boolean snapped) {
- mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isMenuOnLeft() : false;
- mGearSnappedTo = snapped && mCurrIconRow != null;
- }
-
- /**
- * Returns the horizontal space in pixels required to display the gear behind a
- * notification.
- */
- private float getSpaceForGear(View view) {
- if (view instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) view).getSpaceForGear();
- }
- return 0;
- }
-
- private void checkForDrag() {
- if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
- mCheckForDrag = new CheckForDrag();
- mHandler.postDelayed(mCheckForDrag, SHOW_GEAR_DELAY);
- }
- }
-
- private void cancelCheckForDrag() {
- if (mCurrIconRow != null) {
- mCurrIconRow.cancelFadeAnimator();
- }
- mHandler.removeCallbacks(mCheckForDrag);
- }
-
- private final class CheckForDrag implements Runnable {
- @Override
- public void run() {
- if (mTranslatingParentView == null) {
- return;
- }
- final float translation = getTranslation(mTranslatingParentView);
- final float absTransX = Math.abs(translation);
- final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
- final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
- if ((mCurrIconRow != null && (!mCurrIconRow.isVisible()
- || mCurrIconRow.isMenuLocationChange(translation)))
- && absTransX >= bounceBackToGearWidth * 0.4
- && absTransX < notiThreshold) {
- // Fade in the gear
- mCurrIconRow.fadeInMenu(translation > 0 /* fromLeft */, translation,
- notiThreshold);
+ // Touch was outside visible guts / meny notification, close what's visible
+ mStatusBar.dismissPopups(-1, -1, true /* resetMenu */, true /* animate */);
}
}
}
- public void resetExposedGearView(boolean animate, boolean force) {
- if (mGearExposedView == null
- || (!force && mGearExposedView == mTranslatingParentView)) {
- // If no gear is showing or it's showing for this view we do nothing.
+ public void resetExposedMenuView(boolean animate, boolean force) {
+ if (mMenuExposedView == null
+ || (!force && mMenuExposedView == mTranslatingParentView)) {
+ // If no menu is showing or it's showing for this view we do nothing.
return;
}
- final View prevGearExposedView = mGearExposedView;
+ final View prevMenuExposedView = mMenuExposedView;
if (animate) {
- Animator anim = getViewTranslationAnimator(prevGearExposedView,
+ Animator anim = getViewTranslationAnimator(prevMenuExposedView,
0 /* leftTarget */, null /* updateListener */);
if (anim != null) {
anim.start();
}
- } else if (mGearExposedView instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) mGearExposedView).resetTranslation();
+ } else if (mMenuExposedView instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) mMenuExposedView).resetTranslation();
}
- mGearExposedView = null;
- mGearSnappedTo = false;
+ mMenuExposedView = null;
}
}
@@ -4557,8 +4412,8 @@ public class NotificationStackScrollLayout extends ViewGroup
}
}
- public void resetExposedGearView(boolean animate, boolean force) {
- mSwipeHelper.resetExposedGearView(animate, force);
+ public void resetExposedMenuView(boolean animate, boolean force) {
+ mSwipeHelper.resetExposedMenuView(animate, force);
}
public void closeControlsIfOutsideTouch(MotionEvent ev) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
index c2c633639e81..31b9bae846d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
@@ -18,6 +18,8 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
+import android.testing.ViewUtils;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
@@ -35,10 +37,11 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
@Test
public void testAttachDetach() {
- NotificationMenuRow row = new NotificationMenuRow(mContext);
- ViewUtils.attachView(row);
+ NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
+ row.createMenu(null);
+ ViewUtils.attachView(row.getMenuView());
TestableLooper.get(this).processAllMessages();
- ViewUtils.detachView(row);
+ ViewUtils.detachView(row.getMenuView());
TestableLooper.get(this).processAllMessages();
}
}