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.activity;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.view.LayoutInflater;
24 import android.view.Menu;
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.appcompat.app.ActionBar;
34 import androidx.appcompat.app.AppCompatActivity;
35 import androidx.recyclerview.widget.RecyclerView;
36 
37 import com.android.pump.R;
38 import com.android.pump.db.Album;
39 import com.android.pump.db.Artist;
40 import com.android.pump.db.Audio;
41 import com.android.pump.db.MediaDb;
42 import com.android.pump.db.Playlist;
43 import com.android.pump.util.Globals;
44 
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Set;
49 
50 @UiThread
51 public class PlaylistDetailsActivity extends AppCompatActivity implements MediaDb.UpdateCallback {
52     private MediaDb mMediaDb;
53     private Playlist mPlaylist;
54 
start(@onNull Context context, @NonNull Playlist playlist)55     public static void start(@NonNull Context context, @NonNull Playlist playlist) {
56         Intent intent = new Intent(context, PlaylistDetailsActivity.class);
57         // TODO(b/123704452) Pass URI instead
58         intent.putExtra("id", playlist.getId()); // TODO Add constant key
59         context.startActivity(intent);
60     }
61 
62     @Override
onCreate(@ullable Bundle savedInstanceState)63     protected void onCreate(@Nullable Bundle savedInstanceState) {
64         super.onCreate(savedInstanceState);
65         setContentView(R.layout.activity_playlist_details);
66 
67         setSupportActionBar(findViewById(R.id.activity_playlist_details_toolbar));
68         ActionBar actionBar = getSupportActionBar();
69         if (actionBar != null) {
70             actionBar.setDisplayShowTitleEnabled(false);
71             actionBar.setDisplayShowHomeEnabled(true);
72             actionBar.setDisplayHomeAsUpEnabled(true);
73         }
74 
75         mMediaDb = Globals.getMediaDb(this);
76         mMediaDb.addPlaylistUpdateCallback(this);
77 
78         handleIntent();
79     }
80 
81     @Override
onNewIntent(@ullable Intent intent)82     protected void onNewIntent(@Nullable Intent intent) {
83         super.onNewIntent(intent);
84         setIntent(intent);
85 
86         handleIntent();
87     }
88 
89     @Override
onDestroy()90     protected void onDestroy() {
91         mMediaDb.removePlaylistUpdateCallback(this);
92 
93         super.onDestroy();
94     }
95 
96     @Override
onCreateOptionsMenu(@onNull Menu menu)97     public boolean onCreateOptionsMenu(@NonNull Menu menu) {
98         getMenuInflater().inflate(R.menu.activity_pump, menu); // TODO activity_playlist_details ?
99         return true;
100     }
101 
102     @Override
onSupportNavigateUp()103     public boolean onSupportNavigateUp() {
104         // TODO It should not be necessary to override this method
105         onBackPressed();
106         return true;
107     }
108 
109     @Override
onItemsInserted(int index, int count)110     public void onItemsInserted(int index, int count) { }
111 
112     @Override
onItemsUpdated(int index, int count)113     public void onItemsUpdated(int index, int count) {
114         for (int i = index; i < index + count; ++i) {
115             Playlist playlist = mMediaDb.getPlaylists().get(i);
116             if (playlist.equals(mPlaylist)) {
117                 updateViews();
118                 break;
119             }
120         }
121     }
122 
123     @Override
onItemsRemoved(int index, int count)124     public void onItemsRemoved(int index, int count) { }
125 
handleIntent()126     private void handleIntent() {
127         Intent intent = getIntent();
128         Bundle extras = intent != null ? intent.getExtras() : null;
129         if (extras != null) {
130             long id = extras.getLong("id");
131 
132             mPlaylist = mMediaDb.getPlaylistById(id);
133         } else {
134             mPlaylist = null;
135             // TODO This shouldn't happen -- throw exception?
136         }
137 
138         mMediaDb.loadData(mPlaylist);
139         updateViews();
140     }
141 
updateViews()142     private void updateViews() {
143         ImageView image0View = findViewById(R.id.activity_playlist_details_image_0);
144         ImageView image1View = findViewById(R.id.activity_playlist_details_image_1);
145         ImageView image2View = findViewById(R.id.activity_playlist_details_image_2);
146         ImageView image3View = findViewById(R.id.activity_playlist_details_image_3);
147         TextView nameView = findViewById(R.id.activity_playlist_details_name);
148         TextView countView = findViewById(R.id.activity_playlist_details_count);
149 
150         // TODO Find a better way to handle 2x2 art
151         Set<Uri> albumArtUris = new HashSet<>();
152         List<Audio> audios = mPlaylist.getAudios();
153         for (Audio audio : audios) {
154             Album album = audio.getAlbum();
155             if (album != null && album.getAlbumArtUri() != null) {
156                 albumArtUris.add(album.getAlbumArtUri());
157             }
158         }
159 
160         int numAlbumArt = albumArtUris.size();
161         if (numAlbumArt == 0) {
162             image0View.setImageURI(null);
163             image1View.setImageURI(null);
164             image2View.setImageURI(null);
165             image3View.setImageURI(null);
166             image0View.setVisibility(View.VISIBLE);
167             image1View.setVisibility(View.GONE);
168             image2View.setVisibility(View.GONE);
169             image3View.setVisibility(View.GONE);
170         } else if (numAlbumArt < 4) {
171             Iterator<Uri> iterator = albumArtUris.iterator();
172             image0View.setImageURI(iterator.next());
173             image1View.setImageURI(null);
174             image2View.setImageURI(null);
175             image3View.setImageURI(null);
176             image0View.setVisibility(View.VISIBLE);
177             image1View.setVisibility(View.GONE);
178             image2View.setVisibility(View.GONE);
179             image3View.setVisibility(View.GONE);
180         } else {
181             Iterator<Uri> iterator = albumArtUris.iterator();
182             image0View.setImageURI(iterator.next());
183             image1View.setImageURI(iterator.next());
184             image2View.setImageURI(iterator.next());
185             image3View.setImageURI(iterator.next());
186             image0View.setVisibility(View.VISIBLE);
187             image1View.setVisibility(View.VISIBLE);
188             image2View.setVisibility(View.VISIBLE);
189             image3View.setVisibility(View.VISIBLE);
190         }
191         nameView.setText(mPlaylist.getName());
192         // TODO(b/123037263) I18n -- Move to resource
193         countView.setText(mPlaylist.getAudios().size() + " songs");
194 
195         ImageView playView = findViewById(R.id.activity_playlist_details_play);
196         playView.setOnClickListener((view) ->
197                 AudioPlayerActivity.start(view.getContext(), mPlaylist));
198 
199         RecyclerView recyclerView = findViewById(R.id.activity_playlist_details_recycler_view);
200         recyclerView.setHasFixedSize(true);
201         recyclerView.setAdapter(new PlaylistAdapter(mMediaDb, mPlaylist));
202 
203         // TODO(b/123707260) Enable view caching
204         //recyclerView.setItemViewCacheSize(0);
205         //recyclerView.setRecycledViewPool(Globals.getRecycledViewPool(requireContext()));
206     }
207 
208     private static class PlaylistAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
209         private final MediaDb mMediaDb;
210         private final Playlist mPlaylist;
211 
PlaylistAdapter(@onNull MediaDb mediaDb, @NonNull Playlist playlist)212         private PlaylistAdapter(@NonNull MediaDb mediaDb, @NonNull Playlist playlist) {
213             setHasStableIds(true);
214             mMediaDb = mediaDb;
215             mPlaylist = playlist;
216         }
217 
218         @Override
onCreateViewHolder( @onNull ViewGroup parent, int viewType)219         public @NonNull RecyclerView.ViewHolder onCreateViewHolder(
220                 @NonNull ViewGroup parent, int viewType) {
221             return new AudioViewHolder(LayoutInflater.from(parent.getContext())
222                     .inflate(viewType, parent, false));
223         }
224 
225         @Override
onBindViewHolder(@onNull RecyclerView.ViewHolder holder, int position)226         public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
227             Audio audio = mPlaylist.getAudios().get(position);
228             mMediaDb.loadData(audio); // TODO Where should we call this? In bind()?
229             ((AudioViewHolder) holder).bind(mPlaylist, audio);
230         }
231 
232         @Override
getItemCount()233         public int getItemCount() {
234             return mPlaylist.getAudios().size();
235         }
236 
237         @Override
getItemId(int position)238         public long getItemId(int position) {
239             return mPlaylist.getAudios().get(position).getId();
240         }
241 
242         @Override
getItemViewType(int position)243         public int getItemViewType(int position) {
244             return R.layout.audio;
245         }
246     }
247 
248     private static class AudioViewHolder extends RecyclerView.ViewHolder {
AudioViewHolder(@onNull View itemView)249         private AudioViewHolder(@NonNull View itemView) {
250             super(itemView);
251         }
252 
bind(@onNull Playlist playlist, @NonNull Audio audio)253         private void bind(@NonNull Playlist playlist, @NonNull Audio audio) {
254             ImageView imageView = itemView.findViewById(R.id.audio_image);
255             TextView titleView = itemView.findViewById(R.id.audio_title);
256             TextView artistView = itemView.findViewById(R.id.audio_artist);
257 
258             Album album = audio.getAlbum();
259             imageView.setImageURI(album == null ? null : album.getAlbumArtUri());
260             titleView.setText(audio.getTitle());
261             Artist artist = audio.getArtist();
262             artistView.setText(artist == null ? null : artist.getName());
263 
264             itemView.setOnClickListener((view) ->
265                     AudioPlayerActivity.start(view.getContext(), playlist, getAdapterPosition()));
266         }
267     }
268 }
269