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.AdapterView;
28 import android.widget.BaseAdapter;
29 import android.widget.ImageView;
30 import android.widget.Spinner;
31 import android.widget.TextView;
32 
33 import androidx.annotation.LayoutRes;
34 import androidx.annotation.NonNull;
35 import androidx.annotation.Nullable;
36 import androidx.annotation.UiThread;
37 import androidx.appcompat.app.ActionBar;
38 import androidx.appcompat.app.AppCompatActivity;
39 import androidx.recyclerview.widget.RecyclerView;
40 
41 import com.android.pump.R;
42 import com.android.pump.db.Episode;
43 import com.android.pump.db.MediaDb;
44 import com.android.pump.db.Series;
45 import com.android.pump.util.Globals;
46 
47 import java.util.List;
48 
49 @UiThread
50 public class SeriesDetailsActivity extends AppCompatActivity implements MediaDb.UpdateCallback {
51     private MediaDb mMediaDb;
52     private Series mSeries;
53 
start(@onNull Context context, @NonNull Series series)54     public static void start(@NonNull Context context, @NonNull Series series) {
55         Intent intent = new Intent(context, SeriesDetailsActivity.class);
56         // TODO(b/123704452) Pass URI instead
57         intent.putExtra("title", series.getTitle()); // TODO Add constant key
58         if (series.hasYear()) {
59             intent.putExtra("year", series.getYear()); // TODO Add constant key
60         }
61         context.startActivity(intent);
62     }
63 
64     @Override
onCreate(@ullable Bundle savedInstanceState)65     protected void onCreate(@Nullable Bundle savedInstanceState) {
66         super.onCreate(savedInstanceState);
67         setContentView(R.layout.activity_series_details);
68 
69         setSupportActionBar(findViewById(R.id.activity_series_details_toolbar));
70         ActionBar actionBar = getSupportActionBar();
71         if (actionBar != null) {
72             actionBar.setDisplayShowTitleEnabled(false);
73             actionBar.setDisplayShowHomeEnabled(true);
74             actionBar.setDisplayHomeAsUpEnabled(true);
75         }
76 
77         mMediaDb = Globals.getMediaDb(this);
78         mMediaDb.addSeriesUpdateCallback(this);
79 
80         handleIntent();
81     }
82 
83     @Override
onNewIntent(@ullable Intent intent)84     protected void onNewIntent(@Nullable Intent intent) {
85         super.onNewIntent(intent);
86         setIntent(intent);
87 
88         handleIntent();
89     }
90 
91     @Override
onDestroy()92     protected void onDestroy() {
93         mMediaDb.removeSeriesUpdateCallback(this);
94 
95         super.onDestroy();
96     }
97 
98     @Override
onCreateOptionsMenu(@onNull Menu menu)99     public boolean onCreateOptionsMenu(@NonNull Menu menu) {
100         getMenuInflater().inflate(R.menu.activity_pump, menu); // TODO activity_series_details ?
101         return true;
102     }
103 
104     @Override
onSupportNavigateUp()105     public boolean onSupportNavigateUp() {
106         // TODO It should not be necessary to override this method
107         onBackPressed();
108         return true;
109     }
110 
111     @Override
onItemsInserted(int index, int count)112     public void onItemsInserted(int index, int count) { }
113 
114     @Override
onItemsUpdated(int index, int count)115     public void onItemsUpdated(int index, int count) {
116         for (int i = index; i < index + count; ++i) {
117             Series series = mMediaDb.getSeries().get(i);
118             if (series.equals(mSeries)) {
119                 updateViews();
120                 break;
121             }
122         }
123     }
124 
125     @Override
onItemsRemoved(int index, int count)126     public void onItemsRemoved(int index, int count) { }
127 
handleIntent()128     private void handleIntent() {
129         Intent intent = getIntent();
130         Bundle extras = intent != null ? intent.getExtras() : null;
131         if (extras != null) {
132             String title = extras.getString("title");
133             int year = extras.getInt("year", Integer.MIN_VALUE);
134 
135             MediaDb mediaDb = Globals.getMediaDb(this);
136             if (year > 0) {
137                 mSeries = mediaDb.getSeriesById(title, year);
138             } else {
139                 mSeries = mediaDb.getSeriesById(title);
140             }
141         } else {
142             mSeries = null;
143             // TODO This shouldn't happen -- throw exception?
144         }
145 
146         mMediaDb.loadData(mSeries);
147         updateViews();
148     }
149 
updateViews()150     private void updateViews() {
151         // TODO ImageView imageView = findViewById(R.id.activity_series_details_image);
152         ImageView posterView = findViewById(R.id.activity_series_details_poster);
153         TextView titleView = findViewById(R.id.activity_series_details_title);
154         TextView attributesView = findViewById(R.id.activity_series_details_attributes);
155         TextView synopsisView = findViewById(R.id.activity_series_details_description);
156 
157         // TODO imageView.setImageURI(mSeries.get???());
158         posterView.setImageURI(mSeries.getPosterUri());
159         titleView.setText(mSeries.getTitle());
160         attributesView.setText("American Drama Series"); // TODO(b/123707108) Implement
161         synopsisView.setText(mSeries.getDescription());
162 
163         Spinner spinner = findViewById(R.id.activity_series_details_spinner);
164         spinner.setAdapter(new BaseAdapter() {
165             @Override
166             public boolean hasStableIds() {
167                 return true;
168             }
169 
170             @Override
171             public int getCount() {
172                 return mSeries.getSeasons().size();
173             }
174 
175             @Override
176             public @NonNull Integer getItem(int position) {
177                 return mSeries.getSeasons().get(position).get(0).getSeason();
178             }
179 
180             @Override
181             public long getItemId(int position) {
182                 return getItem(position);
183             }
184 
185             @Override
186             public View getView(int position, @Nullable View convertView,
187                     @NonNull ViewGroup parent) {
188                 return getView(android.R.layout.simple_spinner_item, position, convertView, parent);
189             }
190 
191             @Override
192             public View getDropDownView(int position, @Nullable View convertView,
193                     @NonNull ViewGroup parent) {
194                 return getView(R.layout.support_simple_spinner_dropdown_item,
195                         position, convertView, parent);
196             }
197 
198             private View getView(@LayoutRes int resource, int position, @Nullable View convertView,
199                     @NonNull ViewGroup parent) {
200                 View view = convertView;
201                 if (view == null) {
202                     view = getInflater().inflate(resource, parent, false);
203                 }
204 
205                 TextView textView = view.findViewById(android.R.id.text1);
206                 // TODO(b/123037263) I18n -- Move to resource
207                 textView.setText("Season " + getItem(position));
208                 return view;
209             }
210 
211             private @NonNull LayoutInflater getInflater() {
212                 return LayoutInflater.from(SeriesDetailsActivity.this);
213             }
214         });
215 
216         RecyclerView recyclerView = findViewById(R.id.activity_series_details_recycler_view);
217         recyclerView.setHasFixedSize(true);
218         recyclerView.setAdapter(new SeriesAdapter(mMediaDb, mSeries));
219         spinner.setOnItemSelectedListener((SeriesAdapter) recyclerView.getAdapter());
220 
221         // TODO(b/123707260) Enable view caching
222         //recyclerView.setItemViewCacheSize(0);
223         //recyclerView.setRecycledViewPool(Globals.getRecycledViewPool(requireContext()));
224     }
225 
226     private static class SeriesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
227             implements AdapterView.OnItemSelectedListener{
228         private final MediaDb mMediaDb;
229         private final Series mSeries;
230         private int mSeasonPosition;
231 
SeriesAdapter(@onNull MediaDb mediaDb, @NonNull Series series)232         private SeriesAdapter(@NonNull MediaDb mediaDb, @NonNull Series series) {
233             setHasStableIds(true);
234             mMediaDb = mediaDb;
235             mSeries = series;
236         }
237 
238         @Override
onCreateViewHolder( @onNull ViewGroup parent, int viewType)239         public @NonNull RecyclerView.ViewHolder onCreateViewHolder(
240                 @NonNull ViewGroup parent, int viewType) {
241             return new EpisodeViewHolder(LayoutInflater.from(parent.getContext())
242                     .inflate(viewType, parent, false));
243         }
244 
245         @Override
onBindViewHolder(@onNull RecyclerView.ViewHolder holder, int position)246         public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
247             Episode episode = getSeason().get(position);
248             mMediaDb.loadData(episode); // TODO Where should we call this? In bind()?
249             ((EpisodeViewHolder) holder).bind(episode);
250         }
251 
252         @Override
getItemCount()253         public int getItemCount() {
254             return getSeason().size();
255         }
256 
257         @Override
getItemId(int position)258         public long getItemId(int position) {
259             return getSeason().get(position).getId();
260         }
261 
262         @Override
getItemViewType(int position)263         public int getItemViewType(int position) {
264             return R.layout.episode;
265         }
266 
267         @Override
onItemSelected(@onNull AdapterView<?> parent, @NonNull View view, int position, long id)268         public void onItemSelected(@NonNull AdapterView<?> parent, @NonNull View view,
269                 int position, long id) {
270             mSeasonPosition = position;
271             notifyDataSetChanged();
272         }
273 
274         @Override
onNothingSelected(@onNull AdapterView<?> parent)275         public void onNothingSelected(@NonNull AdapterView<?> parent) { }
276 
getSeason()277         private @NonNull List<Episode> getSeason() {
278             return mSeries.getSeasons().get(mSeasonPosition);
279         }
280     }
281 
282     private static class EpisodeViewHolder extends RecyclerView.ViewHolder {
EpisodeViewHolder(@onNull View itemView)283         private EpisodeViewHolder(@NonNull View itemView) {
284             super(itemView);
285         }
286 
bind(@onNull Episode episode)287         private void bind(@NonNull Episode episode) {
288             ImageView imageView = itemView.findViewById(R.id.episode_image);
289             TextView textView = itemView.findViewById(R.id.episode_text);
290 
291             Uri posterUri = episode.getPosterUri();
292             if (posterUri == null) {
293                 posterUri = episode.getThumbnailUri();
294             }
295             imageView.setImageURI(posterUri);
296             // TODO(b/123037263) I18n -- Move to resource
297             textView.setText("Episode " + episode.getEpisode());
298 
299             itemView.setOnClickListener((view) ->
300                     VideoPlayerActivity.start(view.getContext(), episode));
301         }
302     }
303 }
304