1d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez/* 2d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Copyright 2018 The Android Open Source Project 3d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * 4d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Licensed under the Apache License, Version 2.0 (the "License"); 5d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * you may not use this file except in compliance with the License. 6d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * You may obtain a copy of the License at 7d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * 8d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * http://www.apache.org/licenses/LICENSE-2.0 9d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * 10d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Unless required by applicable law or agreed to in writing, software 11d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * distributed under the License is distributed on an "AS IS" BASIS, 12d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * See the License for the specific language governing permissions and 14d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * limitations under the License. 15d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 16d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 17d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezpackage com.android.car.media; 18d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 19d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.annotation.NonNull; 20d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.annotation.Nullable; 21d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.content.Context; 22d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.os.Bundle; 23450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.os.Handler; 24d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.support.v4.app.Fragment; 25d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.support.v7.widget.GridLayoutManager; 26d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.support.v7.widget.RecyclerView; 27d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.view.LayoutInflater; 28d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.view.View; 29d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport android.view.ViewGroup; 30450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.widget.ImageView; 31450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.widget.ProgressBar; 32450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perezimport android.widget.TextView; 33d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 34d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.browse.BrowseAdapter; 35d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.browse.ContentForwardStrategy; 36d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.common.GridSpacingItemDecoration; 37d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.common.MediaItemMetadata; 38d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport com.android.car.media.common.MediaSource; 39a51d23d9109f44187200495fdb4523d47255b14aRoberto Perezimport com.android.car.media.widgets.ViewUtils; 40d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 41d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport java.util.ArrayList; 42d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport java.util.List; 43d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport java.util.Stack; 44d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 45d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezimport androidx.car.widget.PagedListView; 46d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 47d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez/** 48d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * A {@link Fragment} that implements the content forward browsing experience. 49d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 50d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perezpublic class BrowseFragment extends Fragment { 51d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private static final String TAG = "BrowseFragment"; 52d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private static final String TOP_MEDIA_ITEM_KEY = "top_media_item"; 53d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private static final String MEDIA_SOURCE_PACKAGE_NAME_KEY = "media_source"; 54d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private static final String BROWSE_STACK_KEY = "browse_stack"; 55d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 56d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private PagedListView mBrowseList; 57450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private ProgressBar mProgressBar; 58450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private ImageView mErrorIcon; 59450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private TextView mErrorMessage; 60d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private MediaSource mMediaSource; 61d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private BrowseAdapter mBrowseAdapter; 62d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private String mMediaSourcePackageName; 63d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private MediaItemMetadata mTopMediaItem; 64d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private Callbacks mCallbacks; 65450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private int mFadeDuration; 66450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private int mProgressBarDelay; 67450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private Handler mHandler = new Handler(); 68d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private Stack<MediaItemMetadata> mBrowseStack = new Stack<>(); 69d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private MediaSource.Observer mBrowseObserver = new MediaSource.Observer() { 70d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 71d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez protected void onBrowseConnected(boolean success) { 72d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez BrowseFragment.this.onBrowseConnected(success); 73d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 74d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 75d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 76d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez protected void onBrowseDisconnected() { 77d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez BrowseFragment.this.onBrowseDisconnected(); 78d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 79d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez }; 80d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private BrowseAdapter.Observer mBrowseAdapterObserver = new BrowseAdapter.Observer() { 81d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 82d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez protected void onDirty() { 83450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez switch (mBrowseAdapter.getState()) { 84450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez case LOADING: 85450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez case IDLE: 86450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez // Still loading... nothing to do. 87450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez break; 88450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez case LOADED: 89450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez stopLoadingIndicator(); 90450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mBrowseAdapter.update(); 91450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez if (mBrowseAdapter.getItemCount() > 0) { 92a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.showViewAnimated(mBrowseList, mFadeDuration); 931d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez ViewUtils.hideViewAnimated(mErrorIcon, mFadeDuration); 941d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez ViewUtils.hideViewAnimated(mErrorMessage, mFadeDuration); 95450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez } else { 96450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mErrorMessage.setText(R.string.nothing_to_play); 971d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez ViewUtils.hideViewAnimated(mBrowseList, mFadeDuration); 981d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez ViewUtils.hideViewAnimated(mErrorIcon, mFadeDuration); 99a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.showViewAnimated(mErrorMessage, mFadeDuration); 100450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez } 101450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez break; 102450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez case ERROR: 103450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez stopLoadingIndicator(); 104450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mErrorMessage.setText(R.string.unknown_error); 1051d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez ViewUtils.hideViewAnimated(mBrowseList, mFadeDuration); 106a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.showViewAnimated(mErrorMessage, mFadeDuration); 107a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.showViewAnimated(mErrorIcon, mFadeDuration); 108450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez break; 109d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 110d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 111d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 112d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 113d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez protected void onPlayableItemClicked(MediaItemMetadata item) { 114d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mCallbacks.onPlayableItemClicked(mMediaSource, item); 115d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 116d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 117d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 118d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez protected void onBrowseableItemClicked(MediaItemMetadata item) { 119d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez navigateInto(item); 120d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 121d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 122d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 123d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez protected void onMoreButtonClicked(MediaItemMetadata item) { 124d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez navigateInto(item); 125d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 126d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez }; 127d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 128d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 129d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Fragment callbacks (implemented by the hosting Activity) 130d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 131d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public interface Callbacks { 132d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 133d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * @return a {@link MediaSource} corresponding to the given package name 134d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 135d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez MediaSource getMediaSource(String packageName); 136d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 137d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 138d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Method invoked when the back stack changes (for example, when the user moves up or down 139d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * the media tree) 140d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 141d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez void onBackStackChanged(); 142d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 143d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 144d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Method invoked when the user clicks on a playable item 145d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * 146d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * @param mediaSource {@link MediaSource} the playable item belongs to 147d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * @param item item to be played. 148d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 149d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez void onPlayableItemClicked(MediaSource mediaSource, MediaItemMetadata item); 150d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 151d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 152d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 153d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Moves the user one level up in the browse tree, if possible. 154d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 155d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void navigateBack() { 156d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseStack.pop(); 157d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mBrowseAdapter != null) { 158d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter.setParentMediaItemId(getCurrentMediaItem()); 159d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 160d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mCallbacks != null) { 161d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mCallbacks.onBackStackChanged(); 162d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 163d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 164d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 165d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 166d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * @return whether the user is in a level other than the top. 167d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 168d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public boolean isBackEnabled() { 169d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez return !mBrowseStack.isEmpty(); 170d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 171d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 172d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 173d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * Creates a new instance of this fragment. 174d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * 175fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez * @param mediaSource media source being displayed 176d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * @param item media tree node to display on this fragment. 177d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * @return a fully initialized {@link BrowseFragment} 178d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 179d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public static BrowseFragment newInstance(MediaSource mediaSource, MediaItemMetadata item) { 180d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez BrowseFragment fragment = new BrowseFragment(); 181d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez Bundle args = new Bundle(); 182d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez args.putParcelable(TOP_MEDIA_ITEM_KEY, item); 183d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez args.putString(MEDIA_SOURCE_PACKAGE_NAME_KEY, mediaSource.getPackageName()); 184d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez fragment.setArguments(args); 185d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez return fragment; 186d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 187d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 188d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 189d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void onCreate(@Nullable Bundle savedInstanceState) { 190d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez super.onCreate(savedInstanceState); 191d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez Bundle arguments = getArguments(); 192d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (arguments != null) { 193d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mTopMediaItem = arguments.getParcelable(TOP_MEDIA_ITEM_KEY); 194d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mMediaSourcePackageName = arguments.getString(MEDIA_SOURCE_PACKAGE_NAME_KEY); 195d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 196d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (savedInstanceState != null) { 197d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez List<MediaItemMetadata> savedStack = 198d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez savedInstanceState.getParcelableArrayList(BROWSE_STACK_KEY); 199d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseStack.clear(); 200d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (savedStack != null) { 201d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseStack.addAll(savedStack); 202d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 203d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 204d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 205d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 206d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 207d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public View onCreateView(LayoutInflater inflater, final ViewGroup container, 208d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez Bundle savedInstanceState) { 209d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez View view = inflater.inflate(R.layout.fragment_browse, container, false); 210450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mProgressBar = view.findViewById(R.id.loading_spinner); 211450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mProgressBarDelay = getContext().getResources() 212450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez .getInteger(R.integer.progress_indicator_delay); 213d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseList = view.findViewById(R.id.browse_list); 214450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mErrorIcon = view.findViewById(R.id.error_icon); 215450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mErrorMessage = view.findViewById(R.id.error_message); 216450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mFadeDuration = getContext().getResources().getInteger( 217450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez R.integer.new_album_art_fade_in_duration); 218d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez int numColumns = getContext().getResources().getInteger(R.integer.num_browse_columns); 219d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), numColumns); 220d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez RecyclerView recyclerView = mBrowseList.getRecyclerView(); 221d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez recyclerView.setVerticalFadingEdgeEnabled(true); 222d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez recyclerView.setFadingEdgeLength(getResources() 223a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez .getDimensionPixelSize(R.dimen.car_padding_5)); 224d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez recyclerView.setLayoutManager(gridLayoutManager); 225d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez recyclerView.addItemDecoration(new GridSpacingItemDecoration( 226d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez getResources().getDimensionPixelSize(R.dimen.car_padding_4), 227d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez getResources().getDimensionPixelSize(R.dimen.car_keyline_1), 228d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez getResources().getDimensionPixelSize(R.dimen.car_keyline_1) 229d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez )); 230d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez return view; 231d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 232d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 233d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 234d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void onAttach(Context context) { 235d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez super.onAttach(context); 236d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mCallbacks = (Callbacks) context; 237d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 238d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 239d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 240d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void onDetach() { 241d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez super.onDetach(); 242d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mCallbacks = null; 243d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 244d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 245d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 246d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void onStart() { 247d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez super.onStart(); 248450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez startLoadingIndicator(); 249d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mMediaSource = mCallbacks.getMediaSource(mMediaSourcePackageName); 250d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mMediaSource != null) { 251d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mMediaSource.subscribe(mBrowseObserver); 252d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 253d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 254d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 255450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private Runnable mProgressIndicatorRunnable = new Runnable() { 256450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez @Override 257450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez public void run() { 258a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.showViewAnimated(mProgressBar, mFadeDuration); 259450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez } 260450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez }; 261450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez 262450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private void startLoadingIndicator() { 263450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez // Display the indicator after a certain time, to avoid flashing the indicator constantly, 264450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez // even when performance is acceptable. 265450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mHandler.postDelayed(mProgressIndicatorRunnable, mProgressBarDelay); 266450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez } 267450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez 268450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez private void stopLoadingIndicator() { 269450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mHandler.removeCallbacks(mProgressIndicatorRunnable); 270a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.hideViewAnimated(mProgressBar, mFadeDuration); 271450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez } 272450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez 273d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 274d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void onStop() { 275d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez super.onStop(); 276450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez stopLoadingIndicator(); 277d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mMediaSource != null) { 278d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mMediaSource.unsubscribe(mBrowseObserver); 279d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 280d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mBrowseAdapter != null) { 281d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter.stop(); 2821d3e7e02676ed495b9f99701fa4647aa524d3f1bRoberto Perez mBrowseAdapter = null; 283d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 284d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 285d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 286d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez @Override 287d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public void onSaveInstanceState(@NonNull Bundle outState) { 288d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez super.onSaveInstanceState(outState); 289d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez ArrayList<MediaItemMetadata> stack = new ArrayList<>(mBrowseStack); 290d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez outState.putParcelableArrayList(BROWSE_STACK_KEY, stack); 291d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 292d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 293d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private void onBrowseConnected(boolean success) { 294d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mBrowseAdapter != null) { 295d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter.stop(); 296d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter = null; 297d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 298d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (!success) { 299a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.hideViewAnimated(mBrowseList, mFadeDuration); 300450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez stopLoadingIndicator(); 301450c73a79e566f1c4a8758648525b457005e9f9aRoberto Perez mErrorMessage.setText(R.string.cannot_connect_to_app); 302a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.showViewAnimated(mErrorIcon, mFadeDuration); 303a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez ViewUtils.showViewAnimated(mErrorMessage, mFadeDuration); 304d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez return; 305d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 306fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez mBrowseAdapter = new BrowseAdapter(getContext(), mMediaSource, getCurrentMediaItem(), 307fc30bbfdb4221ecd18adf59501d5ca82ca631310Roberto Perez ContentForwardStrategy.DEFAULT_STRATEGY); 308d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseList.setAdapter(mBrowseAdapter); 309a51d23d9109f44187200495fdb4523d47255b14aRoberto Perez mBrowseList.setDividerVisibilityManager(mBrowseAdapter); 310d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter.registerObserver(mBrowseAdapterObserver); 311d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter.start(); 312d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 313d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 314d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private void onBrowseDisconnected() { 315d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mBrowseAdapter != null) { 316d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter.stop(); 317d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter = null; 318d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 319d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 320d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 321d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez private void navigateInto(MediaItemMetadata item) { 322d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseStack.push(item); 323d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mBrowseAdapter.setParentMediaItemId(item); 324d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez mCallbacks.onBackStackChanged(); 325d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 326d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez 327d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez /** 328d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez * @return the current item being displayed 329d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez */ 330d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez public MediaItemMetadata getCurrentMediaItem() { 331d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez if (mBrowseStack.isEmpty()) { 332d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez return mTopMediaItem; 333d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } else { 334d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez return mBrowseStack.lastElement(); 335d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 336d1f0ec11284f6e6ed05d6a6fb7e2e086cc4a9837Roberto Perez } 3375289168183d65b16813447c2b160adab799f1ac8Roberto Perez} 338