diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java new file mode 100644 index 0000000000..40911c68bb --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraint.java @@ -0,0 +1,43 @@ +package org.thoughtcrime.securesms.jobmanager.impl; + +import android.app.job.JobInfo; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import org.thoughtcrime.securesms.jobmanager.Constraint; + +/** + * Job constraint for determining whether or not the device is actively charging. + */ +public class ChargingConstraint implements Constraint { + + public static final String KEY = "ChargingConstraint"; + + private ChargingConstraint() { + } + + @Override + public boolean isMet() { + return ChargingConstraintObserver.isCharging(); + } + + @Override + public @NonNull String getFactoryKey() { + return KEY; + } + + @RequiresApi(26) + @Override + public void applyToJobInfo(@NonNull JobInfo.Builder jobInfoBuilder) { + jobInfoBuilder.setRequiresCharging(true); + } + + public static final class Factory implements Constraint.Factory { + + @Override + public ChargingConstraint create() { + return new ChargingConstraint(); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraintObserver.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraintObserver.java new file mode 100644 index 0000000000..4a478f3f83 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/ChargingConstraintObserver.java @@ -0,0 +1,62 @@ +package org.thoughtcrime.securesms.jobmanager.impl; + +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +import org.thoughtcrime.securesms.jobmanager.ConstraintObserver; + +/** + * Observes the charging state of the device and notifies the JobManager system when appropriate. + */ +public class ChargingConstraintObserver implements ConstraintObserver { + + private static final String REASON = ChargingConstraintObserver.class.getSimpleName(); + private static final int STATUS_BATTERY = 0; + + private final Application application; + + private static volatile boolean charging; + + public ChargingConstraintObserver(@NonNull Application application) { + this.application = application; + } + + @Override + public void register(@NonNull Notifier notifier) { + Intent intent = application.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + boolean wasCharging = charging; + + charging = isCharging(intent); + + if (charging && !wasCharging) { + notifier.onConstraintMet(REASON); + } + } + }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + + charging = isCharging(intent); + } + + public static boolean isCharging() { + return charging; + } + + private static boolean isCharging(@Nullable Intent intent) { + if (intent == null) { + return false; + } + + int status = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, STATUS_BATTERY); + return status != STATUS_BATTERY; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 5e51c4b63e..ceddbab3b8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -10,6 +10,8 @@ import org.thoughtcrime.securesms.jobmanager.ConstraintObserver; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobMigration; import org.thoughtcrime.securesms.jobmanager.impl.CellServiceConstraintObserver; +import org.thoughtcrime.securesms.jobmanager.impl.ChargingConstraint; +import org.thoughtcrime.securesms.jobmanager.impl.ChargingConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint; @@ -158,6 +160,7 @@ public final class JobManagerFactories { public static Map getConstraintFactories(@NonNull Application application) { return new HashMap() {{ + put(ChargingConstraint.KEY, new ChargingConstraint.Factory()); put(NetworkConstraint.KEY, new NetworkConstraint.Factory(application)); put(NetworkOrCellServiceConstraint.KEY, new NetworkOrCellServiceConstraint.Factory(application)); put(NetworkOrCellServiceConstraint.LEGACY_KEY, new NetworkOrCellServiceConstraint.Factory(application)); @@ -168,6 +171,7 @@ public final class JobManagerFactories { public static List getConstraintObservers(@NonNull Application application) { return Arrays.asList(CellServiceConstraintObserver.getInstance(application), + new ChargingConstraintObserver(application), new NetworkConstraintObserver(application), new SqlCipherMigrationConstraintObserver(), new WebsocketDrainedConstraintObserver()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java index 3d1e9f9741..22f35f625c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LocalBackupJob.java @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.NoExternalStorageException; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.impl.ChargingConstraint; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.permissions.Permissions; @@ -36,12 +37,21 @@ public final class LocalBackupJob extends BaseJob { public static final String TEMP_BACKUP_FILE_PREFIX = ".backup"; public static final String TEMP_BACKUP_FILE_SUFFIX = ".tmp"; - public LocalBackupJob() { - this(new Job.Parameters.Builder() - .setQueue("__LOCAL_BACKUP__") - .setMaxInstances(1) - .setMaxAttempts(3) - .build()); + public LocalBackupJob(boolean forceNow) { + this(buildParameters(forceNow)); + } + + private static @NonNull Job.Parameters buildParameters(boolean forceNow) { + Job.Parameters.Builder builder = new Job.Parameters.Builder() + .setQueue("__LOCAL_BACKUP__") + .setMaxInstances(1) + .setMaxAttempts(3); + + if (!forceNow) { + builder.addConstraint(ChargingConstraint.KEY); + } + + return builder.build(); } private LocalBackupJob(@NonNull Job.Parameters parameters) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java index 044c04e77b..8a69538e77 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java @@ -153,7 +153,7 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment { .ifNecessary() .onAllGranted(() -> { Log.i(TAG, "Queing backup..."); - ApplicationDependencies.getJobManager().add(new LocalBackupJob()); + ApplicationDependencies.getJobManager().add(new LocalBackupJob(true)); }) .withPermanentDenialDialog(getString(R.string.ChatsPreferenceFragment_signal_requires_external_storage_permission_in_order_to_create_backups)) .execute(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java index 6a0f30edd8..e52f95b3c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java @@ -24,7 +24,7 @@ public class LocalBackupListener extends PersistentAlarmManagerListener { @Override protected long onAlarm(Context context, long scheduledTime) { if (TextSecurePreferences.isBackupEnabled(context)) { - ApplicationDependencies.getJobManager().add(new LocalBackupJob()); + ApplicationDependencies.getJobManager().add(new LocalBackupJob(false)); } return setNextBackupTimeToIntervalFromNow(context); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java index a7ed94afa5..aed3c4482a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ServiceUtil.java @@ -5,14 +5,12 @@ import android.app.ActivityManager; import android.app.AlarmManager; import android.app.NotificationManager; import android.app.job.JobScheduler; -import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.hardware.display.DisplayManager; import android.location.LocationManager; import android.media.AudioManager; import android.net.ConnectivityManager; -import android.os.Build; import android.os.PowerManager; import android.os.Vibrator; import androidx.annotation.NonNull; @@ -80,7 +78,7 @@ public class ServiceUtil { return (JobScheduler) context.getSystemService(JobScheduler.class); } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1) + @RequiresApi(22) public static @Nullable SubscriptionManager getSubscriptionManager(@NonNull Context context) { return (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); }