summaryrefslogtreecommitdiff
path: root/src/com/android/car
diff options
context:
space:
mode:
authorRoberto Perez <robertoalexis@google.com>2018-04-25 20:37:46 -0700
committerRoberto Perez <robertoalexis@google.com>2018-04-27 14:57:34 -0700
commit5a5a06b91563988c671319c68d9ce1a66f352a95 (patch)
treec93d4aadf304fc0fd6c330cd367f911957cedb29 /src/com/android/car
parenta72deaa7b41c4f2677aac8092823176e61306b93 (diff)
downloadMedia-5a5a06b91563988c671319c68d9ce1a66f352a95.tar.gz
Updating tabs to match design. Add the new app-switch icon.
Implement support for new navigation states. Bug: 78571420 Test: Tested on Big Dog Change-Id: Ic5dd3c5d6711d8ee7a088dd64c5dd62aed0024d8
Diffstat (limited to 'src/com/android/car')
-rw-r--r--src/com/android/car/media/MediaActivity.java74
-rw-r--r--src/com/android/car/media/widgets/AppBarView.java269
-rw-r--r--src/com/android/car/media/widgets/MediaItemTabView.java26
3 files changed, 327 insertions, 42 deletions
diff --git a/src/com/android/car/media/MediaActivity.java b/src/com/android/car/media/MediaActivity.java
index 3ac5396..3a7fa12 100644
--- a/src/com/android/car/media/MediaActivity.java
+++ b/src/com/android/car/media/MediaActivity.java
@@ -19,7 +19,6 @@ import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
-import android.support.design.widget.TabLayout;
import android.support.design.widget.AppBarLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@@ -34,7 +33,7 @@ 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.drawer.MediaDrawerController;
-import com.android.car.media.widgets.MediaItemTabView;
+import com.android.car.media.widgets.AppBarView;
import com.android.car.media.widgets.MetadataView;
import java.util.ArrayList;
@@ -68,16 +67,15 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
private PlaybackModel mPlaybackModel;
/** Layout views */
- private TabLayout mTabLayout;
+ private AppBarView mAppBarView;
private CrossfadeImageView mAlbumBackground;
private PlaybackFragment mPlaybackFragment;
- private AppBarLayout mAppBarLayout;
+ private AppBarLayout mDrawerBarLayout;
private View mBrowseScrim;
private PlaybackControls mPlaybackControls;
private MetadataView mMetadataView;
private ViewGroup mBrowseControlsContainer;
-
/** Current state */
private MediaItemMetadata mCurrentMetadata;
private Fragment mCurrentFragment;
@@ -109,27 +107,30 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
updateMetadata();
}
};
- private TabLayout.OnTabSelectedListener mTabSelectedListener =
- new TabLayout.OnTabSelectedListener() {
+ private AppBarView.AppBarListener mAppBarListener = new AppBarView.AppBarListener() {
@Override
- public void onTabSelected(TabLayout.Tab tab) {
- Log.i(TAG, "onTabSelected: " + tab.getTag());
+ public void onTabSelected(MediaItemMetadata item) {
mMode = Mode.BROWSING;
- updateBrowseFragment((MediaItemMetadata) tab.getTag());
+ updateBrowseFragment(item);
updateMetadata();
}
@Override
- public void onTabUnselected(TabLayout.Tab tab) {
- // Nothing to do
+ public void onBack() {
+ if (mCurrentFragment != null && mCurrentFragment instanceof BrowseFragment) {
+ BrowseFragment fragment = (BrowseFragment) mCurrentFragment;
+ fragment.navigateBack();
+ }
}
@Override
- public void onTabReselected(TabLayout.Tab tab) {
- Log.i(TAG, "onTabReselected: " + tab.getTag());
- mMode = Mode.BROWSING;
- updateBrowseFragment((MediaItemMetadata) tab.getTag());
- updateMetadata();
+ public void onCollapse() {
+ switchToMode(Mode.BROWSING);
+ }
+
+ @Override
+ public void onAppSelection() {
+ // TODO(b/78602199): Implement app selection logic
}
};
@@ -152,12 +153,15 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
.getBoolean(R.bool.force_browse_tabs);
mDrawerController = new MediaDrawerController(this, getDrawerController());
getDrawerController().setRootAdapter(getRootAdapter());
- mTabLayout = findViewById(R.id.tabs);
- mTabLayout.addOnTabSelectedListener(mTabSelectedListener);
+ mAppBarView = findViewById(R.id.app_bar);
+ mAppBarView.setListener(mAppBarListener);
+ // TODO(b/78602199): Implement actual app selection logic
+ mAppBarView.setAppSelection(true);
mPlaybackFragment = new PlaybackFragment();
mPlaybackModel = new PlaybackModel(this);
mMaxBrowserTabs = getResources().getInteger(R.integer.max_browse_tabs);
- mAppBarLayout = findViewById(androidx.car.R.id.appbar);
+ mDrawerBarLayout = findViewById(androidx.car.R.id.appbar);
+ mDrawerBarLayout.setVisibility(View.GONE);
mAlbumBackground = findViewById(R.id.media_background);
mBrowseScrim = findViewById(R.id.browse_scrim);
mPlaybackControls = findViewById(R.id.controls);
@@ -266,6 +270,12 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
mMediaSource.getPackageName());
mMediaSource.subscribe(mMediaSourceObserver);
}
+ // TODO: Show rounded icon
+ mAppBarView.setAppIcon(null);
+ mAppBarView.setTitle(mMediaSource.getName());
+ } else {
+ mAppBarView.setAppIcon(null);
+ mAppBarView.setTitle(null);
}
}
@@ -303,33 +313,19 @@ public class MediaActivity extends CarDrawerActivity implements BrowseFragment.C
.filter(item -> item.isPlayable())
.collect(Collectors.toList());
- Log.i(TAG, "Updating top level: " + browsableTopLevel.size() + " browsable items, "
- + playableTopLevel.size() + " playable items");
- mTabLayout.removeAllTabs();
// Show tabs if:
// - We have some browesable items and we are forced to show them as tabs,
// - or we have only browsable items on the top level and they are not too many.
if ((!browsableTopLevel.isEmpty() && mForceBrowseTabs)
|| (playableTopLevel.isEmpty() && !browsableTopLevel.isEmpty()
&& browsableTopLevel.size() <= mMaxBrowserTabs)) {
- mAppBarLayout.setVisibility(View.GONE);
- mTabLayout.setVisibility(View.VISIBLE);
- // Temporarily removing the listener, to prevent initial tab selection event.
- mTabLayout.removeOnTabSelectedListener(mTabSelectedListener);
- int count = 0;
- for (MediaItemMetadata item : browsableTopLevel) {
- MediaItemTabView tab = new MediaItemTabView(this, item);
- mTabLayout.addTab(mTabLayout.newTab().setCustomView(tab).setTag(item));
- count++;
- if (count >= mMaxBrowserTabs) {
- break;
- }
- }
- mTabLayout.addOnTabSelectedListener(mTabSelectedListener);
+ mDrawerBarLayout.setVisibility(View.GONE);
+ mAppBarView.setVisibility(View.VISIBLE);
+ mAppBarView.setItems(browsableTopLevel);
updateBrowseFragment(browsableTopLevel.get(0));
} else {
- mAppBarLayout.setVisibility(View.VISIBLE);
- mTabLayout.setVisibility(View.INVISIBLE);
+ mDrawerBarLayout.setVisibility(View.VISIBLE);
+ mAppBarView.setVisibility(View.INVISIBLE);
updateBrowseFragment(null);
}
}
diff --git a/src/com/android/car/media/widgets/AppBarView.java b/src/com/android/car/media/widgets/AppBarView.java
new file mode 100644
index 0000000..622e626
--- /dev/null
+++ b/src/com/android/car/media/widgets/AppBarView.java
@@ -0,0 +1,269 @@
+package com.android.car.media.widgets;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.support.design.widget.TabLayout;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.android.car.media.R;
+import com.android.car.media.common.MediaItemMetadata;
+import com.android.car.media.common.MediaSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Media template application bar. A detailed explanation of all possible states of this
+ * application bar can be seen at {@link AppBarView.State}.
+ */
+public class AppBarView extends RelativeLayout {
+ /** Default number of tabs to show on this app bar */
+ private static int DEFAULT_MAX_TABS = 4;
+
+ private List<MediaSource> mMediaSources = new ArrayList<>();
+ private LinearLayout mTabsContainer;
+ private ImageView mAppIcon;
+ private ImageView mAppSwitchIcon;
+ private ImageView mNavIcon;
+ private TextView mTitle;
+ private ViewGroup mAppSwitchContainer;
+ private Context mContext;
+ private int mMaxTabs;
+ private Drawable mArrowDropDown;
+ private Drawable mArrowDropUp;
+ private Drawable mArrowBack;
+ private Drawable mCollapse;
+ private State mState = State.IDLE;
+ private AppBarListener mListener;
+
+ /**
+ * Application bar listener
+ */
+ public interface AppBarListener {
+ /**
+ * Invoked when the user selects an item from the tabs
+ */
+ void onTabSelected(MediaItemMetadata item);
+
+ /**
+ * Invoked when the user clicks on the back button
+ */
+ void onBack();
+
+ /**
+ * Invoked when the user clicks on the collapse button
+ */
+ void onCollapse();
+
+ /**
+ * Invoked when the user clicks on the app selection switch
+ */
+ void onAppSelection();
+ }
+
+ /**
+ * Possible states of this application bar
+ */
+ public enum State {
+ /**
+ * Normal application state. If we are able to obtain media items from the media
+ * source application, we display them as tabs. Otherwise we show the application name.
+ */
+ IDLE,
+ /**
+ * Indicates that the user has navigated into an element. In this case we show
+ * the name of the element and we disable the back button.
+ */
+ STACKED,
+ /**
+ * Indicates that we have expanded a view that can be collapsed. We show the
+ * title of the application and a collapse icon
+ */
+ EXPANDED,
+ /**
+ * Used to indicate that the user is inside the app selector. In this case we disable
+ * navigation, we show the title of the application and we show the app switch icon
+ * point up
+ */
+ APP_SELECTION
+ }
+
+ public AppBarView(Context context) {
+ this(context, null);
+ }
+
+ public AppBarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public AppBarView(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.AppBarView, defStyleAttr, defStyleRes);
+ mMaxTabs = ta.getInteger(R.styleable.AppBarView_max_tabs, DEFAULT_MAX_TABS);
+ ta.recycle();
+
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.appbar_view, this, true);
+
+ mContext = context;
+ mTabsContainer = findViewById(R.id.tabs);
+ mNavIcon = findViewById(R.id.nav_icon);
+ mNavIcon.setOnClickListener(view -> onNavIconClicked());
+ mAppIcon = findViewById(R.id.app_icon);
+ mAppSwitchIcon = findViewById(R.id.app_switch_icon);
+ mAppSwitchContainer = findViewById(R.id.app_switch_container);
+ mAppSwitchContainer.setOnClickListener(view -> onAppSwitchClicked());
+ mTitle = findViewById(R.id.title);
+ mArrowDropDown = getResources().getDrawable(R.drawable.ic_arrow_drop_down, null);
+ mArrowDropUp = getResources().getDrawable(R.drawable.ic_arrow_drop_up, null);
+ mArrowBack = getResources().getDrawable(R.drawable.ic_arrow_back, null);
+ mCollapse = getResources().getDrawable(R.drawable.ic_expand_more, null);
+
+ setState(State.IDLE);
+ }
+
+ private void onNavIconClicked() {
+ if (mListener == null) {
+ return;
+ }
+ switch (mState) {
+ case STACKED:
+ mListener.onBack();
+ break;
+ case EXPANDED:
+ mListener.onCollapse();
+ break;
+ }
+ }
+
+ private void onAppSwitchClicked() {
+ if (mListener == null) {
+ return;
+ }
+ mListener.onAppSelection();
+ }
+
+ /**
+ * Sets a listener of this application bar events. In order to avoid memory leaks, consumers
+ * must reset this reference by setting the listener to null.
+ */
+ public void setListener(AppBarListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Updates the list of items to show in the application bar tabs.
+ */
+ public void setItems(List<MediaItemMetadata> items) {
+ mTabsContainer.removeAllViews();
+
+ if (items != null) {
+ int count = 0;
+ int padding = mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_4);
+ int tabWidth = mContext.getResources().getDimensionPixelSize(R.dimen.browse_tab_width) +
+ 2 * padding;
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+ tabWidth, ViewGroup.LayoutParams.MATCH_PARENT);
+ for (MediaItemMetadata item : items) {
+ MediaItemTabView tab = new MediaItemTabView(mContext, item);
+ mTabsContainer.addView(tab);
+ tab.setLayoutParams(layoutParams);
+ tab.setOnClickListener(view -> {
+ if (mListener != null) {
+ mListener.onTabSelected(item);
+ }
+ });
+ tab.setPadding(padding, 0, padding, 0);
+ tab.requestLayout();
+ count++;
+ if (count >= mMaxTabs) {
+ break;
+ }
+ }
+ }
+
+ if (mState == State.IDLE) {
+ boolean hasItems = items != null && !items.isEmpty();
+ mTabsContainer.setVisibility(hasItems ? View.VISIBLE : View.GONE);
+ mTitle.setVisibility(hasItems ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ /**
+ * Updates the title to display when the bar is not showing tabs.
+ */
+ public void setTitle(CharSequence title) {
+ mTitle.setText(title);
+ }
+
+ /**
+ * Updates the application icon to show next to the application switcher.
+ */
+ public void setAppIcon(Bitmap icon) {
+ mAppIcon.setImageBitmap(icon);
+ mAppIcon.setVisibility(icon != null ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Indicates whether or not the application switcher should be enabled.
+ */
+ public void setAppSelection(boolean enabled) {
+ mAppSwitchIcon.setVisibility(enabled ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Updates the state of the bar.
+ */
+ public void setState(State state) {
+ boolean hasItems = mTabsContainer.getChildCount() > 0;
+ mState = state;
+
+ switch (state) {
+ case IDLE:
+ mNavIcon.setVisibility(View.GONE);
+ mTabsContainer.setVisibility(hasItems ? View.VISIBLE : View.GONE);
+ mTitle.setVisibility(hasItems ? View.GONE : View.VISIBLE);
+ mAppSwitchIcon.setImageDrawable(mArrowDropDown);
+ break;
+ case STACKED:
+ mNavIcon.setImageDrawable(mArrowBack);
+ mNavIcon.setVisibility(View.VISIBLE);
+ mTabsContainer.setVisibility(View.GONE);
+ mTitle.setVisibility(View.VISIBLE);
+ mAppSwitchIcon.setImageDrawable(mArrowDropDown);
+ break;
+ case EXPANDED:
+ mNavIcon.setImageDrawable(mCollapse);
+ mNavIcon.setVisibility(View.VISIBLE);
+ mTabsContainer.setVisibility(View.GONE);
+ mTitle.setVisibility(View.VISIBLE);
+ mAppSwitchIcon.setImageDrawable(mArrowDropDown);
+ break;
+ case APP_SELECTION:
+ mNavIcon.setVisibility(View.GONE);
+ mTabsContainer.setVisibility(View.GONE);
+ mTitle.setVisibility(View.VISIBLE);
+ mAppSwitchIcon.setImageDrawable(mArrowDropUp);
+ break;
+ }
+ }
+}
diff --git a/src/com/android/car/media/widgets/MediaItemTabView.java b/src/com/android/car/media/widgets/MediaItemTabView.java
index c70cdff..9701926 100644
--- a/src/com/android/car/media/widgets/MediaItemTabView.java
+++ b/src/com/android/car/media/widgets/MediaItemTabView.java
@@ -16,7 +16,10 @@
package com.android.car.media.widgets;
+import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -29,22 +32,39 @@ import com.android.car.media.common.MediaItemMetadata;
* A view representing a media item to be included in the tab bar at the top of the UI.
*/
public class MediaItemTabView extends LinearLayout {
- private TextView mTitleView;
- private ImageView mImageView;
+ private final TextView mTitleView;
+ private final ImageView mImageView;
+ private final MediaItemMetadata mItem;
/**
* Creates a new tab for the given media item.
*/
- public MediaItemTabView(Context context, MediaItemMetadata item) {
+ public MediaItemTabView(@NonNull Context context, @NonNull MediaItemMetadata item) {
super(context);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.tab_view, this, true);
setOrientation(LinearLayout.VERTICAL);
+ setFocusable(true);
+ setGravity(Gravity.CENTER);
+ int[] attrs = new int[]{android.R.attr.selectableItemBackground};
+ TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ int backgroundResource = typedArray.getResourceId(0, 0);
+ setBackgroundResource(backgroundResource);
+
+ mItem = item;
mImageView = findViewById(R.id.icon);
MediaItemMetadata.updateImageView(context, item, mImageView, 0);
mTitleView = findViewById(R.id.title);
mTitleView.setText(item.getTitle());
}
+
+ /**
+ * Returns the item represented by this view
+ */
+ @NonNull
+ public MediaItemMetadata getItem() {
+ return mItem;
+ }
}