• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.pump.fragment;
18 
19 import android.content.Context;
20 import android.graphics.Rect;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.util.DisplayMetrics;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.ImageView;
28 import android.widget.TextView;
29 
30 import androidx.annotation.NonNull;
31 import androidx.annotation.Nullable;
32 import androidx.annotation.UiThread;
33 import androidx.core.view.ViewCompat;
34 import androidx.fragment.app.Fragment;
35 import androidx.recyclerview.widget.GridLayoutManager;
36 import androidx.recyclerview.widget.RecyclerView;
37 
38 import com.android.pump.R;
39 import com.android.pump.activity.ArtistDetailsActivity;
40 import com.android.pump.db.Album;
41 import com.android.pump.db.Artist;
42 import com.android.pump.db.MediaDb;
43 import com.android.pump.util.Globals;
44 
45 import java.util.List;
46 
47 @UiThread
48 public class ArtistFragment extends Fragment {
49     private RecyclerView mRecyclerView;
50 
newInstance()51     public static @NonNull Fragment newInstance() {
52         return new ArtistFragment();
53     }
54 
55     @Override
onCreateView(@onNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)56     public @NonNull View onCreateView(@NonNull LayoutInflater inflater,
57             @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
58         View view = inflater.inflate(R.layout.fragment_artist, container, false);
59         mRecyclerView = view.findViewById(R.id.fragment_artist_recycler_view);
60         mRecyclerView.setHasFixedSize(true);
61         mRecyclerView.setAdapter(new ArtistAdapter(requireContext()));
62         mRecyclerView.addItemDecoration(new SpaceItemDecoration(4, 16));
63 
64         GridLayoutManager gridLayoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
65         gridLayoutManager.setSpanSizeLookup(
66                 new HeaderSpanSizeLookup(gridLayoutManager.getSpanCount()));
67 
68         // TODO(b/123707260) Enable view caching
69         //mRecyclerView.setItemViewCacheSize(0);
70         //mRecyclerView.setRecycledViewPool(Globals.getRecycledViewPool(requireContext()));
71         return view;
72     }
73 
74     private static class ArtistAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
75             implements MediaDb.UpdateCallback {
76         private final MediaDb mMediaDb;
77         private final List<Artist> mArtists; // TODO(b/123710968) Use android.support.v7.util.SortedList/android.support.v7.widget.util.SortedListAdapterCallback instead
78 
ArtistAdapter(@onNull Context context)79         private ArtistAdapter(@NonNull Context context) {
80             setHasStableIds(true);
81             mMediaDb = Globals.getMediaDb(context);
82             mArtists = mMediaDb.getArtists();
83         }
84 
onAttachedToRecyclerView(@onNull RecyclerView recyclerView)85         public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
86             mMediaDb.addArtistUpdateCallback(this);
87         }
88 
onDetachedFromRecyclerView(@onNull RecyclerView recyclerView)89         public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
90             mMediaDb.removeArtistUpdateCallback(this);
91         }
92 
93         @Override
onCreateViewHolder( @onNull ViewGroup parent, int viewType)94         public @NonNull RecyclerView.ViewHolder onCreateViewHolder(
95                 @NonNull ViewGroup parent, int viewType) {
96             if (viewType == R.layout.header) {
97                 return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext())
98                         .inflate(viewType, parent, false)) { };
99             } else {
100                 return new ArtistViewHolder(LayoutInflater.from(parent.getContext())
101                         .inflate(viewType, parent, false));
102             }
103         }
104 
105         @Override
onBindViewHolder(@onNull RecyclerView.ViewHolder holder, int position)106         public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
107             if (position == 0) {
108                 // TODO Handle header view
109             } else {
110                 Artist artist = mArtists.get(position - 1);
111                 mMediaDb.loadData(artist); // TODO Where should we call this? In bind()?
112                 ((ArtistViewHolder) holder).bind(artist);
113             }
114         }
115 
116         @Override
getItemCount()117         public int getItemCount() {
118             return mArtists.size() + 1;
119         }
120 
121         @Override
getItemId(int position)122         public long getItemId(int position) {
123             return position == 0 ? -1 : mArtists.get(position - 1).getId();
124         }
125 
126         @Override
getItemViewType(int position)127         public int getItemViewType(int position) {
128             return position == 0 ? R.layout.header : R.layout.artist;
129         }
130 
131         @Override
onItemsInserted(int index, int count)132         public void onItemsInserted(int index, int count) {
133             notifyItemRangeInserted(index + 1, count);
134         }
135 
136         @Override
onItemsUpdated(int index, int count)137         public void onItemsUpdated(int index, int count) {
138             notifyItemRangeChanged(index + 1, count);
139         }
140 
141         @Override
onItemsRemoved(int index, int count)142         public void onItemsRemoved(int index, int count) {
143             notifyItemRangeRemoved(index + 1, count);
144         }
145     }
146 
147     private static class ArtistViewHolder extends RecyclerView.ViewHolder {
ArtistViewHolder(@onNull View itemView)148         private ArtistViewHolder(@NonNull View itemView) {
149             super(itemView);
150         }
151 
bind(@onNull Artist artist)152         private void bind(@NonNull Artist artist) {
153             ImageView imageView = itemView.findViewById(R.id.artist_image);
154             TextView nameView = itemView.findViewById(R.id.artist_name);
155 
156             Uri artUri = null;
157             if (artist.getHeadshotUri() == null) {
158                 // Fallback to album art
159                 List<Album> albums = artist.getAlbums();
160                 for (Album album : albums) {
161                     if (album.getAlbumArtUri() != null) {
162                         artUri = album.getAlbumArtUri();
163                         break;
164                     }
165                 }
166             } else {
167                 artUri = artist.getHeadshotUri();
168             }
169             imageView.setImageURI(artUri);
170             nameView.setText(artist.getName());
171 
172             itemView.setOnClickListener((view) ->
173                     ArtistDetailsActivity.start(view.getContext(), artist));
174         }
175     }
176 
177     private static class SpaceItemDecoration extends RecyclerView.ItemDecoration {
178         private final int mXOffset;
179         private final int mYOffset;
180 
SpaceItemDecoration(int xOffset, int yOffset)181         private SpaceItemDecoration(int xOffset, int yOffset) {
182             mXOffset = xOffset;
183             mYOffset = yOffset;
184         }
185 
186         @Override
getItemOffsets(@onNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state)187         public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
188             DisplayMetrics displayMetrics = new DisplayMetrics();
189             ViewCompat.getDisplay(parent).getMetrics(displayMetrics);
190             outRect.left = outRect.right = (int) Math.ceil(mXOffset * displayMetrics.density);
191             if (parent.getChildAdapterPosition(view) > 0) {
192                 outRect.bottom = (int) Math.ceil(mYOffset * displayMetrics.density);
193             }
194         }
195     }
196 
197     private static class HeaderSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {
198         private final int mSpanCount;
199 
HeaderSpanSizeLookup(int spanCount)200         private HeaderSpanSizeLookup(int spanCount) {
201             mSpanCount = spanCount;
202         }
203 
204         @Override
getSpanSize(int position)205         public int getSpanSize(int position) {
206             return position == 0 ? mSpanCount : 1;
207         }
208 
209         @Override
getSpanIndex(int position, int spanCount)210         public int getSpanIndex(int position, int spanCount) {
211             return position == 0 ? 0 : (position - 1) % spanCount;
212         }
213 
214         @Override
getSpanGroupIndex(int adapterPosition, int spanCount)215         public int getSpanGroupIndex(int adapterPosition, int spanCount) {
216             return adapterPosition == 0 ? 0 : ((adapterPosition - 1) / spanCount) + 1;
217         }
218     }
219 }
220