diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cc8310a30ddad..a205fcf9520b6 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -42,6 +42,7 @@ @string/status_bar_phone_evdo_signal @string/status_bar_phone_signal @string/status_bar_secure + @string/status_bar_media @string/status_bar_managed_profile @string/status_bar_cast @string/status_bar_screen_record @@ -96,6 +97,7 @@ airplane sensors_off screen_record + media diff --git a/packages/SystemUI/res/drawable/stat_sys_media.xml b/packages/SystemUI/res/drawable/stat_sys_media.xml new file mode 100644 index 0000000000000..d48db7bd0d28a --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_media.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8bbcfa0e78982..de483179a92a0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -790,6 +790,9 @@ Sensors off active + + Media is active + Clear all notifications. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index a065b74bda997..2a4475bbe6b52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -46,6 +46,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.media.MediaData; +import com.android.systemui.media.MediaDataManager; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.screenrecord.RecordingController; @@ -80,14 +82,14 @@ import javax.inject.Inject; */ public class PhoneStatusBarPolicy implements BluetoothController.Callback, - CommandQueue.Callbacks, - RotationLockControllerCallback, - Listener, - ZenModeController.Callback, - DeviceProvisionedListener, - KeyguardStateController.Callback, - LocationController.LocationChangeCallback, - RecordingController.RecordingStateChangeCallback { + CommandQueue.Callbacks, + RotationLockControllerCallback, + Listener, + ZenModeController.Callback, + DeviceProvisionedListener, + KeyguardStateController.Callback, + LocationController.LocationChangeCallback, + RecordingController.RecordingStateChangeCallback, MediaDataManager.Listener { private static final String TAG = "PhoneStatusBarPolicy"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -108,6 +110,7 @@ public class PhoneStatusBarPolicy private final String mSlotLocation; private final String mSlotSensorsOff; private final String mSlotScreenRecord; + private final String mSlotMedia; private final int mDisplayId; private final SharedPreferences mSharedPreferences; private final DateFormatUtil mDateFormatUtil; @@ -135,6 +138,7 @@ public class PhoneStatusBarPolicy private final SensorPrivacyController mSensorPrivacyController; private final RecordingController mRecordingController; private final RingerModeTracker mRingerModeTracker; + private final MediaDataManager mMediaDataManager; private boolean mZenVisible; private boolean mVolumeVisible; @@ -159,6 +163,7 @@ public class PhoneStatusBarPolicy SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, AlarmManager alarmManager, UserManager userManager, RecordingController recordingController, + MediaDataManager mediaDataManager, @Nullable TelecomManager telecomManager, @DisplayId int displayId, @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, RingerModeTracker ringerModeTracker) { @@ -185,6 +190,7 @@ public class PhoneStatusBarPolicy mUiBgExecutor = uiBgExecutor; mTelecomManager = telecomManager; mRingerModeTracker = ringerModeTracker; + mMediaDataManager = mediaDataManager; mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast); mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot); @@ -202,6 +208,7 @@ public class PhoneStatusBarPolicy mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off); mSlotScreenRecord = resources.getString( com.android.internal.R.string.status_bar_screen_record); + mSlotMedia = resources.getString(com.android.internal.R.string.status_bar_media); mDisplayId = displayId; mSharedPreferences = sharedPreferences; @@ -280,6 +287,11 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotSensorsOff, mSensorPrivacyController.isSensorPrivacyEnabled()); + // play/pause icon when media is active + mIconController.setIcon(mSlotMedia, R.drawable.stat_sys_media, + mResources.getString(R.string.accessibility_media_active)); + mIconController.setIconVisibility(mSlotMedia, mMediaDataManager.hasActiveMedia()); + // screen record mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null); mIconController.setIconVisibility(mSlotScreenRecord, false); @@ -296,6 +308,7 @@ public class PhoneStatusBarPolicy mSensorPrivacyController.addCallback(mSensorPrivacyListener); mLocationController.addCallback(this); mRecordingController.addCallback(this); + mMediaDataManager.addListener(this); mCommandQueue.addCallback(this); } @@ -700,4 +713,18 @@ public class PhoneStatusBarPolicy if (DEBUG) Log.d(TAG, "screenrecord: hiding icon"); mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); } + + @Override + public void onMediaDataLoaded(String key, MediaData data) { + updateMediaIcon(); + } + + @Override + public void onMediaDataRemoved(String key) { + updateMediaIcon(); + } + + private void updateMediaIcon() { + mIconController.setIconVisibility(mSlotMedia, mMediaDataManager.hasActiveMedia()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.java new file mode 100644 index 0000000000000..a14d57556360c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.java @@ -0,0 +1,168 @@ +/* + * 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.phone; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.UserManager; +import android.telecom.TelecomManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.media.MediaDataManager; +import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.DataSaverController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.HotspotController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.LocationController; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.SensorPrivacyController; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.util.RingerModeLiveData; +import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.time.DateFormatUtil; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.Executor; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class PhoneStatusBarPolicyTest extends SysuiTestCase { + + private static final int DISPLAY_ID = 0; + @Mock + private StatusBarIconController mIconController; + @Mock + private CommandQueue mCommandQueue; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private Executor mBackgroundExecutor; + @Mock + private CastController mCastController; + @Mock + private HotspotController mHotSpotController; + @Mock + private BluetoothController mBluetoothController; + @Mock + private NextAlarmController mNextAlarmController; + @Mock + private UserInfoController mUserInfoController; + @Mock + private RotationLockController mRotationLockController; + @Mock + private DataSaverController mDataSaverController; + @Mock + private ZenModeController mZenModeController; + @Mock + private DeviceProvisionedController mDeviceProvisionerController; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private LocationController mLocationController; + @Mock + private SensorPrivacyController mSensorPrivacyController; + @Mock + private IActivityManager mIActivityManager; + @Mock + private AlarmManager mAlarmManager; + @Mock + private UserManager mUserManager; + @Mock + private RecordingController mRecordingController; + @Mock + private MediaDataManager mMediaDataManager; + @Mock + private TelecomManager mTelecomManager; + @Mock + private SharedPreferences mSharedPreferences; + @Mock + private DateFormatUtil mDateFormatUtil; + @Mock + private RingerModeTracker mRingerModeTracker; + @Mock + private RingerModeLiveData mRingerModeLiveData; + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + private Resources mResources; + private PhoneStatusBarPolicy mPhoneStatusBarPolicy; + + @Before + public void setup() { + mResources = spy(getContext().getResources()); + mPhoneStatusBarPolicy = new PhoneStatusBarPolicy(mIconController, mCommandQueue, + mBroadcastDispatcher, mBackgroundExecutor, mResources, mCastController, + mHotSpotController, mBluetoothController, mNextAlarmController, mUserInfoController, + mRotationLockController, mDataSaverController, mZenModeController, + mDeviceProvisionerController, mKeyguardStateController, mLocationController, + mSensorPrivacyController, mIActivityManager, mAlarmManager, mUserManager, + mRecordingController, mMediaDataManager, mTelecomManager, DISPLAY_ID, + mSharedPreferences, mDateFormatUtil, mRingerModeTracker); + when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); + when(mRingerModeTracker.getRingerModeInternal()).thenReturn(mRingerModeLiveData); + clearInvocations(mIconController); + } + + @Test + public void testInit_registerMediaCallback() { + mPhoneStatusBarPolicy.init(); + verify(mMediaDataManager).addListener(eq(mPhoneStatusBarPolicy)); + } + + @Test + public void testOnMediaDataLoaded_updatesIcon_hasMedia() { + String mediaSlot = mResources.getString(com.android.internal.R.string.status_bar_media); + when(mMediaDataManager.hasActiveMedia()).thenReturn(true); + mPhoneStatusBarPolicy.onMediaDataLoaded(null, null); + verify(mMediaDataManager).hasActiveMedia(); + verify(mIconController).setIconVisibility(eq(mediaSlot), eq(true)); + } + + @Test + public void testOnMediaDataRemoved_updatesIcon_noMedia() { + String mediaSlot = mResources.getString(com.android.internal.R.string.status_bar_media); + mPhoneStatusBarPolicy.onMediaDataRemoved(null); + verify(mMediaDataManager).hasActiveMedia(); + verify(mIconController).setIconVisibility(eq(mediaSlot), eq(false)); + } +}