1 /*
2  * Copyright (C) 2015 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.tv.dvr.provider;
18 
19 import android.database.Cursor;
20 import android.support.annotation.Nullable;
21 import android.util.Log;
22 
23 import com.android.tv.common.concurrent.NamedThreadFactory;
24 import com.android.tv.common.flags.DvrFlags;
25 import com.android.tv.dvr.data.ScheduledRecording;
26 import com.android.tv.dvr.data.SeriesRecording;
27 import com.android.tv.dvr.provider.DvrContract.Schedules;
28 import com.android.tv.dvr.provider.DvrContract.SeriesRecordings;
29 import com.android.tv.util.MainThreadExecutor;
30 
31 import com.google.auto.factory.AutoFactory;
32 import com.google.auto.factory.Provided;
33 import com.google.common.util.concurrent.FutureCallback;
34 import com.google.common.util.concurrent.Futures;
35 import com.google.common.util.concurrent.ListenableFuture;
36 import com.google.common.util.concurrent.ListeningExecutorService;
37 import com.google.common.util.concurrent.MoreExecutors;
38 
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.concurrent.Executors;
42 
43 /** {@link DvrDbFuture} that defaults to executing on its own single threaded Executor Service. */
44 public abstract class DvrDbFuture<ParamsT, ResultT> {
45     private static final NamedThreadFactory THREAD_FACTORY =
46             new NamedThreadFactory(DvrDbFuture.class.getSimpleName());
47     private static final ListeningExecutorService DB_EXECUTOR =
48             MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(THREAD_FACTORY));
49 
50     final DvrDatabaseHelper mDbHelper;
51     private ListenableFuture<ResultT> mFuture;
52 
DvrDbFuture(DvrDatabaseHelper mDbHelper)53     private DvrDbFuture(DvrDatabaseHelper mDbHelper) {
54         this.mDbHelper = mDbHelper;
55     }
56 
57     /** Execute the task on the {@link #DB_EXECUTOR} thread and return Future */
58     @SafeVarargs
executeOnDbThread( FutureCallback<ResultT> callback, ParamsT... params)59     public final ListenableFuture<ResultT> executeOnDbThread(
60             FutureCallback<ResultT> callback, ParamsT... params) {
61         mFuture = DB_EXECUTOR.submit(() -> dbHelperInBackground(params));
62         Futures.addCallback(mFuture, callback, MainThreadExecutor.getInstance());
63         return mFuture;
64     }
65 
66     /** Executes in the background after initializing DbHelper} */
67     @Nullable
dbHelperInBackground(ParamsT... params)68     protected abstract ResultT dbHelperInBackground(ParamsT... params);
69 
isCancelled()70     public final boolean isCancelled() {
71         return mFuture.isCancelled();
72     }
73 
74     /** Inserts schedules. */
75     public static class AddScheduleFuture extends DvrDbFuture<ScheduledRecording, Void> {
AddScheduleFuture(DvrDatabaseHelper dbHelper)76         public AddScheduleFuture(DvrDatabaseHelper dbHelper) {
77             super(dbHelper);
78         }
79 
80         @Override
dbHelperInBackground(ScheduledRecording... params)81         protected final Void dbHelperInBackground(ScheduledRecording... params) {
82             mDbHelper.insertSchedules(params);
83             return null;
84         }
85     }
86 
87     /** Update schedules. */
88     public static class UpdateScheduleFuture extends DvrDbFuture<ScheduledRecording, Void> {
UpdateScheduleFuture(DvrDatabaseHelper dbHelper)89         public UpdateScheduleFuture(DvrDatabaseHelper dbHelper) {
90             super(dbHelper);
91         }
92 
93         @Override
dbHelperInBackground(ScheduledRecording... params)94         protected final Void dbHelperInBackground(ScheduledRecording... params) {
95             mDbHelper.updateSchedules(params);
96             return null;
97         }
98     }
99 
100     /** Delete schedules. */
101     public static class DeleteScheduleFuture extends DvrDbFuture<ScheduledRecording, Void> {
DeleteScheduleFuture(DvrDatabaseHelper dbHelper)102         public DeleteScheduleFuture(DvrDatabaseHelper dbHelper) {
103             super(dbHelper);
104         }
105 
106         @Override
dbHelperInBackground(ScheduledRecording... params)107         protected final Void dbHelperInBackground(ScheduledRecording... params) {
108             mDbHelper.deleteSchedules(params);
109             return null;
110         }
111     }
112 
113     /** Returns all {@link ScheduledRecording}s. */
114     public static class DvrQueryScheduleFuture extends DvrDbFuture<Void, List<ScheduledRecording>> {
115 
116         private final DvrFlags mDvrFlags;
117 
118         /**
119          * Factory for {@link DvrQueryScheduleFuture}.
120          *
121          * <p>This wrapper class keeps other classes from needing to reference the
122          * {@link AutoFactory} generated class.
123          */
124         public interface Factory {
create(DvrDatabaseHelper dbHelper)125             public DvrQueryScheduleFuture create(DvrDatabaseHelper dbHelper);
126         }
127 
128         @AutoFactory(implementing = Factory.class, className = "DvrQueryScheduleFutureFactory")
DvrQueryScheduleFuture(DvrDatabaseHelper dbHelper, @Provided DvrFlags dvrFlags)129         public DvrQueryScheduleFuture(DvrDatabaseHelper dbHelper, @Provided DvrFlags dvrFlags) {
130             super(dbHelper);
131             mDvrFlags = dvrFlags;
132         }
133 
134         @Override
135         @Nullable
dbHelperInBackground(Void... params)136         protected final List<ScheduledRecording> dbHelperInBackground(Void... params) {
137             if (isCancelled()) {
138                 return null;
139             }
140             List<ScheduledRecording> scheduledRecordings = new ArrayList<>();
141             if (mDvrFlags.startEarlyEndLateEnabled()) {
142                 try (Cursor c = mDbHelper.query(Schedules.TABLE_NAME,
143                         ScheduledRecording.PROJECTION_WITH_TIME_OFFSET)) {
144                     while (c.moveToNext() && !isCancelled()) {
145                         scheduledRecordings.add(ScheduledRecording.fromCursorWithTimeOffset(c));
146                     }
147                 }
148             } else {
149                 try (Cursor c = mDbHelper.query(Schedules.TABLE_NAME,
150                         ScheduledRecording.PROJECTION)) {
151                     while (c.moveToNext() && !isCancelled()) {
152                         scheduledRecordings.add(ScheduledRecording.fromCursor(c));
153                     }
154                 }
155             }
156             return scheduledRecordings;
157         }
158     }
159 
160     /** Inserts series recordings. */
161     public static class AddSeriesRecordingFuture extends DvrDbFuture<SeriesRecording, Void> {
AddSeriesRecordingFuture(DvrDatabaseHelper dbHelper)162         public AddSeriesRecordingFuture(DvrDatabaseHelper dbHelper) {
163             super(dbHelper);
164         }
165 
166         @Override
dbHelperInBackground(SeriesRecording... params)167         protected final Void dbHelperInBackground(SeriesRecording... params) {
168             mDbHelper.insertSeriesRecordings(params);
169             return null;
170         }
171     }
172 
173     /** Update series recordings. */
174     public static class UpdateSeriesRecordingFuture extends DvrDbFuture<SeriesRecording, Void> {
UpdateSeriesRecordingFuture(DvrDatabaseHelper dbHelper)175         public UpdateSeriesRecordingFuture(DvrDatabaseHelper dbHelper) {
176             super(dbHelper);
177         }
178 
179         @Override
dbHelperInBackground(SeriesRecording... params)180         protected final Void dbHelperInBackground(SeriesRecording... params) {
181             mDbHelper.updateSeriesRecordings(params);
182             return null;
183         }
184     }
185 
186     /** Delete series recordings. */
187     public static class DeleteSeriesRecordingFuture extends DvrDbFuture<SeriesRecording, Void> {
DeleteSeriesRecordingFuture(DvrDatabaseHelper dbHelper)188         public DeleteSeriesRecordingFuture(DvrDatabaseHelper dbHelper) {
189             super(dbHelper);
190         }
191 
192         @Override
dbHelperInBackground(SeriesRecording... params)193         protected final Void dbHelperInBackground(SeriesRecording... params) {
194             mDbHelper.deleteSeriesRecordings(params);
195             return null;
196         }
197     }
198 
199     /** Returns all {@link SeriesRecording}s. */
200     public static class DvrQuerySeriesRecordingFuture
201             extends DvrDbFuture<Void, List<SeriesRecording>> {
202         private static final String TAG = "DvrQuerySeriesRecording";
203 
DvrQuerySeriesRecordingFuture(DvrDatabaseHelper dbHelper)204         public DvrQuerySeriesRecordingFuture(DvrDatabaseHelper dbHelper) {
205             super(dbHelper);
206         }
207 
208         @Override
209         @Nullable
dbHelperInBackground(Void... params)210         protected final List<SeriesRecording> dbHelperInBackground(Void... params) {
211             if (isCancelled()) {
212                 return null;
213             }
214             List<SeriesRecording> scheduledRecordings = new ArrayList<>();
215             try (Cursor c =
216                     mDbHelper.query(SeriesRecordings.TABLE_NAME, SeriesRecording.PROJECTION)) {
217                 while (c.moveToNext() && !isCancelled()) {
218                     scheduledRecordings.add(SeriesRecording.fromCursor(c));
219                 }
220             } catch (Exception e) {
221                 Log.w(TAG, "Can't query dvr series recording data", e);
222             }
223             return scheduledRecordings;
224         }
225     }
226 }
227