blob: e4bcb09212842cfa9c68c5abd9dbe7694f1d9da0 [file] [log] [blame]
/*
* Copyright (C) 2019 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.qs;
import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
import android.app.Notification;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.R;
import com.android.systemui.media.MediaControlPanel;
import com.android.systemui.media.SeekBarObserver;
import com.android.systemui.media.SeekBarViewModel;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.concurrent.Executor;
/**
* Single media player for carousel in QSPanel
*/
public class QSMediaPlayer extends MediaControlPanel {
private static final String TAG = "QSMediaPlayer";
// Button IDs for QS controls
static final int[] QS_ACTION_IDS = {
R.id.action0,
R.id.action1,
R.id.action2,
R.id.action3,
R.id.action4
};
private final QSPanel mParent;
private final DelayableExecutor mBackgroundExecutor;
private final SeekBarViewModel mSeekBarViewModel;
private final SeekBarObserver mSeekBarObserver;
/**
* Initialize quick shade version of player
* @param context
* @param parent
* @param manager
* @param foregroundExecutor
* @param backgroundExecutor
*/
public QSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
LocalMediaManager routeManager, Executor foregroundExecutor,
DelayableExecutor backgroundExecutor) {
super(context, parent, manager, routeManager, R.layout.qs_media_panel, QS_ACTION_IDS,
foregroundExecutor, backgroundExecutor);
mParent = (QSPanel) parent;
mBackgroundExecutor = backgroundExecutor;
mSeekBarViewModel = new SeekBarViewModel(backgroundExecutor);
mSeekBarObserver = new SeekBarObserver(getView());
// Can't use the viewAttachLifecycle of media player because remove/add is used to adjust
// priority of players. As soon as it is removed, the lifecycle will end and the seek bar
// will stop updating. So, use the lifecycle of the parent instead.
mSeekBarViewModel.getProgress().observe(viewAttachLifecycle(parent), mSeekBarObserver);
SeekBar bar = getView().findViewById(R.id.media_progress_bar);
bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener());
bar.setOnTouchListener(mSeekBarViewModel.getSeekBarTouchListener());
}
/**
* Update media panel view for the given media session
* @param token token for this media session
* @param icon app notification icon
* @param iconColor foreground color (for text, icons)
* @param bgColor background color
* @param actionsContainer a LinearLayout containing the media action buttons
* @param notif reference to original notification
* @param key original notification's key
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor,
int bgColor, View actionsContainer, Notification notif, String key) {
String appName = Notification.Builder.recoverBuilder(getContext(), notif)
.loadHeaderAppName();
super.setMediaSession(token, icon, iconColor, bgColor, notif.contentIntent, appName, key);
// Media controls
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
int i = 0;
for (; i < parentActionsLayout.getChildCount() && i < QS_ACTION_IDS.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
ImageButton thatBtn = parentActionsLayout.findViewById(NOTIF_ACTION_IDS[i]);
if (thatBtn == null || thatBtn.getDrawable() == null
|| thatBtn.getVisibility() != View.VISIBLE) {
thisBtn.setVisibility(View.GONE);
continue;
}
Drawable thatIcon = thatBtn.getDrawable();
thisBtn.setImageDrawable(thatIcon.mutate());
thisBtn.setVisibility(View.VISIBLE);
thisBtn.setOnClickListener(v -> {
Log.d(TAG, "clicking on other button");
thatBtn.performClick();
});
}
// Hide any unused buttons
for (; i < QS_ACTION_IDS.length; i++) {
ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
thisBtn.setVisibility(View.GONE);
}
// Seek Bar
final MediaController controller = new MediaController(getContext(), token);
mBackgroundExecutor.execute(
() -> mSeekBarViewModel.updateController(controller, iconColor));
// Set up long press menu
View guts = mMediaNotifView.findViewById(R.id.media_guts);
View options = mMediaNotifView.findViewById(R.id.qs_media_controls_options);
options.setMinimumHeight(guts.getHeight());
View clearView = options.findViewById(R.id.remove);
clearView.setOnClickListener(b -> {
mParent.removeMediaPlayer(QSMediaPlayer.this);
});
ImageView removeIcon = options.findViewById(R.id.remove_icon);
removeIcon.setImageTintList(ColorStateList.valueOf(iconColor));
TextView removeText = options.findViewById(R.id.remove_text);
removeText.setTextColor(iconColor);
TextView cancelView = options.findViewById(R.id.cancel);
cancelView.setTextColor(iconColor);
cancelView.setOnClickListener(b -> {
options.setVisibility(View.GONE);
guts.setVisibility(View.VISIBLE);
});
// ... but don't enable it yet, and make sure is reset when the session is updated
mMediaNotifView.setOnLongClickListener(null);
options.setVisibility(View.GONE);
guts.setVisibility(View.VISIBLE);
}
@Override
public void clearControls() {
super.clearControls();
mSeekBarViewModel.clearController();
View guts = mMediaNotifView.findViewById(R.id.media_guts);
View options = mMediaNotifView.findViewById(R.id.qs_media_controls_options);
mMediaNotifView.setOnLongClickListener(v -> {
// Replace player view with close/cancel view
guts.setVisibility(View.GONE);
options.setVisibility(View.VISIBLE);
return true; // consumed click
});
}
/**
* Sets the listening state of the player.
*
* Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid
* unnecessary work when the QS panel is closed.
*
* @param listening True when player should be active. Otherwise, false.
*/
public void setListening(boolean listening) {
mSeekBarViewModel.setListening(listening);
}
}