Loading packages/SystemUI/src/com/android/systemui/media/LocalMediaManagerFactory.kt 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * 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.media import android.content.Context import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.media.InfoMediaManager import com.android.settingslib.media.LocalMediaManager import javax.inject.Inject /** * Factory to create [LocalMediaManager] objects. */ class LocalMediaManagerFactory @Inject constructor( private val context: Context, private val localBluetoothManager: LocalBluetoothManager? ) { /** Creates a [LocalMediaManager] for the given package. */ fun create(packageName: String): LocalMediaManager { return InfoMediaManager(context, packageName, null, localBluetoothManager).run { LocalMediaManager(context, localBluetoothManager, this, packageName) } } } packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +58 −135 Original line number Diff line number Diff line Loading @@ -41,7 +41,6 @@ import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; Loading @@ -56,14 +55,11 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import com.android.settingslib.Utils; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSMediaBrowser; import com.android.systemui.util.Assert; import com.android.systemui.util.concurrency.DelayableExecutor; import org.jetbrains.annotations.NotNull; Loading @@ -77,7 +73,6 @@ import java.util.concurrent.Executor; */ public class MediaControlPanel { private static final String TAG = "MediaControlPanel"; @Nullable private final LocalMediaManager mLocalMediaManager; // Button IDs for QS controls static final int[] ACTION_IDS = { Loading @@ -100,7 +95,6 @@ public class MediaControlPanel { private MediaSession.Token mToken; private MediaController mController; private int mBackgroundColor; private MediaDevice mDevice; protected ComponentName mServiceComponent; private boolean mIsRegistered = false; private List<KeyFrames> mKeyFrames; Loading @@ -113,7 +107,6 @@ public class MediaControlPanel { public static final String MEDIA_PREFERENCE_KEY = "browser_components"; private SharedPreferences mSharedPrefs; private boolean mCheckedForResumption = false; private boolean mIsRemotePlayback; private QSMediaBrowser mQSMediaBrowser; private final MediaController.Callback mSessionCallback = new MediaController.Callback() { Loading @@ -122,7 +115,6 @@ public class MediaControlPanel { Log.d(TAG, "session destroyed"); mController.unregisterCallback(mSessionCallback); clearControls(); makeInactive(); } @Override public void onPlaybackStateChanged(PlaybackState state) { Loading @@ -130,31 +122,6 @@ public class MediaControlPanel { if (s == PlaybackState.STATE_NONE) { Log.d(TAG, "playback state change will trigger resumption, state=" + state); clearControls(); makeInactive(); } } }; private final LocalMediaManager.DeviceCallback mDeviceCallback = new LocalMediaManager.DeviceCallback() { @Override public void onDeviceListUpdate(List<MediaDevice> devices) { if (mLocalMediaManager == null) { return; } MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice(); // Check because this can be called several times while changing devices if (mDevice == null || !mDevice.equals(currentDevice)) { mDevice = currentDevice; updateDevice(mDevice); } } @Override public void onSelectedDeviceStateChanged(MediaDevice device, int state) { if (mDevice == null || !mDevice.equals(device)) { mDevice = device; updateDevice(mDevice); } } }; Loading @@ -162,16 +129,13 @@ public class MediaControlPanel { /** * Initialize a new control panel * @param context * @param routeManager Manager used to listen for device change events. * @param foregroundExecutor foreground executor * @param backgroundExecutor background executor, used for processing artwork * @param activityStarter activity starter */ public MediaControlPanel(Context context, @Nullable LocalMediaManager routeManager, Executor foregroundExecutor, DelayableExecutor backgroundExecutor, ActivityStarter activityStarter) { public MediaControlPanel(Context context, Executor foregroundExecutor, DelayableExecutor backgroundExecutor, ActivityStarter activityStarter) { mContext = context; mLocalMediaManager = routeManager; mForegroundExecutor = foregroundExecutor; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; Loading @@ -183,7 +147,6 @@ public class MediaControlPanel { if (mSeekBarObserver != null) { mSeekBarViewModel.getProgress().removeObserver(mSeekBarObserver); } makeInactive(); } private void loadDimens() { Loading Loading @@ -318,11 +281,9 @@ public class MediaControlPanel { artistText.setText(data.getArtist()); // Transfer chip if (mLocalMediaManager != null) { mViewHolder.getSeamless().setVisibility(View.VISIBLE); setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */); setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */); updateDevice(mLocalMediaManager.getCurrentConnectedDevice()); mViewHolder.getSeamless().setOnClickListener(v -> { final Intent intent = new Intent() .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) Loading @@ -332,16 +293,55 @@ public class MediaControlPanel { mActivityStarter.startActivity(intent, false, true /* dismissShade */, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); }); } else { Log.d(TAG, "LocalMediaManager is null. Not binding output chip for pkg=" + pkgName); } final boolean isRemotePlayback; PlaybackInfo playbackInfo = mController.getPlaybackInfo(); if (playbackInfo != null) { mIsRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; isRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; } else { Log.d(TAG, "PlaybackInfo was null. Defaulting to local playback."); mIsRemotePlayback = false; isRemotePlayback = false; } ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); // Update the outline color RippleDrawable bkgDrawable = (RippleDrawable) mViewHolder.getSeamless().getBackground(); GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); rect.setStroke(2, deviceName.getCurrentTextColor()); rect.setColor(Color.TRANSPARENT); if (isRemotePlayback) { mViewHolder.getSeamless().setEnabled(false); // TODO(b/156875717): setEnabled should cause the alpha to change. mViewHolder.getSeamless().setAlpha(0.38f); iconView.setImageResource(R.drawable.ic_hardware_speaker); iconView.setVisibility(View.VISIBLE); deviceName.setText(R.string.media_seamless_remote_device); } else if (data.getDevice() != null && data.getDevice().getIcon() != null && data.getDevice().getName() != null) { mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); Drawable icon = data.getDevice().getIcon(); iconView.setVisibility(View.VISIBLE); if (icon instanceof AdaptiveIcon) { AdaptiveIcon aIcon = (AdaptiveIcon) icon; aIcon.setBackgroundColor(mBackgroundColor); iconView.setImageDrawable(aIcon); } else { iconView.setImageDrawable(icon); } deviceName.setText(data.getDevice().getName()); } else { // Reset to default Log.w(TAG, "device is null. Not binding output chip."); mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); iconView.setVisibility(View.GONE); deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); } List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact(); // Media controls int i = 0; Loading Loading @@ -382,8 +382,6 @@ public class MediaControlPanel { // Set up long press menu // TODO: b/156036025 bring back media guts makeActive(); // Update both constraint sets to regenerate the animation. mViewHolder.getPlayer().updateState(R.id.collapsed, collapsedSet); mViewHolder.getPlayer().updateState(R.id.expanded, expandedSet); Loading Loading @@ -514,60 +512,6 @@ public class MediaControlPanel { return (state.getState() == PlaybackState.STATE_PLAYING); } /** * Update the current device information * @param device device information to display */ private void updateDevice(MediaDevice device) { mForegroundExecutor.execute(() -> { updateChipInternal(device); }); } private void updateChipInternal(MediaDevice device) { if (mViewHolder == null) { return; } ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); // Update the outline color LinearLayout viewLayout = (LinearLayout) mViewHolder.getSeamless(); RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground(); GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); rect.setStroke(2, deviceName.getCurrentTextColor()); rect.setColor(Color.TRANSPARENT); if (mIsRemotePlayback) { mViewHolder.getSeamless().setEnabled(false); mViewHolder.getSeamless().setAlpha(0.38f); iconView.setImageResource(R.drawable.ic_hardware_speaker); iconView.setVisibility(View.VISIBLE); deviceName.setText(R.string.media_seamless_remote_device); } else if (device != null) { mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); Drawable icon = device.getIcon(); iconView.setVisibility(View.VISIBLE); if (icon instanceof AdaptiveIcon) { AdaptiveIcon aIcon = (AdaptiveIcon) icon; aIcon.setBackgroundColor(mBackgroundColor); iconView.setImageDrawable(aIcon); } else { iconView.setImageDrawable(icon); } deviceName.setText(device.getName()); } else { // Reset to default Log.d(TAG, "device is null. Not binding output chip."); mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); iconView.setVisibility(View.GONE); deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); } } /** * Puts controls into a resumption state if possible, or calls removePlayer if no component was * found that could resume playback Loading Loading @@ -642,27 +586,6 @@ public class MediaControlPanel { set.setAlpha(actionId, visible ? 1.0f : 0.0f); } private void makeActive() { Assert.isMainThread(); if (!mIsRegistered) { if (mLocalMediaManager != null) { mLocalMediaManager.registerCallback(mDeviceCallback); mLocalMediaManager.startScan(); } mIsRegistered = true; } } private void makeInactive() { Assert.isMainThread(); if (mIsRegistered) { if (mLocalMediaManager != null) { mLocalMediaManager.stopScan(); mLocalMediaManager.unregisterCallback(mDeviceCallback); } mIsRegistered = false; } } /** * Verify that we can connect to the given component with a MediaBrowser, and if so, add that * component to the list of resumption components Loading packages/SystemUI/src/com/android/systemui/media/MediaData.kt +8 −1 Original line number Diff line number Diff line Loading @@ -34,7 +34,8 @@ data class MediaData( val actionsToShowInCompact: List<Int>, val packageName: String?, val token: MediaSession.Token?, val clickIntent: PendingIntent? val clickIntent: PendingIntent?, val device: MediaDeviceData? ) /** State of a media action. */ Loading @@ -43,3 +44,9 @@ data class MediaAction( val intent: PendingIntent?, val contentDescription: CharSequence? ) /** State of the media device. */ data class MediaDeviceData( val icon: Drawable?, val name: String? ) packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * 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.media import javax.inject.Inject import javax.inject.Singleton /** * Combines updates from [MediaDataManager] with [MediaDeviceManager]. */ @Singleton class MediaDataCombineLatest @Inject constructor( private val dataSource: MediaDataManager, private val deviceSource: MediaDeviceManager ) { private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() private val entries: MutableMap<String, Pair<MediaData?, MediaDeviceData?>> = mutableMapOf() init { dataSource.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, data: MediaData) { entries[key] = data to entries[key]?.second update(key) } override fun onMediaDataRemoved(key: String) { remove(key) } }) deviceSource.addListener(object : MediaDeviceManager.Listener { override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) { entries[key] = entries[key]?.first to data update(key) } override fun onKeyRemoved(key: String) { remove(key) } }) } /** * Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData]. */ fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) /** * Remove a listener registered with addListener. */ fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) private fun update(key: String) { val (entry, device) = entries[key] ?: null to null if (entry != null && device != null) { val data = entry.copy(device = device) listeners.forEach { it.onMediaDataLoaded(key, data) } } } private fun remove(key: String) { entries.remove(key)?.let { listeners.forEach { it.onMediaDataRemoved(key) } } } } packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +15 −18 Original line number Diff line number Diff line Loading @@ -55,7 +55,19 @@ private const val LUMINOSITY_THRESHOLD = 0.05f private const val SATURATION_MULTIPLIER = 0.8f private val LOADING = MediaData(false, 0, null, null, null, null, null, emptyList(), emptyList(), null, null, null) emptyList(), emptyList(), null, null, null, null) fun isMediaNotification(sbn: StatusBarNotification): Boolean { if (!sbn.notification.hasMediaSession()) { return false } val notificationStyle = sbn.notification.notificationStyle if (Notification.DecoratedMediaCustomViewStyle::class.java.equals(notificationStyle) || Notification.MediaStyle::class.java.equals(notificationStyle)) { return true } return false } /** * A class that facilitates management and loading of Media Data, ready for binding. Loading @@ -72,7 +84,7 @@ class MediaDataManager @Inject constructor( private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() fun onNotificationAdded(key: String, sbn: StatusBarNotification) { if (isMediaNotification(sbn)) { if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) { if (!mediaEntries.containsKey(key)) { mediaEntries.put(key, LOADING) } Loading Loading @@ -204,7 +216,7 @@ class MediaDataManager @Inject constructor( foregroundExecutor.execute { onMediaDataLoaded(key, MediaData(true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent)) notif.contentIntent, null)) } } Loading Loading @@ -270,21 +282,6 @@ class MediaDataManager @Inject constructor( } } private fun isMediaNotification(sbn: StatusBarNotification): Boolean { if (!Utils.useQsMediaPlayer(context)) { return false } if (!sbn.notification.hasMediaSession()) { return false } val notificationStyle = sbn.notification.notificationStyle if (Notification.DecoratedMediaCustomViewStyle::class.java.equals(notificationStyle) || Notification.MediaStyle::class.java.equals(notificationStyle)) { return true } return false } /** * Are there any media notifications active? */ Loading Loading
packages/SystemUI/src/com/android/systemui/media/LocalMediaManagerFactory.kt 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * 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.media import android.content.Context import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.media.InfoMediaManager import com.android.settingslib.media.LocalMediaManager import javax.inject.Inject /** * Factory to create [LocalMediaManager] objects. */ class LocalMediaManagerFactory @Inject constructor( private val context: Context, private val localBluetoothManager: LocalBluetoothManager? ) { /** Creates a [LocalMediaManager] for the given package. */ fun create(packageName: String): LocalMediaManager { return InfoMediaManager(context, packageName, null, localBluetoothManager).run { LocalMediaManager(context, localBluetoothManager, this, packageName) } } }
packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +58 −135 Original line number Diff line number Diff line Loading @@ -41,7 +41,6 @@ import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; Loading @@ -56,14 +55,11 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import com.android.settingslib.Utils; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaOutputSliceConstants; import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSMediaBrowser; import com.android.systemui.util.Assert; import com.android.systemui.util.concurrency.DelayableExecutor; import org.jetbrains.annotations.NotNull; Loading @@ -77,7 +73,6 @@ import java.util.concurrent.Executor; */ public class MediaControlPanel { private static final String TAG = "MediaControlPanel"; @Nullable private final LocalMediaManager mLocalMediaManager; // Button IDs for QS controls static final int[] ACTION_IDS = { Loading @@ -100,7 +95,6 @@ public class MediaControlPanel { private MediaSession.Token mToken; private MediaController mController; private int mBackgroundColor; private MediaDevice mDevice; protected ComponentName mServiceComponent; private boolean mIsRegistered = false; private List<KeyFrames> mKeyFrames; Loading @@ -113,7 +107,6 @@ public class MediaControlPanel { public static final String MEDIA_PREFERENCE_KEY = "browser_components"; private SharedPreferences mSharedPrefs; private boolean mCheckedForResumption = false; private boolean mIsRemotePlayback; private QSMediaBrowser mQSMediaBrowser; private final MediaController.Callback mSessionCallback = new MediaController.Callback() { Loading @@ -122,7 +115,6 @@ public class MediaControlPanel { Log.d(TAG, "session destroyed"); mController.unregisterCallback(mSessionCallback); clearControls(); makeInactive(); } @Override public void onPlaybackStateChanged(PlaybackState state) { Loading @@ -130,31 +122,6 @@ public class MediaControlPanel { if (s == PlaybackState.STATE_NONE) { Log.d(TAG, "playback state change will trigger resumption, state=" + state); clearControls(); makeInactive(); } } }; private final LocalMediaManager.DeviceCallback mDeviceCallback = new LocalMediaManager.DeviceCallback() { @Override public void onDeviceListUpdate(List<MediaDevice> devices) { if (mLocalMediaManager == null) { return; } MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice(); // Check because this can be called several times while changing devices if (mDevice == null || !mDevice.equals(currentDevice)) { mDevice = currentDevice; updateDevice(mDevice); } } @Override public void onSelectedDeviceStateChanged(MediaDevice device, int state) { if (mDevice == null || !mDevice.equals(device)) { mDevice = device; updateDevice(mDevice); } } }; Loading @@ -162,16 +129,13 @@ public class MediaControlPanel { /** * Initialize a new control panel * @param context * @param routeManager Manager used to listen for device change events. * @param foregroundExecutor foreground executor * @param backgroundExecutor background executor, used for processing artwork * @param activityStarter activity starter */ public MediaControlPanel(Context context, @Nullable LocalMediaManager routeManager, Executor foregroundExecutor, DelayableExecutor backgroundExecutor, ActivityStarter activityStarter) { public MediaControlPanel(Context context, Executor foregroundExecutor, DelayableExecutor backgroundExecutor, ActivityStarter activityStarter) { mContext = context; mLocalMediaManager = routeManager; mForegroundExecutor = foregroundExecutor; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; Loading @@ -183,7 +147,6 @@ public class MediaControlPanel { if (mSeekBarObserver != null) { mSeekBarViewModel.getProgress().removeObserver(mSeekBarObserver); } makeInactive(); } private void loadDimens() { Loading Loading @@ -318,11 +281,9 @@ public class MediaControlPanel { artistText.setText(data.getArtist()); // Transfer chip if (mLocalMediaManager != null) { mViewHolder.getSeamless().setVisibility(View.VISIBLE); setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */); setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */); updateDevice(mLocalMediaManager.getCurrentConnectedDevice()); mViewHolder.getSeamless().setOnClickListener(v -> { final Intent intent = new Intent() .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT) Loading @@ -332,16 +293,55 @@ public class MediaControlPanel { mActivityStarter.startActivity(intent, false, true /* dismissShade */, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); }); } else { Log.d(TAG, "LocalMediaManager is null. Not binding output chip for pkg=" + pkgName); } final boolean isRemotePlayback; PlaybackInfo playbackInfo = mController.getPlaybackInfo(); if (playbackInfo != null) { mIsRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; isRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; } else { Log.d(TAG, "PlaybackInfo was null. Defaulting to local playback."); mIsRemotePlayback = false; isRemotePlayback = false; } ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); // Update the outline color RippleDrawable bkgDrawable = (RippleDrawable) mViewHolder.getSeamless().getBackground(); GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); rect.setStroke(2, deviceName.getCurrentTextColor()); rect.setColor(Color.TRANSPARENT); if (isRemotePlayback) { mViewHolder.getSeamless().setEnabled(false); // TODO(b/156875717): setEnabled should cause the alpha to change. mViewHolder.getSeamless().setAlpha(0.38f); iconView.setImageResource(R.drawable.ic_hardware_speaker); iconView.setVisibility(View.VISIBLE); deviceName.setText(R.string.media_seamless_remote_device); } else if (data.getDevice() != null && data.getDevice().getIcon() != null && data.getDevice().getName() != null) { mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); Drawable icon = data.getDevice().getIcon(); iconView.setVisibility(View.VISIBLE); if (icon instanceof AdaptiveIcon) { AdaptiveIcon aIcon = (AdaptiveIcon) icon; aIcon.setBackgroundColor(mBackgroundColor); iconView.setImageDrawable(aIcon); } else { iconView.setImageDrawable(icon); } deviceName.setText(data.getDevice().getName()); } else { // Reset to default Log.w(TAG, "device is null. Not binding output chip."); mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); iconView.setVisibility(View.GONE); deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); } List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact(); // Media controls int i = 0; Loading Loading @@ -382,8 +382,6 @@ public class MediaControlPanel { // Set up long press menu // TODO: b/156036025 bring back media guts makeActive(); // Update both constraint sets to regenerate the animation. mViewHolder.getPlayer().updateState(R.id.collapsed, collapsedSet); mViewHolder.getPlayer().updateState(R.id.expanded, expandedSet); Loading Loading @@ -514,60 +512,6 @@ public class MediaControlPanel { return (state.getState() == PlaybackState.STATE_PLAYING); } /** * Update the current device information * @param device device information to display */ private void updateDevice(MediaDevice device) { mForegroundExecutor.execute(() -> { updateChipInternal(device); }); } private void updateChipInternal(MediaDevice device) { if (mViewHolder == null) { return; } ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); // Update the outline color LinearLayout viewLayout = (LinearLayout) mViewHolder.getSeamless(); RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground(); GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); rect.setStroke(2, deviceName.getCurrentTextColor()); rect.setColor(Color.TRANSPARENT); if (mIsRemotePlayback) { mViewHolder.getSeamless().setEnabled(false); mViewHolder.getSeamless().setAlpha(0.38f); iconView.setImageResource(R.drawable.ic_hardware_speaker); iconView.setVisibility(View.VISIBLE); deviceName.setText(R.string.media_seamless_remote_device); } else if (device != null) { mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); Drawable icon = device.getIcon(); iconView.setVisibility(View.VISIBLE); if (icon instanceof AdaptiveIcon) { AdaptiveIcon aIcon = (AdaptiveIcon) icon; aIcon.setBackgroundColor(mBackgroundColor); iconView.setImageDrawable(aIcon); } else { iconView.setImageDrawable(icon); } deviceName.setText(device.getName()); } else { // Reset to default Log.d(TAG, "device is null. Not binding output chip."); mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); iconView.setVisibility(View.GONE); deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); } } /** * Puts controls into a resumption state if possible, or calls removePlayer if no component was * found that could resume playback Loading Loading @@ -642,27 +586,6 @@ public class MediaControlPanel { set.setAlpha(actionId, visible ? 1.0f : 0.0f); } private void makeActive() { Assert.isMainThread(); if (!mIsRegistered) { if (mLocalMediaManager != null) { mLocalMediaManager.registerCallback(mDeviceCallback); mLocalMediaManager.startScan(); } mIsRegistered = true; } } private void makeInactive() { Assert.isMainThread(); if (mIsRegistered) { if (mLocalMediaManager != null) { mLocalMediaManager.stopScan(); mLocalMediaManager.unregisterCallback(mDeviceCallback); } mIsRegistered = false; } } /** * Verify that we can connect to the given component with a MediaBrowser, and if so, add that * component to the list of resumption components Loading
packages/SystemUI/src/com/android/systemui/media/MediaData.kt +8 −1 Original line number Diff line number Diff line Loading @@ -34,7 +34,8 @@ data class MediaData( val actionsToShowInCompact: List<Int>, val packageName: String?, val token: MediaSession.Token?, val clickIntent: PendingIntent? val clickIntent: PendingIntent?, val device: MediaDeviceData? ) /** State of a media action. */ Loading @@ -43,3 +44,9 @@ data class MediaAction( val intent: PendingIntent?, val contentDescription: CharSequence? ) /** State of the media device. */ data class MediaDeviceData( val icon: Drawable?, val name: String? )
packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * 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.media import javax.inject.Inject import javax.inject.Singleton /** * Combines updates from [MediaDataManager] with [MediaDeviceManager]. */ @Singleton class MediaDataCombineLatest @Inject constructor( private val dataSource: MediaDataManager, private val deviceSource: MediaDeviceManager ) { private val listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf() private val entries: MutableMap<String, Pair<MediaData?, MediaDeviceData?>> = mutableMapOf() init { dataSource.addListener(object : MediaDataManager.Listener { override fun onMediaDataLoaded(key: String, data: MediaData) { entries[key] = data to entries[key]?.second update(key) } override fun onMediaDataRemoved(key: String) { remove(key) } }) deviceSource.addListener(object : MediaDeviceManager.Listener { override fun onMediaDeviceChanged(key: String, data: MediaDeviceData?) { entries[key] = entries[key]?.first to data update(key) } override fun onKeyRemoved(key: String) { remove(key) } }) } /** * Add a listener for [MediaData] changes that has been combined with latest [MediaDeviceData]. */ fun addListener(listener: MediaDataManager.Listener) = listeners.add(listener) /** * Remove a listener registered with addListener. */ fun removeListener(listener: MediaDataManager.Listener) = listeners.remove(listener) private fun update(key: String) { val (entry, device) = entries[key] ?: null to null if (entry != null && device != null) { val data = entry.copy(device = device) listeners.forEach { it.onMediaDataLoaded(key, data) } } } private fun remove(key: String) { entries.remove(key)?.let { listeners.forEach { it.onMediaDataRemoved(key) } } } }
packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +15 −18 Original line number Diff line number Diff line Loading @@ -55,7 +55,19 @@ private const val LUMINOSITY_THRESHOLD = 0.05f private const val SATURATION_MULTIPLIER = 0.8f private val LOADING = MediaData(false, 0, null, null, null, null, null, emptyList(), emptyList(), null, null, null) emptyList(), emptyList(), null, null, null, null) fun isMediaNotification(sbn: StatusBarNotification): Boolean { if (!sbn.notification.hasMediaSession()) { return false } val notificationStyle = sbn.notification.notificationStyle if (Notification.DecoratedMediaCustomViewStyle::class.java.equals(notificationStyle) || Notification.MediaStyle::class.java.equals(notificationStyle)) { return true } return false } /** * A class that facilitates management and loading of Media Data, ready for binding. Loading @@ -72,7 +84,7 @@ class MediaDataManager @Inject constructor( private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() fun onNotificationAdded(key: String, sbn: StatusBarNotification) { if (isMediaNotification(sbn)) { if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) { if (!mediaEntries.containsKey(key)) { mediaEntries.put(key, LOADING) } Loading Loading @@ -204,7 +216,7 @@ class MediaDataManager @Inject constructor( foregroundExecutor.execute { onMediaDataLoaded(key, MediaData(true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent)) notif.contentIntent, null)) } } Loading Loading @@ -270,21 +282,6 @@ class MediaDataManager @Inject constructor( } } private fun isMediaNotification(sbn: StatusBarNotification): Boolean { if (!Utils.useQsMediaPlayer(context)) { return false } if (!sbn.notification.hasMediaSession()) { return false } val notificationStyle = sbn.notification.notificationStyle if (Notification.DecoratedMediaCustomViewStyle::class.java.equals(notificationStyle) || Notification.MediaStyle::class.java.equals(notificationStyle)) { return true } return false } /** * Are there any media notifications active? */ Loading