Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add WorkManager extension #5617

Merged
merged 5 commits into from
May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core_settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ include modulePrefix + 'extension-vp9'
include modulePrefix + 'extension-rtmp'
include modulePrefix + 'extension-leanback'
include modulePrefix + 'extension-jobdispatcher'
include modulePrefix + 'extension-workmanager'

project(modulePrefix + 'library').projectDir = new File(rootDir, 'library/all')
project(modulePrefix + 'library-core').projectDir = new File(rootDir, 'library/core')
Expand All @@ -60,3 +61,4 @@ project(modulePrefix + 'extension-vp9').projectDir = new File(rootDir, 'extensio
project(modulePrefix + 'extension-rtmp').projectDir = new File(rootDir, 'extensions/rtmp')
project(modulePrefix + 'extension-leanback').projectDir = new File(rootDir, 'extensions/leanback')
project(modulePrefix + 'extension-jobdispatcher').projectDir = new File(rootDir, 'extensions/jobdispatcher')
project(modulePrefix + 'extension-workmanager').projectDir = new File(rootDir, 'extensions/workmanager')
23 changes: 23 additions & 0 deletions extensions/workmanager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ExoPlayer WorkManager extension #

This extension provides a Scheduler implementation which uses [WorkManager][].

[WorkManager]: https://developer.android.com/topic/libraries/architecture/workmanager.html

## Getting the extension ##

The easiest way to use the extension is to add it as a gradle dependency:

```gradle
implementation 'com.google.android.exoplayer:extension-workmanager:2.X.X'
```

where `2.X.X` is the version, which must match the version of the ExoPlayer
library being used.

Alternatively, you can clone the ExoPlayer repository and depend on the module
locally. Instructions for doing this can be found in ExoPlayer's
[top level README][].

[top level README]: https://github.com/google/ExoPlayer/blob/release-v2/README.md

49 changes: 49 additions & 0 deletions extensions/workmanager/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply from: '../../constants.gradle'
apply plugin: 'com.android.library'

android {
compileSdkVersion project.ext.compileSdkVersion

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
}

testOptions.unitTests.includeAndroidResources = true
}

dependencies {
implementation project(modulePrefix + 'library-core')
implementation 'androidx.work:work-runtime:2.0.1'
}

ext {
javadocTitle = 'WorkManager extension'
}
apply from: '../../javadoc_library.gradle'

ext {
releaseArtifact = 'extension-workmanager'
releaseDescription = 'WorkManager extension for ExoPlayer.'
}
apply from: '../../publish.gradle'
18 changes: 18 additions & 0 deletions extensions/workmanager/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<manifest package="com.google.android.exoplayer2.ext.workmanager"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.ext.workmanager;

import android.content.Context;
import android.content.Intent;
import android.os.Build;

import com.google.android.exoplayer2.scheduler.Requirements;
import com.google.android.exoplayer2.scheduler.Scheduler;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util;

import androidx.annotation.NonNull;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

/**
* A {@link Scheduler} that uses {@link WorkManager}.
*/
public final class WorkManagerScheduler implements Scheduler {

private static final String TAG = "WorkManagerScheduler";
private static final String KEY_SERVICE_ACTION = "service_action";
private static final String KEY_SERVICE_PACKAGE = "service_package";
private static final String KEY_REQUIREMENTS = "requirements";

private final String workName;

/**
* @param workName A name for work scheduled by this instance. If the same name was used by a previous
* instance, anything scheduled by the previous instance will be canceled by this instance if
* {@link #schedule(Requirements, String, String)} or {@link #cancel()} are called.
*/
public WorkManagerScheduler(String workName) {
this.workName = workName;
}

@Override
public boolean schedule(Requirements requirements, String servicePackage, String serviceAction) {
Constraints constraints = buildConstraints(requirements);
Data inputData = buildInputData(requirements, servicePackage, serviceAction);
OneTimeWorkRequest workRequest = buildWorkRequest(constraints, inputData);
logd("Scheduling work: " + workName);
WorkManager.getInstance().enqueueUniqueWork(workName, ExistingWorkPolicy.REPLACE, workRequest);
return true;
}

@Override
public boolean cancel() {
logd("Canceling work: " + workName);
WorkManager.getInstance().cancelUniqueWork(workName);
return true;
}

private static Constraints buildConstraints(Requirements requirements) {
Constraints.Builder builder = new Constraints.Builder();

switch (requirements.getRequiredNetworkType()) {
case Requirements.NETWORK_TYPE_NONE:
builder.setRequiredNetworkType(NetworkType.NOT_REQUIRED);
break;
case Requirements.NETWORK_TYPE_ANY:
builder.setRequiredNetworkType(NetworkType.CONNECTED);
break;
case Requirements.NETWORK_TYPE_UNMETERED:
builder.setRequiredNetworkType(NetworkType.UNMETERED);
break;
default:
throw new UnsupportedOperationException();
}

if (requirements.isChargingRequired()) {
builder.setRequiresCharging(true);
}

if (requirements.isIdleRequired() && Util.SDK_INT >= 23) {
builder.setRequiresDeviceIdle(true);
}

return builder.build();
}

private static Data buildInputData(Requirements requirements, String servicePackage, String serviceAction) {
Data.Builder builder = new Data.Builder();

builder.putInt(KEY_REQUIREMENTS, requirements.getRequirements());
builder.putString(KEY_SERVICE_PACKAGE, servicePackage);
builder.putString(KEY_SERVICE_ACTION, serviceAction);

return builder.build();
}

private static OneTimeWorkRequest buildWorkRequest(Constraints constraints, Data inputData) {
OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(SchedulerWorker.class);

builder.setConstraints(constraints);
builder.setInputData(inputData);

return builder.build();
}

private static void logd(String message) {
if (DEBUG) {
Log.d(TAG, message);
}
}

/** A {@link Worker} that starts the target service if the requirements are met. */
public static final class SchedulerWorker extends Worker {

private final WorkerParameters workerParams;
private final Context context;

public SchedulerWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
this.workerParams = workerParams;
this.context = context;
}

@NonNull
@Override
public Result doWork() {
logd("SchedulerWorker is started");
Data inputData = workerParams.getInputData();
Assertions.checkNotNull(inputData, "Work started without input data.");
Requirements requirements = new Requirements(inputData.getInt(KEY_REQUIREMENTS, 0));
if (requirements.checkRequirements(context)) {
logd("Requirements are met");
String serviceAction = inputData.getString(KEY_SERVICE_ACTION);
String servicePackage = inputData.getString(KEY_SERVICE_PACKAGE);
Assertions.checkNotNull(serviceAction, "Service action missing.");
Assertions.checkNotNull(servicePackage, "Service package missing.");
Intent intent = new Intent(serviceAction).setPackage(servicePackage);
logd("Starting service action: " + serviceAction + " package: " + servicePackage);
Util.startForegroundService(context, intent);
return Result.success();
} else {
logd("Requirements are not met");
return Result.retry();
}
}
}
}