mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-26 14:21:16 +00:00
Fix Syncthing getting killed by ActivityManager when started using Intent (fixes #1247)
This commit is contained in:
parent
f1e578e4fc
commit
346cc35237
12 changed files with 169 additions and 136 deletions
|
@ -257,7 +257,12 @@ public class MainActivity extends StateDialogActivity
|
||||||
|
|
||||||
// SyncthingService needs to be started from this activity as the user
|
// SyncthingService needs to be started from this activity as the user
|
||||||
// can directly launch this activity from the recent activity switcher.
|
// can directly launch this activity from the recent activity switcher.
|
||||||
startService(new Intent(this, SyncthingService.class));
|
Intent serviceIntent = new Intent(this, SyncthingService.class);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
startForegroundService(serviceIntent);
|
||||||
|
} else {
|
||||||
|
startService(serviceIntent);
|
||||||
|
}
|
||||||
|
|
||||||
onNewIntent(getIntent());
|
onNewIntent(getIntent());
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
public static class SettingsFragment extends PreferenceFragment
|
public static class SettingsFragment extends PreferenceFragment
|
||||||
implements SyncthingActivity.OnServiceConnectedListener,
|
implements SyncthingActivity.OnServiceConnectedListener,
|
||||||
SyncthingService.OnServiceStateChangeListener, Preference.OnPreferenceChangeListener,
|
SyncthingService.OnServiceStateChangeListener, Preference.OnPreferenceChangeListener,
|
||||||
Preference.OnPreferenceClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
Preference.OnPreferenceClickListener {
|
||||||
|
|
||||||
private static final String TAG = "SettingsFragment";
|
private static final String TAG = "SettingsFragment";
|
||||||
private static final String KEY_EXPORT_CONFIG = "export_config";
|
private static final String KEY_EXPORT_CONFIG = "export_config";
|
||||||
|
@ -100,7 +100,7 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
@Inject SharedPreferences mPreferences;
|
@Inject SharedPreferences mPreferences;
|
||||||
|
|
||||||
private Preference mCategoryRunConditions;
|
private Preference mCategoryRunConditions;
|
||||||
private CheckBoxPreference mAlwaysRunInBackground;
|
private CheckBoxPreference mStartServiceOnBoot;
|
||||||
private ListPreference mPowerSource;
|
private ListPreference mPowerSource;
|
||||||
private CheckBoxPreference mRunOnMobileData;
|
private CheckBoxPreference mRunOnMobileData;
|
||||||
private CheckBoxPreference mRunOnWifi;
|
private CheckBoxPreference mRunOnWifi;
|
||||||
|
@ -152,7 +152,6 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
((SyncthingApp) getActivity().getApplication()).component().inject(this);
|
((SyncthingApp) getActivity().getApplication()).component().inject(this);
|
||||||
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
|
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
|
||||||
mPreferences.registerOnSharedPreferenceChangeListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,8 +165,8 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.app_settings);
|
addPreferencesFromResource(R.xml.app_settings);
|
||||||
PreferenceScreen screen = getPreferenceScreen();
|
PreferenceScreen screen = getPreferenceScreen();
|
||||||
mAlwaysRunInBackground =
|
mStartServiceOnBoot =
|
||||||
(CheckBoxPreference) findPreference(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND);
|
(CheckBoxPreference) findPreference(Constants.PREF_START_SERVICE_ON_BOOT);
|
||||||
mPowerSource =
|
mPowerSource =
|
||||||
(ListPreference) findPreference(Constants.PREF_POWER_SOURCE);
|
(ListPreference) findPreference(Constants.PREF_POWER_SOURCE);
|
||||||
mRunOnMobileData =
|
mRunOnMobileData =
|
||||||
|
@ -196,10 +195,6 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
categoryBehaviour.removePreference(findPreference(Constants.PREF_NOTIFICATION_TYPE));
|
|
||||||
}
|
|
||||||
|
|
||||||
mDeviceName = (EditTextPreference) findPreference("deviceName");
|
mDeviceName = (EditTextPreference) findPreference("deviceName");
|
||||||
mListenAddresses = (EditTextPreference) findPreference("listenAddresses");
|
mListenAddresses = (EditTextPreference) findPreference("listenAddresses");
|
||||||
mMaxRecvKbps = (EditTextPreference) findPreference("maxRecvKbps");
|
mMaxRecvKbps = (EditTextPreference) findPreference("maxRecvKbps");
|
||||||
|
@ -346,7 +341,6 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
mPreferences.unregisterOnSharedPreferenceChangeListener(this);
|
|
||||||
if (mSyncthingService != null) {
|
if (mSyncthingService != null) {
|
||||||
mSyncthingService.unregisterOnServiceStateChangeListener(this);
|
mSyncthingService.unregisterOnServiceStateChangeListener(this);
|
||||||
}
|
}
|
||||||
|
@ -455,15 +449,16 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
if (mPendingConfig) {
|
if (mSyncthingService != null) {
|
||||||
if (mSyncthingService != null && mApi != null &&
|
mNotificationHandler.updatePersistentNotification(mSyncthingService);
|
||||||
mSyncthingService.getCurrentState() != SyncthingService.State.DISABLED) {
|
if (mPendingConfig) {
|
||||||
mApi.saveConfigAndRestart();
|
if (mApi != null &&
|
||||||
mPendingConfig = false;
|
mSyncthingService.getCurrentState() != SyncthingService.State.DISABLED) {
|
||||||
|
mApi.saveConfigAndRestart();
|
||||||
|
mPendingConfig = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (mPendingRunConditions) {
|
||||||
if (mPendingRunConditions) {
|
|
||||||
if (mSyncthingService != null) {
|
|
||||||
mSyncthingService.evaluateRunConditions();
|
mSyncthingService.evaluateRunConditions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -620,21 +615,6 @@ public class SettingsActivity extends SyncthingActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update notification after that preference changes. We can't use onPreferenceChange() as
|
|
||||||
* the preference value isn't persisted there, and the NotificationHandler accesses the
|
|
||||||
* preference directly.
|
|
||||||
*
|
|
||||||
* This function is called when the activity is opened, so we need to make sure the service
|
|
||||||
* is connected.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
|
||||||
if (key.equals(Constants.PREF_NOTIFICATION_TYPE) && mSyncthingService != null) {
|
|
||||||
mNotificationHandler.updatePersistentNotification(mSyncthingService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables {@link #mUseRoot} preference depending whether root is available.
|
* Enables or disables {@link #mUseRoot} preference depending whether root is available.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -145,7 +145,12 @@ public class WebGuiActivity extends StateDialogActivity
|
||||||
|
|
||||||
// SyncthingService needs to be started from this activity as the user
|
// SyncthingService needs to be started from this activity as the user
|
||||||
// can directly launch this activity from the recent activity switcher.
|
// can directly launch this activity from the recent activity switcher.
|
||||||
startService(new Intent(this, SyncthingService.class));
|
Intent serviceIntent = new Intent(this, SyncthingService.class);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
startForegroundService(serviceIntent);
|
||||||
|
} else {
|
||||||
|
startService(serviceIntent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.nutomic.syncthingandroid.fragments;
|
package com.nutomic.syncthingandroid.fragments;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -53,6 +54,7 @@ public class DrawerFragment extends Fragment implements View.OnClickListener {
|
||||||
private Timer mTimer;
|
private Timer mTimer;
|
||||||
|
|
||||||
private MainActivity mActivity;
|
private MainActivity mActivity;
|
||||||
|
private SharedPreferences sharedPreferences = null;
|
||||||
|
|
||||||
public void onDrawerOpened() {
|
public void onDrawerOpened() {
|
||||||
mTimer = new Timer();
|
mTimer = new Timer();
|
||||||
|
@ -94,6 +96,9 @@ public class DrawerFragment extends Fragment implements View.OnClickListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
mActivity = (MainActivity) getActivity();
|
||||||
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mActivity);
|
||||||
|
|
||||||
mCpuUsage = view.findViewById(R.id.cpu_usage);
|
mCpuUsage = view.findViewById(R.id.cpu_usage);
|
||||||
mRamUsage = view.findViewById(R.id.ram_usage);
|
mRamUsage = view.findViewById(R.id.ram_usage);
|
||||||
mDownload = view.findViewById(R.id.download);
|
mDownload = view.findViewById(R.id.download);
|
||||||
|
@ -246,8 +251,23 @@ public class DrawerFragment extends Fragment implements View.OnClickListener {
|
||||||
mActivity.closeDrawer();
|
mActivity.closeDrawer();
|
||||||
break;
|
break;
|
||||||
case R.id.drawerActionExit:
|
case R.id.drawerActionExit:
|
||||||
mActivity.stopService(new Intent(mActivity, SyncthingService.class));
|
if (sharedPreferences != null && sharedPreferences.getBoolean(Constants.PREF_START_SERVICE_ON_BOOT, false)) {
|
||||||
mActivity.finish();
|
/**
|
||||||
|
* App is running as a service. Show an explanation why exiting syncthing is an
|
||||||
|
* extraordinary request, then ask the user to confirm.
|
||||||
|
*/
|
||||||
|
AlertDialog mExitConfirmationDialog = new AlertDialog.Builder(mActivity)
|
||||||
|
.setTitle(R.string.dialog_exit_while_running_as_service_title)
|
||||||
|
.setMessage(R.string.dialog_exit_while_running_as_service_message)
|
||||||
|
.setPositiveButton(R.string.yes, (d, i) -> {
|
||||||
|
doExit();
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no, (d, i) -> {})
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
// App is not running as a service.
|
||||||
|
doExit();
|
||||||
|
}
|
||||||
mActivity.closeDrawer();
|
mActivity.closeDrawer();
|
||||||
break;
|
break;
|
||||||
case R.id.drawerActionShowQrCode:
|
case R.id.drawerActionShowQrCode:
|
||||||
|
@ -258,6 +278,15 @@ public class DrawerFragment extends Fragment implements View.OnClickListener {
|
||||||
|
|
||||||
private boolean alwaysRunInBackground() {
|
private boolean alwaysRunInBackground() {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
return sp.getBoolean(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND, false);
|
return sp.getBoolean(Constants.PREF_START_SERVICE_ON_BOOT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doExit() {
|
||||||
|
if (mActivity == null || mActivity.isFinishing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Exiting app on user request");
|
||||||
|
mActivity.stopService(new Intent(mActivity, SyncthingService.class));
|
||||||
|
mActivity.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class AppConfigReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the Syncthing-Service
|
* Stop the Syncthing-Service
|
||||||
* If alwaysRunInBackground is enabled the service must not be stopped. Instead a
|
* If startServiceOnBoot is enabled the service must not be stopped. Instead a
|
||||||
* notification is presented to the user.
|
* notification is presented to the user.
|
||||||
*/
|
*/
|
||||||
private static final String ACTION_STOP = "com.nutomic.syncthingandroid.action.STOP";
|
private static final String ACTION_STOP = "com.nutomic.syncthingandroid.action.STOP";
|
||||||
|
@ -40,7 +40,7 @@ public class AppConfigReceiver extends BroadcastReceiver {
|
||||||
BootReceiver.startServiceCompat(context);
|
BootReceiver.startServiceCompat(context);
|
||||||
break;
|
break;
|
||||||
case ACTION_STOP:
|
case ACTION_STOP:
|
||||||
if (alwaysRunInBackground(context)) {
|
if (startServiceOnBoot(context)) {
|
||||||
mNotificationHandler.showStopSyncthingWarningNotification();
|
mNotificationHandler.showStopSyncthingWarningNotification();
|
||||||
} else {
|
} else {
|
||||||
context.stopService(new Intent(context, SyncthingService.class));
|
context.stopService(new Intent(context, SyncthingService.class));
|
||||||
|
@ -49,8 +49,8 @@ public class AppConfigReceiver extends BroadcastReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean alwaysRunInBackground(Context context) {
|
private static boolean startServiceOnBoot(Context context) {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
return sp.getBoolean(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND, false);
|
return sp.getBoolean(Constants.PREF_START_SERVICE_ON_BOOT, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,14 @@ public class BootReceiver extends BroadcastReceiver {
|
||||||
!intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED))
|
!intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!alwaysRunInBackground(context))
|
if (!startServiceOnBoot(context))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
startServiceCompat(context);
|
startServiceCompat(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Workaround for starting service from background on Android 8.
|
* Workaround for starting service from background on Android 8+.
|
||||||
*
|
*
|
||||||
* https://stackoverflow.com/a/44505719/1837158
|
* https://stackoverflow.com/a/44505719/1837158
|
||||||
*/
|
*/
|
||||||
|
@ -39,8 +39,8 @@ public class BootReceiver extends BroadcastReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean alwaysRunInBackground(Context context) {
|
private static boolean startServiceOnBoot(Context context) {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
return sp.getBoolean(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND, false);
|
return sp.getBoolean(Constants.PREF_START_SERVICE_ON_BOOT, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class Constants {
|
||||||
public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so";
|
public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so";
|
||||||
|
|
||||||
// Preferences - Run conditions
|
// Preferences - Run conditions
|
||||||
public static final String PREF_ALWAYS_RUN_IN_BACKGROUND = "always_run_in_background";
|
public static final String PREF_START_SERVICE_ON_BOOT = "always_run_in_background";
|
||||||
public static final String PREF_RUN_ON_MOBILE_DATA = "run_on_mobile_data";
|
public static final String PREF_RUN_ON_MOBILE_DATA = "run_on_mobile_data";
|
||||||
public static final String PREF_RUN_ON_WIFI = "run_on_wifi";
|
public static final String PREF_RUN_ON_WIFI = "run_on_wifi";
|
||||||
public static final String PREF_RUN_ON_METERED_WIFI = "run_on_metered_wifi";
|
public static final String PREF_RUN_ON_METERED_WIFI = "run_on_metered_wifi";
|
||||||
|
@ -25,7 +25,6 @@ public class Constants {
|
||||||
public static final String PREF_FIRST_START = "first_start";
|
public static final String PREF_FIRST_START = "first_start";
|
||||||
public static final String PREF_START_INTO_WEB_GUI = "start_into_web_gui";
|
public static final String PREF_START_INTO_WEB_GUI = "start_into_web_gui";
|
||||||
public static final String PREF_USE_ROOT = "use_root";
|
public static final String PREF_USE_ROOT = "use_root";
|
||||||
public static final String PREF_NOTIFICATION_TYPE = "notification_type";
|
|
||||||
public static final String PREF_ENVIRONMENT_VARIABLES = "environment_variables";
|
public static final String PREF_ENVIRONMENT_VARIABLES = "environment_variables";
|
||||||
public static final String PREF_DEBUG_FACILITIES_ENABLED = "debug_facilities_enabled";
|
public static final String PREF_DEBUG_FACILITIES_ENABLED = "debug_facilities_enabled";
|
||||||
public static final String PREF_USE_WAKE_LOCK = "wakelock_while_binary_running";
|
public static final String PREF_USE_WAKE_LOCK = "wakelock_while_binary_running";
|
||||||
|
|
|
@ -8,7 +8,6 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -43,6 +42,9 @@ public class NotificationHandler {
|
||||||
private final NotificationChannel mPersistentChannelWaiting;
|
private final NotificationChannel mPersistentChannelWaiting;
|
||||||
private final NotificationChannel mInfoChannel;
|
private final NotificationChannel mInfoChannel;
|
||||||
|
|
||||||
|
private Boolean lastStartForegroundService = false;
|
||||||
|
private Boolean appShutdownInProgress = false;
|
||||||
|
|
||||||
public NotificationHandler(Context context) {
|
public NotificationHandler(Context context) {
|
||||||
((SyncthingApp) context.getApplicationContext()).component().inject(this);
|
((SyncthingApp) context.getApplicationContext()).component().inject(this);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
@ -88,84 +90,102 @@ public class NotificationHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows or hides the persistent notification based on running state and
|
* Shows, updates or hides the notification.
|
||||||
* {@link Constants#PREF_NOTIFICATION_TYPE}.
|
|
||||||
*/
|
*/
|
||||||
public void updatePersistentNotification(SyncthingService service) {
|
public void updatePersistentNotification(SyncthingService service) {
|
||||||
String type = mPreferences.getString(Constants.PREF_NOTIFICATION_TYPE, "low_priority");
|
boolean startServiceOnBoot = mPreferences.getBoolean(Constants.PREF_START_SERVICE_ON_BOOT, false);
|
||||||
|
|
||||||
// Always use startForeground() if app is set to always run. This makes sure the app
|
|
||||||
// is not killed, and we don't miss wifi/charging events.
|
|
||||||
// On Android 8, this behaviour is mandatory to receive broadcasts.
|
|
||||||
// https://stackoverflow.com/a/44505719/1837158
|
|
||||||
boolean foreground = mPreferences.getBoolean(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND, false);
|
|
||||||
|
|
||||||
// Foreground priority requires a notification so this ensures that we either have a
|
|
||||||
// "default" or "low_priority" notification, but not "none".
|
|
||||||
if ("none".equals(type) && foreground) {
|
|
||||||
type = "low_priority";
|
|
||||||
}
|
|
||||||
|
|
||||||
State currentServiceState = service.getCurrentState();
|
State currentServiceState = service.getCurrentState();
|
||||||
boolean syncthingRunning = currentServiceState == SyncthingService.State.ACTIVE ||
|
boolean syncthingRunning = currentServiceState == SyncthingService.State.ACTIVE ||
|
||||||
currentServiceState == SyncthingService.State.STARTING;
|
currentServiceState == SyncthingService.State.STARTING;
|
||||||
if (foreground || (syncthingRunning && !type.equals("none"))) {
|
boolean startForegroundService = false;
|
||||||
int title = R.string.syncthing_terminated;
|
if (!appShutdownInProgress) {
|
||||||
switch (currentServiceState) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
case ERROR:
|
/**
|
||||||
case INIT:
|
* Android 7 and lower:
|
||||||
break;
|
* The app may run in background and monitor run conditions even if it is not
|
||||||
case DISABLED:
|
* running as a foreground service. For that reason, we can use a normal
|
||||||
title = R.string.syncthing_disabled;
|
* notification if syncthing is DISABLED.
|
||||||
break;
|
*/
|
||||||
case STARTING:
|
startForegroundService = startServiceOnBoot || syncthingRunning;
|
||||||
case ACTIVE:
|
} else {
|
||||||
title = R.string.syncthing_active;
|
/**
|
||||||
break;
|
* Android 8+:
|
||||||
default:
|
* Always use startForeground.
|
||||||
break;
|
* This makes sure the app is not killed, and we don't miss run condition events.
|
||||||
|
* On Android 8+, this behaviour is mandatory to receive broadcasts.
|
||||||
|
* https://stackoverflow.com/a/44505719/1837158
|
||||||
|
* Foreground priority requires a notification so this ensures that we either have a
|
||||||
|
* "default" or "low_priority" notification, but not "none".
|
||||||
|
*/
|
||||||
|
startForegroundService = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Check if we have to stopForeground.
|
||||||
* We no longer need to launch FirstStartActivity instead of MainActivity as
|
if (startForegroundService != lastStartForegroundService) {
|
||||||
* {@link SyncthingService#onStartCommand} will check for denied permissions.
|
if (!startForegroundService) {
|
||||||
*/
|
Log.v(TAG, "Stopping foreground service");
|
||||||
Intent intent = new Intent(mContext, MainActivity.class);
|
service.stopForeground(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reason for two separate IDs: if one of the notification channels is hidden then
|
// Prepare notification builder.
|
||||||
// the startForeground() below won't update the notification but use the old one
|
int title = R.string.syncthing_terminated;
|
||||||
int idToShow = syncthingRunning ? ID_PERSISTENT : ID_PERSISTENT_WAITING;
|
switch (currentServiceState) {
|
||||||
int idToCancel = syncthingRunning ? ID_PERSISTENT_WAITING : ID_PERSISTENT;
|
case ERROR:
|
||||||
NotificationChannel channel = syncthingRunning ? mPersistentChannel : mPersistentChannelWaiting;
|
case INIT:
|
||||||
NotificationCompat.Builder builder = getNotificationBuilder(channel)
|
break;
|
||||||
.setContentTitle(mContext.getString(title))
|
case DISABLED:
|
||||||
.setSmallIcon(R.drawable.ic_stat_notify)
|
title = R.string.syncthing_disabled;
|
||||||
.setOngoing(true)
|
break;
|
||||||
.setOnlyAlertOnce(true)
|
case STARTING:
|
||||||
.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0));
|
title = R.string.syncthing_starting;
|
||||||
if (type.equals("low_priority"))
|
break;
|
||||||
builder.setPriority(NotificationCompat.PRIORITY_MIN);
|
case ACTIVE:
|
||||||
|
title = R.string.syncthing_active;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (foreground) {
|
/**
|
||||||
|
* Reason for two separate IDs: if one of the notification channels is hidden then
|
||||||
|
* the startForeground() below won't update the notification but use the old one.
|
||||||
|
*/
|
||||||
|
int idToShow = syncthingRunning ? ID_PERSISTENT : ID_PERSISTENT_WAITING;
|
||||||
|
int idToCancel = syncthingRunning ? ID_PERSISTENT_WAITING : ID_PERSISTENT;
|
||||||
|
Intent intent = new Intent(mContext, MainActivity.class);
|
||||||
|
NotificationChannel channel = syncthingRunning ? mPersistentChannel : mPersistentChannelWaiting;
|
||||||
|
NotificationCompat.Builder builder = getNotificationBuilder(channel)
|
||||||
|
.setContentTitle(mContext.getString(title))
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_notify)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setOnlyAlertOnce(true)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
|
.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
if (!appShutdownInProgress) {
|
||||||
|
if (startForegroundService) {
|
||||||
|
Log.v(TAG, "Starting foreground service or updating notification");
|
||||||
service.startForeground(idToShow, builder.build());
|
service.startForeground(idToShow, builder.build());
|
||||||
} else {
|
} else {
|
||||||
service.stopForeground(false); // ensure no longer running with foreground priority
|
Log.v(TAG, "Updating notification");
|
||||||
mNotificationManager.notify(idToShow, builder.build());
|
mNotificationManager.notify(idToShow, builder.build());
|
||||||
}
|
}
|
||||||
mNotificationManager.cancel(idToCancel);
|
|
||||||
} else {
|
} else {
|
||||||
// ensure no longer running with foreground priority
|
mNotificationManager.cancel(idToShow);
|
||||||
cancelPersistentNotification(service);
|
|
||||||
}
|
}
|
||||||
|
mNotificationManager.cancel(idToCancel);
|
||||||
|
|
||||||
|
// Remember last notification visibility.
|
||||||
|
lastStartForegroundService = startForegroundService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelPersistentNotification(SyncthingService service) {
|
/**
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && alwaysRunInBackground())
|
* Called by {@link SyncthingService#onStart} {@link SyncthingService#onDestroy}
|
||||||
return;
|
* to indicate app startup and shutdown.
|
||||||
|
*/
|
||||||
service.stopForeground(false);
|
public void setAppShutdownInProgress(Boolean newValue) {
|
||||||
mNotificationManager.cancel(ID_PERSISTENT);
|
appShutdownInProgress = newValue;
|
||||||
mNotificationManager.cancel(ID_PERSISTENT_WAITING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showCrashedNotification(@StringRes int title, boolean force) {
|
public void showCrashedNotification(@StringRes int title, boolean force) {
|
||||||
|
@ -280,9 +300,4 @@ public class NotificationHandler {
|
||||||
}
|
}
|
||||||
mNotificationManager.notify(ID_STOP_BACKGROUND_WARNING, nb.build());
|
mNotificationManager.notify(ID_STOP_BACKGROUND_WARNING, nb.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean alwaysRunInBackground() {
|
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
|
|
||||||
return sp.getBoolean(Constants.PREF_ALWAYS_RUN_IN_BACKGROUND, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,8 +179,12 @@ public class SyncthingService extends Service {
|
||||||
* We need to recheck if we still have the storage permission.
|
* We need to recheck if we still have the storage permission.
|
||||||
*/
|
*/
|
||||||
mStoragePermissionGranted = (ContextCompat.checkSelfPermission(this,
|
mStoragePermissionGranted = (ContextCompat.checkSelfPermission(this,
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
|
Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
|
||||||
PackageManager.PERMISSION_GRANTED);
|
PackageManager.PERMISSION_GRANTED);
|
||||||
|
|
||||||
|
if (mNotificationHandler != null) {
|
||||||
|
mNotificationHandler.setAppShutdownInProgress(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -449,6 +453,9 @@ public class SyncthingService extends Service {
|
||||||
*/
|
*/
|
||||||
mRunConditionMonitor.shutdown();
|
mRunConditionMonitor.shutdown();
|
||||||
}
|
}
|
||||||
|
if (mNotificationHandler != null) {
|
||||||
|
mNotificationHandler.setAppShutdownInProgress(true);
|
||||||
|
}
|
||||||
if (mStoragePermissionGranted) {
|
if (mStoragePermissionGranted) {
|
||||||
synchronized (mStateLock) {
|
synchronized (mStateLock) {
|
||||||
if (mCurrentState == State.STARTING) {
|
if (mCurrentState == State.STARTING) {
|
||||||
|
@ -494,10 +501,6 @@ public class SyncthingService extends Service {
|
||||||
mApi = null;
|
mApi = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mNotificationHandler != null) {
|
|
||||||
mNotificationHandler.cancelPersistentNotification(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mSyncthingRunnable != null) {
|
if (mSyncthingRunnable != null) {
|
||||||
mSyncthingRunnable.killSyncthing();
|
mSyncthingRunnable.killSyncthing();
|
||||||
if (mSyncthingRunnableThread != null) {
|
if (mSyncthingRunnableThread != null) {
|
||||||
|
@ -556,7 +559,7 @@ public class SyncthingService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to notifiy listeners of an API change.
|
* Called to notify listeners of an API change.
|
||||||
*/
|
*/
|
||||||
private void onServiceStateChange(State newState) {
|
private void onServiceStateChange(State newState) {
|
||||||
Log.v(TAG, "onServiceStateChange: from " + mCurrentState + " to " + newState);
|
Log.v(TAG, "onServiceStateChange: from " + mCurrentState + " to " + newState);
|
||||||
|
|
|
@ -15,10 +15,4 @@
|
||||||
<item>battery_power</item>
|
<item>battery_power</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="notification_type_entry_values">
|
|
||||||
<item>normal</item>
|
|
||||||
<item>low_priority</item>
|
|
||||||
<item>none</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -43,6 +43,10 @@ Please report any problems you encounter via Github.</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Title of the exit app when running as a service confirmation dialog -->
|
||||||
|
<string name="dialog_exit_while_running_as_service_title">Confirm to quit app</string>
|
||||||
|
<string name="dialog_exit_while_running_as_service_message">For your consideration: You configured the app to start automatically on boot. Therefore it monitors run conditions and syncs at any time in the background when conditions match. You should only quit manually if you run into severe problems. Otherwise, disable \'Start automatically on boot \' in the settings. Would you like to quit now until the device rebooted?</string>
|
||||||
|
|
||||||
<!-- Title of the "add folder" menu action -->
|
<!-- Title of the "add folder" menu action -->
|
||||||
<string name="add_folder">Add Folder</string>
|
<string name="add_folder">Add Folder</string>
|
||||||
|
|
||||||
|
@ -135,6 +139,13 @@ Please report any problems you encounter via Github.</string>
|
||||||
<!-- Title for current upload rate -->
|
<!-- Title for current upload rate -->
|
||||||
<string name="upload_title">Upload</string>
|
<string name="upload_title">Upload</string>
|
||||||
|
|
||||||
|
<!-- StatusFragment -->
|
||||||
|
<string name="status_fragment_title">Status</string>
|
||||||
|
<string name="syncthing_starting">Syncthing is starting.</string>
|
||||||
|
<string name="syncthing_running">Syncthing is running.</string>
|
||||||
|
<string name="syncthing_not_running">Syncthing is not running.</string>
|
||||||
|
<string name="syncthing_has_crashed">Syncthing has crashed.</string>
|
||||||
|
|
||||||
<!-- DrawerFragment -->
|
<!-- DrawerFragment -->
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,14 +73,6 @@
|
||||||
android:summary="@string/advanced_folder_picker_summary"
|
android:summary="@string/advanced_folder_picker_summary"
|
||||||
android:defaultValue="false" />
|
android:defaultValue="false" />
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:key="notification_type"
|
|
||||||
android:title="@string/notification_type_title"
|
|
||||||
android:entryValues="@array/notification_type_entry_values"
|
|
||||||
android:entries="@array/notification_type_entries"
|
|
||||||
android:summary="@string/notification_type_summary"
|
|
||||||
android:defaultValue="low_priority" />
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="pref_current_language"
|
android:key="pref_current_language"
|
||||||
android:title="@string/preference_language_title"
|
android:title="@string/preference_language_title"
|
||||||
|
|
Loading…
Reference in a new issue