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.db;
18 
19 import android.net.Uri;
20 
21 import androidx.annotation.AnyThread;
22 import androidx.annotation.NonNull;
23 import androidx.annotation.Nullable;
24 
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 
29 import static com.android.pump.util.Collections.binarySearch;
30 
31 @AnyThread
32 public class Series {
33     private final String mTitle;
34     private final int mYear;
35 
36     // TODO(b/123706949) Lock mutable fields to ensure consistent updates
37     private Uri mPosterUri;
38     private String mDescription;
39     private final List<List<Episode>> mSeasons = new ArrayList<>();
40     private boolean mLoaded;
41 
Series(@onNull String title)42     Series(@NonNull String title) {
43         mTitle = title;
44         mYear = Integer.MIN_VALUE;
45     }
46 
Series(@onNull String title, int year)47     Series(@NonNull String title, int year) {
48         mTitle = title;
49         if (year <= 0) {
50             throw new IllegalArgumentException();
51         }
52         mYear = year;
53     }
54 
getTitle()55     public @NonNull String getTitle() {
56         return mTitle;
57     }
58 
hasYear()59     public boolean hasYear() {
60         return mYear > 0;
61     }
62 
getYear()63     public int getYear() {
64         if (!hasYear()) {
65             throw new IllegalStateException();
66         }
67         return mYear;
68     }
69 
getPosterUri()70     public @Nullable Uri getPosterUri() {
71         return mPosterUri;
72     }
73 
setPosterUri(@onNull Uri posterUri)74     public boolean setPosterUri(@NonNull Uri posterUri) {
75         if (posterUri.equals(mPosterUri)) {
76             return false;
77         }
78         mPosterUri = posterUri;
79         return true;
80     }
81 
getDescription()82     public @Nullable String getDescription() {
83         return mDescription;
84     }
85 
setDescription(@onNull String description)86     public boolean setDescription(@NonNull String description) {
87         if (description.equals(mDescription)) {
88             return false;
89         }
90         mDescription = description;
91         return true;
92     }
93 
getSeasons()94     public @NonNull List<List<Episode>> getSeasons() {
95         return Collections.unmodifiableList(mSeasons);
96     }
97 
addEpisode(@onNull Episode episode)98     boolean addEpisode(@NonNull Episode episode) {
99         int seriesLocation = binarySearch(mSeasons, episode.getSeason(),
100                 (season) -> season.get(0).getSeason());
101         if (seriesLocation >= 0) {
102             List<Episode> series = mSeasons.get(seriesLocation);
103             int episodeLocation = binarySearch(series, episode.getEpisode(), Episode::getEpisode);
104             if (episodeLocation >= 0) {
105                 if (episode.equals(series.get(episodeLocation))) {
106                     return false;
107                 }
108                 // TODO(b/127524752) This should kind of be okay (i.e. handle gracefully)
109                 throw new IllegalStateException("Two episodes with the same season & episode #");
110             } else {
111                 series.add(~episodeLocation, episode);
112             }
113         } else {
114             List<Episode> series = new ArrayList<>();
115             series.add(episode);
116             mSeasons.add(~seriesLocation, series);
117         }
118         return true;
119     }
120 
isLoaded()121     boolean isLoaded() {
122         return mLoaded;
123     }
124 
setLoaded()125     void setLoaded() {
126         mLoaded = true;
127     }
128 
129     @Override
equals(@ullable Object obj)130     public final boolean equals(@Nullable Object obj) {
131         return obj instanceof Series && mTitle.equals(((Series) obj).mTitle)
132                 && mYear == ((Series) obj).mYear;
133     }
134 
135     @Override
hashCode()136     public final int hashCode() {
137         return mTitle.hashCode() ^ mYear;
138     }
139 }
140