Merge "decouple Bubble from NotificationEntry" into rvc-dev
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
index ec7ba28..96bb5ef 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
@@ -44,7 +44,7 @@
/**
* Class for representing how a blob can be shared.
*
- * Note that this class is not thread-safe, callers need to take of synchronizing access.
+ * Note that this class is not thread-safe, callers need to take care of synchronizing access.
*/
class BlobAccessMode {
@Retention(RetentionPolicy.SOURCE)
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index cea7fcc..03573d9 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -61,6 +61,8 @@
import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -349,14 +351,16 @@
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
- synchronized (mMetadataLock) {
- return createRevocableFdLocked(fd, callingPackage);
+ try {
+ return createRevocableFd(fd, callingPackage);
+ } catch (IOException e) {
+ IoUtils.closeQuietly(fd);
+ throw e;
}
}
- @GuardedBy("mMetadataLock")
@NonNull
- private ParcelFileDescriptor createRevocableFdLocked(FileDescriptor fd,
+ private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
String callingPackage) throws IOException {
final RevocableFileDescriptor revocableFd =
new RevocableFileDescriptor(mContext, fd);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 22d5d11..a10b0b3 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -59,6 +59,8 @@
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -207,27 +209,37 @@
throw new IllegalStateException("Not allowed to write in state: "
+ stateToString(mState));
}
+ }
- try {
- return openWriteLocked(offsetBytes, lengthBytes);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
+ FileDescriptor fd = null;
+ try {
+ fd = openWriteInternal(offsetBytes, lengthBytes);
+ final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ IoUtils.closeQuietly(fd);
+ throw new IllegalStateException("Not allowed to write in state: "
+ + stateToString(mState));
+ }
+ trackRevocableFdLocked(revocableFd);
+ return revocableFd.getRevocableFileDescriptor();
}
+ } catch (IOException e) {
+ IoUtils.closeQuietly(fd);
+ throw ExceptionUtils.wrap(e);
}
}
- @GuardedBy("mSessionLock")
@NonNull
- private ParcelFileDescriptor openWriteLocked(@BytesLong long offsetBytes,
+ private FileDescriptor openWriteInternal(@BytesLong long offsetBytes,
@BytesLong long lengthBytes) throws IOException {
// TODO: Add limit on active open sessions/writes/reads
- FileDescriptor fd = null;
try {
final File sessionFile = getSessionFile();
if (sessionFile == null) {
throw new IllegalStateException("Couldn't get the file for this session");
}
- fd = Os.open(sessionFile.getPath(), O_CREAT | O_RDWR, 0600);
+ final FileDescriptor fd = Os.open(sessionFile.getPath(), O_CREAT | O_RDWR, 0600);
if (offsetBytes > 0) {
final long curOffset = Os.lseek(fd, offsetBytes, SEEK_SET);
if (curOffset != offsetBytes) {
@@ -238,10 +250,10 @@
if (lengthBytes > 0) {
mContext.getSystemService(StorageManager.class).allocateBytes(fd, lengthBytes);
}
+ return fd;
} catch (ErrnoException e) {
- e.rethrowAsIOException();
+ throw e.rethrowAsIOException();
}
- return createRevocableFdLocked(fd);
}
@Override
@@ -253,29 +265,40 @@
throw new IllegalStateException("Not allowed to read in state: "
+ stateToString(mState));
}
+ }
- try {
- return openReadLocked();
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
+ FileDescriptor fd = null;
+ try {
+ fd = openReadInternal();
+ final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ IoUtils.closeQuietly(fd);
+ throw new IllegalStateException("Not allowed to read in state: "
+ + stateToString(mState));
+ }
+ trackRevocableFdLocked(revocableFd);
+ return revocableFd.getRevocableFileDescriptor();
}
+
+ } catch (IOException e) {
+ IoUtils.closeQuietly(fd);
+ throw ExceptionUtils.wrap(e);
}
}
- @GuardedBy("mSessionLock")
@NonNull
- private ParcelFileDescriptor openReadLocked() throws IOException {
- FileDescriptor fd = null;
+ private FileDescriptor openReadInternal() throws IOException {
try {
final File sessionFile = getSessionFile();
if (sessionFile == null) {
throw new IllegalStateException("Couldn't get the file for this session");
}
- fd = Os.open(sessionFile.getPath(), O_RDONLY, 0);
+ final FileDescriptor fd = Os.open(sessionFile.getPath(), O_RDONLY, 0);
+ return fd;
} catch (ErrnoException e) {
- e.rethrowAsIOException();
+ throw e.rethrowAsIOException();
}
- return createRevocableFdLocked(fd);
}
@Override
@@ -396,7 +419,7 @@
}
mState = state;
- revokeAllFdsLocked();
+ revokeAllFds();
if (sendCallback) {
mListener.onStateChanged(this);
@@ -433,20 +456,17 @@
}
}
- @GuardedBy("mSessionLock")
- private void revokeAllFdsLocked() {
- for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
- mRevocableFds.get(i).revoke();
+ private void revokeAllFds() {
+ synchronized (mRevocableFds) {
+ for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
+ mRevocableFds.get(i).revoke();
+ }
+ mRevocableFds.clear();
}
- mRevocableFds.clear();
}
@GuardedBy("mSessionLock")
- @NonNull
- private ParcelFileDescriptor createRevocableFdLocked(FileDescriptor fd)
- throws IOException {
- final RevocableFileDescriptor revocableFd =
- new RevocableFileDescriptor(mContext, fd);
+ private void trackRevocableFdLocked(RevocableFileDescriptor revocableFd) {
synchronized (mRevocableFds) {
mRevocableFds.add(revocableFd);
}
@@ -455,7 +475,6 @@
mRevocableFds.remove(revocableFd);
}
});
- return revocableFd.getRevocableFileDescriptor();
}
@Nullable
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 47bab29..6f952f6 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -267,8 +267,11 @@
for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) {
uint64_t reportsListToken =
proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST);
+ // Don't include the current bucket to avoid skipping buckets.
+ // If we need to include the current bucket later, consider changing to NO_TIME_CONSTRAINTS
+ // or other alternatives to avoid skipping buckets for pulled metrics.
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
- true /* includeCurrentBucket */, false /* erase_data */,
+ false /* includeCurrentBucket */, false /* erase_data */,
ADB_DUMP,
FAST,
&proto);
diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
index db402a0..32cecd3 100644
--- a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
+++ b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
@@ -50,13 +50,13 @@
});
vector<thread> threads;
- vector<bool> done(numConditions, false);
+ vector<int> done(numConditions, 0);
int i = 0;
for (const string& conditionName : conditionNames) {
threads.emplace_back([&done, &conditionName, &trigger, i] {
sleep_for(chrono::milliseconds(3));
- done[i] = true;
+ done[i] = 1;
trigger.markComplete(conditionName);
});
i++;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index d22f942..3f03f2a 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -71,6 +71,4 @@
void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
in float[] matrixValues);
-
- void removeImeSurface();
}
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 4bf0fca..cf967c0 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -118,4 +118,7 @@
<item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item>
<item>com.android.systemui.car.volume.VolumeUI</item>
</string-array>
+
+ <!-- How many milliseconds to wait before force hiding the UserSwitchTransitionView -->
+ <integer name="config_userSwitchTransitionViewShownTimeoutMs" translatable="false">5000</integer>
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 8aa7b63..45f3d34 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -33,6 +33,7 @@
import android.widget.TextView;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.car.window.OverlayViewController;
@@ -49,12 +50,22 @@
public class UserSwitchTransitionViewController extends OverlayViewController {
private static final String TAG = "UserSwitchTransition";
private static final String ENABLE_DEVELOPER_MESSAGE_TRUE = "true";
+ private static final boolean DEBUG = false;
private final Context mContext;
private final Handler mHandler;
private final Resources mResources;
private final UserManager mUserManager;
private final IWindowManager mWindowManagerService;
+ private final int mWindowShownTimeoutMs;
+ private final Runnable mWindowShownTimeoutCallback = () -> {
+ if (DEBUG) {
+ Log.w(TAG, "Window was not hidden within " + getWindowShownTimeoutMs() + " ms, so it"
+ + "was hidden by mWindowShownTimeoutCallback.");
+ }
+
+ handleHide();
+ };
@GuardedBy("this")
private boolean mShowing;
@@ -76,6 +87,8 @@
mResources = resources;
mUserManager = userManager;
mWindowManagerService = windowManagerService;
+ mWindowShownTimeoutMs = mResources.getInteger(
+ R.integer.config_userSwitchTransitionViewShownTimeoutMs);
}
/**
@@ -98,6 +111,9 @@
populateDialog(mPreviousUserId, newUserId);
// next time a new user is selected, this current new user will be the previous user.
mPreviousUserId = newUserId;
+ // In case the window is still showing after WINDOW_SHOWN_TIMEOUT_MS, then hide the
+ // window and log a warning message.
+ mHandler.postDelayed(mWindowShownTimeoutCallback, mWindowShownTimeoutMs);
});
}
@@ -105,6 +121,12 @@
if (!mShowing) return;
mShowing = false;
mHandler.post(this::stop);
+ mHandler.removeCallbacks(mWindowShownTimeoutCallback);
+ }
+
+ @VisibleForTesting
+ int getWindowShownTimeoutMs() {
+ return mWindowShownTimeoutMs;
}
private void populateDialog(@UserIdInt int previousUserId, @UserIdInt int newUserId) {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
index 65c5562..797dbf5 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
@@ -18,6 +18,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.content.Context;
@@ -122,6 +124,29 @@
any());
}
+ @Test
+ public void onWindowShownTimeoutPassed_viewNotHidden_hidesUserSwitchTransitionView() {
+ mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ reset(mOverlayViewGlobalStateController);
+
+ getContext().getMainThreadHandler().postDelayed(() -> {
+ verify(mOverlayViewGlobalStateController).hideView(
+ eq(mCarUserSwitchingDialogController), any());
+ }, mCarUserSwitchingDialogController.getWindowShownTimeoutMs() + 10);
+ }
+
+ @Test
+ public void onWindowShownTimeoutPassed_viewHidden_doesNotHideUserSwitchTransitionViewAgain() {
+ mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleHide();
+ reset(mOverlayViewGlobalStateController);
+
+ getContext().getMainThreadHandler().postDelayed(() -> {
+ verify(mOverlayViewGlobalStateController, never()).hideView(
+ eq(mCarUserSwitchingDialogController), any());
+ }, mCarUserSwitchingDialogController.getWindowShownTimeoutMs() + 10);
+ }
+
private final class TestableUserSwitchTransitionViewController extends
UserSwitchTransitionViewController {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 85a9fec..fffcafb 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -34,7 +34,7 @@
String ACTION = "com.android.systemui.action.PLUGIN_QS";
- int VERSION = 7;
+ int VERSION = 8;
String TAG = "QS";
@@ -67,15 +67,12 @@
}
/**
- * We need this to handle nested scrolling for QS..
- * Normally we would do this with requestDisallowInterceptTouchEvent, but when both the
- * scroll containers are using the same touch slop, they try to start scrolling at the
- * same time and NotificationPanelView wins, this lets QS win.
- *
- * TODO: Do this using NestedScroll capabilities.
+ * Should touches from the notification panel be disallowed?
+ * The notification panel might grab any touches rom QS at any time to collapse the shade.
+ * We should disallow that in case we are showing the detail panel.
*/
- default boolean onInterceptTouchEvent(MotionEvent event) {
- return isCustomizing();
+ default boolean disallowPanelTouches() {
+ return isShowingDetail();
}
@ProvidesInterface(version = HeightListener.VERSION)
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 294bd50..59e1a75 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -22,6 +22,7 @@
android:background="@drawable/qs_detail_background"
android:clickable="true"
android:orientation="vertical"
+ android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
android:paddingBottom="8dp"
android:visibility="invisible"
android:elevation="4dp"
@@ -44,7 +45,7 @@
android:scaleType="fitXY"
/>
- <com.android.systemui.qs.NonInterceptingScrollView
+ <ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
@@ -54,7 +55,7 @@
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- </com.android.systemui.qs.NonInterceptingScrollView>
+ </ScrollView>
<include layout="@layout/qs_detail_buttons" />
diff --git a/packages/SystemUI/res/layout/qs_divider.xml b/packages/SystemUI/res/layout/qs_divider.xml
deleted file mode 100644
index 39d48ea..0000000
--- a/packages/SystemUI/res/layout/qs_divider.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:alpha=".12"
- android:background="?android:attr/colorForeground" />
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index ebfd0a0..5c00af5 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -61,7 +61,10 @@
android:clickable="true"
android:gravity="center_vertical"
android:focusable="true"
+ android:singleLine="true"
+ android:ellipsize="end"
android:textAppearance="@style/TextAppearance.QS.Status"
+ android:layout_marginEnd="4dp"
android:visibility="gone"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index a8960d9..5c8b2b0 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -20,7 +20,5 @@
android:id="@+id/tile_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingStart="@dimen/notification_side_paddings"
- android:paddingEnd="@dimen/notification_side_paddings"
android:clipChildren="false"
android:clipToPadding="false" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index cdf8426..761ab03 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -48,18 +48,22 @@
android:clipChildren="false"
android:background="@drawable/qs_bg_gradient" />
-
- <com.android.systemui.qs.QSPanel
- android:id="@+id/quick_settings_panel"
- android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
+ <com.android.systemui.qs.NonInterceptingScrollView
+ android:id="@+id/expanded_qs_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
- android:background="@android:color/transparent"
- android:focusable="true"
- android:accessibilityTraversalBefore="@android:id/edit">
- <include layout="@layout/qs_footer_impl" />
- </com.android.systemui.qs.QSPanel>
+ android:layout_weight="1">
+ <com.android.systemui.qs.QSPanel
+ android:id="@+id/quick_settings_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:focusable="true"
+ android:accessibilityTraversalBefore="@android:id/edit">
+ <include layout="@layout/qs_footer_impl" />
+ </com.android.systemui.qs.QSPanel>
+ </com.android.systemui.qs.NonInterceptingScrollView>
<include layout="@layout/quick_status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 15f398a..e7c7b5f 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -14,8 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+<com.android.systemui.util.NeverExactlyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:paddingBottom="@dimen/qs_tile_padding_top"
@@ -23,23 +23,28 @@
android:paddingStart="@dimen/qs_footer_padding_start"
android:paddingEnd="@dimen/qs_footer_padding_end"
android:gravity="center_vertical"
+ android:layout_gravity="center_vertical|center_horizontal"
android:background="@android:color/transparent">
- <TextView
+ <com.android.systemui.util.AutoMarqueeTextView
android:id="@+id/footer_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:layout_weight="1"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
- style="@style/qs_security_footer"/>
+ android:textColor="?android:attr/textColorPrimary"/>
<ImageView
android:id="@+id/footer_icon"
android:layout_width="@dimen/qs_footer_icon_size"
android:layout_height="@dimen/qs_footer_icon_size"
+ android:layout_marginStart="8dp"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline"
- style="@style/qs_security_footer"/>
+ android:tint="?android:attr/textColorPrimary" />
-</LinearLayout>
+</com.android.systemui.util.NeverExactlyLinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index e6ef9b4..fb82304 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -20,11 +20,12 @@
android:layout_height="@dimen/qs_header_tooltip_height"
android:layout_below="@id/quick_status_bar_system_icons"
android:visibility="invisible"
- android:theme="@style/QSHeaderTheme">
+ android:theme="@style/QSHeaderTheme"
+ android:forceHasOverlappingRendering="false">
<com.android.systemui.qs.QSHeaderInfoLayout
android:id="@+id/status_container"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index abeb331..dc341274 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -29,7 +29,6 @@
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
- android:paddingBottom="10dp"
android:paddingStart="0dp"
android:elevation="4dp" >
@@ -52,6 +51,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
+ android:paddingBottom="10dp"
android:importantForAccessibility="yes" />
<TextView
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index d118d89..2d42ce6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -31,7 +31,6 @@
<dimen name="battery_detail_graph_space_bottom">9dp</dimen>
<integer name="quick_settings_num_columns">4</integer>
- <bool name="quick_settings_wide">true</bool>
<dimen name="qs_detail_margin_top">0dp</dimen>
<dimen name="volume_tool_tip_right_margin">136dp</dimen>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 50261e1..4fdeb6f 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -29,9 +29,4 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
- <style name="qs_security_footer" parent="@style/qs_theme">
- <item name="android:textColor">#B3FFFFFF</item> <!-- 70% white -->
- <item name="android:tint">#FFFFFFFF</item>
- </style>
-
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index c4c4671..3b00ad1 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -15,8 +15,5 @@
~ limitations under the License
-->
<resources>
- <!-- Whether QuickSettings is in a phone landscape -->
- <bool name="quick_settings_wide">false</bool>
-
<integer name="quick_settings_num_columns">3</integer>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
new file mode 100644
index 0000000..40838f3
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!-- Size of the panel of large phones on portrait. This shouldn't fill, but have some padding on the side -->
+ <dimen name="notification_panel_width">416dp</dimen>
+
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 8f73d23..fdf4e3b 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -16,8 +16,6 @@
*/
-->
<resources>
- <!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">416dp</dimen>
<!-- Diameter of outer shape drawable shown in navbar search-->
<dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values-sw900dp-land/dimen.xml b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
deleted file mode 100644
index 1e0600e..0000000
--- a/packages/SystemUI/res/values-sw900dp-land/dimen.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2012, 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.
-*/
--->
-<resources>
- <!-- Standard notification width + gravity for tablet large screen device -->
- <dimen name="notification_panel_width">544dp</dimen>
-
-</resources>
-
diff --git a/packages/SystemUI/res/values-w550dp-land/config.xml b/packages/SystemUI/res/values-w550dp-land/config.xml
index 16d5317..a33f131 100644
--- a/packages/SystemUI/res/values-w550dp-land/config.xml
+++ b/packages/SystemUI/res/values-w550dp-land/config.xml
@@ -20,9 +20,5 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
-
- <!-- Whether QuickSettings is in a phone landscape -->
- <bool name="quick_settings_wide">true</bool>
-
- <integer name="quick_settings_num_columns">4</integer>
+ <integer name="quick_settings_num_columns">6</integer>
</resources>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
deleted file mode 100644
index 017ca69..0000000
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
-*/
--->
-<resources>
- <!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">544dp</dimen>
-
-</resources>
diff --git a/packages/SystemUI/res/values-w650dp-land/dimens.xml b/packages/SystemUI/res/values-w650dp-land/dimens.xml
new file mode 100644
index 0000000..108d6cf
--- /dev/null
+++ b/packages/SystemUI/res/values-w650dp-land/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!-- Standard notification width + gravity -->
+ <dimen name="notification_panel_width">644dp</dimen>
+
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4fc904b..40a4b50 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -203,8 +203,8 @@
<color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
<!-- Bubbles -->
- <color name="bubbles_pointer_light">#FFFFFF</color>
- <color name="bubbles_pointer_dark">@color/GM2_grey_800</color>
+ <color name="bubbles_light">#FFFFFF</color>
+ <color name="bubbles_dark">@color/GM2_grey_800</color>
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 00537ff..848cdb1 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -95,9 +95,6 @@
<!-- The maximum number of tiles in the QuickQSPanel -->
<integer name="quick_qs_panel_max_columns">6</integer>
- <!-- Whether QuickSettings is in a phone landscape -->
- <bool name="quick_settings_wide">false</bool>
-
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 44d3b02..d176fed 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1075,8 +1075,7 @@
<dimen name="edge_margin">8dp</dimen>
<!-- The absolute side margins of quick settings -->
- <dimen name="quick_settings_expanded_bottom_margin">16dp</dimen>
- <dimen name="quick_settings_media_extra_bottom_margin">6dp</dimen>
+ <dimen name="quick_settings_bottom_margin_media">16dp</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
@@ -1264,8 +1263,8 @@
<dimen name="qs_media_panel_outer_padding">16dp</dimen>
<dimen name="qs_media_album_size">52dp</dimen>
<dimen name="qs_seamless_icon_size">20dp</dimen>
- <dimen name="qqs_media_spacing">8dp</dimen>
- <dimen name="qqs_horizonal_tile_padding_bottom">8dp</dimen>
+ <dimen name="qqs_media_spacing">16dp</dimen>
+ <dimen name="qs_footer_horizontal_margin">22dp</dimen>
<dimen name="magnification_border_size">5dp</dimen>
<dimen name="magnification_frame_move_short">5dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8097c01..68c2a38 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -387,11 +387,6 @@
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
</style>
- <style name="qs_security_footer" parent="@style/qs_theme">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- <item name="android:tint">?android:attr/textColorSecondary</item>
- </style>
-
<style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:colorAccent">@color/remote_input_accent</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 471a769..2f7ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -437,10 +437,10 @@
getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (mode) {
case Configuration.UI_MODE_NIGHT_NO:
- mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_pointer_light));
+ mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_light));
break;
case Configuration.UI_MODE_NIGHT_YES:
- mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_pointer_dark));
+ mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_dark));
break;
}
mPointerView.setBackground(mPointerDrawable);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index 0c62e9f..dadcb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -22,9 +22,9 @@
import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
import android.content.Context;
-import android.content.res.TypedArray;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.drawable.AdaptiveIconDrawable;
@@ -88,19 +88,23 @@
false /* attachToRoot */);
mOverflowBtn.setContentDescription(mContext.getResources().getString(
R.string.bubble_overflow_button_content_description));
+ Resources res = mContext.getResources();
- TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.colorBackgroundFloating});
- int bgColor = ta.getColor(0, Color.WHITE /* default */);
- ta.recycle();
-
+ // Set color for button icon and dot
TypedValue typedValue = new TypedValue();
mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
int colorAccent = mContext.getColor(typedValue.resourceId);
mOverflowBtn.getDrawable().setTint(colorAccent);
mDotColor = colorAccent;
- ColorDrawable bg = new ColorDrawable(bgColor);
+ // Set color for button and activity background
+ ColorDrawable bg = new ColorDrawable(res.getColor(R.color.bubbles_light));
+ final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ if (mode == Configuration.UI_MODE_NIGHT_YES) {
+ bg = new ColorDrawable(res.getColor(R.color.bubbles_dark));
+ }
+
+ // Apply icon inset
InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
mBitmapSize - mIconBitmapSize /* inset */);
AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
@@ -110,6 +114,7 @@
null /* user */,
true /* shrinkNonAdaptiveIcons */).icon;
+ // Get path with dot location
float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
null /* outBounds */, null /* path */, null /* outMaskShape */);
float radius = DEFAULT_PATH_SIZE / 2f;
@@ -120,14 +125,9 @@
radius /* pivot y */);
mPath.transform(matrix);
- mOverflowBtn.setVisibility(GONE);
mOverflowBtn.setRenderedBubble(this);
}
- ImageView getBtn() {
- return mOverflowBtn;
- }
-
void setVisible(int visible) {
mOverflowBtn.setVisibility(visible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index ea324af..0b25c44 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -27,6 +27,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -100,7 +101,6 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bubble_overflow_activity);
- setBackgroundColor();
mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
@@ -141,34 +141,25 @@
* Handle theme changes.
*/
void updateTheme() {
- final int mode =
- getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ Resources res = getResources();
+ final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (mode) {
- case Configuration.UI_MODE_NIGHT_NO:
- if (DEBUG_OVERFLOW) {
- Log.d(TAG, "Set overflow UI to light mode");
- }
- mEmptyStateImage.setImageDrawable(
- getResources().getDrawable(R.drawable.ic_empty_bubble_overflow_light));
- break;
case Configuration.UI_MODE_NIGHT_YES:
- if (DEBUG_OVERFLOW) {
- Log.d(TAG, "Set overflow UI to dark mode");
- }
mEmptyStateImage.setImageDrawable(
- getResources().getDrawable(R.drawable.ic_empty_bubble_overflow_dark));
+ res.getDrawable(R.drawable.ic_empty_bubble_overflow_dark));
+ findViewById(android.R.id.content)
+ .setBackgroundColor(res.getColor(R.color.bubbles_dark));
+ break;
+
+ case Configuration.UI_MODE_NIGHT_NO:
+ mEmptyStateImage.setImageDrawable(
+ res.getDrawable(R.drawable.ic_empty_bubble_overflow_light));
+ findViewById(android.R.id.content)
+ .setBackgroundColor(res.getColor(R.color.bubbles_light));
break;
}
}
- void setBackgroundColor() {
- final TypedArray ta = getApplicationContext().obtainStyledAttributes(
- new int[]{android.R.attr.colorBackgroundFloating});
- int bgColor = ta.getColor(0, Color.WHITE);
- ta.recycle();
- findViewById(android.R.id.content).setBackgroundColor(bgColor);
- }
-
void onDataChanged(List<Bubble> bubbles) {
mOverflowBubbles.clear();
mOverflowBubbles.addAll(bubbles);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index a59d2c0..5542592 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -533,6 +533,8 @@
mMagneticTarget,
mIndividualBubbleMagnetListener);
+ hideImeFromExpandedBubble();
+
// Save the magnetized individual bubble so we can dispatch touch events to it.
mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
} else {
@@ -1094,13 +1096,17 @@
mBubbleOverflow = new BubbleOverflow(getContext());
mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
} else {
- mBubbleContainer.removeView(mBubbleOverflow.getBtn());
+ mBubbleContainer.removeView(mBubbleOverflow.getIconView());
mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
overflowBtnIndex = mBubbleContainer.getChildCount();
}
- mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex,
+ mBubbleContainer.addView(mBubbleOverflow.getIconView(), overflowBtnIndex,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- mBubbleOverflow.getBtn().setOnClickListener(v -> setSelectedBubble(mBubbleOverflow));
+ mBubbleOverflow.getIconView().setOnClickListener(v -> {
+ setSelectedBubble(mBubbleOverflow);
+ showManageMenu(false);
+ });
+ updateOverflowVisibility();
}
/**
* Handle theme changes.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 775a1649..b86e1d0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -480,7 +480,17 @@
if (inOverlay) {
rootOverlay!!.add(mediaFrame)
} else {
+ // When adding back to the host, let's make sure to reset the bounds.
+ // Usually adding the view will trigger a layout that does this automatically,
+ // but we sometimes suppress this.
targetHost.addView(mediaFrame)
+ val left = targetHost.paddingLeft
+ val top = targetHost.paddingTop
+ mediaFrame.setLeftTopRightBottom(
+ left,
+ top,
+ left + currentBounds.width(),
+ top + currentBounds.height())
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index 40d317c..dc157a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -139,12 +139,7 @@
}
tilesToShow = actualColumns * NUM_LINES
- val interTileSpace = if (actualColumns <= 2) {
- // Extra "column" of padding to be distributed on each end
- (availableWidth - actualColumns * smallTileSize) / actualColumns
- } else {
- (availableWidth - actualColumns * smallTileSize) / (actualColumns - 1)
- }
+ val spacePerTile = availableWidth / actualColumns
for (index in 0 until mRecords.size) {
val tileView = mRecords[index].tileView
@@ -154,15 +149,16 @@
tileView.visibility = View.VISIBLE
if (index > 0) tileView.updateAccessibilityOrder(mRecords[index - 1].tileView)
val column = index % actualColumns
- val left = getLeftForColumn(column, interTileSpace, actualColumns <= 2)
+ val left = getLeftForColumn(column, spacePerTile)
val top = if (index < actualColumns) 0 else getTopBottomRow()
tileView.layout(left, top, left + smallTileSize, top + smallTileSize)
}
}
}
- private fun getLeftForColumn(column: Int, interSpace: Int, sideMargin: Boolean): Int {
- return (if (sideMargin) interSpace / 2 else 0) + column * (smallTileSize + interSpace)
+ private fun getLeftForColumn(column: Int, spacePerTile: Int): Int {
+ // Distribute the space evenly among all tiles.
+ return (column * spacePerTile + spacePerTile / 2.0f - smallTileSize / 2.0f).toInt()
}
private fun getTopBottomRow() = smallTileSize + cellMarginVertical
diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
index c204d94..aa17c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
@@ -17,6 +17,9 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
import android.widget.ScrollView;
/**
@@ -24,8 +27,12 @@
*/
public class NonInterceptingScrollView extends ScrollView {
+ private final int mTouchSlop;
+ private float mDownY;
+
public NonInterceptingScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
@@ -34,10 +41,52 @@
switch (action) {
case MotionEvent.ACTION_DOWN:
if (canScrollVertically(1)) {
- requestDisallowInterceptTouchEvent(true);
+ // If we can scroll down, make sure we're not intercepted by the parent
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
}
break;
}
return super.onTouchEvent(ev);
}
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // If there's a touch on this view and we can scroll down, we don't want to be intercepted
+ int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ // If we can scroll down, make sure non of our parents intercepts us.
+ if (canScrollVertically(1)) {
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+ mDownY = ev.getY();
+ break;
+ case MotionEvent.ACTION_MOVE: {
+ final int y = (int) ev.getY();
+ final float yDiff = y - mDownY;
+ if (yDiff < -mTouchSlop && !canScrollVertically(1)) {
+ // Don't intercept touches that are overscrolling.
+ return false;
+ }
+ break;
+ }
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ public int getScrollRange() {
+ int scrollRange = 0;
+ if (getChildCount() > 0) {
+ View child = getChildAt(0);
+ scrollRange = Math.max(0,
+ child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+ }
+ return scrollRange;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index aaff9ac..2836992 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -66,6 +66,10 @@
private int mHorizontalClipBound;
private final Rect mClippingRect;
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
+ private int mExcessHeight;
+ private int mLastExcessHeight;
+ private int mMinRows = 1;
+ private int mMaxColumns = TileLayout.NO_MAX_COLUMNS;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -195,11 +199,18 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mPages.add((TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false));
+ mPages.add(createTilePage());
mAdapter.notifyDataSetChanged();
}
+ private TilePage createTilePage() {
+ TilePage page = (TilePage) LayoutInflater.from(getContext())
+ .inflate(R.layout.qs_paged_page, this, false);
+ page.setMinRows(mMinRows);
+ page.setMaxColumns(mMaxColumns);
+ return page;
+ }
+
public void setPageIndicator(PageIndicator indicator) {
mPageIndicator = indicator;
mPageIndicator.setNumPages(mPages.size());
@@ -298,8 +309,7 @@
}
while (mPages.size() < numPages) {
if (DEBUG) Log.d(TAG, "Adding page");
- mPages.add((TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false));
+ mPages.add(createTilePage());
}
while (mPages.size() > numPages) {
if (DEBUG) Log.d(TAG, "Removing page");
@@ -342,17 +352,54 @@
}
@Override
+ public boolean setMinRows(int minRows) {
+ mMinRows = minRows;
+ boolean changed = false;
+ for (int i = 0; i < mPages.size(); i++) {
+ if (mPages.get(i).setMinRows(minRows)) {
+ changed = true;
+ mDistributeTiles = true;
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ public boolean setMaxColumns(int maxColumns) {
+ mMaxColumns = maxColumns;
+ boolean changed = false;
+ for (int i = 0; i < mPages.size(); i++) {
+ if (mPages.get(i).setMaxColumns(maxColumns)) {
+ changed = true;
+ mDistributeTiles = true;
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Set the amount of excess space that we gave this view compared to the actual available
+ * height. This is because this view is in a scrollview.
+ */
+ public void setExcessHeight(int excessHeight) {
+ mExcessHeight = excessHeight;
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int nTiles = mTiles.size();
// If we have no reason to recalculate the number of rows, skip this step. In particular,
// if the height passed by its parent is the same as the last time, we try not to remeasure.
- if (mDistributeTiles || mLastMaxHeight != MeasureSpec.getSize(heightMeasureSpec)) {
+ if (mDistributeTiles || mLastMaxHeight != MeasureSpec.getSize(heightMeasureSpec)
+ || mLastExcessHeight != mExcessHeight) {
mLastMaxHeight = MeasureSpec.getSize(heightMeasureSpec);
+ mLastExcessHeight = mExcessHeight;
// Only change the pages if the number of rows or columns (from updateResources) has
// changed or the tiles have changed
- if (mPages.get(0).updateMaxRows(heightMeasureSpec, nTiles) || mDistributeTiles) {
+ int availableHeight = mLastMaxHeight - mExcessHeight;
+ if (mPages.get(0).updateMaxRows(availableHeight, nTiles) || mDistributeTiles) {
mDistributeTiles = false;
distributeTiles();
}
@@ -485,14 +532,6 @@
// up.
return Math.max(mColumns * mRows, 1);
}
-
- @Override
- public boolean updateResources() {
- final int sidePadding = getContext().getResources().getDimensionPixelSize(
- R.dimen.notification_side_paddings);
- setPadding(sidePadding, 0, sidePadding, 0);
- return super.updateResources();
- }
}
private final PagerAdapter mAdapter = new PagerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index ce00229..bc8f5a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -18,6 +18,7 @@
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
+import android.widget.ScrollView;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.qs.QS;
@@ -30,7 +31,6 @@
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.Collection;
@@ -66,6 +66,7 @@
private TouchAnimator mNonfirstPageAnimator;
private TouchAnimator mNonfirstPageDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
+ private boolean mNeedsAnimatorUpdate = false;
private boolean mOnKeyguard;
@@ -98,6 +99,12 @@
updateAnimators();
}
+
+ public void onQsScrollingChanged() {
+ // Lazily update animators whenever the scrolling changes
+ mNeedsAnimatorUpdate = true;
+ }
+
public void setOnKeyguard(boolean onKeyguard) {
mOnKeyguard = onKeyguard;
updateQQSVisibility();
@@ -172,6 +179,7 @@
}
private void updateAnimators() {
+ mNeedsAnimatorUpdate = false;
TouchAnimator.Builder firstPageBuilder = new Builder();
TouchAnimator.Builder translationXBuilder = new Builder();
TouchAnimator.Builder translationYBuilder = new Builder();
@@ -286,13 +294,16 @@
.setListener(this)
.build();
// Fade in the tiles/labels as we reach the final position.
- mFirstPageDelayedAnimator = new TouchAnimator.Builder()
+ Builder builder = new Builder()
.setStartDelay(EXPANDED_TILE_DELAY)
- .addFloat(tileLayout, "alpha", 0, 1)
- .addFloat(mQsPanel.getDivider(), "alpha", 0, 1)
- .addFloat(mQsPanel.getFooter().getView(), "alpha", 0, 1).build();
- mAllViews.add(mQsPanel.getDivider());
- mAllViews.add(mQsPanel.getFooter().getView());
+ .addFloat(tileLayout, "alpha", 0, 1);
+ if (mQsPanel.getSecurityFooter() != null) {
+ builder.addFloat(mQsPanel.getSecurityFooter().getView(), "alpha", 0, 1);
+ }
+ mFirstPageDelayedAnimator = builder.build();
+ if (mQsPanel.getSecurityFooter() != null) {
+ mAllViews.add(mQsPanel.getSecurityFooter().getView());
+ }
float px = 0;
float py = 1;
if (tiles.size() <= 3) {
@@ -308,7 +319,6 @@
}
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
- .addFloat(mQsPanel.getDivider(), "alpha", 0, 1)
.setListener(mNonFirstPageListener)
.setEndDelay(.5f)
.build();
@@ -339,10 +349,18 @@
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
}
+ if (!(view instanceof PagedTileLayout)) {
+ // Remove the scrolling position of all scroll views other than the viewpager
+ loc1[0] -= view.getScrollX();
+ loc1[1] -= view.getScrollY();
+ }
getRelativePositionInt(loc1, (View) view.getParent(), parent);
}
public void setPosition(float position) {
+ if (mNeedsAnimatorUpdate) {
+ updateAnimators();
+ }
if (mFirstPageAnimator == null) return;
if (mOnKeyguard) {
if (mShowCollapsedOnKeyguard) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 1c3b685..0332bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -43,6 +43,7 @@
private float mQsExpansion;
private QSCustomizer mQSCustomizer;
private View mDragHandle;
+ private View mQSPanelContainer;
private View mBackground;
private View mBackgroundGradient;
@@ -61,6 +62,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQSPanel = findViewById(R.id.quick_settings_panel);
+ mQSPanelContainer = findViewById(R.id.expanded_qs_scroll_view);
mQSDetail = findViewById(R.id.qs_detail);
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
@@ -95,7 +97,7 @@
Configuration config = getResources().getConfiguration();
boolean navBelow = config.smallestScreenWidthDp >= 600
|| config.orientation != Configuration.ORIENTATION_LANDSCAPE;
- MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanel.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
// The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
// subtract its height. We do not care if the collapsed notifications fit in the screen.
@@ -109,12 +111,11 @@
+ layoutParams.rightMargin;
final int qsPanelWidthSpec = getChildMeasureSpec(widthMeasureSpec, padding,
layoutParams.width);
- // Measure with EXACTLY. That way, PagedTileLayout will only use excess height and will be
- // measured last, after other views and padding is accounted for.
- mQSPanel.measure(qsPanelWidthSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.EXACTLY));
- int width = mQSPanel.getMeasuredWidth() + padding;
+ mQSPanelContainer.measure(qsPanelWidthSpec,
+ MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
+ int width = mQSPanelContainer.getMeasuredWidth() + padding;
int height = layoutParams.topMargin + layoutParams.bottomMargin
- + mQSPanel.getMeasuredHeight() + getPaddingBottom();
+ + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
@@ -130,7 +131,7 @@
// Do not measure QSPanel again when doing super.onMeasure.
// This prevents the pages in PagedTileLayout to be remeasured with a different (incorrect)
// size to the one used for determining the number of rows and then the number of pages.
- if (child != mQSPanel) {
+ if (child != mQSPanelContainer) {
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
}
@@ -151,10 +152,10 @@
}
private void updateResources() {
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
+ LayoutParams layoutParams = (LayoutParams) mQSPanelContainer.getLayoutParams();
layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- mQSPanel.setLayoutParams(layoutParams);
+ mQSPanelContainer.setLayoutParams(layoutParams);
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
mContentPaddingStart = getResources().getDimensionPixelSize(
@@ -185,7 +186,7 @@
mQSDetail.setBottom(getTop() + height);
// Pin the drag handle to the bottom of the panel.
mDragHandle.setTranslationY(height - mDragHandle.getHeight());
- mBackground.setTop(mQSPanel.getTop());
+ mBackground.setTop(mQSPanelContainer.getTop());
mBackground.setBottom(height);
}
@@ -223,7 +224,7 @@
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.rightMargin = mSideMargins;
lp.leftMargin = mSideMargins;
- if (view == mQSPanel) {
+ if (view == mQSPanelContainer) {
// QS panel lays out some of its content full width
mQSPanel.setContentMargins(mContentPaddingStart, mContentPaddingEnd);
} else if (view == mHeader) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 6af9e1e..f1bb899 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -24,7 +24,6 @@
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -72,6 +71,7 @@
protected QuickStatusBarHeader mHeader;
private QSCustomizer mQSCustomizer;
protected QSPanel mQSPanel;
+ protected NonInterceptingScrollView mQSPanelScrollView;
private QSDetail mQSDetail;
private boolean mListening;
private QSContainerImpl mContainer;
@@ -122,8 +122,20 @@
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mQSPanel = view.findViewById(R.id.quick_settings_panel);
+ mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
+ mQSPanelScrollView.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ updateQsBounds();
+ });
+ mQSPanelScrollView.setOnScrollChangeListener(
+ (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+ // Lazily update animators whenever the scrolling changes
+ mQSAnimator.onQsScrollingChanged();
+ mHeader.setExpandedScrollAmount(scrollY);
+ });
mQSDetail = view.findViewById(R.id.qs_detail);
mHeader = view.findViewById(R.id.header);
+ mQSPanel.setHeaderContainer(view.findViewById(R.id.header_text_container));
mFooter = view.findViewById(R.id.qs_footer);
mContainer = view.findViewById(id.quick_settings_container);
@@ -133,8 +145,8 @@
mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);
- mQSAnimator = new QSAnimator(this,
- mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
+ mQSAnimator = new QSAnimator(this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
+
mQSCustomizer = view.findViewById(R.id.qs_customize);
mQSCustomizer.setQs(this);
@@ -319,11 +331,6 @@
}
@Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return isCustomizing();
- }
-
- @Override
public void setHeaderClickable(boolean clickable) {
if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable);
}
@@ -394,7 +401,9 @@
mLastViewHeight = currentHeight;
boolean fullyExpanded = expansion == 1;
- int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom();
+ boolean fullyCollapsed = expansion == 0.0f;
+ int heightDiff = mQSPanelScrollView.getBottom() - mHeader.getBottom()
+ + mHeader.getPaddingBottom();
float panelTranslationY = translationScaleY * heightDiff;
// Let the views animate their contents correctly by giving them the necessary context.
@@ -403,19 +412,19 @@
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanel.getQsTileRevealController().setExpansion(expansion);
mQSPanel.getTileLayout().setExpansion(expansion);
- mQSPanel.setTranslationY(translationScaleY * heightDiff);
+ mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
+ if (fullyCollapsed) {
+ mQSPanelScrollView.setScrollY(0);
+ }
mQSDetail.setFullyExpanded(fullyExpanded);
- if (fullyExpanded) {
- // Always draw within the bounds of the view when fully expanded.
- mQSPanel.setClipBounds(null);
- } else {
+ if (!fullyExpanded) {
// Set bounds on the QS panel so it doesn't run over the header when animating.
- mQsBounds.top = (int) -mQSPanel.getTranslationY();
- mQsBounds.right = mQSPanel.getWidth();
- mQsBounds.bottom = mQSPanel.getHeight();
- mQSPanel.setClipBounds(mQsBounds);
+ mQsBounds.top = (int) -mQSPanelScrollView.getTranslationY();
+ mQsBounds.right = mQSPanelScrollView.getWidth();
+ mQsBounds.bottom = mQSPanelScrollView.getHeight();
}
+ updateQsBounds();
if (mQSAnimator != null) {
mQSAnimator.setPosition(expansion);
@@ -423,31 +432,63 @@
updateMediaPositions();
}
+ private void updateQsBounds() {
+ if (mLastQSExpansion == 1.0f) {
+ // Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
+ // it's a scrollview and otherwise wouldn't be clipped.
+ mQsBounds.set(0, 0, mQSPanelScrollView.getWidth(), mQSPanelScrollView.getHeight());
+ }
+ mQSPanelScrollView.setClipBounds(mQsBounds);
+ }
+
private void updateMediaPositions() {
if (Utils.useQsMediaPlayer(getContext())) {
mContainer.getLocationOnScreen(mTmpLocation);
float absoluteBottomPosition = mTmpLocation[1] + mContainer.getHeight();
- pinToBottom(absoluteBottomPosition, mQSPanel.getMediaHost());
- pinToBottom(absoluteBottomPosition - mHeader.getPaddingBottom(),
- mHeader.getHeaderQsPanel().getMediaHost());
+ // The Media can be scrolled off screen by default, let's offset it
+ float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
+ + mQSPanelScrollView.getScrollRange();
+ // The expanded media host should never move below the laid out position
+ pinToBottom(expandedMediaPosition, mQSPanel.getMediaHost(), true /* expanded */);
+ // The expanded media host should never move above the laid out position
+ pinToBottom(absoluteBottomPosition, mHeader.getHeaderQsPanel().getMediaHost(),
+ false /* expanded */);
}
}
- private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost) {
+ private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
View hostView = mediaHost.getHostView();
if (mLastQSExpansion > 0) {
- ViewGroup.MarginLayoutParams params =
- (ViewGroup.MarginLayoutParams) hostView.getLayoutParams();
- float targetPosition = absoluteBottomPosition - params.bottomMargin
+ float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
- hostView.getHeight();
float currentPosition = mediaHost.getCurrentBounds().top
- hostView.getTranslationY();
- hostView.setTranslationY(targetPosition - currentPosition);
+ float translationY = targetPosition - currentPosition;
+ if (expanded) {
+ // Never go below the laid out position. This is necessary since the qs panel can
+ // change in height and we don't want to ever go below it's position
+ translationY = Math.min(translationY, 0);
+ } else {
+ translationY = Math.max(translationY, 0);
+ }
+ hostView.setTranslationY(translationY);
} else {
hostView.setTranslationY(0);
}
}
+ private float getTotalBottomMargin(View startView) {
+ int result = 0;
+ View child = startView;
+ View parent = (View) startView.getParent();
+ while (!(parent instanceof QSContainerImpl) && parent != null) {
+ result += parent.getHeight() - child.getBottom();
+ child = parent;
+ parent = (View) parent.getParent();
+ }
+ return result;
+ }
+
private boolean headerWillBeAnimating() {
return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard
&& !isKeyguardShowing();
@@ -504,7 +545,8 @@
public void notifyCustomizeChanged() {
// The customize state changed, so our height changed.
mContainer.updateExpansion();
- mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
+ mQSPanelScrollView.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE
+ : View.INVISIBLE);
mFooter.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
// Let the panel know the position changed and it needs to update where notifications
// and whatnot are.
@@ -521,9 +563,9 @@
return getView().getHeight();
}
if (mQSDetail.isClosingDetail()) {
- LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
+ LayoutParams layoutParams = (LayoutParams) mQSPanelScrollView.getLayoutParams();
int panelHeight = layoutParams.topMargin + layoutParams.bottomMargin +
- + mQSPanel.getMeasuredHeight();
+ + mQSPanelScrollView.getMeasuredHeight();
return panelHeight + getView().getPaddingBottom();
} else {
return getView().getMeasuredHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7844878..ecdb2c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -16,10 +16,10 @@
package com.android.systemui.qs;
-import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -29,8 +29,8 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.service.quicksettings.Tile;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -39,7 +39,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
+import com.android.internal.widget.RemeasuringLinearLayout;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -83,38 +83,65 @@
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private final BroadcastDispatcher mBroadcastDispatcher;
protected final MediaHost mMediaHost;
+
+ /**
+ * The index where the content starts that needs to be moved between parents
+ */
+ private final int mMovableContentStartIndex;
private String mCachedSpecs = "";
- protected final View mBrightnessView;
+
+ @Nullable
+ protected View mBrightnessView;
+ @Nullable
+ private BrightnessController mBrightnessController;
+
private final H mHandler = new H();
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
- private final QSTileRevealController mQsTileRevealController;
+ private QSTileRevealController mQsTileRevealController;
/** Whether or not the QS media player feature is enabled. */
protected boolean mUsingMediaPlayer;
+ private int mVisualMarginStart;
+ private int mVisualMarginEnd;
protected boolean mExpanded;
protected boolean mListening;
private QSDetail.Callback mCallback;
- private BrightnessController mBrightnessController;
private final DumpManager mDumpManager;
private final QSLogger mQSLogger;
protected final UiEventLogger mUiEventLogger;
protected QSTileHost mHost;
- protected QSSecurityFooter mFooter;
+ @Nullable
+ protected QSSecurityFooter mSecurityFooter;
+
+ @Nullable
+ protected View mFooter;
+
+ @Nullable
+ private ViewGroup mHeaderContainer;
private PageIndicator mFooterPageIndicator;
private boolean mGridContentVisible = true;
private int mContentMarginStart;
private int mContentMarginEnd;
private int mVisualTilePadding;
-
- protected QSTileLayout mTileLayout;
+ private boolean mUsingHorizontalLayout;
private QSCustomizer mCustomizePanel;
private Record mDetailRecord;
private BrightnessMirrorController mBrightnessMirrorController;
- private View mDivider;
+ private LinearLayout mHorizontalLinearLayout;
+ private LinearLayout mHorizontalContentContainer;
+
+ // Only used with media
+ private QSTileLayout mHorizontalTileLayout;
+ protected QSTileLayout mRegularTileLayout;
+ protected QSTileLayout mTileLayout;
+ private int mLastOrientation = -1;
+ private int mMediaTotalBottomMargin;
+ private int mFooterMarginStartHorizontal;
+
@Inject
public QSPanel(
@@ -128,7 +155,13 @@
) {
super(context, attrs);
mUsingMediaPlayer = useQsMediaPlayer(context);
+ mMediaTotalBottomMargin = getResources().getDimensionPixelSize(
+ R.dimen.quick_settings_bottom_margin_media);
mMediaHost = mediaHost;
+ mMediaHost.setVisibleChangedListener((visible) -> {
+ switchTileLayout();
+ return null;
+ });
mContext = context;
mQSLogger = qsLogger;
mDumpManager = dumpManager;
@@ -137,71 +170,97 @@
setOrientation(VERTICAL);
+ addViewsAboveTiles();
+ mMovableContentStartIndex = getChildCount();
+ mRegularTileLayout = createRegularTileLayout();
+
+ if (mUsingMediaPlayer) {
+ mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext);
+ mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ mHorizontalLinearLayout.setClipChildren(false);
+ mHorizontalLinearLayout.setClipToPadding(false);
+
+ mHorizontalContentContainer = new RemeasuringLinearLayout(mContext);
+ mHorizontalContentContainer.setOrientation(LinearLayout.VERTICAL);
+ mHorizontalContentContainer.setClipChildren(false);
+ mHorizontalContentContainer.setClipToPadding(false);
+
+ mHorizontalTileLayout = createHorizontalTileLayout();
+ LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
+ int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
+ lp.setMarginStart(0);
+ lp.setMarginEnd(marginSize);
+ lp.gravity = Gravity.CENTER_VERTICAL;
+ mHorizontalLinearLayout.addView(mHorizontalContentContainer, lp);
+
+ lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1);
+ addView(mHorizontalLinearLayout, lp);
+
+ initMediaHostState();
+ }
+ addSecurityFooter();
+ if (mRegularTileLayout instanceof PagedTileLayout) {
+ mQsTileRevealController = new QSTileRevealController(mContext, this,
+ (PagedTileLayout) mRegularTileLayout);
+ }
+ mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs);
+ updateResources();
+ }
+
+ protected void addSecurityFooter() {
+ mSecurityFooter = new QSSecurityFooter(this, mContext);
+ }
+
+ protected void addViewsAboveTiles() {
mBrightnessView = LayoutInflater.from(mContext).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
addView(mBrightnessView);
-
- mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
- R.layout.qs_paged_tile_layout, this, false);
- mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs);
- mTileLayout.setListening(mListening);
- addView((View) mTileLayout);
-
- mQsTileRevealController = new QSTileRevealController(mContext, this,
- (PagedTileLayout) mTileLayout);
-
- addDivider();
-
- mFooter = new QSSecurityFooter(this, context);
- addView(mFooter.getView());
-
- updateResources();
-
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_slider), mBroadcastDispatcher);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- // Add media carousel at the end
- if (useQsMediaPlayer(getContext())) {
- addMediaHostView();
+ protected QSTileLayout createRegularTileLayout() {
+ if (mRegularTileLayout == null) {
+ mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.qs_paged_tile_layout, this, false);
}
+ return mRegularTileLayout;
}
- protected void addMediaHostView() {
+
+ protected QSTileLayout createHorizontalTileLayout() {
+ return createRegularTileLayout();
+ }
+
+ protected void initMediaHostState() {
mMediaHost.setExpansion(1.0f);
mMediaHost.setShowsOnlyActiveMedia(false);
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
- ViewGroup hostView = mMediaHost.getHostView();
- addView(hostView);
- int bottomPadding = getResources().getDimensionPixelSize(
- R.dimen.quick_settings_expanded_bottom_margin);
- MarginLayoutParams layoutParams = (MarginLayoutParams) hostView.getLayoutParams();
- layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.bottomMargin = bottomPadding;
- hostView.setLayoutParams(layoutParams);
- updateMediaHostContentMargins();
- }
-
- protected void addDivider() {
- mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false);
- mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(),
- getColorForState(mContext, Tile.STATE_ACTIVE)));
- addView(mDivider);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ if (mTileLayout instanceof PagedTileLayout) {
+ // Allow the UI to be as big as it want's to, we're in a scroll view
+ int newHeight = 10000;
+ int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
+ int excessHeight = newHeight - availableHeight;
+ // Measure with EXACTLY. That way, The content will only use excess height and will
+ // be measured last, after other views and padding is accounted for. This only
+ // works because our Layouts in here remeasure themselves with the exact content
+ // height.
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
+ ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
// We want all the logic of LinearLayout#onMeasure, and for it to assign the excess space
// not used by the other children to PagedTileLayout. However, in this case, LinearLayout
// assumes that PagedTileLayout would use all the excess space. This is not the case as
// PagedTileLayout height is quantized (because it shows a certain number of rows).
// Therefore, after everything is measured, we need to make sure that we add up the correct
// total height
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = getPaddingBottom() + getPaddingTop();
int numChildren = getChildCount();
for (int i = 0; i < numChildren; i++) {
@@ -215,10 +274,6 @@
setMeasuredDimension(getMeasuredWidth(), height);
}
- public View getDivider() {
- return mDivider;
- }
-
public QSTileRevealController getQsTileRevealController() {
return mQsTileRevealController;
}
@@ -273,7 +328,7 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (QS_SHOW_BRIGHTNESS.equals(key)) {
+ if (QS_SHOW_BRIGHTNESS.equals(key) && mBrightnessView != null) {
updateViewVisibilityForTuningValue(mBrightnessView, newValue);
}
}
@@ -316,6 +371,7 @@
updateBrightnessMirror();
}
+ @Nullable
View getBrightnessView() {
return mBrightnessView;
}
@@ -328,7 +384,9 @@
mHost = host;
mHost.addCallback(this);
setTiles(mHost.getTiles());
- mFooter.setHostEnvironment(host);
+ if (mSecurityFooter != null) {
+ mSecurityFooter.setHostEnvironment(host);
+ }
mCustomizePanel = customizer;
if (mCustomizePanel != null) {
mCustomizePanel.setHost(mHost);
@@ -341,18 +399,18 @@
* @param pageIndicator indicator to use for page scrolling
*/
public void setFooterPageIndicator(PageIndicator pageIndicator) {
- if (mTileLayout instanceof PagedTileLayout) {
+ if (mRegularTileLayout instanceof PagedTileLayout) {
mFooterPageIndicator = pageIndicator;
updatePageIndicator();
}
}
private void updatePageIndicator() {
- if (mTileLayout instanceof PagedTileLayout) {
+ if (mRegularTileLayout instanceof PagedTileLayout) {
if (mFooterPageIndicator != null) {
mFooterPageIndicator.setVisibility(View.GONE);
- ((PagedTileLayout) mTileLayout).setPageIndicator(mFooterPageIndicator);
+ ((PagedTileLayout) mRegularTileLayout).setPageIndicator(mFooterPageIndicator);
}
}
}
@@ -364,6 +422,8 @@
public void updateResources() {
int tileSize = getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
+ mFooterMarginStartHorizontal = getResources().getDimensionPixelSize(
+ R.dimen.qs_footer_horizontal_margin);
mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f);
updatePadding();
@@ -379,8 +439,15 @@
protected void updatePadding() {
final Resources res = mContext.getResources();
+ int padding = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
+ if (mUsingHorizontalLayout) {
+ // When using the horizontal layout, our space is quite constrained. We therefore
+ // reduce some of the padding on the top, which makes the brightness bar overlapp,
+ // but since that has naturally quite a bit of built in padding, that's fine.
+ padding = (int) (padding * 0.6f);
+ }
setPaddingRelative(getPaddingStart(),
- res.getDimensionPixelSize(R.dimen.qs_panel_padding_top),
+ padding,
getPaddingEnd(),
res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
}
@@ -388,10 +455,165 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mFooter.onConfigurationChanged();
+ if (mSecurityFooter != null) {
+ mSecurityFooter.onConfigurationChanged();
+ }
updateResources();
updateBrightnessMirror();
+
+ if (newConfig.orientation != mLastOrientation) {
+ mLastOrientation = newConfig.orientation;
+ switchTileLayout();
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mFooter = findViewById(R.id.qs_footer);
+ switchTileLayout(true /* force */);
+ }
+
+ boolean switchTileLayout() {
+ return switchTileLayout(false /* force */);
+ }
+
+ private boolean switchTileLayout(boolean force) {
+ /** Whether or not the QuickQSPanel currently contains a media player. */
+ boolean horizontal = shouldUseHorizontalLayout();
+ if (horizontal != mUsingHorizontalLayout || force) {
+ mUsingHorizontalLayout = horizontal;
+ View visibleView = horizontal ? mHorizontalLinearLayout : (View) mRegularTileLayout;
+ View hiddenView = horizontal ? (View) mRegularTileLayout : mHorizontalLinearLayout;
+ ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
+ QSTileLayout newLayout = horizontal ? mHorizontalTileLayout : mRegularTileLayout;
+ if (hiddenView != null &&
+ (mRegularTileLayout != mHorizontalTileLayout ||
+ hiddenView != mRegularTileLayout)) {
+ // Only hide the view if the horizontal and the regular view are different,
+ // otherwise its reattached.
+ hiddenView.setVisibility(View.GONE);
+ }
+ visibleView.setVisibility(View.VISIBLE);
+ switchAllContentToParent(newParent, newLayout);
+ reAttachMediaHost();
+ if (mTileLayout != null) {
+ mTileLayout.setListening(false);
+ for (TileRecord record : mRecords) {
+ mTileLayout.removeTile(record);
+ record.tile.removeCallback(record.callback);
+ }
+ }
+ mTileLayout = newLayout;
+ if (mHost != null) setTiles(mHost.getTiles());
+ newLayout.setListening(mListening);
+ if (needsDynamicRowsAndColumns()) {
+ newLayout.setMinRows(horizontal ? 2 : 1);
+ // Let's use 3 columns to match the current layout
+ newLayout.setMaxColumns(horizontal ? 3 : TileLayout.NO_MAX_COLUMNS);
+ }
+ updateTileLayoutMargins();
+ updateFooterMargin();
+ updateMediaHostContentMargins();
+ updateHorizontalLinearLayoutMargins();
+ updatePadding();
+ return true;
+ }
+ return false;
+ }
+
+ private void updateHorizontalLinearLayoutMargins() {
+ if (mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) {
+ LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams();
+ lp.bottomMargin = mMediaTotalBottomMargin - getPaddingBottom();
+ mHorizontalLinearLayout.setLayoutParams(lp);
+ }
+ }
+
+ /**
+ * @return true if the margin bottom of the media view should be on the media host or false
+ * if they should be on the HorizontalLinearLayout. Returning {@code false} is useful
+ * to visually center the tiles in the Media view, which doesn't work when the
+ * expanded panel actually scrolls.
+ */
+ protected boolean displayMediaMarginsOnMedia() {
+ return true;
+ }
+
+ protected boolean needsDynamicRowsAndColumns() {
+ return true;
+ }
+
+ private void switchAllContentToParent(ViewGroup parent, QSTileLayout newLayout) {
+ int index = parent == this ? mMovableContentStartIndex : 0;
+
+ // Let's first move the tileLayout to the new parent, since that should come first.
+ switchToParent((View) newLayout, parent, index);
+ index++;
+
+ if (mSecurityFooter != null) {
+ View view = mSecurityFooter.getView();
+ LinearLayout.LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
+ if (mUsingHorizontalLayout && mHeaderContainer != null) {
+ // Adding the security view to the header, that enables us to avoid scrolling
+ layoutParams.width = 0;
+ layoutParams.weight = 1.6f;
+ switchToParent(view, mHeaderContainer, 1 /* always in second place */);
+ } else {
+ layoutParams.width = LayoutParams.WRAP_CONTENT;
+ layoutParams.weight = 0;
+ switchToParent(view, parent, index);
+ index++;
+ }
+ view.setLayoutParams(layoutParams);
+ }
+
+ if (mFooter != null) {
+ // Then the footer with the settings
+ switchToParent(mFooter, parent, index);
+ }
+ }
+
+ private void switchToParent(View child, ViewGroup parent, int index) {
+ ViewGroup currentParent = (ViewGroup) child.getParent();
+ if (currentParent != parent || currentParent.indexOfChild(child) != index) {
+ if (currentParent != null) {
+ currentParent.removeView(child);
+ }
+ parent.addView(child, index);
+ }
+ }
+
+ private boolean shouldUseHorizontalLayout() {
+ return mUsingMediaPlayer && mMediaHost.getVisible()
+ && getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ }
+
+ protected void reAttachMediaHost() {
+ if (!mUsingMediaPlayer) {
+ return;
+ }
+ boolean horizontal = shouldUseHorizontalLayout();
+ ViewGroup host = mMediaHost.getHostView();
+ ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
+ ViewGroup currentParent = (ViewGroup) host.getParent();
+ if (currentParent != newParent) {
+ if (currentParent != null) {
+ currentParent.removeView(host);
+ }
+ newParent.addView(host);
+ LinearLayout.LayoutParams layoutParams = (LayoutParams) host.getLayoutParams();
+ layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
+ layoutParams.weight = horizontal ? 1.2f : 0;
+ // Add any bottom margin, such that the total spacing is correct. This is only
+ // necessary if the view isn't horizontal, since otherwise the padding is
+ // carried in the parent of this view (to ensure correct vertical alignment)
+ layoutParams.bottomMargin = !horizontal || displayMediaMarginsOnMedia()
+ ? mMediaTotalBottomMargin - getPaddingBottom() : 0;
+ }
}
public void updateBrightnessMirror() {
@@ -457,13 +679,18 @@
public void setListening(boolean listening, boolean expanded) {
setListening(listening && expanded);
- getFooter().setListening(listening);
+ if (mSecurityFooter != null) {
+ mSecurityFooter.setListening(listening);
+ }
// Set the listening as soon as the QS fragment starts listening regardless of the expansion,
// so it will update the current brightness before the slider is visible.
setBrightnessListening(listening);
}
public void setBrightnessListening(boolean listening) {
+ if (mBrightnessController == null) {
+ return;
+ }
if (listening) {
mBrightnessController.registerCallbacks();
} else {
@@ -472,11 +699,15 @@
}
public void refreshAllTiles() {
- mBrightnessController.checkRestrictionAndSetEnabled();
+ if (mBrightnessController != null) {
+ mBrightnessController.checkRestrictionAndSetEnabled();
+ }
for (TileRecord r : mRecords) {
r.tile.refreshState();
}
- mFooter.refreshState();
+ if (mSecurityFooter != null) {
+ mSecurityFooter.refreshState();
+ }
}
public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
@@ -728,12 +959,15 @@
return null;
}
- public QSSecurityFooter getFooter() {
- return mFooter;
+ @Nullable
+ public QSSecurityFooter getSecurityFooter() {
+ return mSecurityFooter;
}
public void showDeviceMonitoringDialog() {
- mFooter.showDeviceMonitoringDialog();
+ if (mSecurityFooter != null) {
+ mSecurityFooter.showDeviceMonitoringDialog();
+ }
}
public void setContentMargins(int startMargin, int endMargin) {
@@ -744,6 +978,24 @@
updateTileLayoutMargins(mContentMarginStart - mVisualTilePadding,
mContentMarginEnd - mVisualTilePadding);
updateMediaHostContentMargins();
+ updateFooterMargin();
+ }
+
+ private void updateFooterMargin() {
+ if (mFooter != null) {
+ int footerMargin = 0;
+ int indicatorMargin = 0;
+ if (mUsingHorizontalLayout) {
+ footerMargin = mFooterMarginStartHorizontal;
+ indicatorMargin = footerMargin - mVisualMarginEnd;
+ }
+ updateMargins(mFooter, footerMargin, 0);
+ // The page indicator isn't centered anymore because of the visual positioning.
+ // Let's fix it by adding some margin
+ if (mFooterPageIndicator != null) {
+ updateMargins(mFooterPageIndicator, 0, indicatorMargin);
+ }
+ }
}
/**
@@ -754,16 +1006,30 @@
* @param visualMarginEnd the visual end margin of the tile, adjusted for local insets
* to the tile. This can be set on a tileLayout
*/
- protected void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
- updateMargins((View) mTileLayout, visualMarginStart, visualMarginEnd);
+ private void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
+ mVisualMarginStart = visualMarginStart;
+ mVisualMarginEnd = visualMarginEnd;
+ updateTileLayoutMargins();
+ }
+
+ private void updateTileLayoutMargins() {
+ int marginEnd = mVisualMarginEnd;
+ if (mUsingHorizontalLayout) {
+ marginEnd = 0;
+ }
+ updateMargins((View) mTileLayout, mVisualMarginStart, marginEnd);
}
/**
* Update the margins of the media hosts
*/
protected void updateMediaHostContentMargins() {
- if (mUsingMediaPlayer && mMediaHost != null) {
- updateMargins(mMediaHost.getHostView(), mContentMarginStart, mContentMarginEnd);
+ if (mUsingMediaPlayer) {
+ int marginStart = mContentMarginStart;
+ if (mUsingHorizontalLayout) {
+ marginStart = 0;
+ }
+ updateMargins(mMediaHost.getHostView(), marginStart, mContentMarginEnd);
}
}
@@ -785,6 +1051,13 @@
return mMediaHost;
}
+ /**
+ * Set the header container of quick settings.
+ */
+ public void setHeaderContainer(@NonNull ViewGroup headerContainer) {
+ mHeaderContainer = headerContainer;
+ }
+
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
@@ -812,6 +1085,7 @@
}
}
+
protected static class Record {
DetailAdapter detailAdapter;
int x;
@@ -841,6 +1115,26 @@
void setListening(boolean listening);
+ /**
+ * Set the minimum number of rows to show
+ *
+ * @param minRows the minimum.
+ */
+ default boolean setMinRows(int minRows) {
+ return false;
+ }
+
+ /**
+ * Set the max number of collums to show
+ *
+ * @param maxColumns the maximum
+ *
+ * @return true if the number of visible columns has changed.
+ */
+ default boolean setMaxColumns(int maxColumns) {
+ return false;
+ }
+
default void setExpansion(float expansion) {}
int getNumVisibleTiles();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 476af20..7bcaa72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -51,6 +51,7 @@
public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
protected static final String TAG = "QSSecurityFooter";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DEBUG_FORCE_VISIBLE = false;
private final View mRootView;
private final TextView mFooterText;
@@ -60,7 +61,6 @@
private final SecurityController mSecurityController;
private final ActivityStarter mActivityStarter;
private final Handler mMainHandler;
- private final View mDivider;
private final UserManager mUm;
@@ -85,7 +85,6 @@
mActivityStarter = Dependency.get(ActivityStarter.class);
mSecurityController = Dependency.get(SecurityController.class);
mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
- mDivider = qsPanel == null ? null : qsPanel.getDivider();
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
@@ -177,7 +176,7 @@
boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
CharSequence workProfileName) {
- if (isDeviceManaged) {
+ if (isDeviceManaged || DEBUG_FORCE_VISIBLE) {
if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
if (organizationName == null) {
return mContext.getString(
@@ -451,8 +450,7 @@
if (mFooterTextContent != null) {
mFooterText.setText(mFooterTextContent);
}
- mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
- if (mDivider != null) mDivider.setVisibility(mIsVisible ? View.GONE : View.VISIBLE);
+ mRootView.setVisibility(mIsVisible || DEBUG_FORCE_VISIBLE ? View.VISIBLE : View.GONE);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 94b4cee..affb7b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -24,7 +24,6 @@
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.android.internal.logging.UiEventLogger;
@@ -61,15 +60,7 @@
private boolean mDisabledByPolicy;
private int mMaxTiles;
protected QSPanel mFullPanel;
- /** Whether or not the QuickQSPanel currently contains a media player. */
- private boolean mShowHorizontalTileLayout;
- private LinearLayout mHorizontalLinearLayout;
- // Only used with media
- private QSTileLayout mHorizontalTileLayout;
- private QSTileLayout mRegularTileLayout;
- private int mLastOrientation = -1;
- private int mMediaBottomMargin;
@Inject
public QuickQSPanel(
@@ -82,59 +73,8 @@
UiEventLogger uiEventLogger
) {
super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, mediaHost, uiEventLogger);
- if (mFooter != null) {
- removeView(mFooter.getView());
- }
- if (mTileLayout != null) {
- for (int i = 0; i < mRecords.size(); i++) {
- mTileLayout.removeTile(mRecords.get(i));
- }
- removeView((View) mTileLayout);
- }
- mMediaBottomMargin = getResources().getDimensionPixelSize(
- R.dimen.quick_settings_media_extra_bottom_margin);
- if (mUsingMediaPlayer) {
- mHorizontalLinearLayout = new LinearLayout(mContext);
- mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
- mHorizontalLinearLayout.setClipChildren(false);
- mHorizontalLinearLayout.setClipToPadding(false);
-
- DoubleLineTileLayout horizontalTileLayout = new DoubleLineTileLayout(context,
- mUiEventLogger);
- horizontalTileLayout.setPaddingRelative(
- horizontalTileLayout.getPaddingStart(),
- horizontalTileLayout.getPaddingTop(),
- horizontalTileLayout.getPaddingEnd(),
- mContext.getResources().getDimensionPixelSize(
- R.dimen.qqs_horizonal_tile_padding_bottom));
- mHorizontalTileLayout = horizontalTileLayout;
- mRegularTileLayout = new HeaderTileLayout(context, mUiEventLogger);
- LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
- int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
- lp.setMarginStart(0);
- lp.setMarginEnd(marginSize);
- lp.gravity = Gravity.CENTER_VERTICAL;
- mHorizontalLinearLayout.addView((View) mHorizontalTileLayout, lp);
-
- sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
-
- boolean useHorizontal = shouldUseHorizontalTileLayout();
- mTileLayout = useHorizontal ? mHorizontalTileLayout : mRegularTileLayout;
- mTileLayout.setListening(mListening);
- addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */);
- ((View) mRegularTileLayout).setVisibility(!useHorizontal ? View.VISIBLE : View.GONE);
- mHorizontalLinearLayout.setVisibility(useHorizontal ? View.VISIBLE : View.GONE);
- addView((View) mRegularTileLayout, 0);
- super.setPadding(0, 0, 0, 0);
- applyBottomMargin((View) mRegularTileLayout);
- } else {
- sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
- mTileLayout = new HeaderTileLayout(context, mUiEventLogger);
- mTileLayout.setListening(mListening);
- addView((View) mTileLayout, 0 /* Between brightness and footer */);
- super.setPadding(0, 0, 0, 0);
- applyBottomMargin((View) mTileLayout);
- }
+ sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
+ applyBottomMargin((View) mRegularTileLayout);
}
private void applyBottomMargin(View view) {
@@ -144,48 +84,42 @@
view.setLayoutParams(layoutParams);
}
- private void reAttachMediaHost() {
- if (mMediaHost == null) {
- return;
- }
- boolean horizontal = shouldUseHorizontalTileLayout();
- ViewGroup host = mMediaHost.getHostView();
- ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
- ViewGroup currentParent = (ViewGroup) host.getParent();
- if (currentParent != newParent) {
- if (currentParent != null) {
- currentParent.removeView(host);
- }
- newParent.addView(host);
- LinearLayout.LayoutParams layoutParams = (LayoutParams) host.getLayoutParams();
- layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.weight = horizontal ? 1.5f : 0;
- layoutParams.bottomMargin = mMediaBottomMargin;
- }
+ @Override
+ protected void addSecurityFooter() {
+ // No footer needed
}
@Override
- protected void addMediaHostView() {
- mMediaHost.setVisibleChangedListener((visible) -> {
- switchTileLayout();
- return null;
- });
+ protected void addViewsAboveTiles() {
+ // Nothing to add above the tiles
+ }
+
+ @Override
+ protected TileLayout createRegularTileLayout() {
+ return new QuickQSPanel.HeaderTileLayout(mContext, mUiEventLogger);
+ }
+
+ @Override
+ protected QSTileLayout createHorizontalTileLayout() {
+ return new DoubleLineTileLayout(mContext, mUiEventLogger);
+ }
+
+ @Override
+ protected void initMediaHostState() {
mMediaHost.setExpansion(0.0f);
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
- reAttachMediaHost();
- updateMediaHostContentMargins();
}
@Override
- protected void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
- if (mUsingMediaPlayer) {
- updateMargins((View) mRegularTileLayout, visualMarginStart, visualMarginEnd);
- updateMargins((View) mHorizontalTileLayout, visualMarginStart, 0);
- } else {
- updateMargins((View) mTileLayout, visualMarginStart, visualMarginEnd);
- }
+ protected boolean needsDynamicRowsAndColumns() {
+ return false; // QQS always have the same layout
+ }
+
+ @Override
+ protected boolean displayMediaMarginsOnMedia() {
+ // Margins should be on the container to visually center the view
+ return false;
}
@Override
@@ -194,10 +128,6 @@
}
@Override
- protected void addDivider() {
- }
-
- @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(TunerService.class).addTunable(mNumTiles, NUM_QUICK_TILES);
@@ -237,60 +167,6 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (newConfig.orientation != mLastOrientation) {
- mLastOrientation = newConfig.orientation;
- switchTileLayout();
- }
- }
-
- boolean switchTileLayout() {
- if (!mUsingMediaPlayer) return false;
- mShowHorizontalTileLayout = shouldUseHorizontalTileLayout();
- if (mShowHorizontalTileLayout && mHorizontalLinearLayout.getVisibility() == View.GONE) {
- mHorizontalLinearLayout.setVisibility(View.VISIBLE);
- ((View) mRegularTileLayout).setVisibility(View.GONE);
- mTileLayout.setListening(false);
- for (TileRecord record : mRecords) {
- mTileLayout.removeTile(record);
- record.tile.removeCallback(record.callback);
- }
- mTileLayout = mHorizontalTileLayout;
- if (mHost != null) setTiles(mHost.getTiles());
- mTileLayout.setListening(mListening);
- reAttachMediaHost();
- return true;
- } else if (!mShowHorizontalTileLayout
- && mHorizontalLinearLayout.getVisibility() == View.VISIBLE) {
- mHorizontalLinearLayout.setVisibility(View.GONE);
- ((View) mRegularTileLayout).setVisibility(View.VISIBLE);
- mTileLayout.setListening(false);
- for (TileRecord record : mRecords) {
- mTileLayout.removeTile(record);
- record.tile.removeCallback(record.callback);
- }
- mTileLayout = mRegularTileLayout;
- if (mHost != null) setTiles(mHost.getTiles());
- mTileLayout.setListening(mListening);
- reAttachMediaHost();
- return true;
- }
- return false;
- }
-
- private boolean shouldUseHorizontalTileLayout() {
- return mMediaHost.getVisible()
- && getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- }
-
- /** Returns true if this panel currently uses a horizontal tile layout. */
- public boolean usesHorizontalLayout() {
- return mShowHorizontalTileLayout;
- }
-
- @Override
public void setHost(QSTileHost host, QSCustomizer customizer) {
super.setHost(host, customizer);
setTiles(mHost.getTiles());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 20e47b2..311eda2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -36,13 +36,13 @@
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Pair;
import android.view.ContextThemeWrapper;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -55,6 +55,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -149,6 +150,8 @@
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
+ private float mExpandedHeaderAlpha = 1.0f;
+ private float mKeyguardExpansionFraction;
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -356,7 +359,7 @@
private void updateHeaderTextContainerAlphaAnimator() {
mHeaderTextContainerAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mHeaderTextContainerView, "alpha", 0, 0, 1)
+ .addFloat(mHeaderTextContainerView, "alpha", 0, 0, mExpandedHeaderAlpha)
.build();
}
@@ -403,6 +406,7 @@
updateResources();
}
}
+ mKeyguardExpansionFraction = keyguardExpansionFraction;
}
public void disable(int state1, int state2, boolean animate) {
@@ -596,4 +600,22 @@
}
updateClockPadding();
}
+
+ public void setExpandedScrollAmount(int scrollY) {
+ // The scrolling of the expanded qs has changed. Since the header text isn't part of it,
+ // but would overlap content, we're fading it out.
+ float newAlpha = 1.0f;
+ if (mHeaderTextContainerView.getHeight() > 0) {
+ newAlpha = MathUtils.map(0, mHeaderTextContainerView.getHeight() / 2.0f, 1.0f, 0.0f,
+ scrollY);
+ newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
+ }
+ mHeaderTextContainerView.setScrollY(scrollY);
+ if (newAlpha != mExpandedHeaderAlpha) {
+ mExpandedHeaderAlpha = newAlpha;
+ mHeaderTextContainerView.setAlpha(MathUtils.lerp(0.0f, mExpandedHeaderAlpha,
+ mKeyguardExpansionFraction));
+ updateHeaderTextContainerAlphaAnimator();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 383c29d..694492a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -17,6 +17,7 @@
public class TileLayout extends ViewGroup implements QSTileLayout {
+ public static final int NO_MAX_COLUMNS = 100;
private static final float TILE_ASPECT = 1.2f;
private static final String TAG = "TileLayout";
@@ -36,6 +37,9 @@
// Prototyping with less rows
private final boolean mLessRows;
+ private int mMinRows = 1;
+ private int mMaxColumns = NO_MAX_COLUMNS;
+ private int mResourceColumns;
public TileLayout(Context context) {
this(context, null);
@@ -64,6 +68,22 @@
}
}
+ @Override
+ public boolean setMinRows(int minRows) {
+ if (mMinRows != minRows) {
+ mMinRows = minRows;
+ updateResources();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean setMaxColumns(int maxColumns) {
+ mMaxColumns = maxColumns;
+ return updateColumns();
+ }
+
public void addTile(TileRecord tile) {
mRecords.add(tile);
tile.tile.setListening(this, mListening);
@@ -91,21 +111,26 @@
public boolean updateResources() {
final Resources res = mContext.getResources();
- final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
+ mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
- if (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1);
- if (mColumns != columns) {
- mColumns = columns;
+ if (mLessRows) mMaxAllowedRows = Math.max(mMinRows, mMaxAllowedRows - 1);
+ if (updateColumns()) {
requestLayout();
return true;
}
return false;
}
+ private boolean updateColumns() {
+ int oldColumns = mColumns;
+ mColumns = Math.min(mResourceColumns, mMaxColumns);
+ return oldColumns != mColumns;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
@@ -142,18 +167,19 @@
* Determines the maximum number of rows that can be shown based on height. Clips at a minimum
* of 1 and a maximum of mMaxAllowedRows.
*
- * @param heightMeasureSpec Available height.
+ * @param allowedHeight The height this view has visually available
* @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
*/
- public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
- final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop
+ public boolean updateMaxRows(int allowedHeight, int tilesCount) {
+ final int availableHeight = allowedHeight - mCellMarginTop
+ // Add the cell margin in order to divide easily by the height + the margin below
+ mCellMarginVertical;
final int previousRows = mRows;
mRows = availableHeight / (mCellHeight + mCellMarginVertical);
- if (mRows >= mMaxAllowedRows) {
+ if (mRows < mMinRows) {
+ mRows = mMinRows;
+ } else if (mRows >= mMaxAllowedRows) {
mRows = mMaxAllowedRows;
- } else if (mRows <= 1) {
- mRows = 1;
}
if (mRows > (tilesCount + mColumns - 1) / mColumns) {
mRows = (tilesCount + mColumns - 1) / mColumns;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 08be4f8..011ad19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -255,23 +255,11 @@
@Throws(InflationException::class)
private fun createPeopleAvatar(entry: NotificationEntry): Icon? {
- // Attempt to extract form shortcut.
- val conversationId = entry.ranking.channel.conversationId
- val query = LauncherApps.ShortcutQuery()
- .setPackage(entry.sbn.packageName)
- .setQueryFlags(
- LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
- or LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
- .setShortcutIds(listOf(conversationId))
- val shortcuts = launcherApps.getShortcuts(query, entry.sbn.user)
var ic: Icon? = null
- if (shortcuts != null && shortcuts.isNotEmpty()) {
- ic = shortcuts[0].icon
- }
- // Fall back to notification large icon if available
- if (ic == null) {
- ic = entry.sbn.notification.getLargeIcon()
+ val shortcut = entry.ranking.shortcutInfo
+ if (shortcut != null) {
+ ic = launcherApps.getShortcutIcon(shortcut)
}
// Fall back to extract from message
@@ -290,6 +278,11 @@
}
}
+ // Fall back to notification large icon if available
+ if (ic == null) {
+ ic = entry.sbn.notification.getLargeIcon()
+ }
+
// Revert to small icon if still not available
if (ic == null) {
ic = entry.sbn.notification.smallIcon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index d884bdd..9e7bf62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3071,7 +3071,7 @@
return new TouchHandler() {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) {
+ if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
return false;
}
initDownStates(event);
@@ -3098,7 +3098,8 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
- if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
+ if (mBlockTouches || (mQsFullyExpanded && mQs != null
+ && mQs.disallowPanelTouches())) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt
new file mode 100644
index 0000000..bab9347
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+/**
+ * Basically a normal linear layout but doesn't grow its children with weight 1 even when its
+ * measured with exactly.
+ */
+class NeverExactlyLinearLayout @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+
+ val (widthExactly, usedWidthSpec, width) = getNonExactlyMeasureSpec(widthMeasureSpec)
+ val (heightExactly, usedHeightSpec, height) = getNonExactlyMeasureSpec(heightMeasureSpec)
+
+ super.onMeasure(usedWidthSpec, usedHeightSpec)
+ if (widthExactly || heightExactly) {
+ val newWidth = if (widthExactly) width else measuredWidth
+ val newHeight = if (heightExactly) height else measuredHeight
+ setMeasuredDimension(newWidth, newHeight)
+ }
+ }
+
+ /**
+ * Obtain a measurespec that's not exactly
+ *
+ * @return a triple, where we return 1. if this was exactly, 2. the new measurespec, 3. the size
+ * of the measurespec
+ */
+ private fun getNonExactlyMeasureSpec(measureSpec: Int): Triple<Boolean, Int, Int> {
+ var newSpec = measureSpec
+ val isExactly = MeasureSpec.getMode(measureSpec) == MeasureSpec.EXACTLY
+ val size = MeasureSpec.getSize(measureSpec)
+ if (isExactly) {
+ newSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST)
+ }
+ return Triple(isExactly, newSpec, size)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 7b11452..32e3a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -19,13 +19,11 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
@@ -38,7 +36,6 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
-import com.android.internal.view.IInputMethodManager;
import com.android.systemui.TransactionPool;
import com.android.systemui.dagger.qualifiers.Main;
@@ -355,16 +352,6 @@
dispatchEndPositioning(mDisplayId, mCancelled, t);
if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
t.hide(mImeSourceControl.getLeash());
- final IInputMethodManager imms = getImms();
- if (imms != null) {
- try {
- // Remove the IME surface to make the insets invisible for
- // non-client controlled insets.
- imms.removeImeSurface();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to remove IME surface.", e);
- }
- }
}
t.apply();
mTransactionPool.release(t);
@@ -395,9 +382,9 @@
* Called when the IME position is starting to animate.
*
* @param hiddenTop The y position of the top of the IME surface when it is hidden.
- * @param shownTop The y position of the top of the IME surface when it is shown.
- * @param showing {@code true} when we are animating from hidden to shown, {@code false}
- * when animating from shown to hidden.
+ * @param shownTop The y position of the top of the IME surface when it is shown.
+ * @param showing {@code true} when we are animating from hidden to shown, {@code false}
+ * when animating from shown to hidden.
*/
default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean showing, SurfaceControl.Transaction t) {}
@@ -419,9 +406,4 @@
default void onImeEndPositioning(int displayId, boolean cancel,
SurfaceControl.Transaction t) {}
}
-
- public IInputMethodManager getImms() {
- return IInputMethodManager.Stub.asInterface(
- ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 05b31c8..cbb0711 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -51,6 +51,7 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
import org.junit.Test;
@@ -108,12 +109,14 @@
mDependency.injectMockDependency(SecurityController.class);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
+ when(mMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(getContext()));
mUiEventLogger = new UiEventLoggerFake();
mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
mQSLogger, mMediaHost, mUiEventLogger);
+ mQsPanel.onFinishInflate();
// Provides a parent with non-zero size for QSPanel
mParentView = new FrameLayout(mContext);
mParentView.addView(mQsPanel);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
new file mode 100644
index 0000000..b63e66f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.Person
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.os.SystemClock
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner;
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+
+import org.junit.runner.RunWith;
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyInt
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class IconManagerTest: SysuiTestCase() {
+ companion object {
+ private const val TEST_PACKAGE_NAME = "test";
+ private const val TEST_UID = 0;
+ }
+
+
+ private var id = 0
+ private val context = InstrumentationRegistry.getTargetContext();
+ @Mock private lateinit var shortcut: ShortcutInfo
+ @Mock private lateinit var shortcutIc: Icon
+ @Mock private lateinit var messageIc: Icon
+ @Mock private lateinit var largeIc: Icon
+ @Mock private lateinit var smallIc: Icon
+ @Mock private lateinit var drawable: Drawable
+ @Mock private lateinit var row: ExpandableNotificationRow
+
+ @Mock private lateinit var notifCollection: CommonNotifCollection
+ @Mock private lateinit var launcherApps: LauncherApps
+
+ private val iconBuilder = IconBuilder(context)
+
+ private lateinit var iconManager: IconManager
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(shortcutIc.loadDrawableAsUser(any(), anyInt())).thenReturn(drawable)
+ `when`(messageIc.loadDrawableAsUser(any(), anyInt())).thenReturn(drawable)
+ `when`(largeIc.loadDrawableAsUser(any(), anyInt())).thenReturn(drawable)
+ `when`(smallIc.loadDrawableAsUser(any(), anyInt())).thenReturn(drawable)
+
+ `when`(shortcut.icon).thenReturn(shortcutIc)
+ `when`(launcherApps.getShortcutIcon(shortcut)).thenReturn(shortcutIc)
+
+ iconManager = IconManager(notifCollection, launcherApps, iconBuilder)
+ }
+
+ @Test
+ fun testCreateIcons_importantConversation_shortcutIcon() {
+ val entry = notificationEntry(true, true, true)
+ entry?.channel?.isImportantConversation = true
+ entry?.let {
+ iconManager.createIcons(it)
+ }
+ assertEquals(entry?.icons?.statusBarIcon?.sourceIcon, shortcutIc)
+ }
+
+ @Test
+ fun testCreateIcons_importantConversation_messageIcon() {
+ val entry = notificationEntry(false, true, true)
+ entry?.channel?.isImportantConversation = true
+ entry?.let {
+ iconManager.createIcons(it)
+ }
+ assertEquals(entry?.icons?.statusBarIcon?.sourceIcon, messageIc)
+ }
+
+ @Test
+ fun testCreateIcons_importantConversation_largeIcon() {
+ val entry = notificationEntry(false, false, true)
+ entry?.channel?.isImportantConversation = true
+ entry?.let {
+ iconManager.createIcons(it)
+ }
+ assertEquals(entry?.icons?.statusBarIcon?.sourceIcon, largeIc)
+ }
+
+ @Test
+ fun testCreateIcons_importantConversation_smallIcon() {
+ val entry = notificationEntry(false, false, false)
+ entry?.channel?.isImportantConversation = true
+ entry?.let {
+ iconManager.createIcons(it)
+ }
+ assertEquals(entry?.icons?.statusBarIcon?.sourceIcon, smallIc)
+ }
+
+ @Test
+ fun testCreateIcons_notImportantConversation() {
+ val entry = notificationEntry(true, true, true)
+ entry?.let {
+ iconManager.createIcons(it)
+ }
+ assertEquals(entry?.icons?.statusBarIcon?.sourceIcon, smallIc)
+ }
+
+ @Test
+ fun testCreateIcons_sensitiveImportantConversation() {
+ val entry = notificationEntry(true, false, false)
+ entry?.setSensitive(true, true);
+ entry?.channel?.isImportantConversation = true
+ entry?.let {
+ iconManager.createIcons(it)
+ }
+ assertEquals(entry?.icons?.statusBarIcon?.sourceIcon, shortcutIc)
+ assertEquals(entry?.icons?.shelfIcon?.sourceIcon, smallIc)
+ assertEquals(entry?.icons?.aodIcon?.sourceIcon, smallIc)
+ }
+
+ @Test
+ fun testUpdateIcons_sensitivityChange() {
+ val entry = notificationEntry(true, false, false)
+ entry?.channel?.isImportantConversation = true
+ entry?.setSensitive(true, true);
+ entry?.let {
+ iconManager.createIcons(it)
+ }
+ assertEquals(entry?.icons?.aodIcon?.sourceIcon, smallIc)
+ entry?.setSensitive(false, false);
+ entry?.let {
+ iconManager.updateIcons(it)
+ }
+ assertEquals(entry?.icons?.shelfIcon?.sourceIcon, shortcutIc)
+ }
+
+ private fun notificationEntry(
+ hasShortcut: Boolean,
+ hasMessage: Boolean,
+ hasLargeIcon: Boolean
+ ): NotificationEntry? {
+ val n = Notification.Builder(mContext, "id")
+ .setSmallIcon(smallIc)
+ .setContentTitle("Title")
+ .setContentText("Text")
+
+ if (hasMessage) {
+ n.style = Notification.MessagingStyle("")
+ .addMessage(Notification.MessagingStyle.Message(
+ "",
+ SystemClock.currentThreadTimeMillis(),
+ Person.Builder().setIcon(messageIc).build()
+ ))
+ }
+
+ if (hasLargeIcon) {
+ n.setLargeIcon(largeIc)
+ }
+
+ val builder = NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setId(id++)
+ .setNotification(n.build())
+ .setChannel(NotificationChannel("id", "", IMPORTANCE_DEFAULT))
+ .setUser(UserHandle(ActivityManager.getCurrentUser()))
+
+ if (hasShortcut) {
+ builder.setShortcutInfo(shortcut)
+ }
+
+ val entry = builder.build()
+ entry.row = row
+ entry.setSensitive(false, true);
+ return entry
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b647a1a..65a1301 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -207,7 +207,6 @@
static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
static final int MSG_INITIALIZE_IME = 1040;
static final int MSG_CREATE_SESSION = 1050;
- static final int MSG_REMOVE_IME_SURFACE = 1060;
static final int MSG_START_INPUT = 2000;
@@ -3947,12 +3946,6 @@
}
}
- @Override
- public void removeImeSurface() {
- mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
- }
-
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -4223,15 +4216,6 @@
args.recycle();
return true;
}
- case MSG_REMOVE_IME_SURFACE: {
- try {
- if (mEnabledSession != null && mEnabledSession.session != null) {
- mEnabledSession.session.removeImeSurface();
- }
- } catch (RemoteException e) {
- }
- return true;
- }
// ---------------------------------------------------------
case MSG_START_INPUT: {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index d025b0f..6c415ca 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1347,13 +1347,14 @@
for (WindowInfo windowInfo : clientInfo.mWindowMap.values()) {
if (windowInfo.mWindowHandle == targetWindowHandle) {
final IBinder targetWindowToken = windowInfo.mWindowToken;
- // TODO(yukawa): Report targetWindowToken and targetWindowToken to WMS.
if (DEBUG) {
Slog.v(TAG, "reportImeWindowTarget"
+ " clientId=" + clientId
+ " imeWindowToken=" + imeWindowToken
+ " targetWindowToken=" + targetWindowToken);
}
+ mIWindowManagerInternal.updateInputMethodTargetWindow(
+ imeWindowToken, targetWindowToken);
}
}
// not found.
@@ -1462,12 +1463,6 @@
@BinderThread
@Override
- public void removeImeSurface() {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
public boolean showSoftInput(
IInputMethodClient client, IBinder token, int flags,
ResultReceiver resultReceiver) {
@@ -1496,6 +1491,9 @@
case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
try {
clientInfo.mMSInputMethodSession.showSoftInput(flags, resultReceiver);
+
+ // Forcing WM to show IME on imeTargetWindow
+ mWindowManagerInternal.showImePostLayout(token);
} catch (RemoteException e) {
}
break;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a85bd06..53aad23 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2829,14 +2829,12 @@
/**
* A list of 4 customized LTE Reference Signal Signal to Noise Ratio (RSSNR) thresholds.
*
- * 4 threshold integers must be within the boundaries [-200, 300], and the levels are:
- * "NONE: [-200, threshold1)"
+ * 4 threshold integers must be within the boundaries [-20 dB, 30 dB], and the levels are:
+ * "NONE: [-20, threshold1)"
* "POOR: [threshold1, threshold2)"
* "MODERATE: [threshold2, threshold3)"
* "GOOD: [threshold3, threshold4)"
- * "EXCELLENT: [threshold4, 300]"
- * Note: the unit of the values is 10*db; it is derived by multiplying 10 on the original dB
- * value reported by modem.
+ * "EXCELLENT: [threshold4, 30]"
*
* This key is considered invalid if the format is violated. If the key is invalid or
* not configured, a default value set will apply.
@@ -4198,10 +4196,10 @@
});
sDefaults.putIntArray(KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
new int[] {
- -30, /* SIGNAL_STRENGTH_POOR */
- 10, /* SIGNAL_STRENGTH_MODERATE */
- 45, /* SIGNAL_STRENGTH_GOOD */
- 130 /* SIGNAL_STRENGTH_GREAT */
+ -3, /* SIGNAL_STRENGTH_POOR */
+ 1, /* SIGNAL_STRENGTH_MODERATE */
+ 5, /* SIGNAL_STRENGTH_GOOD */
+ 13 /* SIGNAL_STRENGTH_GREAT */
});
sDefaults.putIntArray(KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
new int[] {
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 2529387..c26936e 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -118,7 +118,7 @@
* @param rssi in dBm [-113,-51], UNKNOWN
* @param rsrp in dBm [-140,-43], UNKNOWN
* @param rsrq in dB [-34, 3], UNKNOWN
- * @param rssnr in 10*dB [-200, +300], UNKNOWN
+ * @param rssnr in dB [-20, +30], UNKNOWN
* @param cqi [0, 15], UNKNOWN
* @param timingAdvance [0, 1282], UNKNOWN
*
@@ -131,7 +131,7 @@
mSignalStrength = mRssi;
mRsrp = inRangeOrUnavailable(rsrp, -140, -43);
mRsrq = inRangeOrUnavailable(rsrq, -34, 3);
- mRssnr = inRangeOrUnavailable(rssnr, -200, 300);
+ mRssnr = inRangeOrUnavailable(rssnr, -20, 30);
mCqi = inRangeOrUnavailable(cqi, 0, 15);
mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
updateLevel(null, null);
@@ -143,7 +143,7 @@
this(convertRssiAsuToDBm(lte.signalStrength),
lte.rsrp != CellInfo.UNAVAILABLE ? -lte.rsrp : lte.rsrp,
lte.rsrq != CellInfo.UNAVAILABLE ? -lte.rsrq : lte.rsrq,
- lte.rssnr, lte.cqi, lte.timingAdvance);
+ convertRssnrUnitFromTenDbToDB(lte.rssnr), lte.cqi, lte.timingAdvance);
}
/** @hide */
@@ -208,10 +208,10 @@
};
// Lifted from Default carrier configs and max range of RSSNR
private static final int[] sRssnrThresholds = new int[] {
- -30, /* SIGNAL_STRENGTH_POOR */
- 10, /* SIGNAL_STRENGTH_MODERATE */
- 45, /* SIGNAL_STRENGTH_GOOD */
- 130 /* SIGNAL_STRENGTH_GREAT */
+ -3, /* SIGNAL_STRENGTH_POOR */
+ 1, /* SIGNAL_STRENGTH_MODERATE */
+ 5, /* SIGNAL_STRENGTH_GOOD */
+ 13 /* SIGNAL_STRENGTH_GREAT */
};
private static final int sRsrpBoost = 0;
@@ -556,6 +556,10 @@
Rlog.w(LOG_TAG, s);
}
+ private static int convertRssnrUnitFromTenDbToDB(int rssnr) {
+ return rssnr / 10;
+ }
+
private static int convertRssiAsuToDBm(int rssiAsu) {
if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
return CellInfo.UNAVAILABLE;
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 4273f5a..af62ba4 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -19,8 +19,8 @@
import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
-
import android.telephony.Annotation.NetworkType;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
@@ -241,13 +241,13 @@
.append(",mCellBandwidthDownlinkKhz=")
.append(mCellBandwidthDownlinkKhz)
.append(",mRat=")
- .append(mRat)
+ .append(TelephonyManager.getNetworkTypeName(mRat))
.append(",mFrequencyRange=")
- .append(mFrequencyRange)
+ .append(ServiceState.frequencyRangeToString(mFrequencyRange))
.append(",mChannelNumber=")
.append(mChannelNumber)
.append(",mContextIds=")
- .append(mContextIds.toString())
+ .append(Arrays.toString(mContextIds))
.append(",mPhysicalCellId=")
.append(mPhysicalCellId)
.append("}")
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index c6b06b4..9e2ba68 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1033,6 +1033,26 @@
}
/**
+ * Convert frequency range into string
+ *
+ * @param range The cellular frequency range
+ * @return Frequency range in string format
+ *
+ * @hide
+ */
+ public static @NonNull String frequencyRangeToString(@FrequencyRange int range) {
+ switch (range) {
+ case FREQUENCY_RANGE_UNKNOWN: return "UNKNOWN";
+ case FREQUENCY_RANGE_LOW: return "LOW";
+ case FREQUENCY_RANGE_MID: return "MID";
+ case FREQUENCY_RANGE_HIGH: return "HIGH";
+ case FREQUENCY_RANGE_MMWAVE: return "MMWAVE";
+ default:
+ return Integer.toString(range);
+ }
+ }
+
+ /**
* Convert RIL Service State to String
*
* @param serviceState
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fadebaa..ee14608 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8197,6 +8197,140 @@
return false;
}
+ /** @hide */
+ @IntDef({
+ ALLOWED_NETWORK_TYPES_REASON_POWER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AllowedNetworkTypesReason{}
+
+ /**
+ * To indicate allowed network type change is requested by power manager.
+ * Power Manger configuration won't affect the settings configured through
+ * {@link setAllowedNetworkTypes} and will result in allowing network types that are in both
+ * configurations (i.e intersection of both sets).
+ * @hide
+ */
+ public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 0;
+
+ /**
+ * Set the allowed network types of the device and
+ * provide the reason triggering the allowed network change.
+ * This can be called for following reasons
+ * <ol>
+ * <li>Allowed network types control by power manager
+ * {@link #ALLOWED_NETWORK_TYPES_REASON_POWER}
+ * </ol>
+ * This API will result in allowing an intersection of allowed network types for all reasons,
+ * including the configuration done through {@link setAllowedNetworkTypes}.
+ * While this API and {@link setAllowedNetworkTypes} is controlling allowed network types
+ * on device, user preference will still be set through {@link #setPreferredNetworkTypeBitmask}.
+ * Thus resultant network type configured on modem will be an intersection of the network types
+ * from setAllowedNetworkTypesForReason, {@link setAllowedNetworkTypes}
+ * and {@link #setPreferredNetworkTypeBitmask}.
+ *
+ * @param reason the reason the allowed network type change is taking place
+ * @param allowedNetworkTypes The bitmask of allowed network types.
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason,
+ @NetworkTypeBitMask long allowedNetworkTypes) {
+ if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) {
+ throw new IllegalArgumentException("invalid AllowedNetworkTypesReason.");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setAllowedNetworkTypesForReason(getSubId(), reason,
+ allowedNetworkTypes);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setAllowedNetworkTypesForReason RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the allowed network types for certain reason.
+ *
+ * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
+ * specific reason. For effective allowed network types configured on device,
+ * query {@link getEffectiveAllowedNetworkTypes}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *s
+ * @param reason the reason the allowed network type change is taking place
+ * @return the allowed network type bitmask
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
+ @AllowedNetworkTypesReason int reason) {
+ if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) {
+ throw new IllegalArgumentException("invalid AllowedNetworkTypesReason.");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getAllowedNetworkTypesForReason(getSubId(), reason);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getAllowedNetworkTypesForReason RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ return -1;
+ }
+
+ /**
+ * Get bit mask of all network types.
+ *
+ * @return bit mask of all network types
+ * @hide
+ */
+ public static @NetworkTypeBitMask long getAllNetworkTypesBitmask() {
+ return NETWORK_STANDARDS_FAMILY_BITMASK_3GPP | NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2;
+ }
+
+ /**
+ * Get the allowed network types configured on the device.
+ * This API will return an intersection of allowed network types for all reasons,
+ * including the configuration done through setAllowedNetworkTypes
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @return the allowed network type bitmask
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @NetworkTypeBitMask long getEffectiveAllowedNetworkTypes() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getEffectiveAllowedNetworkTypes(getSubId());
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getEffectiveAllowedNetworkTypes RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ return -1;
+ }
+
/**
* Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
*
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3690200..b70937c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -956,6 +956,35 @@
boolean setAllowedNetworkTypes(int subId, long allowedNetworkTypes);
/**
+ * Get the allowed network types for certain reason.
+ *
+ * @param subId the id of the subscription.
+ * @param reason the reason the allowed network type change is taking place
+ * @return allowedNetworkTypes the allowed network types.
+ */
+ long getAllowedNetworkTypesForReason(int subId, int reason);
+
+ /**
+ * Get the effective allowed network types on the device. This API will
+ * return an intersection of allowed network types for all reasons,
+ * including the configuration done through setAllowedNetworkTypes
+ *
+ * @param subId the id of the subscription.
+ * @return allowedNetworkTypes the allowed network types.
+ */
+ long getEffectiveAllowedNetworkTypes(int subId);
+
+ /**
+ * Set the allowed network types and provide the reason triggering the allowed network change.
+ *
+ * @param subId the id of the subscription.
+ * @param reason the reason the allowed network type change is taking place
+ * @param allowedNetworkTypes the allowed network types.
+ * @return true on success; false on any failure.
+ */
+ boolean setAllowedNetworkTypesForReason(int subId, int reason, long allowedNetworkTypes);
+
+ /**
* Set the preferred network type.
* Used for device configuration by some CDMA operators.
*