public final class

WorkManagerScheduler

extends java.lang.Object

implements Scheduler

 java.lang.Object

↳androidx.media3.exoplayer.workmanager.WorkManagerScheduler

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer-workmanager', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer-workmanager
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-exoplayer-workmanager:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

A Scheduler that uses WorkManager.

Summary

Constructors
publicWorkManagerScheduler(Context context, java.lang.String workName)

Methods
public booleancancel()

public RequirementsgetSupportedRequirements(Requirements requirements)

public booleanschedule(Requirements requirements, java.lang.String servicePackage, java.lang.String serviceAction)

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public WorkManagerScheduler(Context context, java.lang.String workName)

Parameters:

context: A context.
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 WorkManagerScheduler.schedule(Requirements, String, String) or WorkManagerScheduler.cancel() are called.

Methods

public boolean schedule(Requirements requirements, java.lang.String servicePackage, java.lang.String serviceAction)

public boolean cancel()

public Requirements getSupportedRequirements(Requirements requirements)

Source

/*
 * Copyright (C) 2019 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 androidx.media3.exoplayer.workmanager;

import android.content.Context;
import android.content.Intent;
import androidx.annotation.RequiresApi;
import androidx.media3.common.MediaLibraryInfo;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.scheduler.Requirements;
import androidx.media3.exoplayer.scheduler.Scheduler;
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}. */
@UnstableApi
public final class WorkManagerScheduler implements Scheduler {

  static {
    MediaLibraryInfo.registerModule("media3.exoplayer.workmanager");
  }

  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 static final int SUPPORTED_REQUIREMENTS =
      Requirements.NETWORK
          | Requirements.NETWORK_UNMETERED
          | (Util.SDK_INT >= 23 ? Requirements.DEVICE_IDLE : 0)
          | Requirements.DEVICE_CHARGING
          | Requirements.DEVICE_STORAGE_NOT_LOW;

  private final WorkManager workManager;
  private final String workName;

  /**
   * @param context A context.
   * @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(Context context, String workName) {
    this.workName = workName;
    workManager = WorkManager.getInstance(context.getApplicationContext());
  }

  @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);
    workManager.enqueueUniqueWork(workName, ExistingWorkPolicy.REPLACE, workRequest);
    return true;
  }

  @Override
  public boolean cancel() {
    workManager.cancelUniqueWork(workName);
    return true;
  }

  @Override
  public Requirements getSupportedRequirements(Requirements requirements) {
    return requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
  }

  private static Constraints buildConstraints(Requirements requirements) {
    Requirements filteredRequirements = requirements.filterRequirements(SUPPORTED_REQUIREMENTS);
    if (!filteredRequirements.equals(requirements)) {
      Log.w(
          TAG,
          "Ignoring unsupported requirements: "
              + (filteredRequirements.getRequirements() ^ requirements.getRequirements()));
    }

    Constraints.Builder builder = new Constraints.Builder();
    if (requirements.isUnmeteredNetworkRequired()) {
      builder.setRequiredNetworkType(NetworkType.UNMETERED);
    } else if (requirements.isNetworkRequired()) {
      builder.setRequiredNetworkType(NetworkType.CONNECTED);
    } else {
      builder.setRequiredNetworkType(NetworkType.NOT_REQUIRED);
    }
    if (Util.SDK_INT >= 23 && requirements.isIdleRequired()) {
      setRequiresDeviceIdle(builder);
    }
    if (requirements.isChargingRequired()) {
      builder.setRequiresCharging(true);
    }
    if (requirements.isStorageNotLowRequired()) {
      builder.setRequiresStorageNotLow(true);
    }

    return builder.build();
  }

  @RequiresApi(23)
  private static void setRequiresDeviceIdle(Constraints.Builder builder) {
    builder.setRequiresDeviceIdle(true);
  }

  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();
  }

  /** A {@link Worker} that starts the target service if the requirements are met. */
  // This class needs to be public so that WorkManager can instantiate it.
  public static final class SchedulerWorker extends Worker {

    private final WorkerParameters workerParams;
    private final Context context;

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

    @Override
    public Result doWork() {
      Data inputData = Assertions.checkNotNull(workerParams.getInputData());
      Requirements requirements = new Requirements(inputData.getInt(KEY_REQUIREMENTS, 0));
      int notMetRequirements = requirements.getNotMetRequirements(context);
      if (notMetRequirements == 0) {
        String serviceAction = Assertions.checkNotNull(inputData.getString(KEY_SERVICE_ACTION));
        String servicePackage = Assertions.checkNotNull(inputData.getString(KEY_SERVICE_PACKAGE));
        Intent intent = new Intent(serviceAction).setPackage(servicePackage);
        Util.startForegroundService(context, intent);
        return Result.success();
      } else {
        Log.w(TAG, "Requirements not met: " + notMetRequirements);
        return Result.retry();
      }
    }
  }
}