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