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