summaryrefslogtreecommitdiff
path: root/src/com/android
diff options
context:
space:
mode:
authorJoshua Brown <keyboardr@google.com>2018-04-27 13:20:02 -0700
committerJoshua Brown <keyboardr@google.com>2018-05-02 11:40:05 -0700
commit30ff1007ab6729566d053649d3e973ac9fd33627 (patch)
tree523c2a5effee661a20c9b1b6be3b0b6a8876b86d /src/com/android
parent3b78b872b97b59880ad46c96114bc349570fc333 (diff)
downloadMedia-30ff1007ab6729566d053649d3e973ac9fd33627.tar.gz
DO NOT MERGE Show/hide playback queue
Flattens layouts so that they can be easily transitioned with ConstraintSets. Extracts metadata logic into MetadataController. Bug: 78784683 Test: Manual Change-Id: I55f2de884e278fc38fe23a64a84a48d156959747
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/car/media/MetadataController.java153
-rw-r--r--src/com/android/car/media/PlaybackFragment.java140
-rw-r--r--src/com/android/car/media/widgets/MetadataView.java155
3 files changed, 246 insertions, 202 deletions
diff --git a/src/com/android/car/media/MetadataController.java b/src/com/android/car/media/MetadataController.java
new file mode 100644
index 0000000..f5c382a
--- /dev/null
+++ b/src/com/android/car/media/MetadataController.java
@@ -0,0 +1,153 @@
+package com.android.car.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.android.car.media.common.MediaItemMetadata;
+import com.android.car.media.common.PlaybackModel;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Common controller for displaying current track's metadata.
+ */
+public class MetadataController {
+
+ private static final DateFormat TIME_FORMAT = new SimpleDateFormat("m:ss", Locale.US);
+
+ @NonNull
+ private final TextView mTitle;
+ @NonNull
+ private final TextView mSubtitle;
+ @Nullable
+ private final TextView mTime;
+ @NonNull
+ private final SeekBar mSeekBar;
+ @Nullable
+ private final ImageView mAlbumArt;
+
+ @Nullable
+ private PlaybackModel mModel;
+ @Nullable
+ private MediaItemMetadata mCurrentMetadata;
+
+ private final PlaybackModel.PlaybackObserver mPlaybackObserver =
+ new PlaybackModel.PlaybackObserver() {
+ @Override
+ public void onPlaybackStateChanged() {
+ updateState();
+ }
+
+ @Override
+ public void onSourceChanged() {
+ updateState();
+ updateMetadata();
+ }
+
+ @Override
+ public void onMetadataChanged() {
+ updateMetadata();
+ }
+ };
+
+ /**
+ * Create a new MetadataController that operates on the provided Views
+ * @param title Displays the track's title. Must not be {@code null}.
+ * @param subtitle Displays the track's artist. Must not be {@code null}.
+ * @param time Displays the track's progress as text. May be {@code null}.
+ * @param seekBar Displays the track's progress visually. Must not be {@code null}.
+ * @param albumArt Displays the track's album art. May be {@code null}.
+ */
+ public MetadataController(@NonNull TextView title, @NonNull TextView subtitle,
+ @Nullable TextView time, @NonNull SeekBar seekBar, @Nullable ImageView albumArt) {
+ mTitle = title;
+ mSubtitle = subtitle;
+ mTime = time;
+ mSeekBar = seekBar;
+ mAlbumArt = albumArt;
+ }
+
+ /**
+ * Registers the {@link PlaybackModel} this widget will use to follow playback state.
+ * Consumers of this class must unregister the {@link PlaybackModel} by calling this method with
+ * null.
+ *
+ * @param model {@link PlaybackModel} to subscribe, or null to unsubscribe.
+ */
+ public void setModel(@Nullable PlaybackModel model) {
+ if (mModel != null) {
+ mModel.unregisterObserver(mPlaybackObserver);
+ }
+ mModel = model;
+ if (mModel != null) {
+ mModel.registerObserver(mPlaybackObserver);
+ }
+ }
+
+ private void updateState() {
+ updateProgress();
+
+ if (mModel != null && mModel.isPlaying()) {
+ mSeekBar.post(mSeekBarRunnable);
+ } else {
+ mSeekBar.removeCallbacks(mSeekBarRunnable);
+ }
+ }
+
+ private void updateMetadata() {
+ MediaItemMetadata metadata = mModel != null ? mModel.getMetadata() : null;
+ if (Objects.equals(mCurrentMetadata, metadata)) {
+ return;
+ }
+ mCurrentMetadata = metadata;
+ mTitle.setText(metadata != null ? metadata.getTitle() : null);
+ mSubtitle.setText(metadata != null ? metadata.getSubtitle() : null);
+ if (mAlbumArt != null) {
+ MediaItemMetadata.updateImageView(mAlbumArt.getContext(), metadata, mAlbumArt, 0);
+ }
+ }
+
+ private static final long SEEK_BAR_UPDATE_TIME_INTERVAL_MS = 1000;
+
+ private final Runnable mSeekBarRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mModel == null || !mModel.isPlaying()) {
+ return;
+ }
+ updateProgress();
+ mSeekBar.postDelayed(this, SEEK_BAR_UPDATE_TIME_INTERVAL_MS);
+
+ }
+ };
+
+ private void updateProgress() {
+ if (mModel == null) {
+ mTime.setVisibility(View.INVISIBLE);
+ mSeekBar.setVisibility(View.INVISIBLE);
+ return;
+ }
+ long maxProgress = mModel.getMaxProgress();
+ int visibility = maxProgress > 0 ? View.VISIBLE : View.INVISIBLE;
+ if (mTime != null) {
+ String time = String.format("%s / %s",
+ TIME_FORMAT.format(new Date(mModel.getProgress())),
+ TIME_FORMAT.format(new Date(maxProgress)));
+ mTime.setVisibility(visibility);
+ mTime.setText(time);
+ }
+ mSeekBar.setVisibility(visibility);
+ mSeekBar.setMax((int) mModel.getMaxProgress());
+ mSeekBar.setProgress((int) mModel.getProgress());
+ }
+
+
+}
diff --git a/src/com/android/car/media/PlaybackFragment.java b/src/com/android/car/media/PlaybackFragment.java
index 6b4994a..b133ff8 100644
--- a/src/com/android/car/media/PlaybackFragment.java
+++ b/src/com/android/car/media/PlaybackFragment.java
@@ -18,30 +18,33 @@ package com.android.car.media;
import android.content.Context;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.constraint.ConstraintLayout;
+import android.support.constraint.ConstraintSet;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
+import android.transition.Transition;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.TextListItem;
import com.android.car.media.common.MediaItemMetadata;
import com.android.car.media.common.MediaSource;
import com.android.car.media.common.PlaybackControls;
import com.android.car.media.common.PlaybackModel;
-import com.android.car.media.widgets.MetadataView;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
-import androidx.car.widget.ListItem;
-import androidx.car.widget.ListItemAdapter;
-import androidx.car.widget.ListItemProvider;
-import androidx.car.widget.PagedListView;
-import androidx.car.widget.TextListItem;
/**
* A {@link Fragment} that implements both the playback and the content forward browsing experience.
@@ -50,34 +53,32 @@ import androidx.car.widget.TextListItem;
*/
public class PlaybackFragment extends Fragment {
private static final String TAG = "PlaybackFragment";
- private static final DateFormat TIME_FORMAT = new SimpleDateFormat("m:ss", Locale.US);
private PlaybackModel mModel;
private PlaybackControls mPlaybackControls;
- private ImageView mAlbumArt;
- private MetadataView mMetadataView;
- private PagedListView mQueueList;
private QueueItemsAdapter mQueueAdapter;
- private MediaItemMetadata mCurrentMetadata;
- private boolean mQueueIsVisible;
- private PlaybackModel.PlaybackObserver mPlaybackObserver = new PlaybackModel.PlaybackObserver() {
- @Override
- public void onPlaybackStateChanged() {
- updateState();
- }
- @Override
- public void onSourceChanged() {
- updateState();
- updateMetadata();
- updateAccentColor();
- }
+ private MetadataController mMetadataController;
+ private ConstraintLayout mRootView;
- @Override
- public void onMetadataChanged() {
- updateMetadata();
- }
- };
+ private boolean mQueueIsVisible;
+ private PlaybackModel.PlaybackObserver mPlaybackObserver =
+ new PlaybackModel.PlaybackObserver() {
+ @Override
+ public void onPlaybackStateChanged() {
+ updateState();
+ }
+
+ @Override
+ public void onSourceChanged() {
+ updateAccentColor();
+ updateState();
+ }
+
+ @Override
+ public void onMetadataChanged() {
+ }
+ };
private ListItemProvider mQueueItemsProvider = new ListItemProvider() {
@Override
public ListItem get(int position) {
@@ -95,6 +96,7 @@ public class PlaybackFragment extends Fragment {
textListItem.setOnClickListener(v -> onQueueItemClicked(item));
return textListItem;
}
+
@Override
public int size() {
if (!mModel.hasQueue()) {
@@ -103,6 +105,7 @@ public class PlaybackFragment extends Fragment {
return mModel.getQueue().size();
}
};
+
private static class QueueItemsAdapter extends ListItemAdapter {
QueueItemsAdapter(Context context, ListItemProvider itemProvider) {
super(context, itemProvider, BackgroundStyle.SOLID);
@@ -114,70 +117,91 @@ public class PlaybackFragment extends Fragment {
this.notifyDataSetChanged();
}
}
+
private PlaybackControls.Listener mPlaybackControlsListener = new PlaybackControls.Listener() {
@Override
public void onToggleQueue() {
mQueueIsVisible = !mQueueIsVisible;
mPlaybackControls.setQueueVisible(mQueueIsVisible);
+ setQueueVisible(mQueueIsVisible);
}
};
@Override
- public View onCreateView(LayoutInflater inflater, final ViewGroup container,
+ public View onCreateView(@NonNull LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_playback, container, false);
+ mRootView = view.findViewById(R.id.playback_container);
mModel = new PlaybackModel(getContext());
- mPlaybackControls = view.findViewById(R.id.playback_controls);
+
+ initPlaybackControls(view.findViewById(R.id.playback_controls));
+ initQueue(view.findViewById(R.id.queue_list));
+ initMetadataController(view);
+ return view;
+ }
+
+ private void initPlaybackControls(PlaybackControls playbackControls) {
+ mPlaybackControls = playbackControls;
mPlaybackControls.setModel(mModel);
mPlaybackControls.setListener(mPlaybackControlsListener);
- ViewGroup playbackContainer = view.findViewById(R.id.playback_container);
- mPlaybackControls.setAnimationViewGroup(playbackContainer);
- mAlbumArt = view.findViewById(R.id.album_art);
- mMetadataView = view.findViewById(R.id.metadata);
- mQueueList = view.findViewById(R.id.queue_list);
- RecyclerView recyclerView = mQueueList.getRecyclerView();
+ mPlaybackControls.setAnimationViewGroup(mRootView);
+ }
+
+ private void initQueue(PagedListView queueList) {
+ RecyclerView recyclerView = queueList.getRecyclerView();
recyclerView.setVerticalFadingEdgeEnabled(true);
recyclerView.setFadingEdgeLength(getResources()
.getDimensionPixelSize(R.dimen.car_padding_4));
mQueueAdapter = new QueueItemsAdapter(getContext(), mQueueItemsProvider);
- mQueueList.setAdapter(mQueueAdapter);
+ queueList.setAdapter(mQueueAdapter);
+ }
- return view;
+ private void initMetadataController(View view) {
+ ImageView albumArt = view.findViewById(R.id.album_art);
+ TextView title = view.findViewById(R.id.title);
+ TextView subtitle = view.findViewById(R.id.subtitle);
+ SeekBar seekbar = view.findViewById(R.id.seek_bar);
+ TextView time = view.findViewById(R.id.time);
+ mMetadataController = new MetadataController(title, subtitle, time, seekbar, albumArt);
+ mMetadataController.setModel(mModel);
+ }
+
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mPlaybackControls.setModel(null);
+ mMetadataController.setModel(null);
+ mMetadataController = null;
}
@Override
public void onStart() {
super.onStart();
mModel.registerObserver(mPlaybackObserver);
- mMetadataView.setModel(mModel);
}
@Override
public void onStop() {
super.onStop();
mModel.unregisterObserver(mPlaybackObserver);
- mMetadataView.setModel(null);
- mCurrentMetadata = null;
}
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- mPlaybackControls.setModel(null);
+ public void setQueueVisible(boolean visible) {
+ Transition transition = TransitionInflater.from(getContext()).inflateTransition(
+ visible ? R.transition.queue_in : R.transition.queue_out);
+ TransitionManager.beginDelayedTransition(mRootView, transition);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mRootView.getContext(),
+ visible ? R.layout.fragment_playback_with_queue : R.layout.fragment_playback);
+ constraintSet.applyTo(mRootView);
+
}
private void updateState() {
mQueueAdapter.refresh();
}
- private void updateMetadata() {
- MediaItemMetadata metadata = mModel.getMetadata();
- if (Objects.equals(mCurrentMetadata, metadata)) {
- return;
- }
- mCurrentMetadata = metadata;
- MediaItemMetadata.updateImageView(getContext(), metadata, mAlbumArt, 0);
- }
private void updateAccentColor() {
int defaultColor = getResources().getColor(android.R.color.background_dark, null);
diff --git a/src/com/android/car/media/widgets/MetadataView.java b/src/com/android/car/media/widgets/MetadataView.java
index 4b3fb38..6d9e72a 100644
--- a/src/com/android/car/media/widgets/MetadataView.java
+++ b/src/com/android/car/media/widgets/MetadataView.java
@@ -1,117 +1,43 @@
package com.android.car.media.widgets;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
-import android.view.View;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
+import com.android.car.media.MetadataController;
import com.android.car.media.R;
-import com.android.car.media.common.MediaItemMetadata;
import com.android.car.media.common.PlaybackModel;
-import java.lang.annotation.Retention;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Objects;
-
/**
* A view that can be used to display the metadata and playback progress of a media item.
* This view can be styled using the "MetadataView" styleable attributes.
*/
public class MetadataView extends RelativeLayout {
- private static final DateFormat TIME_FORMAT = new SimpleDateFormat("m:ss", Locale.US);
-
- @Nullable
- private MediaItemMetadata mCurrentMetadata;
- private TextView mTitle;
- private TextView mTime;
- private TextView mSubtitle;
- private SeekBar mSeekbar;
- @Nullable
- private PlaybackModel mModel;
-
- private PlaybackModel.PlaybackObserver mPlaybackObserver =
- new PlaybackModel.PlaybackObserver() {
- @Override
- public void onPlaybackStateChanged() {
- updateState();
- }
-
- @Override
- public void onSourceChanged() {
- updateState();
- updateMetadata();
- }
-
- @Override
- public void onMetadataChanged() {
- updateMetadata();
- }
- };
-
- /**
- * The possible styles of this widget.
- */
- @IntDef({
- MetadataView.Style.COMPACT,
- MetadataView.Style.NORMAL
- })
- @Retention(SOURCE)
- public @interface Style {
- /** Compact style (small space between elements, no time progress indicator) */
- int COMPACT = 0;
- /** Normal style (normal spacing and progress indicator) */
- int NORMAL = 1;
- }
+ private final MetadataController mMetadataController;
public MetadataView(Context context) {
- super(context);
- init(context, null, 0, 0);
+ this(context, null);
}
public MetadataView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs, 0, 0);
+ this(context, attrs, 0);
}
public MetadataView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context, attrs, defStyleAttr, 0);
+ this(context, attrs, defStyleAttr, 0);
}
public MetadataView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init(context, attrs, defStyleAttr, defStyleRes);
- }
-
- private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- TypedArray ta = context.obtainStyledAttributes(
- attrs, R.styleable.MetadataView, defStyleAttr, defStyleRes);
- @Style int style = ta.getInteger(R.styleable.MetadataView_style, Style.NORMAL);
- ta.recycle();
-
- int layoutId = style == Style.COMPACT
- ? R.layout.metadata_compact
- : R.layout.metadata_normal;
-
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(layoutId, this, true);
-
- mTitle = findViewById(R.id.title);
- mSubtitle = findViewById(R.id.subtitle);
- mSeekbar = findViewById(R.id.seek_bar);
- mTime = findViewById(R.id.time);
+ LayoutInflater.from(context).inflate(R.layout.metadata_compact, this, true);
+ TextView title = findViewById(R.id.title);
+ TextView subtitle = findViewById(R.id.subtitle);
+ SeekBar seekBar = findViewById(R.id.seek_bar);
+ mMetadataController = new MetadataController(title, subtitle, null, seekBar, null);
}
/**
@@ -122,66 +48,7 @@ public class MetadataView extends RelativeLayout {
* @param model {@link PlaybackModel} to subscribe, or null to unsubscribe.
*/
public void setModel(@Nullable PlaybackModel model) {
- if (mModel != null) {
- mModel.unregisterObserver(mPlaybackObserver);
- }
- mModel = model;
- if (mModel != null) {
- mModel.registerObserver(mPlaybackObserver);
- }
- }
-
- private void updateState() {
- updateProgress();
-
- if (mModel != null && mModel.isPlaying()) {
- mSeekbar.post(mSeekBarRunnable);
- } else {
- mSeekbar.removeCallbacks(mSeekBarRunnable);
- }
+ mMetadataController.setModel(model);
}
- private void updateMetadata() {
- MediaItemMetadata metadata = mModel != null ? mModel.getMetadata() : null;
- if (Objects.equals(mCurrentMetadata, metadata)) {
- return;
- }
- mCurrentMetadata = metadata;
- mTitle.setText(metadata != null ? metadata.getTitle() : null);
- mSubtitle.setText(metadata != null ? metadata.getSubtitle() : null);
- }
-
- private static final long SEEK_BAR_UPDATE_TIME_INTERVAL_MS = 1000;
-
- private final Runnable mSeekBarRunnable = new Runnable() {
- @Override
- public void run() {
- if (mModel == null || !mModel.isPlaying()) {
- return;
- }
- updateProgress();
- mSeekbar.postDelayed(this, SEEK_BAR_UPDATE_TIME_INTERVAL_MS);
-
- }
- };
-
- private void updateProgress() {
- if (mModel == null) {
- mTime.setVisibility(View.INVISIBLE);
- mSeekbar.setVisibility(View.INVISIBLE);
- return;
- }
- long maxProgress = mModel.getMaxProgress();
- int visibility = maxProgress > 0 ? View.VISIBLE : View.INVISIBLE;
- if (mTime != null) {
- String time = String.format("%s / %s",
- TIME_FORMAT.format(new Date(mModel.getProgress())),
- TIME_FORMAT.format(new Date(maxProgress)));
- mTime.setVisibility(visibility);
- mTime.setText(time);
- }
- mSeekbar.setVisibility(visibility);
- mSeekbar.setMax((int) mModel.getMaxProgress());
- mSeekbar.setProgress((int) mModel.getProgress());
- }
}