mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-26 14:21:16 +00:00
Show reasons for disabled service in notification dialog (#1264)
This commit is contained in:
parent
9796d7beb1
commit
700c55e9d3
5 changed files with 233 additions and 37 deletions
|
@ -5,18 +5,23 @@ import android.content.Intent;
|
||||||
import android.databinding.DataBindingUtil;
|
import android.databinding.DataBindingUtil;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.databinding.DialogLoadingBinding;
|
import com.nutomic.syncthingandroid.databinding.DialogLoadingBinding;
|
||||||
|
import com.nutomic.syncthingandroid.model.RunConditionCheckResult;
|
||||||
import com.nutomic.syncthingandroid.service.SyncthingService;
|
import com.nutomic.syncthingandroid.service.SyncthingService;
|
||||||
import com.nutomic.syncthingandroid.service.SyncthingService.State;
|
import com.nutomic.syncthingandroid.service.SyncthingService.State;
|
||||||
import com.nutomic.syncthingandroid.util.Util;
|
import com.nutomic.syncthingandroid.util.Util;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.nutomic.syncthingandroid.model.RunConditionCheckResult.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles loading/disabled dialogs.
|
* Handles loading/disabled dialogs.
|
||||||
*/
|
*/
|
||||||
|
@ -32,8 +37,10 @@ public abstract class StateDialogActivity extends SyncthingActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
registerOnServiceConnectedListener(() ->
|
registerOnServiceConnectedListener(() -> {
|
||||||
getService().registerOnServiceStateChangeListener(this::onServiceStateChange));
|
getService().registerOnServiceStateChangeListener(this::onServiceStateChange);
|
||||||
|
getService().registerOnRunConditionCheckResultChange(this::onRunConditionCheckResultChange);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,6 +69,7 @@ public abstract class StateDialogActivity extends SyncthingActivity {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (getService() != null) {
|
if (getService() != null) {
|
||||||
getService().unregisterOnServiceStateChangeListener(this::onServiceStateChange);
|
getService().unregisterOnServiceStateChangeListener(this::onServiceStateChange);
|
||||||
|
getService().unregisterOnRunConditionCheckResultChange(this::onRunConditionCheckResultChange);
|
||||||
}
|
}
|
||||||
dismissDisabledDialog();
|
dismissDisabledDialog();
|
||||||
}
|
}
|
||||||
|
@ -89,13 +97,20 @@ public abstract class StateDialogActivity extends SyncthingActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onRunConditionCheckResultChange(RunConditionCheckResult result) {
|
||||||
|
if (mDisabledDialog != null && mDisabledDialog.isShowing()) {
|
||||||
|
mDisabledDialog.setMessage(getDisabledDialogMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void showDisabledDialog() {
|
private void showDisabledDialog() {
|
||||||
if (this.isFinishing() && (mDisabledDialog != null)) {
|
if (this.isFinishing() && (mDisabledDialog != null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mDisabledDialog = new AlertDialog.Builder(this)
|
mDisabledDialog = new AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.syncthing_disabled_title)
|
.setTitle(R.string.syncthing_disabled_title)
|
||||||
.setMessage(R.string.syncthing_disabled_message)
|
.setMessage(getDisabledDialogMessage())
|
||||||
.setPositiveButton(R.string.syncthing_disabled_change_settings,
|
.setPositiveButton(R.string.syncthing_disabled_change_settings,
|
||||||
(dialogInterface, i) -> {
|
(dialogInterface, i) -> {
|
||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
|
@ -110,6 +125,26 @@ public abstract class StateDialogActivity extends SyncthingActivity {
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private StringBuilder getDisabledDialogMessage() {
|
||||||
|
StringBuilder message = new StringBuilder();
|
||||||
|
message.append(this.getResources().getString(R.string.syncthing_disabled_message));
|
||||||
|
Collection<BlockerReason> reasons = getService().getCurrentRunConditionCheckResult().getBlockReasons();
|
||||||
|
if (!reasons.isEmpty()) {
|
||||||
|
message.append("\n");
|
||||||
|
message.append("\n");
|
||||||
|
message.append(this.getResources().getString(R.string.syncthing_disabled_reason_heading));
|
||||||
|
int count = 0;
|
||||||
|
for (BlockerReason reason : reasons) {
|
||||||
|
count++;
|
||||||
|
message.append("\n");
|
||||||
|
if (reasons.size() > 1) message.append(count + ". ");
|
||||||
|
message.append(this.getString(reason.getResId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
private void dismissDisabledDialog() {
|
private void dismissDisabledDialog() {
|
||||||
Util.dismissDialogSafe(mDisabledDialog, this);
|
Util.dismissDialogSafe(mDisabledDialog, this);
|
||||||
mDisabledDialog = null;
|
mDisabledDialog = null;
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.nutomic.syncthingandroid.model;
|
||||||
|
|
||||||
|
import com.nutomic.syncthingandroid.R;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RunConditionCheckResult {
|
||||||
|
|
||||||
|
public enum BlockerReason {
|
||||||
|
ON_BATTERY(R.string.syncthing_disabled_reason_on_battery),
|
||||||
|
ON_CHARGER(R.string.syncthing_disabled_reason_on_charger),
|
||||||
|
POWERSAVING_ENABLED(R.string.syncthing_disabled_reason_powersaving),
|
||||||
|
GLOBAL_SYNC_DISABLED(R.string.syncthing_disabled_reason_android_sync_disabled),
|
||||||
|
WIFI_SSID_NOT_WHITELISTED(R.string.syncthing_disabled_reason_wifi_ssid_not_whitelisted),
|
||||||
|
WIFI_WIFI_IS_METERED(R.string.syncthing_disabled_reason_wifi_is_metered),
|
||||||
|
NO_NETWORK_OR_FLIGHTMODE(R.string.syncthing_disabled_reason_no_network_or_flightmode),
|
||||||
|
NO_MOBILE_CONNECTION(R.string.syncthing_disabled_reason_no_mobile_connection),
|
||||||
|
NO_WIFI_CONNECTION(R.string.syncthing_disabled_reason_no_wifi_connection),
|
||||||
|
NO_ALLOWED_NETWORK(R.string.syncthing_disabled_reason_no_allowed_method);
|
||||||
|
|
||||||
|
private final int resId;
|
||||||
|
|
||||||
|
BlockerReason(int resId) {
|
||||||
|
this.resId = resId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getResId() {
|
||||||
|
return resId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final RunConditionCheckResult SHOULD_RUN = new RunConditionCheckResult();
|
||||||
|
|
||||||
|
private final boolean mShouldRun;
|
||||||
|
private final List<BlockerReason> mBlockReasons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use SHOULD_RUN instead.
|
||||||
|
* Note: of course anybody could still construct it by providing an empty list to the other
|
||||||
|
* constructor.
|
||||||
|
*/
|
||||||
|
private RunConditionCheckResult() {
|
||||||
|
this(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RunConditionCheckResult(List<BlockerReason> blockReasons) {
|
||||||
|
mBlockReasons = Collections.unmodifiableList(blockReasons);
|
||||||
|
mShouldRun = blockReasons.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<BlockerReason> getBlockReasons() {
|
||||||
|
return mBlockReasons;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShouldRun() {
|
||||||
|
return mShouldRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
RunConditionCheckResult that = (RunConditionCheckResult) o;
|
||||||
|
|
||||||
|
if (mShouldRun != that.mShouldRun) return false;
|
||||||
|
return mBlockReasons.equals(that.mBlockReasons);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = (mShouldRun ? 1 : 0);
|
||||||
|
result = 31 * result + mBlockReasons.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,19 +16,21 @@ import android.os.BatteryManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.nutomic.syncthingandroid.SyncthingApp;
|
import com.nutomic.syncthingandroid.SyncthingApp;
|
||||||
import com.nutomic.syncthingandroid.service.ReceiverManager;
|
import com.nutomic.syncthingandroid.model.RunConditionCheckResult;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static com.nutomic.syncthingandroid.model.RunConditionCheckResult.*;
|
||||||
|
import static com.nutomic.syncthingandroid.model.RunConditionCheckResult.BlockerReason.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds information about the current wifi and charging state of the device.
|
* Holds information about the current wifi and charging state of the device.
|
||||||
*
|
*
|
||||||
|
@ -52,7 +54,7 @@ public class RunConditionMonitor {
|
||||||
};
|
};
|
||||||
|
|
||||||
public interface OnRunConditionChangedListener {
|
public interface OnRunConditionChangedListener {
|
||||||
void onRunConditionChanged(boolean shouldRun);
|
void onRunConditionChanged(RunConditionCheckResult result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
@ -60,14 +62,14 @@ public class RunConditionMonitor {
|
||||||
private ReceiverManager mReceiverManager;
|
private ReceiverManager mReceiverManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sending callback notifications through {@link OnDeviceStateChangedListener} is enabled if not null.
|
* Sending callback notifications through {@link OnRunConditionChangedListener} is enabled if not null.
|
||||||
*/
|
*/
|
||||||
private @Nullable OnRunConditionChangedListener mOnRunConditionChangedListener = null;
|
private @Nullable OnRunConditionChangedListener mOnRunConditionChangedListener = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the result of the last call to {@link decideShouldRun}.
|
* Stores the result of the last call to {@link #decideShouldRun()}.
|
||||||
*/
|
*/
|
||||||
private boolean lastDeterminedShouldRun = false;
|
private RunConditionCheckResult lastRunConditionCheckResult;
|
||||||
|
|
||||||
public RunConditionMonitor(Context context, OnRunConditionChangedListener listener) {
|
public RunConditionMonitor(Context context, OnRunConditionChangedListener listener) {
|
||||||
Log.v(TAG, "Created new instance");
|
Log.v(TAG, "Created new instance");
|
||||||
|
@ -140,21 +142,25 @@ public class RunConditionMonitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateShouldRunDecision() {
|
public void updateShouldRunDecision() {
|
||||||
// Check if the current conditions changed the result of decideShouldRun()
|
// Reason if the current conditions changed the result of decideShouldRun()
|
||||||
// compared to the last determined result.
|
// compared to the last determined result.
|
||||||
boolean newShouldRun = decideShouldRun();
|
RunConditionCheckResult result = decideShouldRun();
|
||||||
if (newShouldRun != lastDeterminedShouldRun) {
|
boolean change;
|
||||||
if (mOnRunConditionChangedListener != null) {
|
synchronized (this) {
|
||||||
mOnRunConditionChangedListener.onRunConditionChanged(newShouldRun);
|
change = lastRunConditionCheckResult == null || !lastRunConditionCheckResult.equals(result);
|
||||||
|
lastRunConditionCheckResult = result;
|
||||||
|
}
|
||||||
|
if (change) {
|
||||||
|
if (mOnRunConditionChangedListener != null) {
|
||||||
|
mOnRunConditionChangedListener.onRunConditionChanged(result);
|
||||||
}
|
}
|
||||||
lastDeterminedShouldRun = newShouldRun;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if Syncthing should currently run.
|
* Determines if Syncthing should currently run.
|
||||||
*/
|
*/
|
||||||
private boolean decideShouldRun() {
|
private RunConditionCheckResult decideShouldRun() {
|
||||||
// Get run conditions preferences.
|
// Get run conditions preferences.
|
||||||
boolean prefRunOnMobileData= mPreferences.getBoolean(Constants.PREF_RUN_ON_MOBILE_DATA, false);
|
boolean prefRunOnMobileData= mPreferences.getBoolean(Constants.PREF_RUN_ON_MOBILE_DATA, false);
|
||||||
boolean prefRunOnWifi= mPreferences.getBoolean(Constants.PREF_RUN_ON_WIFI, true);
|
boolean prefRunOnWifi= mPreferences.getBoolean(Constants.PREF_RUN_ON_WIFI, true);
|
||||||
|
@ -166,18 +172,20 @@ public class RunConditionMonitor {
|
||||||
boolean prefRespectPowerSaving = mPreferences.getBoolean(Constants.PREF_RESPECT_BATTERY_SAVING, true);
|
boolean prefRespectPowerSaving = mPreferences.getBoolean(Constants.PREF_RESPECT_BATTERY_SAVING, true);
|
||||||
boolean prefRespectMasterSync = mPreferences.getBoolean(Constants.PREF_RESPECT_MASTER_SYNC, false);
|
boolean prefRespectMasterSync = mPreferences.getBoolean(Constants.PREF_RESPECT_MASTER_SYNC, false);
|
||||||
|
|
||||||
|
List<BlockerReason> blockerReasons = new ArrayList<>();
|
||||||
|
|
||||||
// PREF_POWER_SOURCE
|
// PREF_POWER_SOURCE
|
||||||
switch (prefPowerSource) {
|
switch (prefPowerSource) {
|
||||||
case POWER_SOURCE_CHARGER:
|
case POWER_SOURCE_CHARGER:
|
||||||
if (!isCharging()) {
|
if (!isCharging()) {
|
||||||
Log.v(TAG, "decideShouldRun: POWER_SOURCE_AC && !isCharging");
|
Log.v(TAG, "decideShouldRun: POWER_SOURCE_AC && !isCharging");
|
||||||
return false;
|
blockerReasons.add(ON_BATTERY);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case POWER_SOURCE_BATTERY:
|
case POWER_SOURCE_BATTERY:
|
||||||
if (isCharging()) {
|
if (isCharging()) {
|
||||||
Log.v(TAG, "decideShouldRun: POWER_SOURCE_BATTERY && isCharging");
|
Log.v(TAG, "decideShouldRun: POWER_SOURCE_BATTERY && isCharging");
|
||||||
return false;
|
blockerReasons.add(ON_CHARGER);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case POWER_SOURCE_CHARGER_BATTERY:
|
case POWER_SOURCE_CHARGER_BATTERY:
|
||||||
|
@ -189,35 +197,43 @@ public class RunConditionMonitor {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
if (prefRespectPowerSaving && isPowerSaving()) {
|
if (prefRespectPowerSaving && isPowerSaving()) {
|
||||||
Log.v(TAG, "decideShouldRun: prefRespectPowerSaving && isPowerSaving");
|
Log.v(TAG, "decideShouldRun: prefRespectPowerSaving && isPowerSaving");
|
||||||
return false;
|
blockerReasons.add(POWERSAVING_ENABLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Android global AutoSync setting.
|
// Android global AutoSync setting.
|
||||||
if (prefRespectMasterSync && !ContentResolver.getMasterSyncAutomatically()) {
|
if (prefRespectMasterSync && !ContentResolver.getMasterSyncAutomatically()) {
|
||||||
Log.v(TAG, "decideShouldRun: prefRespectMasterSync && !getMasterSyncAutomatically");
|
Log.v(TAG, "decideShouldRun: prefRespectMasterSync && !getMasterSyncAutomatically");
|
||||||
return false;
|
blockerReasons.add(GLOBAL_SYNC_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run on mobile data.
|
// Run on mobile data.
|
||||||
if (prefRunOnMobileData && isMobileDataConnection()) {
|
if (blockerReasons.isEmpty() && prefRunOnMobileData && isMobileDataConnection()) {
|
||||||
Log.v(TAG, "decideShouldRun: prefRunOnMobileData && isMobileDataConnection");
|
Log.v(TAG, "decideShouldRun: prefRunOnMobileData && isMobileDataConnection");
|
||||||
return true;
|
return SHOULD_RUN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run on wifi.
|
// Run on wifi.
|
||||||
if (prefRunOnWifi && isWifiOrEthernetConnection()) {
|
if (prefRunOnWifi && isWifiOrEthernetConnection()) {
|
||||||
if (prefRunOnMeteredWifi) {
|
if (prefRunOnMeteredWifi) {
|
||||||
// We are on non-metered or metered wifi. Check if wifi whitelist run condition is met.
|
// We are on non-metered or metered wifi. Reason if wifi whitelist run condition is met.
|
||||||
if (wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) {
|
if (wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) {
|
||||||
Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && prefRunOnMeteredWifi && wifiWhitelistConditionMet");
|
Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && prefRunOnMeteredWifi && wifiWhitelistConditionMet");
|
||||||
return true;
|
if (blockerReasons.isEmpty()) return SHOULD_RUN;
|
||||||
|
} else {
|
||||||
|
blockerReasons.add(WIFI_SSID_NOT_WHITELISTED);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Check if we are on a non-metered wifi and if wifi whitelist run condition is met.
|
// Reason if we are on a non-metered wifi and if wifi whitelist run condition is met.
|
||||||
if (!isMeteredNetworkConnection() && wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) {
|
if (!isMeteredNetworkConnection()) {
|
||||||
|
if (wifiWhitelistConditionMet(prefWifiWhitelistEnabled, whitelistedWifiSsids)) {
|
||||||
Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && !prefRunOnMeteredWifi && !isMeteredNetworkConnection && wifiWhitelistConditionMet");
|
Log.v(TAG, "decideShouldRun: prefRunOnWifi && isWifiOrEthernetConnection && !prefRunOnMeteredWifi && !isMeteredNetworkConnection && wifiWhitelistConditionMet");
|
||||||
return true;
|
if (blockerReasons.isEmpty()) return SHOULD_RUN;
|
||||||
|
} else {
|
||||||
|
blockerReasons.add(WIFI_SSID_NOT_WHITELISTED);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blockerReasons.add(WIFI_WIFI_IS_METERED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,14 +241,27 @@ public class RunConditionMonitor {
|
||||||
// Run in flight mode.
|
// Run in flight mode.
|
||||||
if (prefRunInFlightMode && isFlightMode()) {
|
if (prefRunInFlightMode && isFlightMode()) {
|
||||||
Log.v(TAG, "decideShouldRun: prefRunInFlightMode && isFlightMode");
|
Log.v(TAG, "decideShouldRun: prefRunInFlightMode && isFlightMode");
|
||||||
return true;
|
if (blockerReasons.isEmpty()) return SHOULD_RUN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If none of the above run conditions matched, don't run.
|
* If none of the above run conditions matched, don't run.
|
||||||
*/
|
*/
|
||||||
Log.v(TAG, "decideShouldRun: return false");
|
Log.v(TAG, "decideShouldRun: return false");
|
||||||
return false;
|
if (blockerReasons.isEmpty()) {
|
||||||
|
if (isFlightMode()) {
|
||||||
|
blockerReasons.add(NO_NETWORK_OR_FLIGHTMODE);
|
||||||
|
} else if (!prefRunOnWifi && !prefRunOnMobileData) {
|
||||||
|
blockerReasons.add(NO_ALLOWED_NETWORK);
|
||||||
|
} else if (prefRunOnMobileData) {
|
||||||
|
blockerReasons.add(NO_MOBILE_CONNECTION);
|
||||||
|
} else if (prefRunOnWifi) {
|
||||||
|
blockerReasons.add(NO_WIFI_CONNECTION);
|
||||||
|
} else {
|
||||||
|
blockerReasons.add(NO_NETWORK_OR_FLIGHTMODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new RunConditionCheckResult(blockerReasons);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,33 +1,34 @@
|
||||||
package com.nutomic.syncthingandroid.service;
|
package com.nutomic.syncthingandroid.service;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.Manifest;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.android.PRNGFixes;
|
import com.android.PRNGFixes;
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.nutomic.syncthingandroid.R;
|
import com.nutomic.syncthingandroid.R;
|
||||||
import com.nutomic.syncthingandroid.SyncthingApp;
|
import com.nutomic.syncthingandroid.SyncthingApp;
|
||||||
import com.nutomic.syncthingandroid.http.PollWebGuiAvailableTask;
|
import com.nutomic.syncthingandroid.http.PollWebGuiAvailableTask;
|
||||||
import com.nutomic.syncthingandroid.model.Folder;
|
import com.nutomic.syncthingandroid.model.RunConditionCheckResult;
|
||||||
import com.nutomic.syncthingandroid.util.ConfigXml;
|
import com.nutomic.syncthingandroid.util.ConfigXml;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ -99,6 +100,10 @@ public class SyncthingService extends Service {
|
||||||
void onServiceStateChange(State currentState);
|
void onServiceStateChange(State currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnRunConditionCheckResultListener {
|
||||||
|
void onRunConditionCheckResultChanged(RunConditionCheckResult result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates the current state of SyncthingService and of Syncthing itself.
|
* Indicates the current state of SyncthingService and of Syncthing itself.
|
||||||
*/
|
*/
|
||||||
|
@ -124,6 +129,7 @@ public class SyncthingService extends Service {
|
||||||
* {@link onStartCommand}.
|
* {@link onStartCommand}.
|
||||||
*/
|
*/
|
||||||
private State mCurrentState = State.DISABLED;
|
private State mCurrentState = State.DISABLED;
|
||||||
|
private AtomicReference<RunConditionCheckResult> mCurrentCheckResult = new AtomicReference<>(RunConditionCheckResult.SHOULD_RUN);
|
||||||
|
|
||||||
private ConfigXml mConfig;
|
private ConfigXml mConfig;
|
||||||
private @Nullable PollWebGuiAvailableTask mPollWebGuiAvailableTask = null;
|
private @Nullable PollWebGuiAvailableTask mPollWebGuiAvailableTask = null;
|
||||||
|
@ -136,6 +142,7 @@ public class SyncthingService extends Service {
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
|
|
||||||
private final HashSet<OnServiceStateChangeListener> mOnServiceStateChangeListeners = new HashSet<>();
|
private final HashSet<OnServiceStateChangeListener> mOnServiceStateChangeListeners = new HashSet<>();
|
||||||
|
private final HashSet<OnRunConditionCheckResultListener> mOnRunConditionCheckResultListeners = new HashSet<>();
|
||||||
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
|
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
|
||||||
|
|
||||||
@Inject NotificationHandler mNotificationHandler;
|
@Inject NotificationHandler mNotificationHandler;
|
||||||
|
@ -262,7 +269,13 @@ public class SyncthingService extends Service {
|
||||||
* function is called to notify this class to run/terminate the syncthing binary.
|
* function is called to notify this class to run/terminate the syncthing binary.
|
||||||
* {@link #onServiceStateChange} is called while applying the decision change.
|
* {@link #onServiceStateChange} is called while applying the decision change.
|
||||||
*/
|
*/
|
||||||
private void onUpdatedShouldRunDecision(boolean newShouldRunDecision) {
|
private void onUpdatedShouldRunDecision(RunConditionCheckResult result) {
|
||||||
|
boolean newShouldRunDecision = result.isShouldRun();
|
||||||
|
boolean reasonsChanged = !mCurrentCheckResult.getAndSet(result).equals(result);
|
||||||
|
if (reasonsChanged) {
|
||||||
|
onRunConditionCheckResultChange(result);
|
||||||
|
}
|
||||||
|
|
||||||
if (newShouldRunDecision != mLastDeterminedShouldRun) {
|
if (newShouldRunDecision != mLastDeterminedShouldRun) {
|
||||||
Log.i(TAG, "shouldRun decision changed to " + newShouldRunDecision + " according to configured run conditions.");
|
Log.i(TAG, "shouldRun decision changed to " + newShouldRunDecision + " according to configured run conditions.");
|
||||||
mLastDeterminedShouldRun = newShouldRunDecision;
|
mLastDeterminedShouldRun = newShouldRunDecision;
|
||||||
|
@ -578,6 +591,30 @@ public class SyncthingService extends Service {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerOnRunConditionCheckResultChange(OnRunConditionCheckResultListener listener) {
|
||||||
|
listener.onRunConditionCheckResultChanged(mCurrentCheckResult.get());
|
||||||
|
mOnRunConditionCheckResultListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterOnRunConditionCheckResultChange(OnRunConditionCheckResultListener listener) {
|
||||||
|
mOnRunConditionCheckResultListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRunConditionCheckResultChange(RunConditionCheckResult result) {
|
||||||
|
mHandler.post(() -> {
|
||||||
|
for (Iterator<OnRunConditionCheckResultListener> i = mOnRunConditionCheckResultListeners.iterator();
|
||||||
|
i.hasNext(); ) {
|
||||||
|
OnRunConditionCheckResultListener listener = i.next();
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onRunConditionCheckResultChanged(result);
|
||||||
|
} else {
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public URL getWebGuiUrl() {
|
public URL getWebGuiUrl() {
|
||||||
return mConfig.getWebGuiUrl();
|
return mConfig.getWebGuiUrl();
|
||||||
}
|
}
|
||||||
|
@ -586,6 +623,10 @@ public class SyncthingService extends Service {
|
||||||
return mCurrentState;
|
return mCurrentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RunConditionCheckResult getCurrentRunConditionCheckResult() {
|
||||||
|
return mCurrentCheckResult.get();
|
||||||
|
}
|
||||||
|
|
||||||
public NotificationHandler getNotificationHandler() {
|
public NotificationHandler getNotificationHandler() {
|
||||||
return mNotificationHandler;
|
return mNotificationHandler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,6 +653,19 @@ Please report any problems you encounter via Github.</string>
|
||||||
<!-- Button text on the "syncthing disabled" dialog, used as menu item to stop syncthing service if "always_run_in_background" is true -->
|
<!-- Button text on the "syncthing disabled" dialog, used as menu item to stop syncthing service if "always_run_in_background" is true -->
|
||||||
<string name="exit">Exit</string>
|
<string name="exit">Exit</string>
|
||||||
|
|
||||||
|
<!-- Reasons for "syncthing disabled" dialog -->
|
||||||
|
<string name="syncthing_disabled_reason_heading">Reasons:</string>
|
||||||
|
<string name="syncthing_disabled_reason_on_battery">Device is running on battery</string>
|
||||||
|
<string name="syncthing_disabled_reason_on_charger">Device is running on charger</string>
|
||||||
|
<string name="syncthing_disabled_reason_powersaving">Device is in power-saving mode</string>
|
||||||
|
<string name="syncthing_disabled_reason_android_sync_disabled">Global Synchronization is disabled</string>
|
||||||
|
<string name="syncthing_disabled_reason_wifi_ssid_not_whitelisted">Current WiFi SSID is not whitelisted</string>
|
||||||
|
<string name="syncthing_disabled_reason_wifi_is_metered">Current WiFi is metered</string>
|
||||||
|
<string name="syncthing_disabled_reason_no_network_or_flightmode">No network connection or airplane mode enabled</string>
|
||||||
|
<string name="syncthing_disabled_reason_no_mobile_connection">Not connected to mobile data</string>
|
||||||
|
<string name="syncthing_disabled_reason_no_wifi_connection">Not connected to WiFi</string>
|
||||||
|
<string name="syncthing_disabled_reason_no_allowed_method">Not configured to sync on WiFi nor mobile data</string>
|
||||||
|
|
||||||
<!-- Title of the notification shown while syncthing is running and enabled -->
|
<!-- Title of the notification shown while syncthing is running and enabled -->
|
||||||
<string name="syncthing_active">Syncthing is running</string>
|
<string name="syncthing_active">Syncthing is running</string>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue