From 760d3218870a200c1b25ab858539da56d2c0df3e Mon Sep 17 00:00:00 2001 From: Adrian Ulrich Date: Sun, 15 Oct 2017 14:12:12 +0200 Subject: [PATCH] Initial commit of ScheduledLibraryUpdate --- AndroidManifest.xml | 5 + .../android/vanilla/PlaybackService.java | 2 + .../vanilla/ScheduledLibraryUpdate.java | 155 ++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 src/ch/blinkenlights/android/vanilla/ScheduledLibraryUpdate.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 5b9fb0eb..c4320e5f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -161,6 +161,11 @@ THE SOFTWARE. + + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package ch.blinkenlights.android.vanilla; + +import ch.blinkenlights.android.medialibrary.MediaLibrary; + +import android.annotation.TargetApi; +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.Context; +import android.content.ComponentName; +import android.database.ContentObserver; +import android.os.Build; + +import android.util.Log; +import java.io.FileWriter; +import java.text.SimpleDateFormat; +import java.util.Date; + + +@TargetApi(21) +public class ScheduledLibraryUpdate extends JobService { + /** + * The unique job id of this package - do not change + * as we would otherwise end up with multiple scheduled + * jobs (until the next reboot happens) + */ + private final static int JOB_ID_UPDATE = 9; + /** + * The job parameters of the currently running job + */ + private JobParameters mJobParams; + + + /** + * Schedules a new media library scan job. + * + * @return true if job was scheduled, false otherwise + */ + public static boolean scheduleUpdate(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return false; // JobScheduler requires API 21 + + JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + + if (scheduler.getPendingJob(JOB_ID_UPDATE) != null) + return false; // no need to re-schedule the job + + ComponentName componentName = new ComponentName(context, ScheduledLibraryUpdate.class); + JobInfo job = new JobInfo.Builder(JOB_ID_UPDATE, componentName) + .setRequiresBatteryNotLow(true) + .setRequiresCharging(true) + .setRequiresDeviceIdle(true) + .setPeriodic(3600000 * 8) // run at most every ~8 hours + .build(); + + scheduler.schedule(job); + + xlog("Job with id "+JOB_ID_UPDATE+" scheduled for execution"); + return true; + } + + + /** + * Called by the scheduler to launch the job + * + * @param params the parameters of this job + * @return true if the job has been launched + */ + @Override + public boolean onStartJob(JobParameters params) { + xlog("++ onStartJob called on "+params.getJobId()); + + if (params.getJobId() != JOB_ID_UPDATE) + return false; // orphaned job, do not start + + final boolean fullScan = (Math.random() > 0.7); + + mJobParams = params; + MediaLibrary.registerContentObserver(mObserver); + MediaLibrary.startLibraryScan(this, fullScan, false); + + xlog("++ onStartJob called on "+params.getJobId()+", running full scan? -> "+fullScan); + return true; + } + + /** + * Called by the scheduler to abort the job + * + * @param params the parameters of the job to abort + * @return false as we do not want to get backed-off + */ + @Override + public boolean onStopJob(JobParameters params) { + xlog("++ onStopJob called on "+params.getJobId()); + finalizeScan(); + return false; + } + + /** + * Aborts a running scan job + */ + private void finalizeScan() { + xlog("++ finalize called"); + MediaLibrary.unregisterContentObserver(mObserver); + MediaLibrary.abortLibraryScan(this); + mJobParams = null; + } + + /** + * The content observer registered to the media library. + * The observer will receive callbacks for changed songs, + * the last callback will have `ongoing` set to `false`, + * which indicates that our job completed. + */ + private final ContentObserver mObserver = new ContentObserver(null) { + @Override + public void onChange(boolean ongoing) { + xlog("CHANGE: "+ongoing); + if (!ongoing) { + jobFinished(mJobParams, false); + finalizeScan(); + } + } + }; + + private static void xlog(String str) { + try { + String sdf = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")).format(new Date()); + FileWriter fw = new FileWriter("/sdcard/vanilla-log.txt", true); + Log.v("VanillaMusic", str); + fw.write(String.format("%s: %s\n", sdf, str)); + fw.close(); + } catch(Exception e) { + Log.v("VanillaMusic", "LOGFAIL: "+e); + } + } +}