Merge "Import translations. DO NOT MERGE" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index b9f932b..1d891a6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5225,6 +5225,7 @@
public class NotificationManager {
method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
+ method public boolean areNotificationsEnabled();
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5232,6 +5233,7 @@
method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules();
method public final int getCurrentInterruptionFilter();
+ method public int getImportance(java.lang.String);
method public android.app.NotificationManager.Policy getNotificationPolicy();
method public boolean isNotificationPolicyAccessGranted();
method public void notify(int, android.app.Notification);
diff --git a/api/system-current.txt b/api/system-current.txt
index b404bb0..1ab5837 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5357,6 +5357,7 @@
public class NotificationManager {
method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
+ method public boolean areNotificationsEnabled();
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5364,6 +5365,7 @@
method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules();
method public final int getCurrentInterruptionFilter();
+ method public int getImportance(java.lang.String);
method public android.app.NotificationManager.Policy getNotificationPolicy();
method public boolean isNotificationPolicyAccessGranted();
method public void notify(int, android.app.Notification);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9f8b8a8..49b8ffe 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5225,6 +5225,7 @@
public class NotificationManager {
method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
+ method public boolean areNotificationsEnabled();
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5232,6 +5233,7 @@
method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules();
method public final int getCurrentInterruptionFilter();
+ method public int getImportance(java.lang.String);
method public android.app.NotificationManager.Policy getNotificationPolicy();
method public boolean isNotificationPolicyAccessGranted();
method public void notify(int, android.app.Notification);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 40e58af..0d35cf0 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -46,6 +46,7 @@
void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled);
boolean areNotificationsEnabledForPackage(String pkg, int uid);
+ boolean areNotificationsEnabled(String pkg);
ParceledListSlice getTopics(String pkg, int uid);
void setVisibilityOverride(String pkg, int uid, in Notification.Topic topic, int visibility);
@@ -54,6 +55,7 @@
int getPriority(String pkg, int uid, in Notification.Topic topic);
void setImportance(String pkg, int uid, in Notification.Topic topic, int importance);
int getImportance(String pkg, int uid, in Notification.Topic topic);
+ int getTopicImportance(String pkg, String topicId);
boolean doesAppUseTopics(String pkg, int uid);
boolean hasBannedTopics(String pkg, int uid);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index a83225d9..1f17024 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,6 +16,8 @@
package android.app;
+import com.android.internal.util.Preconditions;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -37,6 +39,7 @@
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.service.notification.IConditionListener;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
@@ -503,6 +506,25 @@
return false;
}
+ public int getImportance(String topicId) {
+ Preconditions.checkNotNull(topicId);
+ INotificationManager service = getService();
+ try {
+ return service.getTopicImportance(mContext.getPackageName(), topicId);
+ } catch (RemoteException e) {
+ }
+ return NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ }
+
+ public boolean areNotificationsEnabled() {
+ INotificationManager service = getService();
+ try {
+ return service.areNotificationsEnabled(mContext.getPackageName());
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
/**
* Checks the ability to read/modify notification policy for the calling package.
*
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 605f067..9ab62cc 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.os.UserHandle;
import android.test.InstrumentationTestCase;
import android.util.Pair;
@@ -116,14 +117,14 @@
}
}
- Mockito.when(mMockPm.queryBroadcastReceivers(
+ Mockito.when(mMockPm.queryBroadcastReceiversAsUser(
Mockito.argThat(new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Object object) {
Intent intent = (Intent) object;
return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals(intent.getAction());
}
- }), Mockito.eq(0)))
+ }), Mockito.eq(0), Mockito.eq(UserHandle.USER_SYSTEM)))
.thenReturn(receivers);
}
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_subdirectory_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_subdirectory_arrow_am_alpha.png
deleted file mode 100644
index e4881dd..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_subdirectory_arrow_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_subdirectory_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_subdirectory_arrow_am_alpha.png
deleted file mode 100644
index 66725e5..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_subdirectory_arrow_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_subdirectory_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_subdirectory_arrow_am_alpha.png
deleted file mode 100644
index cdf21b1..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_subdirectory_arrow_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_subdirectory_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_subdirectory_arrow_am_alpha.png
deleted file mode 100644
index 050313f..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_subdirectory_arrow_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_subdirectory_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_subdirectory_arrow_am_alpha.png
deleted file mode 100644
index 8fddb85..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_subdirectory_arrow_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/ic_subdirectory_arrow.xml b/packages/DocumentsUI/res/drawable/ic_subdirectory_arrow.xml
index 5723233..0f34ba4 100644
--- a/packages/DocumentsUI/res/drawable/ic_subdirectory_arrow.xml
+++ b/packages/DocumentsUI/res/drawable/ic_subdirectory_arrow.xml
@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_subdirectory_arrow_am_alpha"
- android:tint="?android:attr/colorControlNormal"
- android:autoMirrored="true" />
+<!--
+Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="?android:attr/colorControlNormal"
+ android:pathData="M19 15l-6 6,-1.42,-1.42L15.17 16H4V4h2v10h9.17l-3.59,-3.58L13 9l6 6z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/layout/item_subdir.xml b/packages/DocumentsUI/res/layout/item_subdir.xml
index b2a739a..821432d 100644
--- a/packages/DocumentsUI/res/layout/item_subdir.xml
+++ b/packages/DocumentsUI/res/layout/item_subdir.xml
@@ -26,8 +26,8 @@
<ImageView
android:id="@+id/subdir"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
android:paddingEnd="8dp"
android:scaleType="centerInside"
android:visibility="gone"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 7fe881e..174984c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -151,6 +151,7 @@
private MultiSelectManager mSelectionManager;
private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
private ItemEventListener mItemEventListener = new ItemEventListener();
+ private FocusManager mFocusManager;
private IconHelper mIconHelper;
@@ -262,6 +263,8 @@
mSelectionManager.addCallback(selectionListener);
+ mFocusManager = new FocusManager(mRecView, mSelectionManager);
+
mModel = new Model();
mModel.addUpdateListener(mAdapter);
mModel.addUpdateListener(mModelUpdateListener);
@@ -1249,165 +1252,17 @@
return false;
}
- boolean handled = false;
- if (Events.isNavigationKeyCode(keyCode)) {
- // Find the target item and focus it.
- int endPos = findTargetPosition(doc.itemView, keyCode);
-
- if (endPos != RecyclerView.NO_POSITION) {
- focusItem(endPos);
-
- // Handle any necessary adjustments to selection.
- boolean extendSelection = event.isShiftPressed();
- if (extendSelection) {
- int startPos = doc.getAdapterPosition();
- mSelectionManager.selectRange(startPos, endPos);
- }
- handled = true;
- }
- } else {
- // Handle enter key events
- if (keyCode == KeyEvent.KEYCODE_ENTER) {
- handled = onActivate(doc);
- }
+ if (mFocusManager.handleKey(doc, keyCode, event)) {
+ return true;
}
- return handled;
+ // Handle enter key events
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ return onActivate(doc);
+ }
+
+ return false;
}
-
- /**
- * Finds the destination position where the focus should land for a given navigation event.
- *
- * @param view The view that received the event.
- * @param keyCode The key code for the event.
- * @return The adapter position of the destination item. Could be RecyclerView.NO_POSITION.
- */
- private int findTargetPosition(View view, int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_MOVE_HOME:
- return 0;
- case KeyEvent.KEYCODE_MOVE_END:
- return mAdapter.getItemCount() - 1;
- case KeyEvent.KEYCODE_PAGE_UP:
- case KeyEvent.KEYCODE_PAGE_DOWN:
- return findTargetPositionByPage(view, keyCode);
- }
-
- // Find a navigation target based on the arrow key that the user pressed.
- int searchDir = -1;
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_UP:
- searchDir = View.FOCUS_UP;
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- searchDir = View.FOCUS_DOWN;
- break;
- case KeyEvent.KEYCODE_DPAD_LEFT:
- searchDir = View.FOCUS_LEFT;
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- searchDir = View.FOCUS_RIGHT;
- break;
- }
-
- if (searchDir != -1) {
- View targetView = view.focusSearch(searchDir);
- // TargetView can be null, for example, if the user pressed <down> at the bottom
- // of the list.
- if (targetView != null) {
- // Ignore navigation targets that aren't items in the RecyclerView.
- if (targetView.getParent() == mRecView) {
- return mRecView.getChildAdapterPosition(targetView);
- }
- }
- }
-
- return RecyclerView.NO_POSITION;
- }
-
- /**
- * Given a PgUp/PgDn event and the current view, find the position of the target view.
- * This returns:
- * <li>The position of the topmost (or bottom-most) visible item, if the current item is not
- * the top- or bottom-most visible item.
- * <li>The position of an item that is one page's worth of items up (or down) if the current
- * item is the top- or bottom-most visible item.
- * <li>The first (or last) item, if paging up (or down) would go past those limits.
- * @param view The view that received the key event.
- * @param keyCode Must be KEYCODE_PAGE_UP or KEYCODE_PAGE_DOWN.
- * @return The adapter position of the target item.
- */
- private int findTargetPositionByPage(View view, int keyCode) {
- int first = mLayout.findFirstVisibleItemPosition();
- int last = mLayout.findLastVisibleItemPosition();
- int current = mRecView.getChildAdapterPosition(view);
- int pageSize = last - first + 1;
-
- if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
- if (current > first) {
- // If the current item isn't the first item, target the first item.
- return first;
- } else {
- // If the current item is the first item, target the item one page up.
- int target = current - pageSize;
- return target < 0 ? 0 : target;
- }
- }
-
- if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
- if (current < last) {
- // If the current item isn't the last item, target the last item.
- return last;
- } else {
- // If the current item is the last item, target the item one page down.
- int target = current + pageSize;
- int max = mAdapter.getItemCount() - 1;
- return target < max ? target : max;
- }
- }
-
- throw new IllegalArgumentException("Unsupported keyCode: " + keyCode);
- }
-
- /**
- * Requests focus for the item in the given adapter position, scrolling the RecyclerView if
- * necessary.
- *
- * @param pos
- */
- public void focusItem(final int pos) {
- // If the item is already in view, focus it; otherwise, scroll to it and focus it.
- RecyclerView.ViewHolder vh = mRecView.findViewHolderForAdapterPosition(pos);
- if (vh != null) {
- vh.itemView.requestFocus();
- } else {
- mRecView.smoothScrollToPosition(pos);
- // Set a one-time listener to request focus when the scroll has completed.
- mRecView.addOnScrollListener(
- new RecyclerView.OnScrollListener() {
- @Override
- public void onScrollStateChanged (RecyclerView view, int newState) {
- if (newState == RecyclerView.SCROLL_STATE_IDLE) {
- // When scrolling stops, find the item and focus it.
- RecyclerView.ViewHolder vh =
- view.findViewHolderForAdapterPosition(pos);
- if (vh != null) {
- vh.itemView.requestFocus();
- } else {
- // This might happen in weird corner cases, e.g. if the user is
- // scrolling while a delete operation is in progress. In that
- // case, just don't attempt to focus the missing item.
- Log.w(
- TAG, "Unable to focus position " + pos + " after a scroll");
- }
- view.removeOnScrollListener(this);
- }
- }
- });
- }
- }
-
-
}
private final class ModelUpdateListener implements Model.UpdateListener {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
new file mode 100644
index 0000000..86b9146
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FocusManager.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.documentsui.dirlist;
+
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.android.documentsui.Events;
+
+/**
+ * A class that handles navigation and focus within the DirectoryFragment.
+ */
+class FocusManager {
+ private static final String TAG = "FocusManager";
+
+ private RecyclerView mView;
+ private RecyclerView.Adapter<?> mAdapter;
+ private LinearLayoutManager mLayout;
+ private MultiSelectManager mSelectionManager;
+
+ public FocusManager(RecyclerView view, MultiSelectManager selectionManager) {
+ mView = view;
+ mAdapter = view.getAdapter();
+ mLayout = (LinearLayoutManager) view.getLayoutManager();
+ mSelectionManager = selectionManager;
+ }
+
+ /**
+ * Handles navigation (setting focus, adjusting selection if needed) arising from incoming key
+ * events.
+ *
+ * @param doc The DocumentHolder receiving the key event.
+ * @param keyCode
+ * @param event
+ * @return Whether the event was handled.
+ */
+ public boolean handleKey(DocumentHolder doc, int keyCode, KeyEvent event) {
+ boolean handled = false;
+ if (Events.isNavigationKeyCode(keyCode)) {
+ // Find the target item and focus it.
+ int endPos = findTargetPosition(doc.itemView, keyCode, event);
+
+ if (endPos != RecyclerView.NO_POSITION) {
+ focusItem(endPos);
+
+ // Handle any necessary adjustments to selection.
+ boolean extendSelection = event.isShiftPressed();
+ if (extendSelection) {
+ int startPos = doc.getAdapterPosition();
+ mSelectionManager.selectRange(startPos, endPos);
+ }
+ handled = true;
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * Finds the destination position where the focus should land for a given navigation event.
+ *
+ * @param view The view that received the event.
+ * @param keyCode The key code for the event.
+ * @param event
+ * @return The adapter position of the destination item. Could be RecyclerView.NO_POSITION.
+ */
+ private int findTargetPosition(View view, int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MOVE_HOME:
+ return 0;
+ case KeyEvent.KEYCODE_MOVE_END:
+ return mAdapter.getItemCount() - 1;
+ case KeyEvent.KEYCODE_PAGE_UP:
+ case KeyEvent.KEYCODE_PAGE_DOWN:
+ return findPagedTargetPosition(view, keyCode, event);
+ }
+
+ // Find a navigation target based on the arrow key that the user pressed.
+ int searchDir = -1;
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_UP:
+ searchDir = View.FOCUS_UP;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ searchDir = View.FOCUS_DOWN;
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ searchDir = View.FOCUS_LEFT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ searchDir = View.FOCUS_RIGHT;
+ break;
+ }
+
+ if (searchDir != -1) {
+ View targetView = view.focusSearch(searchDir);
+ // TargetView can be null, for example, if the user pressed <down> at the bottom
+ // of the list.
+ if (targetView != null) {
+ // Ignore navigation targets that aren't items in the RecyclerView.
+ if (targetView.getParent() == mView) {
+ return mView.getChildAdapterPosition(targetView);
+ }
+ }
+ }
+
+ return RecyclerView.NO_POSITION;
+ }
+
+ /**
+ * Given a PgUp/PgDn event and the current view, find the position of the target view.
+ * This returns:
+ * <li>The position of the topmost (or bottom-most) visible item, if the current item is not
+ * the top- or bottom-most visible item.
+ * <li>The position of an item that is one page's worth of items up (or down) if the current
+ * item is the top- or bottom-most visible item.
+ * <li>The first (or last) item, if paging up (or down) would go past those limits.
+ * @param view The view that received the key event.
+ * @param keyCode Must be KEYCODE_PAGE_UP or KEYCODE_PAGE_DOWN.
+ * @param event
+ * @return The adapter position of the target item.
+ */
+ private int findPagedTargetPosition(View view, int keyCode, KeyEvent event) {
+ int first = mLayout.findFirstVisibleItemPosition();
+ int last = mLayout.findLastVisibleItemPosition();
+ int current = mView.getChildAdapterPosition(view);
+ int pageSize = last - first + 1;
+
+ if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
+ if (current > first) {
+ // If the current item isn't the first item, target the first item.
+ return first;
+ } else {
+ // If the current item is the first item, target the item one page up.
+ int target = current - pageSize;
+ return target < 0 ? 0 : target;
+ }
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
+ if (current < last) {
+ // If the current item isn't the last item, target the last item.
+ return last;
+ } else {
+ // If the current item is the last item, target the item one page down.
+ int target = current + pageSize;
+ int max = mAdapter.getItemCount() - 1;
+ return target < max ? target : max;
+ }
+ }
+
+ throw new IllegalArgumentException("Unsupported keyCode: " + keyCode);
+ }
+
+ /**
+ * Requests focus for the item in the given adapter position, scrolling the RecyclerView if
+ * necessary.
+ *
+ * @param pos
+ */
+ private void focusItem(final int pos) {
+ // If the item is already in view, focus it; otherwise, scroll to it and focus it.
+ RecyclerView.ViewHolder vh = mView.findViewHolderForAdapterPosition(pos);
+ if (vh != null) {
+ vh.itemView.requestFocus();
+ } else {
+ mView.smoothScrollToPosition(pos);
+ // Set a one-time listener to request focus when the scroll has completed.
+ mView.addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView view, int newState) {
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ // When scrolling stops, find the item and focus it.
+ RecyclerView.ViewHolder vh =
+ view.findViewHolderForAdapterPosition(pos);
+ if (vh != null) {
+ vh.itemView.requestFocus();
+ } else {
+ // This might happen in weird corner cases, e.g. if the user is
+ // scrolling while a delete operation is in progress. In that
+ // case, just don't attempt to focus the missing item.
+ Log.w(TAG, "Unable to focus position " + pos + " after scroll");
+ }
+ view.removeOnScrollListener(this);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index bb37b83..28b9a5e6 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -19,12 +19,15 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/qs_background_primary"
- android:paddingBottom="8dp"
- android:elevation="2dp">
+ android:paddingBottom="8dp">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
android:background="#0000"
+ android:layout_marginTop="@dimen/status_bar_header_height"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
+
+ <include layout="@layout/quick_status_bar_expanded_header" />
+
</com.android.systemui.qs.QSContainer>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 89abe2d..0ebbdd8 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -31,6 +31,12 @@
android:layout_height="wrap_content"
android:visibility="gone" />
+ <include
+ layout="@layout/qs_panel"
+ android:layout_width="@dimen/notification_panel_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="@integer/notification_panel_layout_gravity" />
+
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -51,11 +57,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <include
- layout="@layout/qs_panel"
- android:layout_marginTop="@dimen/status_bar_header_height_expanded"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
<!-- A view to reserve space for the collapsed stack -->
<!-- Layout height: notification_min_height + bottom_stack_peek_amount -->
@@ -90,12 +91,6 @@
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
- <ViewStub
- android:id="@+id/status_bar_header"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_gravity="@integer/notification_panel_layout_gravity" />
-
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/qs_navbar_scrim"
android:layout_height="96dp"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index cfe8d07..5c2b642 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -16,19 +16,38 @@
package com.android.systemui.qs;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
-
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.BaseStatusBarHeader;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
- * Wrapper view with background which contains {@link QSPanel}
+ * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
+ *
+ * Also manages animations for the QS Header and Panel.
*/
public class QSContainer extends FrameLayout {
+ private static final String TAG = "QSContainer";
+ private static final boolean DEBUG = false;
private int mHeightOverride = -1;
private QSPanel mQSPanel;
+ protected BaseStatusBarHeader mHeader;
+ private float mQsExpansion;
+ private boolean mQsExpanded;
+ private boolean mHeaderAnimating;
+ private boolean mKeyguardShowing;
+ private boolean mStackScrollerOverscrolling;
+
+ private long mDelay;
public QSContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -38,6 +57,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQSPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
+ mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
}
@Override
@@ -63,14 +83,133 @@
*/
public int getDesiredHeight() {
if (mQSPanel.isClosingDetail()) {
- return mQSPanel.getGridHeight() + getPaddingTop() + getPaddingBottom();
+ return mQSPanel.getGridHeight() + mHeader.getCollapsedHeight() + getPaddingBottom();
} else {
return getMeasuredHeight();
}
}
private void updateBottom() {
- int height = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
+ int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
+ int height = (int) (mQsExpansion * (heightOverride - mHeader.getCollapsedHeight()))
+ + mHeader.getCollapsedHeight();
setBottom(getTop() + height);
}
+
+ private void updateQsState() {
+ boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
+ mQSPanel.setExpanded(mQsExpanded);
+ mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
+ ? View.VISIBLE
+ : View.INVISIBLE);
+ mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
+ || (mQsExpanded && !mStackScrollerOverscrolling));
+ mQSPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
+ setVisibility(mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
+ }
+
+ public BaseStatusBarHeader getHeader() {
+ return mHeader;
+ }
+
+ public QSPanel getQsPanel() {
+ return mQSPanel;
+ }
+
+ public void setHeaderClickable(boolean clickable) {
+ if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
+ mHeader.setClickable(clickable);
+ }
+
+ public void setExpanded(boolean expanded) {
+ if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
+ mQsExpanded = expanded;
+ updateQsState();
+ }
+
+ public void setKeyguardShowing(boolean keyguardShowing) {
+ if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
+ mKeyguardShowing = keyguardShowing;
+ updateQsState();
+ }
+
+ public void setOverscrolling(boolean stackScrollerOverscrolling) {
+ if (DEBUG) Log.d(TAG, "setOverscrolling " + stackScrollerOverscrolling);
+ mStackScrollerOverscrolling = stackScrollerOverscrolling;
+ updateQsState();
+ }
+
+ public void setListening(boolean listening) {
+ if (DEBUG) Log.d(TAG, "setListening " + listening);
+ mQSPanel.setListening(listening);
+ mHeader.setListening(listening);
+ }
+
+ public void setQsExpansion(float expansion, float headerTranslation) {
+ if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
+ mQsExpansion = expansion;
+ final float translationScaleY = expansion - 1;
+ if (!mHeaderAnimating) {
+ setTranslationY(mKeyguardShowing ? (translationScaleY * mHeader.getHeight())
+ : headerTranslation);
+ }
+ mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
+ mQSPanel.setTranslationY(translationScaleY * mQSPanel.getHeight());
+ updateBottom();
+ }
+
+ public void animateHeaderSlidingIn(long delay) {
+ if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn");
+ // If the QS is already expanded we don't need to slide in the header as it's already
+ // visible.
+ if (!mQsExpanded) {
+ mHeaderAnimating = true;
+ mDelay = delay;
+ getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+ }
+ }
+
+ public void animateHeaderSlidingOut() {
+ if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut");
+ mHeaderAnimating = true;
+ animate().y(-mHeader.getHeight())
+ .setStartDelay(0)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animate().setListener(null);
+ mHeaderAnimating = false;
+ updateQsState();
+ }
+ })
+ .start();
+ }
+
+ private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
+ = new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ animate()
+ .translationY(0f)
+ .setStartDelay(mDelay)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(mAnimateHeaderSlidingInListener)
+ .start();
+ setY(-mHeader.getHeight());
+ return true;
+ }
+ };
+
+ private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
+ = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHeaderAnimating = false;
+ updateQsState();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index f822bd5..24e2678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -19,7 +19,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.StatusBarManager;
@@ -35,13 +34,11 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.TextView;
-
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.DejankUtils;
@@ -51,7 +48,6 @@
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.qs.QSContainer;
-import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -91,11 +87,9 @@
public static final long DOZE_ANIMATION_DURATION = 700;
private KeyguardAffordanceHelper mAfforanceHelper;
- protected BaseStatusBarHeader mHeader;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
private QSContainer mQsContainer;
- private QSPanel mQsPanel;
private KeyguardStatusView mKeyguardStatusView;
private ObservableScrollView mScrollView;
private TextView mClockView;
@@ -175,8 +169,6 @@
private Runnable mLaunchAnimationEndRunnable;
private boolean mOnlyAffordanceInThisMotion;
private boolean mKeyguardStatusViewAnimating;
- private boolean mHeaderAnimating;
- private ObjectAnimator mQsContainerAnimator;
private ValueAnimator mQsSizeChangeAnimator;
private boolean mShadeEmpty;
@@ -223,14 +215,10 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- ViewStub stub = (ViewStub) findViewById(R.id.status_bar_header);
- stub.setLayoutResource(R.layout.quick_status_bar_expanded_header);
- mHeader = (BaseStatusBarHeader) stub.inflate();
- mHeader.setOnClickListener(this);
mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
mQsContainer = (QSContainer) findViewById(R.id.quick_settings_container);
- mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
+ mQsContainer.getHeader().setOnClickListener(this);
mClockView = (TextView) findViewById(R.id.clock_view);
mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
mScrollView.setListener(this);
@@ -285,12 +273,12 @@
public void updateResources() {
int panelWidth = getResources().getDimensionPixelSize(R.dimen.notification_panel_width);
int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mHeader.getLayoutParams();
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsContainer.getLayoutParams();
if (lp.width != panelWidth) {
lp.width = panelWidth;
lp.gravity = panelGravity;
- mHeader.setLayoutParams(lp);
- mHeader.post(mUpdateHeader);
+ mQsContainer.setLayoutParams(lp);
+ mQsContainer.post(mUpdateHeader);
}
lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
@@ -318,8 +306,8 @@
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mHeader.getCollapsedHeight() + mQsPeekHeight;
- mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getDesiredHeight();
+ mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQsContainer.getHeader().getHeight();
+ mQsMaxExpansionHeight = mQsContainer.getDesiredHeight();
positionClockAndNotifications();
if (mQsExpanded && mQsFullyExpanded) {
mQsExpansionHeight = mQsMaxExpansionHeight;
@@ -361,7 +349,7 @@
requestScrollerTopPaddingUpdate(false /* animate */);
requestPanelHeightUpdate();
int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
- mQsContainer.setHeightOverride(height - mHeader.getExpandedHeight());
+ mQsContainer.setHeightOverride(height);
}
});
mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
@@ -381,7 +369,7 @@
boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
if (mStatusBarState != StatusBarState.KEYGUARD) {
- int bottom = mHeader.getCollapsedHeight();
+ int bottom = mQsContainer.getHeader().getHeight();
stackScrollerPadding = mStatusBarState == StatusBarState.SHADE
? bottom + mQsPeekHeight
: mKeyguardStatusBar.getHeight();
@@ -485,7 +473,7 @@
public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
mQsExpansionEnabled = qsExpansionEnabled;
- mHeader.setClickable(qsExpansionEnabled);
+ mQsContainer.setHeaderClickable(qsExpansionEnabled);
}
@Override
@@ -948,7 +936,7 @@
amount = 0f;
}
float rounded = amount >= 1f ? amount : 0f;
- mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
+ setOverScrolling(rounded != 0f && isRubberbanded);
mQsExpansionFromOverscroll = rounded != 0f;
mLastOverscroll = rounded;
updateQsState();
@@ -964,12 +952,17 @@
@Override
public void run() {
mStackScrollerOverscrolling = false;
- mQsExpansionFromOverscroll = false;
+ setOverScrolling(false);
updateQsState();
}
}, false /* isClick */);
}
+ private void setOverScrolling(boolean overscrolling) {
+ mStackScrollerOverscrolling = overscrolling;
+ mQsContainer.setOverscrolling(overscrolling);
+ }
+
private void onQsExpansionStarted() {
onQsExpansionStarted(0);
}
@@ -997,7 +990,6 @@
mFalsingManager.setQsExpanded(expanded);
mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
mStatusBar.setQsExpanded(expanded);
- mQsPanel.setExpanded(expanded);
mNotificationContainerParent.setQsExpanded(expanded);
}
}
@@ -1011,15 +1003,18 @@
mStatusBarState = statusBarState;
mKeyguardShowing = keyguardShowing;
+ mQsContainer.setKeyguardShowing(mKeyguardShowing);
if (goingToFullShade || (oldState == StatusBarState.KEYGUARD
&& statusBarState == StatusBarState.SHADE_LOCKED)) {
animateKeyguardStatusBarOut();
- animateHeaderSlidingIn();
+ long delay = mStatusBarState == StatusBarState.SHADE_LOCKED
+ ? 0 : mStatusBar.calculateGoingToFullShadeDelay();
+ mQsContainer.animateHeaderSlidingIn(delay);
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == StatusBarState.KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- animateHeaderSlidingOut();
+ mQsContainer.animateHeaderSlidingOut();
} else {
mKeyguardStatusBar.setAlpha(1f);
mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
@@ -1050,95 +1045,6 @@
}
};
- private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
- = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeaderAnimating = false;
- mQsContainerAnimator = null;
- mQsContainer.removeOnLayoutChangeListener(mQsContainerAnimatorUpdater);
- }
- };
-
- private final OnLayoutChangeListener mQsContainerAnimatorUpdater
- = new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- int oldHeight = oldBottom - oldTop;
- int height = bottom - top;
- if (height != oldHeight && mQsContainerAnimator != null) {
- PropertyValuesHolder[] values = mQsContainerAnimator.getValues();
- float newEndValue = mHeader.getCollapsedHeight() + mQsPeekHeight - height - top;
- float newStartValue = -height - top;
- values[0].setFloatValues(newStartValue, newEndValue);
- mQsContainerAnimator.setCurrentPlayTime(mQsContainerAnimator.getCurrentPlayTime());
- }
- }
- };
-
- private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
- = new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- getViewTreeObserver().removeOnPreDrawListener(this);
- long delay = mStatusBarState == StatusBarState.SHADE_LOCKED
- ? 0
- : mStatusBar.calculateGoingToFullShadeDelay();
- mHeader.setTranslationY(-mHeader.getCollapsedHeight() - mQsPeekHeight);
- mHeader.animate()
- .translationY(0f)
- .setStartDelay(delay)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .start();
- mQsContainer.setY(-mQsContainer.getHeight());
- mQsContainerAnimator = ObjectAnimator.ofFloat(mQsContainer, View.TRANSLATION_Y,
- mQsContainer.getTranslationY(),
- mHeader.getCollapsedHeight() + mQsPeekHeight - mQsContainer.getHeight()
- - mQsContainer.getTop());
- mQsContainerAnimator.setStartDelay(delay);
- mQsContainerAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
- mQsContainerAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mQsContainerAnimator.addListener(mAnimateHeaderSlidingInListener);
- mQsContainerAnimator.start();
- mQsContainer.addOnLayoutChangeListener(mQsContainerAnimatorUpdater);
- return true;
- }
- };
-
- private void animateHeaderSlidingIn() {
- // If the QS is already expanded we don't need to slide in the header as it's already
- // visible.
- if (!mQsExpanded) {
- mHeaderAnimating = true;
- getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
- }
- }
-
- private void animateHeaderSlidingOut() {
- mHeaderAnimating = true;
- mHeader.animate().y(-mHeader.getHeight())
- .setStartDelay(0)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeader.animate().setListener(null);
- mHeaderAnimating = false;
- updateQsState();
- }
- })
- .start();
- mQsContainer.animate()
- .y(-mQsContainer.getHeight())
- .setStartDelay(0)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .start();
- }
-
private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
@Override
public void run() {
@@ -1262,18 +1168,10 @@
}
private void updateQsState() {
- boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
- mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
- ? View.VISIBLE
- : View.INVISIBLE);
- mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating)
- || (mQsExpanded && !mStackScrollerOverscrolling));
+ mQsContainer.setExpanded(mQsExpanded);
mNotificationStackScroller.setScrollingEnabled(
mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded
|| mQsExpansionFromOverscroll));
- mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
- mQsContainer.setVisibility(
- mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
mScrollView.setTouchEnabled(mQsExpanded);
updateEmptyShadeView();
mQsNavbarScrim.setVisibility(mStatusBarState == StatusBarState.SHADE && mQsExpanded
@@ -1298,11 +1196,10 @@
}
}
mQsExpansionHeight = height;
- mHeader.setExpansion(getHeaderExpansionFraction());
- setQsTranslation(height);
+ updateQsExpansion();
requestScrollerTopPaddingUpdate(false /* animate */);
if (mKeyguardShowing) {
- updateHeaderKeyguard();
+ updateHeaderKeyguardAlpha();
}
if (mStatusBarState == StatusBarState.SHADE_LOCKED
|| mStatusBarState == StatusBarState.KEYGUARD) {
@@ -1329,6 +1226,10 @@
}
}
+ private void updateQsExpansion() {
+ mQsContainer.setQsExpansion(getQsExpansionFraction(), getHeaderTranslation());
+ }
+
private String getKeyguardOrLockScreenString() {
if (mStatusBarState == StatusBarState.KEYGUARD) {
return getContext().getString(R.string.accessibility_desc_lock_screen);
@@ -1337,23 +1238,6 @@
}
}
- private float getHeaderExpansionFraction() {
- if (!mKeyguardShowing) {
- return getQsExpansionFraction();
- } else {
- return 1f;
- }
- }
-
- private void setQsTranslation(float height) {
- if (!mHeaderAnimating) {
- mQsContainer.setY(height - mQsContainer.getDesiredHeight() + getHeaderTranslation());
- }
- if (mKeyguardShowing && !mHeaderAnimating) {
- mHeader.setY(interpolate(getQsExpansionFraction(), -mHeader.getHeight(), 0));
- }
- }
-
private float calculateQsTopPadding() {
if (mKeyguardShowing
&& (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
@@ -1478,7 +1362,7 @@
if (!mQsExpansionEnabled || mCollapsedOnDown) {
return false;
}
- View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader;
+ View header = mKeyguardShowing ? mKeyguardStatusBar : mQsContainer;
boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
@@ -1678,18 +1562,9 @@
*/
private void updateHeader() {
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
- updateHeaderKeyguard();
- } else {
- updateHeaderShade();
+ updateHeaderKeyguardAlpha();
}
-
- }
-
- private void updateHeaderShade() {
- if (!mHeaderAnimating) {
- mHeader.setTranslationY(getHeaderTranslation());
- }
- setQsTranslation(mQsExpansionHeight);
+ updateQsExpansion();
}
private float getHeaderTranslation() {
@@ -1744,11 +1619,6 @@
&& !mDozing ? VISIBLE : INVISIBLE);
}
- private void updateHeaderKeyguard() {
- updateHeaderKeyguardAlpha();
- setQsTranslation(mQsExpansionHeight);
- }
-
private void updateKeyguardBottomAreaAlpha() {
float alpha = Math.min(getKeyguardContentsAlpha(), 1 - getQsExpansionFraction());
mKeyguardBottomArea.setAlpha(alpha);
@@ -1811,9 +1681,8 @@
}
private void setListening(boolean listening) {
- mHeader.setListening(listening);
+ mQsContainer.setListening(listening);
mKeyguardStatusBar.setListening(listening);
- mQsPanel.setListening(listening);
}
@Override
@@ -1931,7 +1800,7 @@
@Override
public void onClick(View v) {
- if (v == mHeader) {
+ if (v == mQsContainer.getHeader()) {
onQsExpansionStarted();
if (mQsExpanded) {
flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
@@ -2162,11 +2031,11 @@
}
public boolean isQsDetailShowing() {
- return mQsPanel.isShowingDetail();
+ return mQsContainer.getQsPanel().isShowingDetail();
}
public void closeQsDetail() {
- mQsPanel.closeDetail();
+ mQsContainer.getQsPanel().closeDetail();
}
@Override
@@ -2254,7 +2123,7 @@
private final Runnable mUpdateHeader = new Runnable() {
@Override
public void run() {
- mHeader.updateEverything();
+ mQsContainer.getHeader().updateEverything();
}
};
@@ -2401,7 +2270,7 @@
protected void setVerticalPanelTranslation(float translation) {
mNotificationStackScroller.setTranslationX(translation);
mScrollView.setTranslationX(translation);
- mHeader.setTranslationX(translation);
+ mQsContainer.setTranslationX(translation);
}
private void updateStackHeight(float stackHeight) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d2bcf13..36ddbcf 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1265,8 +1265,16 @@
* Use this when you just want to know if notifications are OK for this package.
*/
@Override
+ public boolean areNotificationsEnabled(String pkg) {
+ return areNotificationsEnabledForPackage(pkg, Binder.getCallingUid());
+ }
+
+ /**
+ * Use this when you just want to know if notifications are OK for this package.
+ */
+ @Override
public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
- checkCallerIsSystem();
+ checkCallerIsSystemOrSameApp(pkg);
return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
== AppOpsManager.MODE_ALLOWED) && !isApplicationSuspended(pkg, uid);
}
@@ -1330,6 +1338,12 @@
}
@Override
+ public int getTopicImportance(String pkg, String topicId) {
+ checkCallerIsSystemOrSameApp(pkg);
+ return mRankingHelper.getImportance(pkg, Binder.getCallingUid(), topicId);
+ }
+
+ @Override
public int getImportance(String pkg, int uid, Notification.Topic topic) {
checkCallerIsSystem();
return mRankingHelper.getImportance(pkg, uid, topic);
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 1a7e355..17bb907 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -39,4 +39,6 @@
boolean doesAppUseTopics(String packageName, int uid);
boolean hasBannedTopics(String packageName, int uid);
+
+ int getImportance(String packageName, int uid, String topicId);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index aa36e29..6554bf9 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -467,6 +468,24 @@
}
/**
+ * Gets the importance of a topic. Unlike {@link #getImportance(String, int, String)}, does not
+ * create package or topic records if they don't exist.
+ */
+ @Override
+ public int getImportance(String packageName, int uid, String topicId) {
+ final String key = recordKey(packageName, uid);
+ Record r = mRecords.get(key);
+ if (r == null) {
+ return Ranking.IMPORTANCE_UNSPECIFIED;
+ }
+ Topic t = r.topics.get(topicId);
+ if (t == null) {
+ return Ranking.IMPORTANCE_UNSPECIFIED;
+ }
+ return t.importance;
+ }
+
+ /**
* Gets importance. If a topic is given, returns the importance of that topic. Otherwise, the
* importance of the app.
*/
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 3a30230..46de201 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -37,6 +37,7 @@
// private NM API
import android.app.INotificationManager;
+import android.widget.Toast;
public class NotificationTestList extends TestActivity
{
@@ -233,6 +234,30 @@
}
},
+ new Test("Is blocked?") {
+ public void run() {
+ Toast.makeText(NotificationTestList.this,
+ "package enabled? " + mNM.areNotificationsEnabled(),
+ Toast.LENGTH_LONG).show();
+ }
+ },
+
+ new Test("Topic banana importance?") {
+ public void run() {
+ Toast.makeText(NotificationTestList.this,
+ "bananas importance? " + mNM.getImportance("bananas"),
+ Toast.LENGTH_LONG).show();
+ }
+ },
+
+ new Test("Topic garbage importance?") {
+ public void run() {
+ Toast.makeText(NotificationTestList.this,
+ "garbage importance? " + mNM.getImportance("garbage"),
+ Toast.LENGTH_LONG).show();
+ }
+ },
+
new Test("Whens") {
public void run()
{