diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9702a58763..a0b805b1dd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -371,6 +371,9 @@
It looks like you don\'t have any apps to share to.
Friends don\'t let friends chat unencrypted.
+
+ Working in the background...
+
Failed to send
New safety number
@@ -477,6 +480,9 @@
The version of Google Play Services you have installed is not functioning correctly. Please reinstall Google Play Services and try again.
+
+ Retrieving a message...
+
Rate this app
If you enjoy using this app, please take a moment to help us by rating it.
@@ -556,6 +562,9 @@
Contacts
Messages
+
+ Sending a message...
+
Add to Contacts
Invite to Signal
diff --git a/src/org/thoughtcrime/securesms/jobmanager/Job.java b/src/org/thoughtcrime/securesms/jobmanager/Job.java
index 3bf88bf82d..179d3f28a1 100644
--- a/src/org/thoughtcrime/securesms/jobmanager/Job.java
+++ b/src/org/thoughtcrime/securesms/jobmanager/Job.java
@@ -5,10 +5,13 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.ApplicationContext;
+import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.jobmanager.dependencies.ContextDependent;
+import org.thoughtcrime.securesms.jobmanager.requirements.NetworkRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.SqlCipherMigrationRequirement;
import org.thoughtcrime.securesms.logging.Log;
+import org.thoughtcrime.securesms.service.GenericForegroundService;
import java.io.Serializable;
import java.util.UUID;
@@ -26,6 +29,7 @@ public abstract class Job extends Worker implements Serializable {
static final String KEY_RETRY_COUNT = "Job_retry_count";
static final String KEY_RETRY_UNTIL = "Job_retry_until";
static final String KEY_SUBMIT_TIME = "Job_submit_time";
+ static final String KEY_REQUIRES_NETWORK = "Job_requires_network";
static final String KEY_REQUIRES_MASTER_SECRET = "Job_requires_master_secret";
static final String KEY_REQUIRES_SQLCIPHER = "Job_requires_sqlcipher";
@@ -58,10 +62,19 @@ public abstract class Job extends Worker implements Serializable {
initialize(new SafeData(data));
+ boolean foregroundRunning = false;
+
try {
if (withinRetryLimits(data)) {
if (requirementsMet(data)) {
+ if (needsForegroundService(data)) {
+ Log.i(TAG, "Running a foreground service with description '" + getDescription() + "' to aid in job execution." + logSuffix());
+ GenericForegroundService.startForegroundTask(getApplicationContext(), getDescription());
+ foregroundRunning = true;
+ }
+
onRun();
+
log("Successfully completed." + logSuffix());
return Result.SUCCESS;
} else {
@@ -79,6 +92,11 @@ public abstract class Job extends Worker implements Serializable {
}
warn("Failing due to an exception." + logSuffix(), e);
return cancel();
+ } finally {
+ if (foregroundRunning) {
+ Log.i(TAG, "Stopping the foreground service." + logSuffix());
+ GenericForegroundService.stopForegroundTask(getApplicationContext());
+ }
}
}
@@ -95,6 +113,14 @@ public abstract class Job extends Worker implements Serializable {
onAdded();
}
+ /**
+ * @return A string that represents what the task does. Will be shown in a foreground notification
+ * if necessary.
+ */
+ protected String getDescription() {
+ return getApplicationContext().getString(R.string.Job_working_in_the_background);
+ }
+
/**
* Called after a run has finished and we've determined a retry is required, but before the next
* attempt is run.
@@ -158,7 +184,7 @@ public abstract class Job extends Worker implements Serializable {
return Result.SUCCESS;
}
- private boolean requirementsMet(Data data) {
+ private boolean requirementsMet(@NonNull Data data) {
boolean met = true;
if (data.getBoolean(KEY_REQUIRES_MASTER_SECRET, false)) {
@@ -172,7 +198,7 @@ public abstract class Job extends Worker implements Serializable {
return met;
}
- private boolean withinRetryLimits(Data data) {
+ private boolean withinRetryLimits(@NonNull Data data) {
int retryCount = data.getInt(KEY_RETRY_COUNT, 0);
long retryUntil = data.getLong(KEY_RETRY_UNTIL, 0);
@@ -183,6 +209,13 @@ public abstract class Job extends Worker implements Serializable {
return System.currentTimeMillis() < retryUntil;
}
+ private boolean needsForegroundService(@NonNull Data data) {
+ NetworkRequirement networkRequirement = new NetworkRequirement(getApplicationContext());
+ boolean requiresNetwork = data.getBoolean(KEY_REQUIRES_NETWORK, false);
+
+ return requiresNetwork && !networkRequirement.isPresent();
+ }
+
private void log(@NonNull String message) {
log(message, null);
}
diff --git a/src/org/thoughtcrime/securesms/jobmanager/JobManager.java b/src/org/thoughtcrime/securesms/jobmanager/JobManager.java
index c408d742ed..b87c0afbe0 100644
--- a/src/org/thoughtcrime/securesms/jobmanager/JobManager.java
+++ b/src/org/thoughtcrime/securesms/jobmanager/JobManager.java
@@ -41,6 +41,7 @@ public class JobManager {
Data.Builder dataBuilder = new Data.Builder().putInt(Job.KEY_RETRY_COUNT, jobParameters.getRetryCount())
.putLong(Job.KEY_RETRY_UNTIL, jobParameters.getRetryUntil())
.putLong(Job.KEY_SUBMIT_TIME, System.currentTimeMillis())
+ .putBoolean(Job.KEY_REQUIRES_NETWORK, jobParameters.requiresNetwork())
.putBoolean(Job.KEY_REQUIRES_MASTER_SECRET, jobParameters.requiresMasterSecret())
.putBoolean(Job.KEY_REQUIRES_SQLCIPHER, jobParameters.requiresSqlCipher());
Data data = job.serialize(dataBuilder);
diff --git a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
index a8d4331b2a..f552ec6dc4 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
+import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.JobParameters;
import org.thoughtcrime.securesms.jobmanager.SafeData;
@@ -43,6 +44,11 @@ public class PushNotificationReceiveJob extends PushReceivedJob implements Injec
return dataBuilder.build();
}
+ @Override
+ protected String getDescription() {
+ return context.getString(R.string.PushNotificationReceiveJob_retrieving_a_message);
+ }
+
@Override
public void onRun() throws IOException {
receiver.retrieveMessages(new SignalServiceMessageReceiver.MessageReceivedCallback() {
diff --git a/src/org/thoughtcrime/securesms/jobs/SendJob.java b/src/org/thoughtcrime/securesms/jobs/SendJob.java
index 70e807e2a3..6b229668ed 100644
--- a/src/org/thoughtcrime/securesms/jobs/SendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/SendJob.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.BuildConfig;
+import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.TextSecureExpiredException;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
@@ -31,6 +32,11 @@ public abstract class SendJob extends MasterSecretJob {
super(context, parameters);
}
+ @Override
+ protected String getDescription() {
+ return context.getString(R.string.SendJob_sending_a_message);
+ }
+
@Override
public final void onRun(MasterSecret masterSecret) throws Exception {
if (Util.getDaysTillBuildExpiry() <= 0) {