summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mady Mellor <madym@google.com> 2017-01-10 12:05:27 -0800
committer Mady Mellor <madym@google.com> 2017-03-22 19:04:50 -0700
commit95d743c38c919dd500d9dcacf9f998500d403d9e (patch)
tree2162d018b15a53e1e83da81ca78dc937fd17915b
parentfc7f4ceb44c2f18fc112ef53580d5fed520e0f9d (diff)
Add plugin interfaces to control notification menu / swipe behavior
1) Updates the notification menu plugin to do more: - The plugin can now supply the view it wants to display behind a notification as the menu - Touch interaction (i.e. notification translating on top) is piped through to the plugin so it can update the view behind the notification 2) Makes NotificationMenuRow an implementor of the plugin which moves a bunch of that logic out of NotificationStackScroller Test: runtest systemui Change-Id: I8723b96c195ff7311317d5523418cfcbe2a79b52
-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();
}
}