summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/test-current.txt124
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h4
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp40
-rw-r--r--core/java/android/hardware/hdmi/HdmiClient.java4
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java27
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java469
-rw-r--r--core/java/android/hardware/hdmi/HdmiPortInfo.java9
-rw-r--r--core/java/android/hardware/hdmi/HdmiSwitchClient.java5
-rw-r--r--core/java/android/inputmethodservice/AbstractInputMethodService.java6
-rw-r--r--core/java/android/view/GestureDetector.java26
-rw-r--r--core/res/res/layout/notification_material_action_list.xml3
-rw-r--r--core/res/res/values/dimens.xml6
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java9
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/drawable/ic_create_bubble.xml8
-rw-r--r--packages/SystemUI/res/drawable/ic_stop_bubble.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeRepeatableExecutor.java34
37 files changed, 1179 insertions, 113 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index ed4c9b13dacd..66b5015902f0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1298,6 +1298,130 @@ package android.hardware.display {
}
+package android.hardware.hdmi {
+
+ public final class HdmiControlManager {
+ method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
+ method @RequiresPermission("android.permission.HDMI_CEC") public void setStandbyMode(boolean);
+ field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
+ field public static final int AVR_VOLUME_MUTED = 101; // 0x65
+ field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
+ field public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 160; // 0xa0
+ field public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 161; // 0xa1
+ field public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 128; // 0x80
+ field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 2; // 0x2
+ field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 1; // 0x1
+ field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0; // 0x0
+ field public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; // 0x1
+ field public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; // 0x3
+ field public static final int CONTROL_STATE_CHANGED_REASON_START = 0; // 0x0
+ field public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; // 0x2
+ field public static final int DEVICE_EVENT_ADD_DEVICE = 1; // 0x1
+ field public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; // 0x2
+ field public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; // 0x3
+ field public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
+ field public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
+ field public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 18; // 0x12
+ field public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 51; // 0x33
+ field public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 49; // 0x31
+ field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 13; // 0xd
+ field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 14; // 0xe
+ field public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 50; // 0x32
+ field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 10; // 0xa
+ field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 9; // 0x9
+ field public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 21; // 0x15
+ field public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 19; // 0x13
+ field public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 22; // 0x16
+ field public static final int ONE_TOUCH_RECORD_NO_MEDIA = 16; // 0x10
+ field public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 12; // 0xc
+ field public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 20; // 0x14
+ field public static final int ONE_TOUCH_RECORD_OTHER_REASON = 31; // 0x1f
+ field public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 23; // 0x17
+ field public static final int ONE_TOUCH_RECORD_PLAYING = 17; // 0x11
+ field public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 48; // 0x30
+ field public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 27; // 0x1b
+ field public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 3; // 0x3
+ field public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 1; // 0x1
+ field public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 2; // 0x2
+ field public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 4; // 0x4
+ field public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 26; // 0x1a
+ field public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 6; // 0x6
+ field public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 5; // 0x5
+ field public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 7; // 0x7
+ field public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 11; // 0xb
+ field public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1; // 0x1
+ field public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2; // 0x2
+ field public static final int POWER_STATUS_ON = 0; // 0x0
+ field public static final int POWER_STATUS_STANDBY = 1; // 0x1
+ field public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; // 0x2
+ field public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; // 0x3
+ field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
+ field @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
+ field public static final int RESULT_COMMUNICATION_FAILED = 7; // 0x7
+ field public static final int RESULT_EXCEPTION = 5; // 0x5
+ field public static final int RESULT_INCORRECT_MODE = 6; // 0x6
+ field public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; // 0x2
+ field public static final int RESULT_SUCCESS = 0; // 0x0
+ field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3
+ field public static final int RESULT_TIMEOUT = 1; // 0x1
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 1; // 0x1
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 2; // 0x2
+ field public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0; // 0x0
+ field public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2; // 0x2
+ field public static final int TIMER_RECORDING_TYPE_DIGITAL = 1; // 0x1
+ field public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3; // 0x3
+ field public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 2; // 0x2
+ field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0; // 0x0
+ field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 1; // 0x1
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 6; // 0x6
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 10; // 0xa
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 2; // 0x2
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 14; // 0xe
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 5; // 0x5
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 4; // 0x4
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 3; // 0x3
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 7; // 0x7
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 1; // 0x1
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON = 9; // 0x9
+ field public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 8; // 0x8
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 8; // 0x8
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 11; // 0xb
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 9; // 0x9
+ field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
+ }
+
+ public final class HdmiControlServiceWrapper {
+ ctor public HdmiControlServiceWrapper();
+ method @NonNull public android.hardware.hdmi.HdmiControlManager createHdmiControlManager();
+ method @BinderThread public void setDeviceTypes(@NonNull int[]);
+ method @BinderThread public void setPortInfo(@NonNull java.util.List<android.hardware.hdmi.HdmiPortInfo>);
+ field public static final int DEVICE_PURE_CEC_SWITCH = 6; // 0x6
+ }
+
+ public final class HdmiPortInfo implements android.os.Parcelable {
+ ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean);
+ method public int describeContents();
+ method public int getAddress();
+ method public int getId();
+ method public int getType();
+ method public boolean isArcSupported();
+ method public boolean isCecSupported();
+ method public boolean isMhlSupported();
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.hdmi.HdmiPortInfo> CREATOR;
+ field public static final int PORT_INPUT = 0; // 0x0
+ field public static final int PORT_OUTPUT = 1; // 0x1
+ }
+
+ public class HdmiSwitchClient {
+ method public int getDeviceType();
+ method @NonNull public java.util.List<android.hardware.hdmi.HdmiPortInfo> getPortInfo();
+ method public void sendKeyEvent(int, boolean);
+ method public void sendVendorCommand(int, byte[], boolean);
+ }
+
+}
+
package android.hardware.lights {
public final class Light implements android.os.Parcelable {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 3de5b99a2b09..505b23921806 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -75,7 +75,7 @@ public:
if (!mSplitBucketForAppUpgrade) {
return;
}
- if (mIsPulled && mCondition) {
+ if (mIsPulled && mCondition == ConditionState::kTrue) {
pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
@@ -84,7 +84,7 @@ public:
// ValueMetric needs special logic if it's a pulled atom.
void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
- if (mIsPulled && mCondition) {
+ if (mIsPulled && mCondition == ConditionState::kTrue) {
pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1bcc35d99d18..58a3259bbf2c 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -4722,6 +4722,46 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
}
+/*
+ * Test bucket splits when condition is unknown.
+ */
+TEST(ValueMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric,
+ ConditionState::kUnknown);
+
+ // App update event.
+ int64_t appUpdateTimeNs = bucketStartTimeNs + 1000;
+ valueProducer->notifyAppUpgrade(appUpdateTimeNs);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
+ valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index bff8c39e8c56..a921215c6133 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -1,6 +1,7 @@
package android.hardware.hdmi;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
import android.os.RemoteException;
@@ -84,7 +85,8 @@ public abstract class HdmiClient {
* @param hasVendorId {@code true} if the command type will be &lt;Vendor Command With ID&gt;.
* {@code false} if the command will be &lt;Vendor Command&gt;
*/
- public void sendVendorCommand(int targetAddress, byte[] params, boolean hasVendorId) {
+ public void sendVendorCommand(int targetAddress,
+ @SuppressLint("MissingNullability") byte[] params, boolean hasVendorId) {
try {
mService.sendVendorCommand(getDeviceType(), targetAddress, params, hasVendorId);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 208406566e52..1ce9b9c71c0e 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -29,6 +29,7 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -57,6 +58,7 @@ import java.util.concurrent.Executor;
* @hide
*/
@SystemApi
+@TestApi
@SystemService(Context.HDMI_CONTROL_SERVICE)
@RequiresFeature(PackageManager.FEATURE_HDMI_CEC)
public final class HdmiControlManager {
@@ -139,6 +141,8 @@ public final class HdmiControlManager {
public static final int POWER_STATUS_TRANSIENT_TO_ON = 2;
public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3;
+ /** @hide */
+ @SystemApi
@IntDef ({
RESULT_SUCCESS,
RESULT_TIMEOUT,
@@ -400,8 +404,11 @@ public final class HdmiControlManager {
* See {@link HdmiDeviceInfo#DEVICE_PLAYBACK}
* See {@link HdmiDeviceInfo#DEVICE_TV}
* See {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}
+ *
+ * @hide
*/
@Nullable
+ @SystemApi
@SuppressLint("Doclava125")
public HdmiClient getClient(int type) {
if (mService == null) {
@@ -430,8 +437,11 @@ public final class HdmiControlManager {
* system if the system is configured to host more than one type of HDMI-CEC logical devices.
*
* @return {@link HdmiPlaybackClient} instance. {@code null} on failure.
+ *
+ * @hide
*/
@Nullable
+ @SystemApi
@SuppressLint("Doclava125")
public HdmiPlaybackClient getPlaybackClient() {
return (HdmiPlaybackClient) getClient(HdmiDeviceInfo.DEVICE_PLAYBACK);
@@ -445,8 +455,11 @@ public final class HdmiControlManager {
* system if the system is configured to host more than one type of HDMI-CEC logical devices.
*
* @return {@link HdmiTvClient} instance. {@code null} on failure.
+ *
+ * @hide
*/
@Nullable
+ @SystemApi
@SuppressLint("Doclava125")
public HdmiTvClient getTvClient() {
return (HdmiTvClient) getClient(HdmiDeviceInfo.DEVICE_TV);
@@ -478,10 +491,8 @@ public final class HdmiControlManager {
* system if the system is configured to host more than one type of HDMI-CEC logical device.
*
* @return {@link HdmiSwitchClient} instance. {@code null} on failure.
- * @hide
*/
@Nullable
- @SystemApi
@SuppressLint("Doclava125")
public HdmiSwitchClient getSwitchClient() {
return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
@@ -790,7 +801,10 @@ public final class HdmiControlManager {
/**
* Listener used to get hotplug event from HDMI port.
+ *
+ * @hide
*/
+ @SystemApi
public interface HotplugEventListener {
void onReceived(HdmiHotplugEvent event);
}
@@ -840,7 +854,10 @@ public final class HdmiControlManager {
/**
* Listener used to get vendor-specific commands.
+ *
+ * @hide
*/
+ @SystemApi
public interface VendorCommandListener {
/**
* Called when a vendor command is received.
@@ -879,7 +896,10 @@ public final class HdmiControlManager {
*
* @param listener {@link HotplugEventListener} instance
* @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
+ *
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void addHotplugEventListener(HotplugEventListener listener) {
if (mService == null) {
@@ -903,7 +923,10 @@ public final class HdmiControlManager {
* Removes a listener to stop getting informed of {@link HdmiHotplugEvent}.
*
* @param listener {@link HotplugEventListener} instance to be removed
+ *
+ * @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void removeHotplugEventListener(HotplugEventListener listener) {
if (mService == null) {
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
new file mode 100644
index 000000000000..02896351ea3a
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright 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 android.hardware.hdmi;
+
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+
+import java.util.List;
+
+/**
+ * A wrapper of the Binder interface that clients running in the application process
+ * will use to perform HDMI-CEC features by communicating with other devices
+ * on the bus.
+ *
+ * @hide
+ */
+@TestApi
+public final class HdmiControlServiceWrapper {
+
+ /** Pure CEC switch device type. */
+ public static final int DEVICE_PURE_CEC_SWITCH = HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
+
+ private List<HdmiPortInfo> mInfoList = null;
+ private int[] mTypes = null;
+
+ /**
+ * Create a new HdmiControlManager with the current HdmiControlService wrapper
+ *
+ * @return the created HdmiControlManager
+ */
+ @NonNull
+ public HdmiControlManager createHdmiControlManager() {
+ return new HdmiControlManager(mInterface);
+ }
+
+ private final IHdmiControlService mInterface = new IHdmiControlService.Stub() {
+
+ @Override
+ public int[] getSupportedTypes() {
+ return HdmiControlServiceWrapper.this.getSupportedTypes();
+ }
+
+ @Override
+ public HdmiDeviceInfo getActiveSource() {
+ return HdmiControlServiceWrapper.this.getActiveSource();
+ }
+
+ @Override
+ public void oneTouchPlay(IHdmiControlCallback callback) {
+ HdmiControlServiceWrapper.this.oneTouchPlay(callback);
+ }
+
+ @Override
+ public void queryDisplayStatus(IHdmiControlCallback callback) {
+ HdmiControlServiceWrapper.this.queryDisplayStatus(callback);
+ }
+
+ @Override
+ public void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener) {
+ HdmiControlServiceWrapper.this.addHdmiControlStatusChangeListener(listener);
+ }
+
+ @Override
+ public void removeHdmiControlStatusChangeListener(
+ IHdmiControlStatusChangeListener listener) {
+ HdmiControlServiceWrapper.this.removeHdmiControlStatusChangeListener(listener);
+ }
+
+ @Override
+ public void addHotplugEventListener(IHdmiHotplugEventListener listener) {
+ HdmiControlServiceWrapper.this.addHotplugEventListener(listener);
+ }
+
+ @Override
+ public void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
+ HdmiControlServiceWrapper.this.removeHotplugEventListener(listener);
+ }
+
+ @Override
+ public void addDeviceEventListener(IHdmiDeviceEventListener listener) {
+ HdmiControlServiceWrapper.this.addDeviceEventListener(listener);
+ }
+
+ @Override
+ public void deviceSelect(int deviceId, IHdmiControlCallback callback) {
+ HdmiControlServiceWrapper.this.deviceSelect(deviceId, callback);
+ }
+
+ @Override
+ public void portSelect(int portId, IHdmiControlCallback callback) {
+ HdmiControlServiceWrapper.this.portSelect(portId, callback);
+ }
+
+ @Override
+ public void sendKeyEvent(int deviceType, int keyCode, boolean isPressed) {
+ HdmiControlServiceWrapper.this.sendKeyEvent(deviceType, keyCode, isPressed);
+ }
+
+ @Override
+ public void sendVolumeKeyEvent(int deviceType, int keyCode, boolean isPressed) {
+ HdmiControlServiceWrapper.this.sendVolumeKeyEvent(deviceType, keyCode, isPressed);
+ }
+
+ @Override
+ public List<HdmiPortInfo> getPortInfo() {
+ return HdmiControlServiceWrapper.this.getPortInfo();
+ }
+
+ @Override
+ public boolean canChangeSystemAudioMode() {
+ return HdmiControlServiceWrapper.this.canChangeSystemAudioMode();
+ }
+
+ @Override
+ public boolean getSystemAudioMode() {
+ return HdmiControlServiceWrapper.this.getSystemAudioMode();
+ }
+
+ @Override
+ public int getPhysicalAddress() {
+ return HdmiControlServiceWrapper.this.getPhysicalAddress();
+ }
+
+ @Override
+ public void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback) {
+ HdmiControlServiceWrapper.this.setSystemAudioMode(enabled, callback);
+ }
+
+ @Override
+ public void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
+ HdmiControlServiceWrapper.this.addSystemAudioModeChangeListener(listener);
+ }
+
+ @Override
+ public void removeSystemAudioModeChangeListener(
+ IHdmiSystemAudioModeChangeListener listener) {
+ HdmiControlServiceWrapper.this.removeSystemAudioModeChangeListener(listener);
+ }
+
+ @Override
+ public void setArcMode(boolean enabled) {
+ HdmiControlServiceWrapper.this.setArcMode(enabled);
+ }
+
+ @Override
+ public void setProhibitMode(boolean enabled) {
+ HdmiControlServiceWrapper.this.setProhibitMode(enabled);
+ }
+
+ @Override
+ public void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex) {
+ HdmiControlServiceWrapper.this.setSystemAudioVolume(oldIndex, newIndex, maxIndex);
+ }
+
+ @Override
+ public void setSystemAudioMute(boolean mute) {
+ HdmiControlServiceWrapper.this.setSystemAudioMute(mute);
+ }
+
+ @Override
+ public void setInputChangeListener(IHdmiInputChangeListener listener) {
+ HdmiControlServiceWrapper.this.setInputChangeListener(listener);
+ }
+
+ @Override
+ public List<HdmiDeviceInfo> getInputDevices() {
+ return HdmiControlServiceWrapper.this.getInputDevices();
+ }
+
+ @Override
+ public List<HdmiDeviceInfo> getDeviceList() {
+ return HdmiControlServiceWrapper.this.getDeviceList();
+ }
+
+ @Override
+ public void powerOffRemoteDevice(int logicalAddress, int powerStatus) {
+ HdmiControlServiceWrapper.this.powerOffRemoteDevice(logicalAddress, powerStatus);
+ }
+
+ @Override
+ public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {
+ HdmiControlServiceWrapper.this.powerOnRemoteDevice(logicalAddress, powerStatus);
+ }
+
+ @Override
+ public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
+ HdmiControlServiceWrapper.this.askRemoteDeviceToBecomeActiveSource(physicalAddress);
+ }
+
+ @Override
+ public void sendVendorCommand(int deviceType, int targetAddress, byte[] params,
+ boolean hasVendorId) {
+ HdmiControlServiceWrapper.this.sendVendorCommand(
+ deviceType, targetAddress, params, hasVendorId);
+ }
+
+ @Override
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
+ HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType);
+ }
+
+ @Override
+ public void sendStandby(int deviceType, int deviceId) {
+ HdmiControlServiceWrapper.this.sendStandby(deviceType, deviceId);
+ }
+
+ @Override
+ public void setHdmiRecordListener(IHdmiRecordListener callback) {
+ HdmiControlServiceWrapper.this.setHdmiRecordListener(callback);
+ }
+
+ @Override
+ public void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
+ HdmiControlServiceWrapper.this.startOneTouchRecord(recorderAddress, recordSource);
+ }
+
+ @Override
+ public void stopOneTouchRecord(int recorderAddress) {
+ HdmiControlServiceWrapper.this.stopOneTouchRecord(recorderAddress);
+ }
+
+ @Override
+ public void startTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {
+ HdmiControlServiceWrapper.this.startTimerRecording(
+ recorderAddress, sourceType, recordSource);
+ }
+
+ @Override
+ public void clearTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {
+ HdmiControlServiceWrapper.this.clearTimerRecording(
+ recorderAddress, sourceType, recordSource);
+ }
+
+ @Override
+ public void sendMhlVendorCommand(int portId, int offset, int length, byte[] data) {
+ HdmiControlServiceWrapper.this.sendMhlVendorCommand(portId, offset, length, data);
+ }
+
+ @Override
+ public void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
+ HdmiControlServiceWrapper.this.addHdmiMhlVendorCommandListener(listener);
+ }
+
+ @Override
+ public void setStandbyMode(boolean isStandbyModeOn) {
+ HdmiControlServiceWrapper.this.setStandbyMode(isStandbyModeOn);
+ }
+
+ @Override
+ public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
+ HdmiControlServiceWrapper.this.setHdmiCecVolumeControlEnabled(
+ isHdmiCecVolumeControlEnabled);
+ }
+
+ @Override
+ public boolean isHdmiCecVolumeControlEnabled() {
+ return HdmiControlServiceWrapper.this.isHdmiCecVolumeControlEnabled();
+ }
+
+ @Override
+ public void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute) {
+ HdmiControlServiceWrapper.this.reportAudioStatus(deviceType, volume, maxVolume, isMute);
+ }
+
+ @Override
+ public void setSystemAudioModeOnForAudioOnlySource() {
+ HdmiControlServiceWrapper.this.setSystemAudioModeOnForAudioOnlySource();
+ }
+
+ @Override
+ public void addHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ HdmiControlServiceWrapper.this.addHdmiCecVolumeControlFeatureListener(listener);
+ }
+
+ @Override
+ public void removeHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ HdmiControlServiceWrapper.this.removeHdmiCecVolumeControlFeatureListener(listener);
+ }
+ };
+
+ @BinderThread
+ public void setPortInfo(@NonNull List<HdmiPortInfo> infoList) {
+ mInfoList = infoList;
+ }
+
+ @BinderThread
+ public void setDeviceTypes(@NonNull int[] types) {
+ mTypes = types;
+ }
+
+ /** @hide */
+ public List<HdmiPortInfo> getPortInfo() {
+ return mInfoList;
+ }
+
+ /** @hide */
+ public int[] getSupportedTypes() {
+ return mTypes;
+ }
+
+ /** @hide */
+ public HdmiDeviceInfo getActiveSource() {
+ return null;
+ }
+
+ /** @hide */
+ public void oneTouchPlay(IHdmiControlCallback callback) {}
+
+ /** @hide */
+ public void queryDisplayStatus(IHdmiControlCallback callback) {}
+
+ /** @hide */
+ public void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener) {}
+
+ /** @hide */
+ public void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener) {}
+
+ /** @hide */
+ public void addHotplugEventListener(IHdmiHotplugEventListener listener) {}
+
+ /** @hide */
+ public void removeHotplugEventListener(IHdmiHotplugEventListener listener) {}
+
+ /** @hide */
+ public void addDeviceEventListener(IHdmiDeviceEventListener listener) {}
+
+ /** @hide */
+ public void deviceSelect(int deviceId, IHdmiControlCallback callback) {}
+
+ /** @hide */
+ public void portSelect(int portId, IHdmiControlCallback callback) {}
+
+ /** @hide */
+ public void sendKeyEvent(int deviceType, int keyCode, boolean isPressed) {}
+
+ /** @hide */
+ public void sendVolumeKeyEvent(int deviceType, int keyCode, boolean isPressed) {}
+
+ /** @hide */
+ public boolean canChangeSystemAudioMode() {
+ return true;
+ }
+
+ /** @hide */
+ public boolean getSystemAudioMode() {
+ return true;
+ }
+
+ /** @hide */
+ public int getPhysicalAddress() {
+ return 0xffff;
+ }
+
+ /** @hide */
+ public void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback) {}
+
+ /** @hide */
+ public void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {}
+
+ /** @hide */
+ public void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {}
+
+ /** @hide */
+ public void setArcMode(boolean enabled) {}
+
+ /** @hide */
+ public void setProhibitMode(boolean enabled) {}
+
+ /** @hide */
+ public void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex) {}
+
+ /** @hide */
+ public void setSystemAudioMute(boolean mute) {}
+
+ /** @hide */
+ public void setInputChangeListener(IHdmiInputChangeListener listener) {}
+
+ /** @hide */
+ public List<HdmiDeviceInfo> getInputDevices() {
+ return null;
+ }
+
+ /** @hide */
+ public List<HdmiDeviceInfo> getDeviceList() {
+ return null;
+ }
+
+ /** @hide */
+ public void powerOffRemoteDevice(int logicalAddress, int powerStatus) {}
+
+ /** @hide */
+ public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {}
+
+ /** @hide */
+ public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {}
+
+ /** @hide */
+ public void sendVendorCommand(int deviceType, int targetAddress, byte[] params,
+ boolean hasVendorId) {}
+
+ /** @hide */
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {}
+
+ /** @hide */
+ public void sendStandby(int deviceType, int deviceId) {}
+
+ /** @hide */
+ public void setHdmiRecordListener(IHdmiRecordListener callback) {}
+
+ /** @hide */
+ public void startOneTouchRecord(int recorderAddress, byte[] recordSource) {}
+
+ /** @hide */
+ public void stopOneTouchRecord(int recorderAddress) {}
+
+ /** @hide */
+ public void startTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {}
+
+ /** @hide */
+ public void clearTimerRecording(int recorderAddress, int sourceType, byte[] recordSource) {}
+
+ /** @hide */
+ public void sendMhlVendorCommand(int portId, int offset, int length, byte[] data) {}
+
+ /** @hide */
+ public void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {}
+
+ /** @hide */
+ public void setStandbyMode(boolean isStandbyModeOn) {}
+
+ /** @hide */
+ public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {}
+
+ /** @hide */
+ public boolean isHdmiCecVolumeControlEnabled() {
+ return true;
+ }
+
+ /** @hide */
+ public void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute) {}
+
+ /** @hide */
+ public void setSystemAudioModeOnForAudioOnlySource() {}
+
+ /** @hide */
+ public void addHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {}
+
+ /** @hide */
+ public void removeHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {}
+}
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
index 2623458aff5b..52c3628f358b 100644
--- a/core/java/android/hardware/hdmi/HdmiPortInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -18,6 +18,7 @@ package android.hardware.hdmi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,6 +29,7 @@ import android.os.Parcelable;
* @hide
*/
@SystemApi
+@TestApi
public final class HdmiPortInfo implements Parcelable {
/** HDMI port type: Input */
public static final int PORT_INPUT = 0;
@@ -153,7 +155,9 @@ public final class HdmiPortInfo implements Parcelable {
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
+ * @hide
*/
+ @SystemApi
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
@@ -187,4 +191,9 @@ public final class HdmiPortInfo implements Parcelable {
&& mCecSupported == other.mCecSupported && mArcSupported == other.mArcSupported
&& mMhlSupported == other.mMhlSupported;
}
+
+ @Override
+ public int hashCode() {
+ return mId;
+ }
}
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
index 7833653e41b0..913edfd0ebf4 100644
--- a/core/java/android/hardware/hdmi/HdmiSwitchClient.java
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -18,6 +18,7 @@ package android.hardware.hdmi;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.hardware.hdmi.HdmiControlManager.ControlCallbackResult;
import android.os.Binder;
import android.os.RemoteException;
@@ -38,6 +39,7 @@ import java.util.concurrent.Executor;
* @hide
*/
@SystemApi
+@TestApi
public class HdmiSwitchClient extends HdmiClient {
private static final String TAG = "HdmiSwitchClient";
@@ -187,11 +189,8 @@ public class HdmiSwitchClient extends HdmiClient {
* <p>This returns an empty list when the current device does not have HDMI input.
*
* @return a list of {@link HdmiPortInfo}
- *
- * @hide
*/
@NonNull
- @SystemApi
public List<HdmiPortInfo> getPortInfo() {
try {
return mService.getPortInfo();
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index b0fca006d1e2..d7ca63a54987 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -260,4 +260,10 @@ public abstract class AbstractInputMethodService extends Service
*/
public void notifyUserActionIfNecessary() {
}
+
+ /** @hide */
+ @Override
+ public final boolean isUiContext() {
+ return true;
+ }
}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index f6c72c4eefbc..55c527ba6fa6 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.os.StrictMode.vmIncorrectContextUseEnabled;
+
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
@@ -26,9 +28,12 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.StrictMode;
import android.os.SystemClock;
+import android.util.Log;
import com.android.internal.util.FrameworkStatsLog;
@@ -228,6 +233,7 @@ public class GestureDetector {
}
}
+ private static final String TAG = GestureDetector.class.getSimpleName();
@UnsupportedAppUsage
private int mTouchSlopSquare;
private int mDoubleTapTouchSlopSquare;
@@ -378,7 +384,8 @@ public class GestureDetector {
* You may only use this constructor from a {@link android.os.Looper} thread.
* @see android.os.Handler#Handler()
*
- * @param context the application's context
+ * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
* {@link OnContextClickListener} then it will also be set as the listener for
@@ -395,7 +402,8 @@ public class GestureDetector {
* thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
- * @param context the application's context
+ * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
* {@link OnContextClickListener} then it will also be set as the listener for
@@ -425,7 +433,8 @@ public class GestureDetector {
* thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
- * @param context the application's context
+ * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use for running deferred listener events.
@@ -456,6 +465,17 @@ public class GestureDetector {
mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
mAmbiguousGestureMultiplier = ViewConfiguration.getAmbiguousGestureMultiplier();
} else {
+ if (!context.isUiContext() && vmIncorrectContextUseEnabled()) {
+ final String errorMessage =
+ "Tried to access UI constants from a non-visual Context.";
+ final String message = "GestureDetector must be accessed from Activity or other "
+ + "visual Context. Use an Activity or a Context created with "
+ + "Context#createWindowContext(int, Bundle), which are adjusted to the "
+ + "configuration and visual bounds of an area on screen.";
+ final Exception exception = new IllegalArgumentException(errorMessage);
+ StrictMode.onIncorrectContextUsed(message, exception);
+ Log.e(TAG, errorMessage + message, exception);
+ }
final ViewConfiguration configuration = ViewConfiguration.get(context);
touchSlop = configuration.getScaledTouchSlop();
doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index ec54091e5a20..3615b9e2f9cb 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -22,10 +22,11 @@
android:layout_gravity="bottom">
<LinearLayout
+ android:id="@+id/actions_container_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:paddingEnd="12dp"
+ android:paddingEnd="@dimen/bubble_gone_padding_end"
>
<com.android.internal.widget.NotificationActionListLayout
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index ad3d20ee5f24..59bb052cbdf5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -312,6 +312,12 @@
<!-- The margin of the content to an image-->
<dimen name="notification_content_image_margin_end">8dp</dimen>
+ <!-- The padding at the end of actions when the bubble button is visible-->
+ <dimen name="bubble_visible_padding_end">3dp</dimen>
+
+ <!-- The padding at the end of actions when the bubble button is gone-->
+ <dimen name="bubble_gone_padding_end">12dp</dimen>
+
<!-- The spacing between messages in Notification.MessagingStyle -->
<dimen name="notification_messaging_spacing">6dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 53cdeb5e5c1c..f3345b015c87 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2766,6 +2766,7 @@
<java-symbol type="bool" name="config_mainBuiltInDisplayIsRound" />
<java-symbol type="id" name="actions_container" />
+ <java-symbol type="id" name="actions_container_layout" />
<java-symbol type="id" name="smart_reply_container" />
<java-symbol type="id" name="remote_input_tag" />
<java-symbol type="id" name="pending_intent_tag" />
@@ -3533,6 +3534,8 @@
<java-symbol type="id" name="clip_to_padding_tag" />
<java-symbol type="id" name="clip_children_tag" />
<java-symbol type="id" name="bubble_button" />
+ <java-symbol type="dimen" name="bubble_visible_padding_end" />
+ <java-symbol type="dimen" name="bubble_gone_padding_end" />
<java-symbol type="dimen" name="messaging_avatar_size" />
<java-symbol type="dimen" name="messaging_group_sending_progress_size" />
<java-symbol type="dimen" name="messaging_image_rounding" />
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index f13a11e9edfb..17d1389a6602 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -23,12 +23,14 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.app.ActivityThread;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
+import android.inputmethodservice.InputMethodService;
import android.media.ImageReader;
import android.os.UserHandle;
import android.view.Display;
@@ -136,6 +138,13 @@ public class ContextTest {
}
@Test
+ public void testIsUiContext_InputMethodService_returnsTrue() {
+ final InputMethodService ims = new InputMethodService();
+
+ assertTrue(ims.isUiContext());
+ }
+
+ @Test
public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() {
verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 44349c5f5ab4..4e17062fc52c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -201,6 +201,9 @@
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
+ <!-- Permission required for IncrementalLogCollectionTest -->
+ <uses-permission android:name="android.permission.LOADER_USAGE_STATS" />
+
<!-- Permission required for storage tests - FuseDaemonHostTest -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
diff --git a/packages/SystemUI/res/drawable/ic_create_bubble.xml b/packages/SystemUI/res/drawable/ic_create_bubble.xml
index d58e9a347a2f..4abbc8179b4d 100644
--- a/packages/SystemUI/res/drawable/ic_create_bubble.xml
+++ b/packages/SystemUI/res/drawable/ic_create_bubble.xml
@@ -15,11 +15,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="20dp"
+ android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
- android:pathData="M22,12C22,12 22,12 22,12C22,12 22,12 22,12c0,0.56 -0.06,1.1 -0.15,1.64l-1.97,-0.33c0.15,-0.91 0.15,-1.84 -0.02,-2.75c-0.01,-0.03 -0.01,-0.07 -0.02,-0.1c-0.03,-0.18 -0.08,-0.36 -0.13,-0.54c-0.02,-0.08 -0.04,-0.16 -0.06,-0.24c-0.04,-0.14 -0.09,-0.27 -0.14,-0.41c-0.04,-0.12 -0.08,-0.24 -0.13,-0.35c-0.04,-0.09 -0.08,-0.18 -0.13,-0.27c-0.07,-0.15 -0.14,-0.3 -0.22,-0.45c-0.03,-0.05 -0.06,-0.09 -0.08,-0.14c-0.72,-1.26 -1.77,-2.31 -3.03,-3.03c-0.05,-0.03 -0.09,-0.06 -0.14,-0.08c-0.15,-0.08 -0.3,-0.15 -0.45,-0.22c-0.09,-0.04 -0.18,-0.09 -0.27,-0.13c-0.11,-0.05 -0.23,-0.09 -0.35,-0.13c-0.14,-0.05 -0.27,-0.1 -0.41,-0.14c-0.08,-0.02 -0.16,-0.04 -0.23,-0.06c-0.18,-0.05 -0.36,-0.1 -0.54,-0.13c-0.03,-0.01 -0.07,-0.01 -0.1,-0.01c-0.95,-0.17 -1.93,-0.17 -2.88,0c-0.03,0.01 -0.07,0.01 -0.1,0.01c-0.18,0.04 -0.36,0.08 -0.54,0.13C9.85,4.3 9.77,4.32 9.69,4.34C9.55,4.38 9.42,4.44 9.28,4.49C9.17,4.53 9.05,4.57 8.93,4.61C8.84,4.65 8.75,4.7 8.66,4.74c-0.15,0.07 -0.3,0.14 -0.45,0.22C8.16,4.98 8.12,5.01 8.07,5.04C5.64,6.42 4,9.02 4,12c0,2.74 1.39,5.16 3.49,6.6c0.01,0.01 0.03,0.02 0.04,0.03c0.16,0.11 0.33,0.2 0.49,0.3c0.06,0.04 0.12,0.08 0.19,0.11c0.13,0.07 0.27,0.13 0.4,0.19c0.11,0.05 0.21,0.1 0.32,0.15c0.1,0.04 0.2,0.07 0.29,0.11c0.15,0.06 0.31,0.11 0.46,0.16c0.05,0.02 0.11,0.03 0.17,0.04c1.11,0.31 2.27,0.35 3.4,0.18l0.35,1.98c-0.54,0.09 -1.08,0.14 -1.62,0.14V22c-0.65,0 -1.28,-0.07 -1.9,-0.19c-0.01,0 -0.01,0 -0.02,0c-0.25,-0.05 -0.49,-0.11 -0.73,-0.18c-0.08,-0.02 -0.16,-0.04 -0.23,-0.06c-0.19,-0.06 -0.37,-0.13 -0.55,-0.19c-0.13,-0.05 -0.26,-0.09 -0.39,-0.14c-0.13,-0.05 -0.25,-0.12 -0.38,-0.18c-0.18,-0.08 -0.35,-0.16 -0.53,-0.25c-0.07,-0.04 -0.14,-0.08 -0.21,-0.13c-0.22,-0.12 -0.43,-0.25 -0.64,-0.39c-0.01,-0.01 -0.02,-0.02 -0.04,-0.03c-0.51,-0.35 -1,-0.74 -1.45,-1.2l0,0C3.12,17.26 2,14.76 2,12c0,-2.76 1.12,-5.26 2.93,-7.07l0,0c0.45,-0.45 0.93,-0.84 1.44,-1.19C6.39,3.73 6.4,3.72 6.42,3.71c0.2,-0.14 0.41,-0.26 0.62,-0.38c0.08,-0.05 0.15,-0.09 0.23,-0.14c0.17,-0.09 0.33,-0.16 0.5,-0.24c0.13,-0.06 0.27,-0.13 0.4,-0.19C8.3,2.71 8.42,2.67 8.55,2.63c0.19,-0.07 0.38,-0.14 0.58,-0.2c0.07,-0.02 0.14,-0.03 0.21,-0.05C10.18,2.14 11.07,2 12,2c0.65,0 1.29,0.07 1.91,0.19c0,0 0,0 0,0c0.25,0.05 0.5,0.11 0.75,0.18c0.07,0.02 0.14,0.03 0.22,0.06c0.19,0.06 0.38,0.13 0.57,0.2c0.12,0.05 0.25,0.09 0.37,0.14c0.14,0.06 0.27,0.12 0.4,0.18c0.17,0.08 0.34,0.16 0.51,0.25c0.08,0.04 0.15,0.09 0.23,0.14c0.21,0.12 0.42,0.24 0.62,0.38c0.01,0.01 0.03,0.02 0.04,0.03c0.51,0.35 0.99,0.74 1.45,1.19c0.24,0.24 0.47,0.49 0.68,0.75c0.04,0.04 0.06,0.09 0.1,0.13c0.17,0.22 0.34,0.45 0.5,0.68c0.01,0.01 0.02,0.03 0.03,0.04c0.69,1.05 1.17,2.21 1.42,3.44c0,0 0,0.01 0,0.01c0.06,0.29 0.1,0.58 0.13,0.87c0.01,0.04 0.01,0.09 0.02,0.13C21.98,11.32 22,11.66 22,12zM18.5,15c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S20.43,15 18.5,15z"/>
-</vector> \ No newline at end of file
+ android:pathData="M23,5v8h-2V5H3v14h10v2v0H3c-1.1,0 -2,-0.9 -2,-2V5c0,-1.1 0.9,-2 2,-2h18C22.1,3 23,3.9 23,5zM10,8v2.59L5.71,6.29L4.29,7.71L8.59,12H6v2h6V8H10zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_stop_bubble.xml b/packages/SystemUI/res/drawable/ic_stop_bubble.xml
index 11bc741a4f17..6cf67a77ff55 100644
--- a/packages/SystemUI/res/drawable/ic_stop_bubble.xml
+++ b/packages/SystemUI/res/drawable/ic_stop_bubble.xml
@@ -15,11 +15,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="20dp"
+ android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
- android:pathData="M21.98,18.32l-3.3,-3.3C20.47,15.11 21.89,16.53 21.98,18.32zM8.66,4.74C8.75,4.7 8.84,4.65 8.93,4.61c0.11,-0.05 0.23,-0.09 0.35,-0.13c0.14,-0.05 0.27,-0.1 0.41,-0.14C9.77,4.32 9.85,4.3 9.92,4.28c0.18,-0.05 0.36,-0.1 0.54,-0.13c0.03,-0.01 0.07,-0.01 0.1,-0.01c0.95,-0.17 1.93,-0.17 2.88,0c0.03,0.01 0.07,0.01 0.1,0.01c0.18,0.04 0.36,0.08 0.54,0.13c0.08,0.02 0.16,0.04 0.23,0.06c0.14,0.04 0.27,0.09 0.41,0.14c0.12,0.04 0.23,0.08 0.35,0.13c0.09,0.04 0.18,0.09 0.27,0.13c0.15,0.07 0.3,0.14 0.45,0.22c0.05,0.03 0.09,0.06 0.14,0.08c1.26,0.72 2.31,1.77 3.03,3.03c0.03,0.05 0.06,0.09 0.08,0.14c0.08,0.15 0.15,0.3 0.22,0.45c0.04,0.09 0.09,0.18 0.13,0.27c0.05,0.11 0.09,0.23 0.13,0.35c0.05,0.13 0.1,0.27 0.14,0.41c0.02,0.08 0.04,0.16 0.06,0.24c0.05,0.18 0.1,0.36 0.13,0.54c0.01,0.03 0.01,0.07 0.02,0.1c0.16,0.91 0.17,1.84 0.02,2.75l1.97,0.33C21.94,13.1 22,12.56 22,12c0,0 0,0 0,0s0,0 0,0c0,-0.34 -0.02,-0.68 -0.05,-1.01c0,-0.04 -0.01,-0.09 -0.02,-0.13c-0.03,-0.29 -0.07,-0.58 -0.13,-0.87c0,0 0,-0.01 0,-0.01c-0.25,-1.23 -0.73,-2.39 -1.42,-3.44c-0.01,-0.01 -0.02,-0.03 -0.03,-0.04c-0.15,-0.23 -0.32,-0.46 -0.5,-0.68c-0.03,-0.04 -0.06,-0.09 -0.1,-0.13c-0.21,-0.26 -0.44,-0.51 -0.68,-0.75c-0.45,-0.45 -0.94,-0.84 -1.45,-1.19c-0.01,-0.01 -0.03,-0.02 -0.04,-0.03c-0.2,-0.14 -0.41,-0.26 -0.62,-0.38c-0.08,-0.04 -0.15,-0.09 -0.23,-0.14c-0.17,-0.09 -0.34,-0.17 -0.51,-0.25c-0.13,-0.06 -0.26,-0.13 -0.4,-0.18c-0.12,-0.05 -0.25,-0.09 -0.37,-0.14c-0.19,-0.07 -0.38,-0.14 -0.57,-0.2c-0.07,-0.02 -0.14,-0.04 -0.22,-0.06c-0.25,-0.07 -0.5,-0.13 -0.75,-0.18c0,0 0,0 0,0C13.29,2.07 12.65,2 12,2c-0.93,0 -1.82,0.14 -2.67,0.37C9.26,2.39 9.19,2.41 9.12,2.43c-0.2,0.06 -0.39,0.13 -0.58,0.2C8.42,2.67 8.3,2.71 8.18,2.76c-0.14,0.06 -0.27,0.12 -0.4,0.19C7.61,3.03 7.44,3.1 7.27,3.19C7.19,3.24 7.12,3.29 7.04,3.33C7.03,3.34 7.02,3.34 7.01,3.35l1.48,1.48C8.55,4.8 8.6,4.77 8.66,4.74zM2.71,1.29L1.29,2.71l2.97,2.97C2.85,7.4 2,9.6 2,12c0,2.76 1.12,5.26 2.93,7.07l0,0c0.45,0.45 0.94,0.85 1.45,1.2c0.01,0.01 0.02,0.02 0.04,0.03c0.21,0.14 0.42,0.27 0.64,0.39c0.07,0.04 0.14,0.09 0.21,0.13c0.17,0.09 0.35,0.17 0.53,0.25c0.13,0.06 0.25,0.12 0.38,0.18c0.13,0.05 0.26,0.1 0.39,0.14c0.18,0.07 0.36,0.14 0.55,0.19c0.08,0.02 0.16,0.04 0.23,0.06c0.24,0.07 0.48,0.13 0.73,0.18c0.01,0 0.01,0 0.02,0C10.72,21.93 11.35,22 12,22v-0.01c0.54,0 1.08,-0.05 1.62,-0.14l-0.35,-1.98c-1.13,0.18 -2.29,0.13 -3.4,-0.18c-0.06,-0.02 -0.11,-0.03 -0.17,-0.04c-0.16,-0.05 -0.31,-0.11 -0.46,-0.16c-0.1,-0.04 -0.2,-0.07 -0.29,-0.11c-0.11,-0.05 -0.22,-0.1 -0.32,-0.15c-0.13,-0.06 -0.27,-0.12 -0.4,-0.19c-0.06,-0.03 -0.13,-0.08 -0.19,-0.11c-0.17,-0.1 -0.33,-0.19 -0.49,-0.3c-0.01,-0.01 -0.03,-0.02 -0.04,-0.03C5.39,17.16 4,14.74 4,12c0,-1.85 0.64,-3.54 1.7,-4.89l9.73,9.73C15.16,17.33 15,17.9 15,18.5c0,1.93 1.57,3.5 3.5,3.5c0.6,0 1.17,-0.16 1.66,-0.43l1.13,1.13l1.41,-1.41L2.71,1.29z"/>
+ android:pathData="M11.29,14.71L7,10.41V13H5V7h6v2H8.41l4.29,4.29L11.29,14.71zM21,3H3C1.9,3 1,3.9 1,5v14c0,1.1 0.9,2 2,2h10v0v-2H3V5h18v8h2V5C23,3.9 22.1,3 21,3zM19,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S20.66,15 19,15z"/>
</vector>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 15eda0689101..97a73043aa08 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -21,6 +21,7 @@ import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import android.annotation.DimenRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -104,27 +105,39 @@ class Bubble implements BubbleViewProvider {
private Path mDotPath;
private int mFlags;
+ @NonNull
+ private UserHandle mUser;
+ @NonNull
+ private String mPackageName;
+ private int mDesiredHeight;
+ @DimenRes
+ private int mDesiredHeightResId;
+
/**
* Create a bubble with limited information based on given {@link ShortcutInfo}.
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
- Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo) {
+ Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
+ final int desiredHeight, final int desiredHeightResId) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mShortcutInfo = shortcutInfo;
mKey = key;
mFlags = 0;
+ mUser = shortcutInfo.getUserHandle();
+ mPackageName = shortcutInfo.getPackage();
+ mDesiredHeight = desiredHeight;
+ mDesiredHeightResId = desiredHeightResId;
}
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
- Bubble(NotificationEntry e,
- BubbleController.NotificationSuppressionChangedListener listener) {
- mEntry = e;
+ Bubble(@NonNull final NotificationEntry e,
+ @Nullable final BubbleController.NotificationSuppressionChangedListener listener) {
+ Objects.requireNonNull(e);
mKey = e.getKey();
- mLastUpdated = e.getSbn().getPostTime();
mSuppressionListener = listener;
- mFlags = e.getSbn().getNotification().flags;
+ setEntry(e);
}
@Override
@@ -137,17 +150,14 @@ class Bubble implements BubbleViewProvider {
return mEntry;
}
- @Nullable
+ @NonNull
public UserHandle getUser() {
- if (mEntry != null) return mEntry.getSbn().getUser();
- if (mShortcutInfo != null) return mShortcutInfo.getUserHandle();
- return null;
+ return mUser;
}
+ @NonNull
public String getPackageName() {
- return mEntry == null
- ? mShortcutInfo == null ? null : mShortcutInfo.getPackage()
- : mEntry.getSbn().getPackageName();
+ return mPackageName;
}
@Override
@@ -318,9 +328,18 @@ class Bubble implements BubbleViewProvider {
/**
* Sets the entry associated with this bubble.
*/
- void setEntry(NotificationEntry entry) {
+ void setEntry(@NonNull final NotificationEntry entry) {
+ Objects.requireNonNull(entry);
+ Objects.requireNonNull(entry.getSbn());
mEntry = entry;
mLastUpdated = entry.getSbn().getPostTime();
+ mFlags = entry.getSbn().getNotification().flags;
+ mPackageName = entry.getSbn().getPackageName();
+ mUser = entry.getSbn().getUser();
+ if (entry.getBubbleMetadata() != null) {
+ mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
+ mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
+ }
}
/**
@@ -434,28 +453,30 @@ class Bubble implements BubbleViewProvider {
return mFlyoutMessage;
}
+ int getRawDesiredHeight() {
+ return mDesiredHeight;
+ }
+
+ int getRawDesiredHeightResId() {
+ return mDesiredHeightResId;
+ }
+
float getDesiredHeight(Context context) {
- if (mEntry == null) return 0;
- Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
- boolean useRes = data.getDesiredHeightResId() != 0;
+ boolean useRes = mDesiredHeightResId != 0;
if (useRes) {
- return getDimenForPackageUser(context, data.getDesiredHeightResId(),
- mEntry.getSbn().getPackageName(),
- mEntry.getSbn().getUser().getIdentifier());
+ return getDimenForPackageUser(context, mDesiredHeightResId, mPackageName,
+ mUser.getIdentifier());
} else {
- return data.getDesiredHeight()
- * context.getResources().getDisplayMetrics().density;
+ return mDesiredHeight * context.getResources().getDisplayMetrics().density;
}
}
String getDesiredHeightString() {
- if (mEntry == null) return String.valueOf(0);
- Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
- boolean useRes = data.getDesiredHeightResId() != 0;
+ boolean useRes = mDesiredHeightResId != 0;
if (useRes) {
- return String.valueOf(data.getDesiredHeightResId());
+ return String.valueOf(mDesiredHeightResId);
} else {
- return String.valueOf(data.getDesiredHeight());
+ return String.valueOf(mDesiredHeight);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index c2b9195c8ba3..d20f40559b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -77,7 +77,8 @@ internal class BubbleDataRepository @Inject constructor(
var shortcutId = b.shortcutInfo?.id
if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId
if (shortcutId == null) return@mapNotNull null
- BubbleEntity(userId, b.packageName, shortcutId, b.key)
+ BubbleEntity(userId, b.packageName, shortcutId, b.key, b.rawDesiredHeight,
+ b.rawDesiredHeightResId)
}
}
@@ -158,7 +159,8 @@ internal class BubbleDataRepository @Inject constructor(
val bubbles = entities.mapNotNull { entity ->
shortcutMap[ShortcutKey(entity.userId, entity.packageName)]
?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id }
- ?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo) }
+ ?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo, entity.desiredHeight,
+ entity.desiredHeightResId) }
}
uiScope.launch { cb(bubbles) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
index 43482616da2c..355c4b115c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
@@ -15,11 +15,14 @@
*/
package com.android.systemui.bubbles.storage
+import android.annotation.DimenRes
import android.annotation.UserIdInt
data class BubbleEntity(
@UserIdInt val userId: Int,
val packageName: String,
val shortcutId: String,
- val key: String
+ val key: String,
+ val desiredHeight: Int,
+ @DimenRes val desiredHeightResId: Int
)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
index 1df9f72022f0..a8faf258da07 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
@@ -31,6 +31,8 @@ private const val ATTR_USER_ID = "uid"
private const val ATTR_PACKAGE = "pkg"
private const val ATTR_SHORTCUT_ID = "sid"
private const val ATTR_KEY = "key"
+private const val ATTR_DESIRED_HEIGHT = "h"
+private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid"
/**
* Writes the bubbles in xml format into given output stream.
@@ -59,6 +61,8 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleEntity) {
serializer.attribute(null, ATTR_PACKAGE, bubble.packageName)
serializer.attribute(null, ATTR_SHORTCUT_ID, bubble.shortcutId)
serializer.attribute(null, ATTR_KEY, bubble.key)
+ serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString())
+ serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString())
serializer.endTag(null, TAG_BUBBLE)
} catch (e: IOException) {
throw RuntimeException(e)
@@ -86,7 +90,9 @@ private fun readXmlEntry(parser: XmlPullParser): BubbleEntity? {
parser.getAttributeWithName(ATTR_USER_ID)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_PACKAGE) ?: return null,
parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null,
- parser.getAttributeWithName(ATTR_KEY) ?: return null
+ parser.getAttributeWithName(ATTR_KEY) ?: return null,
+ parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null,
+ parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index b8c1842a1780..f039fc2d84ae 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -49,15 +49,17 @@ import com.android.settingslib.Utils;
import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.util.animation.TransitionLayout;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.Executor;
+import javax.inject.Inject;
+
/**
* A view controller used for Media Playback.
*/
@@ -93,12 +95,14 @@ public class MediaControlPanel {
* @param backgroundExecutor background executor, used for processing artwork
* @param activityStarter activity starter
*/
- public MediaControlPanel(Context context, DelayableExecutor backgroundExecutor,
- ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager) {
+ @Inject
+ public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
+ ActivityStarter activityStarter, MediaHostStatesManager mediaHostStatesManager,
+ SeekBarViewModel seekBarViewModel) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter;
- mSeekBarViewModel = new SeekBarViewModel(backgroundExecutor);
+ mSeekBarViewModel = seekBarViewModel;
mMediaViewController = new MediaViewController(context, mediaHostStatesManager);
loadDimens();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
index 3557b04a57bc..9b9a6b4b13ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewManager.kt
@@ -11,14 +11,12 @@ import android.widget.HorizontalScrollView
import android.widget.LinearLayout
import androidx.core.view.GestureDetectorCompat
import com.android.systemui.R
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.PageIndicator
import com.android.systemui.statusbar.notification.VisualStabilityManager
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.animation.requiresRemeasuring
-import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
+import javax.inject.Provider
import javax.inject.Singleton
private const val FLING_SLOP = 1000000
@@ -30,9 +28,8 @@ private const val FLING_SLOP = 1000000
@Singleton
class MediaViewManager @Inject constructor(
private val context: Context,
- @Background private val backgroundExecutor: DelayableExecutor,
+ private val mediaControlPanelFactory: Provider<MediaControlPanel>,
private val visualStabilityManager: VisualStabilityManager,
- private val activityStarter: ActivityStarter,
private val mediaHostStatesManager: MediaHostStatesManager,
mediaManager: MediaDataCombineLatest
) {
@@ -265,8 +262,7 @@ class MediaViewManager @Inject constructor(
}
var existingPlayer = mediaPlayers[key]
if (existingPlayer == null) {
- existingPlayer = MediaControlPanel(context, backgroundExecutor, activityStarter,
- mediaHostStatesManager)
+ existingPlayer = mediaControlPanelFactory.get()
existingPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context),
mediaContent))
mediaPlayers[key] = existingPlayer
@@ -289,7 +285,7 @@ class MediaViewManager @Inject constructor(
needsReordering = true
}
}
- existingPlayer.bind(data)
+ existingPlayer?.bind(data)
updateMediaPaddings()
updatePageIndicator()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 75ad06962afe..efc476d0c86f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -27,8 +27,10 @@ import androidx.annotation.AnyThread
import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.LiveData
-
-import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.concurrency.RepeatableExecutor
+import java.util.concurrent.Executor
+import javax.inject.Inject
private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
@@ -65,7 +67,7 @@ private fun PlaybackState.computePosition(duration: Long): Long {
}
/** ViewModel for seek bar in QS media player. */
-class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
+class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: RepeatableExecutor) {
private var _data = Progress(false, false, null, null)
set(value) {
@@ -89,10 +91,10 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
private var callback = object : MediaController.Callback() {
override fun onPlaybackStateChanged(state: PlaybackState) {
playbackState = state
- if (shouldPollPlaybackPosition()) {
- checkPlaybackPosition()
- } else if (PlaybackState.STATE_NONE.equals(playbackState)) {
+ if (PlaybackState.STATE_NONE.equals(playbackState)) {
clearController()
+ } else {
+ checkIfPollingNeeded()
}
}
@@ -100,12 +102,14 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
clearController()
}
}
+ private var cancel: Runnable? = null
/** Listening state (QS open or closed) is used to control polling of progress. */
var listening = true
- set(value) {
- if (value) {
- checkPlaybackPosition()
+ set(value) = bgExecutor.execute {
+ if (field != value) {
+ field = value
+ checkIfPollingNeeded()
}
}
@@ -137,9 +141,7 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
playbackState?.getState() == PlaybackState.STATE_NONE ||
(duration != null && duration <= 0)) false else true
_data = Progress(enabled, seekAvailable, position, duration)
- if (shouldPollPlaybackPosition()) {
- checkPlaybackPosition()
- }
+ checkIfPollingNeeded()
}
/**
@@ -151,6 +153,8 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
fun clearController() = bgExecutor.execute {
controller = null
playbackState = null
+ cancel?.run()
+ cancel = null
_data = _data.copy(enabled = false)
}
@@ -158,26 +162,34 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
* Call to clean up any resources.
*/
@AnyThread
- fun onDestroy() {
+ fun onDestroy() = bgExecutor.execute {
controller = null
playbackState = null
+ cancel?.run()
+ cancel = null
}
- @AnyThread
- private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({
+ @WorkerThread
+ private fun checkPlaybackPosition() {
val duration = _data.duration ?: -1
val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt()
if (currentPosition != null && _data.elapsedTime != currentPosition) {
_data = _data.copy(elapsedTime = currentPosition)
}
- if (shouldPollPlaybackPosition()) {
- checkPlaybackPosition()
- }
- }, POSITION_UPDATE_INTERVAL_MILLIS)
+ }
@WorkerThread
- private fun shouldPollPlaybackPosition(): Boolean {
- return listening && playbackState?.isInMotion() ?: false
+ private fun checkIfPollingNeeded() {
+ val needed = listening && playbackState?.isInMotion() ?: false
+ if (needed) {
+ if (cancel == null) {
+ cancel = bgExecutor.executeRepeatedly(this::checkPlaybackPosition, 0L,
+ POSITION_UPDATE_INTERVAL_MILLIS)
+ }
+ } else {
+ cancel?.run()
+ cancel = null
+ }
}
/** Gets a listener to attach to the seek bar to handle seeking. */
@@ -194,7 +206,7 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
private class SeekBarChangeListener(
val viewModel: SeekBarViewModel,
- val bgExecutor: DelayableExecutor
+ val bgExecutor: Executor
) : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 3dda15b5ce39..a8c03243c117 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -42,7 +42,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.HashMap;
@@ -150,9 +149,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
- boolean hideMedia = Utils.useQsMediaPlayer(mContext);
if (ent.isRowDismissed() || ent.isRowRemoved()
- || (ent.isMediaNotification() && hideMedia)
|| mBubbleController.isBubbleNotificationSuppressedFromShade(ent)
|| mFgsSectionController.hasEntry(ent)) {
// we don't want to update removed notifications because they could
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 3afd6235b287..6335a09cde2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import static com.android.systemui.media.MediaDataManagerKt.isMediaNotification;
+
import android.Manifest;
import android.app.AppGlobals;
import android.app.Notification;
@@ -27,6 +29,7 @@ import android.service.notification.StatusBarNotification;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -46,6 +49,7 @@ public class NotificationFilter {
private final NotificationGroupManager mGroupManager = Dependency.get(
NotificationGroupManager.class);
private final StatusBarStateController mStatusBarStateController;
+ private final Boolean mIsMediaFlagEnabled;
private NotificationEntryManager.KeyguardEnvironment mEnvironment;
private ShadeController mShadeController;
@@ -53,8 +57,11 @@ public class NotificationFilter {
private NotificationLockscreenUserManager mUserManager;
@Inject
- public NotificationFilter(StatusBarStateController statusBarStateController) {
+ public NotificationFilter(
+ StatusBarStateController statusBarStateController,
+ MediaFeatureFlag mediaFeatureFlag) {
mStatusBarStateController = statusBarStateController;
+ mIsMediaFlagEnabled = mediaFeatureFlag.getEnabled();
}
private NotificationEntryManager.KeyguardEnvironment getEnvironment() {
@@ -133,6 +140,10 @@ public class NotificationFilter {
}
}
}
+
+ if (mIsMediaFlagEnabled && isMediaNotification(sbn)) {
+ return true;
+ }
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
new file mode 100644
index 000000000000..026a3ffb73cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.collection.coordinator;
+
+import static com.android.systemui.media.MediaDataManagerKt.isMediaNotification;
+
+import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import javax.inject.Inject;
+
+/**
+ * Coordinates hiding (filtering) of media notifications.
+ */
+public class MediaCoordinator implements Coordinator {
+ private static final String TAG = "MediaCoordinator";
+
+ private final Boolean mIsMediaFeatureEnabled;
+
+ private final NotifFilter mMediaFilter = new NotifFilter(TAG) {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ return mIsMediaFeatureEnabled && isMediaNotification(entry.getSbn());
+ }
+ };
+
+ @Inject
+ public MediaCoordinator(MediaFeatureFlag featureFlag) {
+ mIsMediaFeatureEnabled = featureFlag.getEnabled();
+ }
+
+ @Override
+ public void attach(NotifPipeline pipeline) {
+ pipeline.addFinalizeFilter(mMediaFilter);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 2b279bbd553a..ac4296439507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -57,7 +57,8 @@ public class NotifCoordinators implements Dumpable {
BubbleCoordinator bubbleCoordinator,
HeadsUpCoordinator headsUpCoordinator,
ConversationCoordinator conversationCoordinator,
- PreparationCoordinator preparationCoordinator) {
+ PreparationCoordinator preparationCoordinator,
+ MediaCoordinator mediaCoordinator) {
dumpManager.registerDumpable(TAG, this);
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
mCoordinators.add(hideNotifsForOtherUsersCoordinator);
@@ -72,6 +73,7 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(preparationCoordinator);
}
// TODO: add new Coordinators here! (b/112656837)
+ mCoordinators.add(mediaCoordinator);
// TODO: add the sections in a particular ORDER (HeadsUp < People < Alerting)
for (Coordinator c : mCoordinators) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 8a4fdc24dc1b..b0861bfbd643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1348,7 +1348,9 @@ public class NotificationContentView extends FrameLayout {
}
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
- if (bubbleButton == null || actionContainer == null) {
+ LinearLayout actionContainerLayout =
+ layout.findViewById(com.android.internal.R.id.actions_container_layout);
+ if (bubbleButton == null || actionContainer == null || actionContainerLayout == null) {
return;
}
boolean isPersonWithShortcut =
@@ -1374,8 +1376,16 @@ public class NotificationContentView extends FrameLayout {
bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
bubbleButton.setVisibility(VISIBLE);
actionContainer.setVisibility(VISIBLE);
+
+ int paddingEnd = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.bubble_visible_padding_end);
+ actionContainerLayout.setPaddingRelative(0, 0, paddingEnd, 0);
} else {
bubbleButton.setVisibility(GONE);
+
+ int paddingEnd = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.bubble_gone_padding_end);
+ actionContainerLayout.setPaddingRelative(0, 0, paddingEnd, 0);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
index f46819252fac..2aed75e6519f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -29,9 +29,9 @@ import org.junit.runner.RunWith
class BubblePersistentRepositoryTest : SysuiTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1"),
- BubbleEntity(10, "com.example.chat", "alice and bob", "key-2"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3")
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
)
private lateinit var repository: BubblePersistentRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 2bb6bb8ebe14..f9d611c2bb33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -37,9 +37,9 @@ class BubbleVolatileRepositoryTest : SysuiTestCase() {
private val user0 = UserHandle.of(0)
private val user10 = UserHandle.of(10)
- private val bubble1 = BubbleEntity(0, PKG_MESSENGER, "shortcut-1", "k1")
- private val bubble2 = BubbleEntity(10, PKG_CHAT, "alice and bob", "k2")
- private val bubble3 = BubbleEntity(0, PKG_MESSENGER, "shortcut-2", "k3")
+ private val bubble1 = BubbleEntity(0, PKG_MESSENGER, "shortcut-1", "k1", 120, 0)
+ private val bubble2 = BubbleEntity(10, PKG_CHAT, "alice and bob", "k2", 0, 16537428)
+ private val bubble3 = BubbleEntity(0, PKG_MESSENGER, "shortcut-2", "k3", 120, 0)
private val bubbles = listOf(bubble1, bubble2, bubble3)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
index 79701ecf70f8..49467874dd8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
@@ -31,17 +31,17 @@ import java.io.ByteArrayOutputStream
class BubbleXmlHelperTest : SysuiTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1"),
- BubbleEntity(10, "com.example.chat", "alice and bob", "k2"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3")
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
)
@Test
fun testWriteXml() {
val expectedEntries = """
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" />
- <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" />
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+ <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
""".trimIndent()
ByteArrayOutputStream().use {
writeXml(it, bubbles)
@@ -56,9 +56,9 @@ class BubbleXmlHelperTest : SysuiTestCase() {
val src = """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<bs>
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" />
- <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" />
- <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+ <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
</bs>
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 1ba36e19b404..737ced63eed0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -31,6 +31,7 @@ import android.widget.ImageButton
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
+import androidx.lifecycle.LiveData
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -41,6 +42,7 @@ import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -48,6 +50,7 @@ import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private const val KEY = "TEST_KEY"
private const val APP = "APP"
@@ -73,6 +76,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var holder: PlayerViewHolder
@Mock private lateinit var view: TransitionLayout
@Mock private lateinit var mediaHostStatesManager: MediaHostStatesManager
+ @Mock private lateinit var seekBarViewModel: SeekBarViewModel
+ @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
private lateinit var appIcon: ImageView
private lateinit var appName: TextView
private lateinit var albumView: ImageView
@@ -94,18 +99,17 @@ public class MediaControlPanelTest : SysuiTestCase() {
private val device = MediaDeviceData(true, null, DEVICE_NAME)
private val disabledDevice = MediaDeviceData(false, null, null)
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+
@Before
fun setUp() {
bgExecutor = FakeExecutor(FakeSystemClock())
- activityStarter = mock(ActivityStarter::class.java)
- mediaHostStatesManager = mock(MediaHostStatesManager::class.java)
-
- player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager)
+ player = MediaControlPanel(context, bgExecutor, activityStarter, mediaHostStatesManager,
+ seekBarViewModel)
+ whenever(seekBarViewModel.progress).thenReturn(seekBarData)
// Mock out a view holder for the player to attach to.
- holder = mock(PlayerViewHolder::class.java)
- view = mock(TransitionLayout::class.java)
whenever(holder.player).thenReturn(view)
appIcon = ImageView(context)
whenever(holder.appIcon).thenReturn(appIcon)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index 19e15b3c4307..24e9bd837d5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -29,6 +29,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.concurrency.FakeRepeatableExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -71,7 +72,7 @@ public class SeekBarViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
fakeExecutor = FakeExecutor(FakeSystemClock())
- viewModel = SeekBarViewModel(fakeExecutor)
+ viewModel = SeekBarViewModel(FakeRepeatableExecutor(fakeExecutor))
mockController = mock(MediaController::class.java)
whenever(mockController.sessionToken).thenReturn(token1)
mockTransport = mock(MediaController.TransportControls::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index 595ba89ca3b6..5a81d36ea744 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -27,8 +27,10 @@ import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.Notification;
+import android.app.Notification.MediaStyle;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.media.session.MediaSession;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
@@ -40,6 +42,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
@@ -51,6 +54,7 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,10 +77,16 @@ public class NotificationFilterTest extends SysuiTestCase {
ForegroundServiceController mFsc;
@Mock
KeyguardEnvironment mEnvironment;
+ @Mock
+ MediaFeatureFlag mMediaFeatureFlag;
+ @Mock
+ StatusBarStateController mStatusBarStateController;
private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
private NotificationFilter mNotificationFilter;
private ExpandableNotificationRow mRow;
+ private NotificationEntry mMediaEntry;
+ private MediaSession mMediaSession;
@Before
public void setUp() throws Exception {
@@ -84,6 +94,12 @@ public class NotificationFilterTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
+ mMediaSession = new MediaSession(mContext, "TEST_MEDIA_SESSION");
+ NotificationEntryBuilder builder = new NotificationEntryBuilder();
+ builder.modifyNotification(mContext).setStyle(
+ new MediaStyle().setMediaSession(mMediaSession.getSessionToken()));
+ mMediaEntry = builder.build();
+
when(mMockPackageManager.checkUidPermission(
eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
eq(UID_NORMAL)))
@@ -107,7 +123,12 @@ public class NotificationFilterTest extends SysuiTestCase {
mDependency,
TestableLooper.get(this));
mRow = testHelper.createRow();
- mNotificationFilter = new NotificationFilter(mock(StatusBarStateController.class));
+ mNotificationFilter = new NotificationFilter(mStatusBarStateController, mMediaFeatureFlag);
+ }
+
+ @After
+ public void tearDown() {
+ mMediaSession.release();
}
@Test
@@ -218,6 +239,56 @@ public class NotificationFilterTest extends SysuiTestCase {
assertFalse(mNotificationFilter.shouldFilterOut(entry));
}
+ @Test
+ public void shouldFilterOtherNotificationWhenDisabled() {
+ // GIVEN that the media feature is disabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
+ NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ mMediaFeatureFlag);
+ // WHEN the media filter is asked about an entry
+ NotificationEntry otherEntry = new NotificationEntryBuilder().build();
+ final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
+ // THEN it shouldn't be filtered
+ assertFalse(shouldFilter);
+ }
+
+ @Test
+ public void shouldFilterOtherNotificationWhenEnabled() {
+ // GIVEN that the media feature is enabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
+ NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ mMediaFeatureFlag);
+ // WHEN the media filter is asked about an entry
+ NotificationEntry otherEntry = new NotificationEntryBuilder().build();
+ final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
+ // THEN it shouldn't be filtered
+ assertFalse(shouldFilter);
+ }
+
+ @Test
+ public void shouldFilterMediaNotificationWhenDisabled() {
+ // GIVEN that the media feature is disabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
+ NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ mMediaFeatureFlag);
+ // WHEN the media filter is asked about a media entry
+ final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
+ // THEN it shouldn't be filtered
+ assertFalse(shouldFilter);
+ }
+
+ @Test
+ public void shouldFilterMediaNotificationWhenEnabled() {
+ // GIVEN that the media feature is enabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
+ NotificationFilter filter = new NotificationFilter(mStatusBarStateController,
+ mMediaFeatureFlag);
+ // WHEN the media filter is asked about a media entry
+ final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
+ // THEN it should be filtered
+ assertTrue(shouldFilter);
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
new file mode 100644
index 000000000000..c5dc2b4d4f03
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.collection.coordinator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification.MediaStyle;
+import android.media.session.MediaSession;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public final class MediaCoordinatorTest extends SysuiTestCase {
+
+ private MediaSession mMediaSession;
+ private NotificationEntry mOtherEntry;
+ private NotificationEntry mMediaEntry;
+
+ @Mock private NotifPipeline mNotifPipeline;
+ @Mock private MediaFeatureFlag mMediaFeatureFlag;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mOtherEntry = new NotificationEntryBuilder().build();
+ mMediaSession = new MediaSession(mContext, "TEST_MEDIA_SESSION");
+ NotificationEntryBuilder builder = new NotificationEntryBuilder();
+ builder.modifyNotification(mContext).setStyle(
+ new MediaStyle().setMediaSession(mMediaSession.getSessionToken()));
+ mMediaEntry = builder.build();
+ }
+
+ @After
+ public void tearDown() {
+ mMediaSession.release();
+ }
+
+ @Test
+ public void shouldFilterOtherNotificationWhenDisabled() {
+ // GIVEN that the media feature is disabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
+ MediaCoordinator coordinator = new MediaCoordinator(mMediaFeatureFlag);
+ // WHEN the media filter is asked about an entry
+ NotifFilter filter = captureFilter(coordinator);
+ final boolean shouldFilter = filter.shouldFilterOut(mOtherEntry, 0);
+ // THEN it shouldn't be filtered
+ assertThat(shouldFilter).isFalse();
+ }
+
+ @Test
+ public void shouldFilterOtherNotificationWhenEnabled() {
+ // GIVEN that the media feature is enabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
+ MediaCoordinator coordinator = new MediaCoordinator(mMediaFeatureFlag);
+ // WHEN the media filter is asked about an entry
+ NotifFilter filter = captureFilter(coordinator);
+ final boolean shouldFilter = filter.shouldFilterOut(mOtherEntry, 0);
+ // THEN it shouldn't be filtered
+ assertThat(shouldFilter).isFalse();
+ }
+
+ @Test
+ public void shouldFilterMediaNotificationWhenDisabled() {
+ // GIVEN that the media feature is disabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
+ MediaCoordinator coordinator = new MediaCoordinator(mMediaFeatureFlag);
+ // WHEN the media filter is asked about a media entry
+ NotifFilter filter = captureFilter(coordinator);
+ final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry, 0);
+ // THEN it shouldn't be filtered
+ assertThat(shouldFilter).isFalse();
+ }
+
+ @Test
+ public void shouldFilterMediaNotificationWhenEnabled() {
+ // GIVEN that the media feature is enabled
+ when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
+ MediaCoordinator coordinator = new MediaCoordinator(mMediaFeatureFlag);
+ // WHEN the media filter is asked about a media entry
+ NotifFilter filter = captureFilter(coordinator);
+ final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry, 0);
+ // THEN it should be filtered
+ assertThat(shouldFilter).isTrue();
+ }
+
+ private NotifFilter captureFilter(MediaCoordinator coordinator) {
+ ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
+ coordinator.attach(mNotifPipeline);
+ verify(mNotifPipeline).addFinalizeFilter(filterCaptor.capture());
+ return filterCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeRepeatableExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeRepeatableExecutor.java
new file mode 100644
index 000000000000..477f615faf2b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeRepeatableExecutor.java
@@ -0,0 +1,34 @@
+/*
+ * 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.concurrency;
+
+/**
+ * A fake to use in tests.
+ */
+public class FakeRepeatableExecutor extends RepeatableExecutorImpl {
+
+ /**
+ * Initializes a fake RepeatableExecutor from a fake executor.
+ *
+ * Use the fake executor to actually process tasks.
+ *
+ * @param executor fake executor.
+ */
+ public FakeRepeatableExecutor(FakeExecutor executor) {
+ super(executor);
+ }
+}