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