mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-12-01 16:51:16 +00:00
* AndroidManifest: Add SyncTriggerJobService * Add Constants#isRunningOnEmulator * Add Constants: WAIT_FOR_NEXT_SYNC_DELAY_SECS, TRIGGERED_SYNC_DURATION_SECS * WIP: Schedule Job in BootReceiver * Add Util/JobUtils * Util/JobUtils: Improve log text * Add service/SyncTriggerJobService scheduled by JobScheduler. See JobUtils#scheduleSyncTriggerServiceJob for more details. * RunConditionMonitor: Add SyncTriggerReceiver via LocalBroadcastReceiver. * BootReceiver: Add ToDo * Add pref: PREF_RUN_ON_TIME_SCHEDULE * Fine tune debug constants - time intervals * JobUtils: In seconds please * Add strings: en-GB * RunConditionMonitor: Implement hourly sync time frames (fixes #15) * Imported translation: de * Fix lint: .JOB_SCHEDULER_SERVICE, API 21 instead of 23 * JobUtils: Noop on Android API level before 21 (L) * Fix lint: RequiresApi(21) for SyncTriggerJobService * Hide pref "run on time schedule" on Android < 5.x * JobUtils: Show time of grace in brackets when logged * BootReceiver: Realign comment, remove unnecessary code
This commit is contained in:
parent
3e248fa2b3
commit
976d9f9bad
10 changed files with 192 additions and 0 deletions
|
@ -147,6 +147,11 @@
|
|||
android:value=".activities.MainActivity" />
|
||||
</activity>
|
||||
<service android:name=".service.SyncthingService" />
|
||||
<service
|
||||
android:name=".service.SyncTriggerJobService"
|
||||
android:label="SyncTriggerJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" >
|
||||
</service>
|
||||
<receiver android:name=".receiver.BootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
|
|
@ -298,6 +298,11 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
);
|
||||
|
||||
mCategoryRunConditions = (PreferenceScreen) findPreference("category_run_conditions");
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
// Remove pref as we use JobScheduler implementation which is not available on API < 21.
|
||||
CheckBoxPreference prefRunOnTimeSchedule = (CheckBoxPreference) findPreference(Constants.PREF_RUN_ON_TIME_SCHEDULE);
|
||||
mCategoryRunConditions.removePreference(prefRunOnTimeSchedule);
|
||||
}
|
||||
setPreferenceCategoryChangeListener(mCategoryRunConditions, this::onRunConditionPreferenceChange);
|
||||
|
||||
/* Behaviour */
|
||||
|
|
|
@ -18,6 +18,10 @@ public class BootReceiver extends BroadcastReceiver {
|
|||
|
||||
private static final String TAG = "BootReceiver";
|
||||
|
||||
/**
|
||||
* For testing purposes:
|
||||
* adb root & adb shell am broadcast -a android.intent.action.BOOT_COMPLETED
|
||||
*/
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Boolean bootCompleted = intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED);
|
||||
|
|
|
@ -27,6 +27,7 @@ public class Constants {
|
|||
public static final String PREF_RESPECT_BATTERY_SAVING = "respect_battery_saving";
|
||||
public static final String PREF_RESPECT_MASTER_SYNC = "respect_master_sync";
|
||||
public static final String PREF_RUN_IN_FLIGHT_MODE = "run_in_flight_mode";
|
||||
public static final String PREF_RUN_ON_TIME_SCHEDULE = "run_on_time_schedule";
|
||||
|
||||
// Preferences - Behaviour
|
||||
public static final String PREF_USE_ROOT = "use_root";
|
||||
|
@ -123,6 +124,13 @@ public class Constants {
|
|||
: 5
|
||||
);
|
||||
|
||||
/**
|
||||
* If the user enabled hourly one-time shot sync, the following
|
||||
* parameters are effective.
|
||||
*/
|
||||
public static final int WAIT_FOR_NEXT_SYNC_DELAY_SECS = isRunningOnEmulator() ? 10 : 3600;
|
||||
public static final int TRIGGERED_SYNC_DURATION_SECS = isRunningOnEmulator() ? 20 : 300;
|
||||
|
||||
/**
|
||||
* Directory where config is exported to and imported from.
|
||||
*/
|
||||
|
|
|
@ -19,11 +19,13 @@ import android.os.Handler;
|
|||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.SyncthingApp;
|
||||
import com.nutomic.syncthingandroid.service.ReceiverManager;
|
||||
import com.nutomic.syncthingandroid.util.JobUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -41,6 +43,9 @@ public class RunConditionMonitor {
|
|||
|
||||
private Boolean ENABLE_VERBOSE_LOG = false;
|
||||
|
||||
public static final String ACTION_SYNC_TRIGGER_FIRED =
|
||||
"com.github.catfriend1.syncthingandroid.service.RunConditionMonitor.ACTION_SYNC_TRIGGER_FIRED";
|
||||
|
||||
private static final String POWER_SOURCE_CHARGER_BATTERY = "ac_and_battery_power";
|
||||
private static final String POWER_SOURCE_CHARGER = "ac_power";
|
||||
private static final String POWER_SOURCE_BATTERY = "battery_power";
|
||||
|
@ -87,9 +92,17 @@ public class RunConditionMonitor {
|
|||
|
||||
private final Context mContext;
|
||||
private ReceiverManager mReceiverManager;
|
||||
private @Nullable SyncTriggerReceiver mSyncTriggerReceiver = null;
|
||||
private Resources res;
|
||||
private String mRunDecisionExplanation = "";
|
||||
|
||||
/**
|
||||
* Only relevant if the user has enabled turning Syncthing on by
|
||||
* time schedule for a specific amount of time periodically.
|
||||
* Holds true if we are within a "SyncthingNative should run" time frame.
|
||||
*/
|
||||
private Boolean mTimeConditionMatch = false;
|
||||
|
||||
@Inject
|
||||
SharedPreferences mPreferences;
|
||||
|
||||
|
@ -142,16 +155,31 @@ public class RunConditionMonitor {
|
|||
mSyncStatusObserverHandle = ContentResolver.addStatusChangeListener(
|
||||
ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, mSyncStatusObserver);
|
||||
|
||||
// SyncTriggerReceiver
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(mContext);
|
||||
mSyncTriggerReceiver = new SyncTriggerReceiver();
|
||||
localBroadcastManager.registerReceiver(mSyncTriggerReceiver,
|
||||
new IntentFilter(ACTION_SYNC_TRIGGER_FIRED));
|
||||
|
||||
// Initially determine if syncthing should run under current circumstances.
|
||||
updateShouldRunDecision();
|
||||
|
||||
// Initially schedule the SyncTrigger job.
|
||||
JobUtils.scheduleSyncTriggerServiceJob(context, Constants.WAIT_FOR_NEXT_SYNC_DELAY_SECS);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
LogV("Shutting down");
|
||||
JobUtils.cancelAllScheduledJobs(mContext);
|
||||
if (mSyncStatusObserverHandle != null) {
|
||||
ContentResolver.removeStatusChangeListener(mSyncStatusObserverHandle);
|
||||
mSyncStatusObserverHandle = null;
|
||||
}
|
||||
if (mSyncTriggerReceiver != null) {
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(mContext);
|
||||
localBroadcastManager.unregisterReceiver(mSyncTriggerReceiver);
|
||||
mSyncTriggerReceiver = null;
|
||||
}
|
||||
mReceiverManager.unregisterAllReceivers(mContext);
|
||||
}
|
||||
|
||||
|
@ -183,6 +211,38 @@ public class RunConditionMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
private class SyncTriggerReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
LogV("SyncTriggerReceiver: onReceive");
|
||||
boolean prefRunOnTimeSchedule = mPreferences.getBoolean(Constants.PREF_RUN_ON_TIME_SCHEDULE, false);
|
||||
if (!prefRunOnTimeSchedule) {
|
||||
mTimeConditionMatch = false;
|
||||
} else {
|
||||
/**
|
||||
* Toggle the "digital input" for this condition as the condition change is
|
||||
* triggered by a time schedule and not the OS notifying us.
|
||||
*/
|
||||
mTimeConditionMatch = !mTimeConditionMatch;
|
||||
updateShouldRunDecision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reschedule the job.
|
||||
* If we are within a "SyncthingNative should run" time frame,
|
||||
* let the receiver fire and change to "SyncthingNative shouldn't run" after
|
||||
* TRIGGERED_SYNC_DURATION_SECS seconds elapsed.
|
||||
* If we are within a "SyncthingNative shouldn't run" time frame,
|
||||
* let the receiver fire and change to "SyncthingNative should run" after
|
||||
* WAIT_FOR_NEXT_SYNC_DELAY_SECS seconds elapsed.
|
||||
*/
|
||||
JobUtils.scheduleSyncTriggerServiceJob(
|
||||
context,
|
||||
mTimeConditionMatch ? Constants.TRIGGERED_SYNC_DURATION_SECS : Constants.WAIT_FOR_NEXT_SYNC_DELAY_SECS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler that is fired after preconditions changed.
|
||||
* We then need to decide if syncthing should run.
|
||||
|
@ -301,6 +361,15 @@ public class RunConditionMonitor {
|
|||
boolean prefRespectPowerSaving = mPreferences.getBoolean(Constants.PREF_RESPECT_BATTERY_SAVING, true);
|
||||
boolean prefRespectMasterSync = mPreferences.getBoolean(Constants.PREF_RESPECT_MASTER_SYNC, false);
|
||||
boolean prefRunInFlightMode = mPreferences.getBoolean(Constants.PREF_RUN_IN_FLIGHT_MODE, false);
|
||||
boolean prefRunOnTimeSchedule = mPreferences.getBoolean(Constants.PREF_RUN_ON_TIME_SCHEDULE, false);
|
||||
|
||||
// PREF_RUN_ON_TIME_SCHEDULE
|
||||
if (prefRunOnTimeSchedule && !mTimeConditionMatch) {
|
||||
// Currently, we aren't within a "SyncthingNative should run" time frame.
|
||||
LogV("decideShouldRun: PREF_RUN_ON_TIME_SCHEDULE && !mTimeConditionMatch");
|
||||
mRunDecisionExplanation = res.getString(R.string.reason_not_within_time_frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
// PREF_POWER_SOURCE
|
||||
switch (prefPowerSource) {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package com.nutomic.syncthingandroid.service;
|
||||
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
// import android.util.Log;
|
||||
|
||||
import com.nutomic.syncthingandroid.service.Constants;
|
||||
import com.nutomic.syncthingandroid.service.RunConditionMonitor;
|
||||
import com.nutomic.syncthingandroid.util.JobUtils;
|
||||
|
||||
/**
|
||||
* SyncTriggerJobService to be scheduled by the JobScheduler.
|
||||
* See {@link JobUtils#scheduleSyncTriggerServiceJob} for more details.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
public class SyncTriggerJobService extends JobService {
|
||||
private static final String TAG = "SyncTriggerJobService";
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
// Log.v(TAG, "onStartJob: Job fired.");
|
||||
Context context = getApplicationContext();
|
||||
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||
Intent intent = new Intent(RunConditionMonitor.ACTION_SYNC_TRIGGER_FIRED);
|
||||
localBroadcastManager.sendBroadcast(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters params) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.nutomic.syncthingandroid.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.nutomic.syncthingandroid.service.SyncTriggerJobService;
|
||||
|
||||
public class JobUtils {
|
||||
|
||||
private static final String TAG = "JobUtils";
|
||||
|
||||
private static final int TOLERATED_INACCURACY_IN_SECONDS = 120;
|
||||
|
||||
@TargetApi(21)
|
||||
public static void scheduleSyncTriggerServiceJob(Context context, int delayInSeconds) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
ComponentName serviceComponent = new ComponentName(context, SyncTriggerJobService.class);
|
||||
JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
|
||||
|
||||
// Wait at least "delayInSeconds".
|
||||
builder.setMinimumLatency(delayInSeconds * 1000);
|
||||
|
||||
// Maximum tolerated delay.
|
||||
builder.setOverrideDeadline((delayInSeconds + TOLERATED_INACCURACY_IN_SECONDS) * 1000);
|
||||
|
||||
// Schedule the start of "SyncTriggerJobService" in "X" seconds.
|
||||
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(context.JOB_SCHEDULER_SERVICE);
|
||||
jobScheduler.schedule(builder.build());
|
||||
Log.i(TAG, "Scheduled SyncTriggerJobService to run in " +
|
||||
Integer.toString(delayInSeconds) +
|
||||
"(+" + Integer.toString(TOLERATED_INACCURACY_IN_SECONDS) + ") seconds.");
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
public static void cancelAllScheduledJobs(Context context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return;
|
||||
}
|
||||
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(context.JOB_SCHEDULER_SERVICE);
|
||||
jobScheduler.cancelAll();
|
||||
}
|
||||
}
|
|
@ -454,6 +454,9 @@ Bitte melden Sie auftretende Probleme via GitHub.</string>
|
|||
<string name="run_in_flight_mode_title">Starte ohne Netzwerkverbindung</string>
|
||||
<string name="run_in_flight_mode_summary">Durch Aktivieren der Option wird Syncthing auch dann laufen, wenn Du offline bist. Aktiviere dies, wenn dein Telefon Probleme beim Erkennen von manuell hergestellten WLAN-Verbindungen im Flugmodus hat.</string>
|
||||
|
||||
<string name="run_on_time_schedule_title">Gemäß Zeitplan synchronisieren</string>
|
||||
<string name="run_on_time_schedule_summary">Durch Aktivieren dieser Option wird versucht, stündlich für 5 Minuten zu synchronisieren. Dies kann eine Menge Batterie einsparen, erfordert jedoch, dass Synchronisierungspartner online sind. Hinweis: Dies kann unvollständige temporäre Dateien zurücklassen, bis die nächste geplante Synchronisierung stattfindet und abgeschlossen ist.</string>
|
||||
|
||||
<!-- Preferences - Behaviour -->
|
||||
<string name="behaviour_autostart_title">Autostart</string>
|
||||
<string name="behaviour_autostart_summary">Starte die App automatisch beim Hochfahren.</string>
|
||||
|
@ -760,6 +763,7 @@ Bitte melden Sie auftretende Probleme via GitHub.</string>
|
|||
<!-- RunConditionMonitor -->
|
||||
|
||||
<!-- Explanations why syncthing is running or not running -->
|
||||
<string name="reason_not_within_time_frame">Sie haben \'Gemäß Zeitplan synchronisieren\' aktiviert, und die letzte Synchronisierung ist nicht länger als eine Stunde her.</string>
|
||||
<string name="reason_not_charging">Telefon wird nicht aufgeladen</string>
|
||||
<string name="reason_not_on_battery_power">Telefon wird nicht batteriebetrieben</string>
|
||||
<string name="reason_not_while_power_saving">Syncthing läuft nicht, weil das Telefon im Energiesparmodus ist.</string>
|
||||
|
|
|
@ -457,6 +457,9 @@ Please report any problems you encounter via Github.</string>
|
|||
<string name="run_in_flight_mode_title">Run without network connection</string>
|
||||
<string name="run_in_flight_mode_summary">Enabling this option will cause Syncthing to run even when you\'re offline. Enable if your device has problems detecting manual Wi-Fi connections during flight mode.</string>
|
||||
|
||||
<string name="run_on_time_schedule_title">Run according to time schedule</string>
|
||||
<string name="run_on_time_schedule_summary">Enabling this option will attempt to sync every hour for the duration of 5 minutes. This can save a lot of battery but requires sync partners to be online. Please note: This may leave incomplete temporary files behind until the next scheduled sync takes place.</string>
|
||||
|
||||
<!-- Preferences - Behaviour -->
|
||||
<string name="behaviour_autostart_title">Autostart</string>
|
||||
<string name="behaviour_autostart_summary">Start app automatically on operating system startup.</string>
|
||||
|
@ -781,6 +784,7 @@ Please report any problems you encounter via Github.</string>
|
|||
<!-- RunConditionMonitor -->
|
||||
|
||||
<!-- Explanations why syncthing is running or not running -->
|
||||
<string name="reason_not_within_time_frame">You enabled \'Run according to time schedule\' and the last sync was no more than an hour ago.</string>
|
||||
<string name="reason_not_charging">Phone is not charging.</string>
|
||||
<string name="reason_not_on_battery_power">Phone is not running on battery power.</string>
|
||||
<string name="reason_not_while_power_saving">Syncthing is not running as the phone is currently power saving.</string>
|
||||
|
|
|
@ -72,6 +72,13 @@
|
|||
android:summary="@string/run_in_flight_mode_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<!-- Run on time schedule -->
|
||||
<CheckBoxPreference
|
||||
android:key="run_on_time_schedule"
|
||||
android:title="@string/run_on_time_schedule_title"
|
||||
android:summary="@string/run_on_time_schedule_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
<PreferenceScreen
|
||||
|
|
Loading…
Reference in a new issue