Merge "Reset when playback state is NONE, and refactoring" into rvc-dev

This commit is contained in:
Beth Thibodeau
2020-03-07 18:04:38 +00:00
committed by Android (Google) Code Review
7 changed files with 515 additions and 518 deletions

View File

@@ -32,7 +32,6 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/header"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
> >
@@ -73,7 +72,7 @@
<!-- Song name --> <!-- Song name -->
<TextView <TextView
android:id="@+id/header_text" android:id="@+id/header_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"

View File

@@ -0,0 +1,425 @@
/*
* 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.annotation.LayoutRes;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
import android.graphics.drawable.RippleDrawable;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationMediaManager;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Base media control panel for System UI
*/
public class MediaControlPanel implements NotificationMediaManager.MediaListener {
private static final String TAG = "MediaControlPanel";
private final NotificationMediaManager mMediaManager;
private final Executor mBackgroundExecutor;
private Context mContext;
protected LinearLayout mMediaNotifView;
private View mSeamless;
private MediaSession.Token mToken;
private MediaController mController;
private int mForegroundColor;
private int mBackgroundColor;
protected ComponentName mRecvComponent;
private final int[] mActionIds;
// Button IDs used in notifications
protected static final int[] NOTIF_ACTION_IDS = {
com.android.internal.R.id.action0,
com.android.internal.R.id.action1,
com.android.internal.R.id.action2,
com.android.internal.R.id.action3,
com.android.internal.R.id.action4
};
private MediaController.Callback mSessionCallback = new MediaController.Callback() {
@Override
public void onSessionDestroyed() {
Log.d(TAG, "session destroyed");
mController.unregisterCallback(mSessionCallback);
clearControls();
}
};
/**
* Initialize a new control panel
* @param context
* @param parent
* @param manager
* @param layoutId layout resource to use for this control panel
* @param actionIds resource IDs for action buttons in the layout
* @param backgroundExecutor background executor, used for processing artwork
*/
public MediaControlPanel(Context context, ViewGroup parent, NotificationMediaManager manager,
@LayoutRes int layoutId, int[] actionIds, Executor backgroundExecutor) {
mContext = context;
LayoutInflater inflater = LayoutInflater.from(mContext);
mMediaNotifView = (LinearLayout) inflater.inflate(layoutId, parent, false);
mMediaManager = manager;
mActionIds = actionIds;
mBackgroundExecutor = backgroundExecutor;
}
/**
* Get the view used to display media controls
* @return the view
*/
public View getView() {
return mMediaNotifView;
}
/**
* Get the context
* @return context
*/
public Context getContext() {
return mContext;
}
/**
* Update the media panel view for the given media session
* @param token
* @param icon
* @param iconColor
* @param bgColor
* @param contentIntent
* @param appNameString
* @param device
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
int bgColor, PendingIntent contentIntent, String appNameString, MediaDevice device) {
mToken = token;
mForegroundColor = iconColor;
mBackgroundColor = bgColor;
mController = new MediaController(mContext, mToken);
MediaMetadata mediaMetadata = mController.getMetadata();
// Try to find a receiver for the media button that matches this app
PackageManager pm = mContext.getPackageManager();
Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
if (info != null) {
for (ResolveInfo inf : info) {
if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
mRecvComponent = inf.getComponentInfo().getComponentName();
}
}
}
mController.registerCallback(mSessionCallback);
if (mediaMetadata == null) {
Log.e(TAG, "Media metadata was null");
return;
}
ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
if (albumView != null) {
// Resize art in a background thread
mBackgroundExecutor.execute(() -> processAlbumArt(mediaMetadata, albumView));
}
mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
// Click action
mMediaNotifView.setOnClickListener(v -> {
try {
contentIntent.send();
// Also close shade
mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was canceled", e);
}
});
// App icon
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
Drawable iconDrawable = icon.loadDrawable(mContext);
iconDrawable.setTint(mForegroundColor);
appIcon.setImageDrawable(iconDrawable);
// Song name
TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
String songName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
titleText.setTextColor(mForegroundColor);
// Not in mini player:
// App title
TextView appName = mMediaNotifView.findViewById(R.id.app_name);
if (appName != null) {
appName.setText(appNameString);
appName.setTextColor(mForegroundColor);
}
// Artist name
TextView artistText = mMediaNotifView.findViewById(R.id.header_artist);
if (artistText != null) {
String artistName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
artistText.setText(artistName);
artistText.setTextColor(mForegroundColor);
}
// Transfer chip
mSeamless = mMediaNotifView.findViewById(R.id.media_seamless);
if (mSeamless != null) {
mSeamless.setVisibility(View.VISIBLE);
updateDevice(device);
ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
mSeamless.setOnClickListener(v -> {
final Intent intent = new Intent()
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
mController.getPackageName())
.putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken);
mActivityStarter.startActivity(intent, false, true /* dismissShade */,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
});
}
// Ensure is only added once
mMediaManager.removeCallback(this);
mMediaManager.addCallback(this);
}
/**
* Return the token for the current media session
* @return the token
*/
public MediaSession.Token getMediaSessionToken() {
return mToken;
}
/**
* Get the current media controller
* @return the controller
*/
public MediaController getController() {
return mController;
}
/**
* Get the name of the package associated with the current media controller
* @return the package name
*/
public String getMediaPlayerPackage() {
return mController.getPackageName();
}
/**
* Check whether this player has an attached media session.
* @return whether there is a controller with a current media session.
*/
public boolean hasMediaSession() {
return mController != null && mController.getPlaybackState() != null;
}
/**
* Check whether the media controlled by this player is currently playing
* @return whether it is playing, or false if no controller information
*/
public boolean isPlaying() {
return isPlaying(mController);
}
/**
* Check whether the given controller is currently playing
* @param controller media controller to check
* @return whether it is playing, or false if no controller information
*/
protected boolean isPlaying(MediaController controller) {
if (controller == null) {
return false;
}
PlaybackState state = controller.getPlaybackState();
if (state == null) {
return false;
}
return (state.getState() == PlaybackState.STATE_PLAYING);
}
/**
* Process album art for layout
* @param metadata media metadata
* @param albumView view to hold the album art
*/
private void processAlbumArt(MediaMetadata metadata, ImageView albumView) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
RoundedBitmapDrawable roundedDrawable = null;
if (albumArt != null) {
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
int albumSize = (int) mContext.getResources().getDimension(
R.dimen.qs_media_album_size);
Bitmap scaled = Bitmap.createScaledBitmap(original, albumSize, albumSize, false);
roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
roundedDrawable.setCornerRadius(radius);
} else {
Log.e(TAG, "No album art available");
}
// Now that it's resized, update the UI
final RoundedBitmapDrawable result = roundedDrawable;
albumView.getHandler().post(() -> {
if (result != null) {
albumView.setImageDrawable(result);
albumView.setVisibility(View.VISIBLE);
} else {
albumView.setImageDrawable(null);
albumView.setVisibility(View.GONE);
}
});
}
/**
* Update the current device information
* @param device device information to display
*/
public void updateDevice(MediaDevice device) {
if (mSeamless == null) {
return;
}
Handler handler = mSeamless.getHandler();
handler.post(() -> {
updateChipInternal(device);
});
}
private void updateChipInternal(MediaDevice device) {
ColorStateList fgTintList = ColorStateList.valueOf(mForegroundColor);
// Update the outline color
LinearLayout viewLayout = (LinearLayout) mSeamless;
RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
rect.setStroke(2, mForegroundColor);
rect.setColor(mBackgroundColor);
ImageView iconView = mSeamless.findViewById(R.id.media_seamless_image);
TextView deviceName = mSeamless.findViewById(R.id.media_seamless_text);
deviceName.setTextColor(fgTintList);
if (device != null) {
Drawable icon = device.getIcon();
iconView.setVisibility(View.VISIBLE);
iconView.setImageTintList(fgTintList);
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
iconView.setVisibility(View.GONE);
deviceName.setText(com.android.internal.R.string.ext_media_seamless_action);
}
}
/**
* Put controls into a resumption state
*/
public void clearControls() {
// Hide all the old buttons
for (int i = 0; i < mActionIds.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(mActionIds[i]);
if (thisBtn != null) {
thisBtn.setVisibility(View.GONE);
}
}
// Add a restart button
ImageButton btn = mMediaNotifView.findViewById(mActionIds[0]);
btn.setOnClickListener(v -> {
Log.d(TAG, "Attempting to restart session");
// Send a media button event to previously found receiver
if (mRecvComponent != null) {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.setComponent(mRecvComponent);
int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
intent.putExtra(
Intent.EXTRA_KEY_EVENT,
new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
mContext.sendBroadcast(intent);
} else {
Log.d(TAG, "No receiver to restart");
// If we don't have a receiver, try relaunching the activity instead
try {
mController.getSessionActivity().send();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was canceled", e);
}
}
});
btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
btn.setVisibility(View.VISIBLE);
}
@Override
public void onMetadataOrStateChanged(MediaMetadata metadata, int state) {
if (state == PlaybackState.STATE_NONE) {
clearControls();
mMediaManager.removeCallback(this);
}
}
}

View File

@@ -17,158 +17,53 @@
package com.android.systemui.qs; package com.android.systemui.qs;
import android.app.Notification; import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.graphics.drawable.RippleDrawable;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession; import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.android.settingslib.media.MediaDevice; import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.Dependency;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.media.MediaControlPanel;
import com.android.systemui.statusbar.NotificationMediaManager;
import java.util.List; import java.util.concurrent.Executor;
/** /**
* Single media player for carousel in QSPanel * Single media player for carousel in QSPanel
*/ */
public class QSMediaPlayer { public class QSMediaPlayer extends MediaControlPanel {
private static final String TAG = "QSMediaPlayer"; private static final String TAG = "QSMediaPlayer";
private Context mContext; // Button IDs for QS controls
private LinearLayout mMediaNotifView; static final int[] QS_ACTION_IDS = {
private View mSeamless; R.id.action0,
private MediaSession.Token mToken; R.id.action1,
private MediaController mController; R.id.action2,
private int mForegroundColor; R.id.action3,
private int mBackgroundColor; R.id.action4
private ComponentName mRecvComponent;
private QSPanel mParent;
private MediaController.Callback mSessionCallback = new MediaController.Callback() {
@Override
public void onSessionDestroyed() {
Log.d(TAG, "session destroyed");
mController.unregisterCallback(mSessionCallback);
// Hide all the old buttons
final int[] actionIds = {
R.id.action0,
R.id.action1,
R.id.action2,
R.id.action3,
R.id.action4
};
for (int i = 0; i < actionIds.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
if (thisBtn != null) {
thisBtn.setVisibility(View.GONE);
}
}
// Add a restart button
ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
btn.setOnClickListener(v -> {
Log.d(TAG, "Attempting to restart session");
// Send a media button event to previously found receiver
if (mRecvComponent != null) {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.setComponent(mRecvComponent);
int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
intent.putExtra(
Intent.EXTRA_KEY_EVENT,
new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
mContext.sendBroadcast(intent);
} else {
Log.d(TAG, "No receiver to restart");
// If we don't have a receiver, try relaunching the activity instead
try {
mController.getSessionActivity().send();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was canceled");
e.printStackTrace();
}
}
});
btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
btn.setVisibility(View.VISIBLE);
// Add long-click option to remove the player
ViewGroup mMediaCarousel = (ViewGroup) mMediaNotifView.getParent();
mMediaNotifView.setOnLongClickListener(v -> {
// Replace player view with delete/cancel view
v.setVisibility(View.GONE);
View options = LayoutInflater.from(mContext).inflate(
R.layout.qs_media_panel_options, null, false);
ImageButton btnDelete = options.findViewById(R.id.remove);
btnDelete.setOnClickListener(b -> {
mMediaCarousel.removeView(options);
mParent.removeMediaPlayer(QSMediaPlayer.this);
});
ImageButton btnCancel = options.findViewById(R.id.cancel);
btnCancel.setOnClickListener(b -> {
mMediaCarousel.removeView(options);
v.setVisibility(View.VISIBLE);
});
int pos = mMediaCarousel.indexOfChild(v);
mMediaCarousel.addView(options, pos, v.getLayoutParams());
return true; // consumed click
});
}
}; };
/** /**
* * Initialize quick shade version of player
* @param context * @param context
* @param parent * @param parent
* @param manager
* @param backgroundExecutor
*/ */
public QSMediaPlayer(Context context, ViewGroup parent) { public QSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
mContext = context; Executor backgroundExecutor) {
LayoutInflater inflater = LayoutInflater.from(mContext); super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, backgroundExecutor);
mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qs_media_panel, parent, false);
}
public View getView() {
return mMediaNotifView;
} }
/** /**
* Create or update the player view for the given media session * Update media panel view for the given media session
* @param parent the parent QSPanel
* @param token token for this media session * @param token token for this media session
* @param icon app notification icon * @param icon app notification icon
* @param iconColor foreground color (for text, icons) * @param iconColor foreground color (for text, icons)
@@ -177,114 +72,20 @@ public class QSMediaPlayer {
* @param notif reference to original notification * @param notif reference to original notification
* @param device current playback device * @param device current playback device
*/ */
public void setMediaSession(QSPanel parent, MediaSession.Token token, Icon icon, int iconColor, public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
int bgColor, View actionsContainer, Notification notif, MediaDevice device) { int bgColor, View actionsContainer, Notification notif, MediaDevice device) {
mParent = parent;
mToken = token;
mForegroundColor = iconColor;
mBackgroundColor = bgColor;
mController = new MediaController(mContext, token);
// Try to find a receiver for the media button that matches this app String appName = Notification.Builder.recoverBuilder(getContext(), notif)
PackageManager pm = mContext.getPackageManager(); .loadHeaderAppName();
Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON); super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent,
List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser()); appName, device);
if (info != null) {
for (ResolveInfo inf : info) {
if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
mRecvComponent = inf.getComponentInfo().getComponentName();
}
}
}
// reset in case we had previously restarted the stream
mMediaNotifView.setOnLongClickListener(null);
mController.registerCallback(mSessionCallback);
MediaMetadata mMediaMetadata = mController.getMetadata();
if (mMediaMetadata == null) {
Log.e(TAG, "Media metadata was null");
return;
}
Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
// Album art
addAlbumArt(mMediaMetadata, bgColor);
LinearLayout headerView = mMediaNotifView.findViewById(R.id.header);
// App icon
ImageView appIcon = headerView.findViewById(R.id.icon);
Drawable iconDrawable = icon.loadDrawable(mContext);
iconDrawable.setTint(iconColor);
appIcon.setImageDrawable(iconDrawable);
// App title
TextView appName = headerView.findViewById(R.id.app_name);
String appNameString = builder.loadHeaderAppName();
appName.setText(appNameString);
appName.setTextColor(iconColor);
// Action
mMediaNotifView.setOnClickListener(v -> {
try {
notif.contentIntent.send();
// Also close shade
mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was canceled");
e.printStackTrace();
}
});
// Transfer chip
mSeamless = headerView.findViewById(R.id.media_seamless);
mSeamless.setVisibility(View.VISIBLE);
updateChip(device);
ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
mSeamless.setOnClickListener(v -> {
final Intent intent = new Intent()
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
mController.getPackageName())
.putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, token);
mActivityStarter.startActivity(intent, false, true /* dismissShade */,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
});
// Artist name
TextView artistText = headerView.findViewById(R.id.header_artist);
String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
artistText.setText(artistName);
artistText.setTextColor(iconColor);
// Song name
TextView titleText = headerView.findViewById(R.id.header_text);
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
titleText.setTextColor(iconColor);
// Media controls // Media controls
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
final int[] actionIds = {
R.id.action0,
R.id.action1,
R.id.action2,
R.id.action3,
R.id.action4
};
final int[] notifActionIds = {
com.android.internal.R.id.action0,
com.android.internal.R.id.action1,
com.android.internal.R.id.action2,
com.android.internal.R.id.action3,
com.android.internal.R.id.action4
};
int i = 0; int i = 0;
for (; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) { for (; i < parentActionsLayout.getChildCount() && i < QS_ACTION_IDS.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]); ImageButton thatBtn = parentActionsLayout.findViewById(NOTIF_ACTION_IDS[i]);
if (thatBtn == null || thatBtn.getDrawable() == null if (thatBtn == null || thatBtn.getDrawable() == null
|| thatBtn.getVisibility() != View.VISIBLE) { || thatBtn.getVisibility() != View.VISIBLE) {
thisBtn.setVisibility(View.GONE); thisBtn.setVisibility(View.GONE);
@@ -301,116 +102,9 @@ public class QSMediaPlayer {
} }
// Hide any unused buttons // Hide any unused buttons
for (; i < actionIds.length; i++) { for (; i < QS_ACTION_IDS.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
thisBtn.setVisibility(View.GONE); thisBtn.setVisibility(View.GONE);
} }
} }
public MediaSession.Token getMediaSessionToken() {
return mToken;
}
public String getMediaPlayerPackage() {
return mController.getPackageName();
}
/**
* Check whether the media controlled by this player is currently playing
* @return whether it is playing, or false if no controller information
*/
public boolean isPlaying() {
if (mController == null) {
return false;
}
PlaybackState state = mController.getPlaybackState();
if (state == null) {
return false;
}
return (state.getState() == PlaybackState.STATE_PLAYING);
}
private void addAlbumArt(MediaMetadata metadata, int bgColor) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
if (albumArt != null) {
Log.d(TAG, "updating album art");
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
int albumSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_album_size);
Bitmap scaled = scaleBitmap(original, albumSize, albumSize);
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
mContext.getResources(), scaled);
roundedDrawable.setCornerRadius(radius);
albumView.setImageDrawable(roundedDrawable);
} else {
Log.e(TAG, "No album art available");
albumView.setImageDrawable(null);
}
mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(bgColor));
}
private Bitmap scaleBitmap(Bitmap original, int width, int height) {
Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(cropped);
float scale = (float) cropped.getWidth() / (float) original.getWidth();
float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f;
Matrix transformation = new Matrix();
transformation.postTranslate(0, dy);
transformation.preScale(scale, scale);
Paint paint = new Paint();
paint.setFilterBitmap(true);
canvas.drawBitmap(original, transformation, paint);
return cropped;
}
protected void updateChip(MediaDevice device) {
if (mSeamless == null) {
return;
}
Handler handler = mSeamless.getHandler();
handler.post(() -> {
updateChipInternal(device);
});
}
private void updateChipInternal(MediaDevice device) {
ColorStateList fgTintList = ColorStateList.valueOf(mForegroundColor);
// Update the outline color
LinearLayout viewLayout = (LinearLayout) mSeamless;
RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
rect.setStroke(2, mForegroundColor);
rect.setColor(mBackgroundColor);
ImageView iconView = mSeamless.findViewById(R.id.media_seamless_image);
TextView deviceName = mSeamless.findViewById(R.id.media_seamless_text);
deviceName.setTextColor(fgTintList);
if (device != null) {
Drawable icon = device.getIcon();
iconView.setVisibility(View.VISIBLE);
iconView.setImageTintList(fgTintList);
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
iconView.setVisibility(View.GONE);
deviceName.setText(com.android.internal.R.string.ext_media_seamless_action);
}
}
} }

View File

@@ -50,6 +50,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.Dumpable; import com.android.systemui.Dumpable;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile;
@@ -60,6 +61,7 @@ import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSliderView; import com.android.systemui.settings.ToggleSliderView;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService;
@@ -70,6 +72,7 @@ import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
@@ -94,6 +97,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private final LinearLayout mMediaCarousel; private final LinearLayout mMediaCarousel;
private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>(); private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
private final NotificationMediaManager mNotificationMediaManager;
private final Executor mBackgroundExecutor;
private LocalMediaManager mLocalMediaManager; private LocalMediaManager mLocalMediaManager;
private MediaDevice mDevice; private MediaDevice mDevice;
private boolean mUpdateCarousel = false; private boolean mUpdateCarousel = false;
@@ -128,7 +133,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (mDevice == null || !mDevice.equals(currentDevice)) { if (mDevice == null || !mDevice.equals(currentDevice)) {
mDevice = currentDevice; mDevice = currentDevice;
for (QSMediaPlayer p : mMediaPlayers) { for (QSMediaPlayer p : mMediaPlayers) {
p.updateChip(mDevice); p.updateDevice(mDevice);
} }
} }
} }
@@ -138,7 +143,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (mDevice == null || !mDevice.equals(device)) { if (mDevice == null || !mDevice.equals(device)) {
mDevice = device; mDevice = device;
for (QSMediaPlayer p : mMediaPlayers) { for (QSMediaPlayer p : mMediaPlayers) {
p.updateChip(mDevice); p.updateDevice(mDevice);
} }
} }
} }
@@ -150,12 +155,16 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
AttributeSet attrs, AttributeSet attrs,
DumpManager dumpManager, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger QSLogger qsLogger,
NotificationMediaManager notificationMediaManager,
@Background Executor backgroundExecutor
) { ) {
super(context, attrs); super(context, attrs);
mContext = context; mContext = context;
mQSLogger = qsLogger; mQSLogger = qsLogger;
mDumpManager = dumpManager; mDumpManager = dumpManager;
mNotificationMediaManager = notificationMediaManager;
mBackgroundExecutor = backgroundExecutor;
setOrientation(VERTICAL); setOrientation(VERTICAL);
@@ -255,7 +264,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (player == null) { if (player == null) {
Log.d(TAG, "creating new player"); Log.d(TAG, "creating new player");
player = new QSMediaPlayer(mContext, this); player = new QSMediaPlayer(mContext, this, mNotificationMediaManager,
mBackgroundExecutor);
if (player.isPlaying()) { if (player.isPlaying()) {
mMediaCarousel.addView(player.getView(), 0, lp); // add in front mMediaCarousel.addView(player.getView(), 0, lp); // add in front
@@ -268,7 +278,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
} }
Log.d(TAG, "setting player session"); Log.d(TAG, "setting player session");
player.setMediaSession(this, token, icon, iconColor, bgColor, actionsContainer, player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
notif.getNotification(), mDevice); notif.getNotification(), mDevice);
if (mMediaPlayers.size() > 0) { if (mMediaPlayers.size() > 0) {

View File

@@ -17,108 +17,47 @@
package com.android.systemui.qs; package com.android.systemui.qs;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController; import android.media.session.MediaController;
import android.media.session.MediaSession; import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.media.MediaControlPanel;
import com.android.systemui.statusbar.NotificationMediaManager;
import java.util.List; import java.util.concurrent.Executor;
/** /**
* QQS mini media player * QQS mini media player
*/ */
public class QuickQSMediaPlayer { public class QuickQSMediaPlayer extends MediaControlPanel {
private static final String TAG = "QQSMediaPlayer"; private static final String TAG = "QQSMediaPlayer";
private Context mContext; // Button IDs for QS controls
private LinearLayout mMediaNotifView; private static final int[] QQS_ACTION_IDS = {R.id.action0, R.id.action1, R.id.action2};
private MediaSession.Token mToken;
private MediaController mController;
private int mForegroundColor;
private ComponentName mRecvComponent;
private MediaController.Callback mSessionCallback = new MediaController.Callback() {
@Override
public void onSessionDestroyed() {
Log.d(TAG, "session destroyed");
mController.unregisterCallback(mSessionCallback);
// Hide all the old buttons
final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
for (int i = 0; i < actionIds.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
if (thisBtn != null) {
thisBtn.setVisibility(View.GONE);
}
}
// Add a restart button
ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
btn.setOnClickListener(v -> {
Log.d(TAG, "Attempting to restart session");
// Send a media button event to previously found receiver
if (mRecvComponent != null) {
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.setComponent(mRecvComponent);
int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
intent.putExtra(
Intent.EXTRA_KEY_EVENT,
new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
mContext.sendBroadcast(intent);
} else {
Log.d(TAG, "No receiver to restart");
// If we don't have a receiver, try relaunching the activity instead
try {
mController.getSessionActivity().send();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was canceled");
e.printStackTrace();
}
}
});
btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
btn.setVisibility(View.VISIBLE);
}
};
/** /**
* * Initialize mini media player for QQS
* @param context * @param context
* @param parent * @param parent
* @param manager
* @param backgroundExecutor
*/ */
public QuickQSMediaPlayer(Context context, ViewGroup parent) { public QuickQSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
mContext = context; Executor backgroundExecutor) {
LayoutInflater inflater = LayoutInflater.from(mContext); super(context, parent, manager, R.layout.qqs_media_panel, QQS_ACTION_IDS,
mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qqs_media_panel, parent, false); backgroundExecutor);
}
public View getView() {
return mMediaNotifView;
} }
/** /**
* * Update media panel view for the given media session
* @param token token for this media session * @param token token for this media session
* @param icon app notification icon * @param icon app notification icon
* @param iconColor foreground color (for text, icons) * @param iconColor foreground color (for text, icons)
@@ -130,84 +69,30 @@ public class QuickQSMediaPlayer {
*/ */
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) { View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) {
mToken = token; // Only update if this is a different session and currently playing
mForegroundColor = iconColor;
String oldPackage = ""; String oldPackage = "";
if (mController != null) { if (getController() != null) {
oldPackage = mController.getPackageName(); oldPackage = getController().getPackageName();
} }
MediaController controller = new MediaController(mContext, token); MediaController controller = new MediaController(getContext(), token);
boolean samePlayer = mToken.equals(token) && oldPackage.equals(controller.getPackageName()); MediaSession.Token currentToken = getMediaSessionToken();
if (mController != null && !samePlayer && !isPlaying(controller)) { boolean samePlayer = currentToken != null
// Only update if this is a different session and currently playing && currentToken.equals(token)
return; && oldPackage.equals(controller.getPackageName());
} if (getController() != null && !samePlayer && !isPlaying(controller)) {
mController = controller;
MediaMetadata mMediaMetadata = mController.getMetadata();
// Try to find a receiver for the media button that matches this app
PackageManager pm = mContext.getPackageManager();
Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
if (info != null) {
for (ResolveInfo inf : info) {
if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
mRecvComponent = inf.getComponentInfo().getComponentName();
}
}
}
mController.registerCallback(mSessionCallback);
if (mMediaMetadata == null) {
Log.e(TAG, "Media metadata was null");
return; return;
} }
// Action super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null, null);
mMediaNotifView.setOnClickListener(v -> {
try {
contentIntent.send();
mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Pending intent was canceled: " + e.getMessage());
}
});
mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(bgColor));
// App icon
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
Drawable iconDrawable = icon.loadDrawable(mContext);
iconDrawable.setTint(mForegroundColor);
appIcon.setImageDrawable(iconDrawable);
// Song name
TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
titleText.setTextColor(mForegroundColor);
// Buttons we can display
final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
// Existing buttons in the notification
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
final int[] notifActionIds = {
com.android.internal.R.id.action0,
com.android.internal.R.id.action1,
com.android.internal.R.id.action2,
com.android.internal.R.id.action3,
com.android.internal.R.id.action4
};
int i = 0; int i = 0;
if (actionsToShow != null) { if (actionsToShow != null) {
int maxButtons = Math.min(actionsToShow.length, parentActionsLayout.getChildCount()); int maxButtons = Math.min(actionsToShow.length, parentActionsLayout.getChildCount());
maxButtons = Math.min(maxButtons, actionIds.length); maxButtons = Math.min(maxButtons, QQS_ACTION_IDS.length);
for (; i < maxButtons; i++) { for (; i < maxButtons; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); ImageButton thisBtn = mMediaNotifView.findViewById(QQS_ACTION_IDS[i]);
int thatId = notifActionIds[actionsToShow[i]]; int thatId = NOTIF_ACTION_IDS[actionsToShow[i]];
ImageButton thatBtn = parentActionsLayout.findViewById(thatId); ImageButton thatBtn = parentActionsLayout.findViewById(thatId);
if (thatBtn == null || thatBtn.getDrawable() == null if (thatBtn == null || thatBtn.getDrawable() == null
|| thatBtn.getVisibility() != View.VISIBLE) { || thatBtn.getVisibility() != View.VISIBLE) {
@@ -225,38 +110,9 @@ public class QuickQSMediaPlayer {
} }
// Hide any unused buttons // Hide any unused buttons
for (; i < actionIds.length; i++) { for (; i < QQS_ACTION_IDS.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); ImageButton thisBtn = mMediaNotifView.findViewById(QQS_ACTION_IDS[i]);
thisBtn.setVisibility(View.GONE); thisBtn.setVisibility(View.GONE);
} }
} }
public MediaSession.Token getMediaSessionToken() {
return mToken;
}
/**
* Check whether the media controlled by this player is currently playing
* @return whether it is playing, or false if no controller information
*/
public boolean isPlaying(MediaController controller) {
if (controller == null) {
return false;
}
PlaybackState state = controller.getPlaybackState();
if (state == null) {
return false;
}
return (state.getState() == PlaybackState.STATE_PLAYING);
}
/**
* Check whether this player has an attached media session.
* @return whether there is a controller with a current media session.
*/
public boolean hasMediaSession() {
return mController != null && mController.getPlaybackState() != null;
}
} }

View File

@@ -29,18 +29,21 @@ import android.widget.LinearLayout;
import com.android.systemui.Dependency; import com.android.systemui.Dependency;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState; import com.android.systemui.plugins.qs.QSTile.SignalState;
import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.Utils; import com.android.systemui.util.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Executor;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@@ -72,9 +75,12 @@ public class QuickQSPanel extends QSPanel {
AttributeSet attrs, AttributeSet attrs,
DumpManager dumpManager, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger QSLogger qsLogger,
NotificationMediaManager notificationMediaManager,
@Background Executor backgroundExecutor
) { ) {
super(context, attrs, dumpManager, broadcastDispatcher, qsLogger); super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, notificationMediaManager,
backgroundExecutor);
if (mFooter != null) { if (mFooter != null) {
removeView(mFooter.getView()); removeView(mFooter.getView());
} }
@@ -93,7 +99,8 @@ public class QuickQSPanel extends QSPanel {
mHorizontalLinearLayout.setClipToPadding(false); mHorizontalLinearLayout.setClipToPadding(false);
int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing); int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout); mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout,
notificationMediaManager, backgroundExecutor);
LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1); LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
lp2.setMarginEnd(marginSize); lp2.setMarginEnd(marginSize);
lp2.setMarginStart(0); lp2.setMarginStart(0);

View File

@@ -42,6 +42,7 @@ import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.NotificationMediaManager;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -53,6 +54,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@RunWithLooper @RunWithLooper
@@ -79,6 +81,10 @@ public class QSPanelTest extends SysuiTestCase {
private QSDetail.Callback mCallback; private QSDetail.Callback mCallback;
@Mock @Mock
private QSTileView mQSTileView; private QSTileView mQSTileView;
@Mock
private NotificationMediaManager mNotificationMediaManager;
@Mock
private Executor mBackgroundExecutor;
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
@@ -88,7 +94,7 @@ public class QSPanelTest extends SysuiTestCase {
mTestableLooper.runWithLooper(() -> { mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher, mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
mQSLogger); mQSLogger, mNotificationMediaManager, mBackgroundExecutor);
// Provides a parent with non-zero size for QSPanel // Provides a parent with non-zero size for QSPanel
mParentView = new FrameLayout(mContext); mParentView = new FrameLayout(mContext);
mParentView.addView(mQsPanel); mParentView.addView(mQsPanel);