From 87f36fcf13f1da7d1f2b10f66c258121780fdc93 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 19 Oct 2016 21:34:16 +0900 Subject: [PATCH] Use Gson to (de-)serialize config, instead of working on raw JSON --- .../activities/MainActivity.java | 12 +- .../activities/RestartActivity.java | 2 +- .../fragments/DeviceFragment.java | 13 +- .../fragments/DeviceListFragment.java | 9 +- .../fragments/FolderFragment.java | 12 +- .../fragments/FolderListFragment.java | 9 +- .../fragments/SettingsFragment.java | 536 +++++++++--------- .../syncthingandroid/http/RestTask.java | 7 +- .../syncthingandroid/model/Config.java | 23 + .../syncthingandroid/model/Device.java | 2 +- .../syncthingandroid/model/Folder.java | 2 +- .../syncthingandroid/model/Options.java | 50 ++ .../syncthingandroid/syncthing/RestApi.java | 481 ++++------------ .../syncthing/SyncthingService.java | 3 +- .../syncthingandroid/util/ConfigXml.java | 1 - .../views/WifiSsidPreference.java | 4 +- src/main/res/values-bg/strings.xml | 7 +- src/main/res/values-cs/strings.xml | 7 +- src/main/res/values-de/strings.xml | 7 +- src/main/res/values-es-rMX/strings.xml | 7 +- src/main/res/values-es/strings.xml | 2 +- src/main/res/values-fr/strings.xml | 7 +- src/main/res/values-hu/strings.xml | 5 +- src/main/res/values-in/strings.xml | 1 - src/main/res/values-it/strings.xml | 7 +- src/main/res/values-ja/strings.xml | 7 +- src/main/res/values-ko/strings.xml | 7 +- src/main/res/values-nb/strings.xml | 7 +- src/main/res/values-nl/strings.xml | 7 +- src/main/res/values-nn/strings.xml | 7 +- src/main/res/values-pl-rPL/strings.xml | 1 - src/main/res/values-pl/strings.xml | 5 +- src/main/res/values-pt-rBR/strings.xml | 7 +- src/main/res/values-pt/strings.xml | 5 +- src/main/res/values-ru/strings.xml | 5 +- src/main/res/values-sk/strings.xml | 5 +- src/main/res/values-sv-rSE/strings.xml | 7 +- src/main/res/values-sv/strings.xml | 7 +- src/main/res/values-tr/strings.xml | 5 +- src/main/res/values-vi/strings.xml | 7 +- src/main/res/values-zh-rCN/strings.xml | 7 +- src/main/res/values-zh-rTW/strings.xml | 4 +- src/main/res/values/strings.xml | 19 +- src/main/res/xml/app_settings.xml | 223 ++++---- 44 files changed, 651 insertions(+), 907 deletions(-) create mode 100644 src/main/java/com/nutomic/syncthingandroid/model/Config.java create mode 100644 src/main/java/com/nutomic/syncthingandroid/model/Options.java diff --git a/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java b/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java index 49914513..fa164681 100644 --- a/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/activities/MainActivity.java @@ -29,6 +29,7 @@ import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.fragments.DeviceListFragment; import com.nutomic.syncthingandroid.fragments.DrawerFragment; import com.nutomic.syncthingandroid.fragments.FolderListFragment; +import com.nutomic.syncthingandroid.model.Options; import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.SyncthingService; @@ -87,7 +88,7 @@ public class MainActivity extends SyncthingActivity mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); mDrawerFragment.requestGuiUpdate(); if (new Date().getTime() > getFirstStartTime() + USAGE_REPORTING_DIALOG_DELAY && - getApi().getUsageReportAccepted() == RestApi.USAGE_REPORTING_UNDECIDED) { + getApi().getOptions().getUsageReportValue() == Options.USAGE_REPORTING_UNDECIDED) { showUsageReportingDialog(); } break; @@ -390,21 +391,20 @@ public class MainActivity extends SyncthingActivity */ private void showUsageReportingDialog() { final DialogInterface.OnClickListener listener = (dialog, which) -> { + Options options = getApi().getOptions(); switch (which) { case DialogInterface.BUTTON_POSITIVE: - getApi().setUsageReportAccepted(RestApi.USAGE_REPORTING_ACCEPTED, - MainActivity.this); + options.urAccepted = Options.USAGE_REPORTING_ACCEPTED; break; case DialogInterface.BUTTON_NEGATIVE: - getApi().setUsageReportAccepted(RestApi.USAGE_REPORTING_DENIED, - MainActivity.this); + options.urAccepted = Options.USAGE_REPORTING_DENIED; break; case DialogInterface.BUTTON_NEUTRAL: Uri uri = Uri.parse("https://data.syncthing.net"); startActivity(new Intent(Intent.ACTION_VIEW, uri)); break; } - + getApi().editOptions(options, this); }; getApi().getUsageReport(report -> { diff --git a/src/main/java/com/nutomic/syncthingandroid/activities/RestartActivity.java b/src/main/java/com/nutomic/syncthingandroid/activities/RestartActivity.java index 0a73548e..b860cf79 100644 --- a/src/main/java/com/nutomic/syncthingandroid/activities/RestartActivity.java +++ b/src/main/java/com/nutomic/syncthingandroid/activities/RestartActivity.java @@ -30,7 +30,7 @@ public class RestartActivity extends SyncthingActivity { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(R.string.restart_title) .setPositiveButton(R.string.restart_now, (dialogInterface, i) -> { - getService().getApi().updateConfig(); + getService().getApi().restart(); finish(); }) .setNegativeButton(R.string.restart_later, (dialogInterface, i) -> { diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceFragment.java index b00fe8f9..b4211328 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceFragment.java @@ -19,6 +19,8 @@ import android.widget.CompoundButton; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; + +import com.google.gson.Gson; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; import com.nutomic.syncthingandroid.R; @@ -159,7 +161,7 @@ public class DeviceFragment extends Fragment implements if (mIsCreateMode) { if (savedInstanceState != null) { - mDevice = (Device) savedInstanceState.getSerializable("device"); + mDevice = new Gson().fromJson(savedInstanceState.getString("device"), Device.class); } if (mDevice == null) { initDevice(); @@ -192,7 +194,7 @@ public class DeviceFragment extends Fragment implements @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putSerializable("device", mDevice); + outState.putString("device", new Gson().toJson(mDevice)); } @Nullable @@ -322,7 +324,7 @@ public class DeviceFragment extends Fragment implements .show(); return true; } - mSyncthingService.getApi().editDevice(mDevice, getActivity(), this); + mSyncthingService.getApi().addDevice(mDevice, this); getActivity().finish(); return true; case R.id.share_device_id: @@ -331,7 +333,8 @@ public class DeviceFragment extends Fragment implements case R.id.remove: new AlertDialog.Builder(getActivity()) .setMessage(R.string.remove_device_confirm) - .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mSyncthingService.getApi().deleteDevice(mDevice, getActivity())) + .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> + mSyncthingService.getApi().removeDevice(mDevice.deviceID)) .setNegativeButton(android.R.string.no, null) .show(); return true; @@ -391,7 +394,7 @@ public class DeviceFragment extends Fragment implements */ private void updateDevice() { if (!mIsCreateMode && mDeviceNeedsToUpdate && mDevice != null) { - mSyncthingService.getApi().editDevice(mDevice, getActivity(), this); + mSyncthingService.getApi().editDevice(mDevice); } } diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceListFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceListFragment.java index 7b93fd6e..25563e2f 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceListFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/DeviceListFragment.java @@ -66,18 +66,11 @@ public class DeviceListFragment extends ListFragment implements SyncthingService public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + setHasOptionsMenu(true); setEmptyText(getString(R.string.devices_list_empty)); getListView().setOnItemClickListener(this); } - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - updateList(); - setHasOptionsMenu(true); - } - /** * Refreshes ListView by updating devices and info. * diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java index a7bbc87c..97eb3129 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/FolderFragment.java @@ -25,6 +25,7 @@ import android.widget.TextView; import android.widget.Toast; import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.activities.FolderPickerActivity; import com.nutomic.syncthingandroid.activities.SettingsActivity; @@ -156,7 +157,7 @@ public class FolderFragment extends Fragment if (mIsCreateMode) { if (savedInstanceState != null) { - mFolder = (Folder) savedInstanceState.getSerializable("folder"); + mFolder = new Gson().fromJson(savedInstanceState.getString("folder"), Folder.class); } if (mFolder == null) { initFolder(); @@ -189,7 +190,7 @@ public class FolderFragment extends Fragment @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putSerializable("folder", mFolder); + outState.putString("folder", new Gson().toJson(mFolder)); } @Override @@ -327,13 +328,14 @@ public class FolderFragment extends Fragment .show(); return true; } - mSyncthingService.getApi().editFolder(mFolder, true, getActivity()); + mSyncthingService.getApi().addFolder(mFolder); getActivity().finish(); return true; case R.id.remove: new AlertDialog.Builder(getActivity()) .setMessage(R.string.remove_folder_confirm) - .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> mSyncthingService.getApi().deleteFolder(mFolder, getActivity())) + .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> + mSyncthingService.getApi().removeFolder(mFolder.id)) .setNegativeButton(android.R.string.no, null) .show(); return true; @@ -403,7 +405,7 @@ public class FolderFragment extends Fragment private void updateFolder() { if (!mIsCreateMode) { - mSyncthingService.getApi().editFolder(mFolder, false, getActivity()); + mSyncthingService.getApi().editFolder(mFolder); } } } diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java index 9d6eafbb..66b2f18d 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/FolderListFragment.java @@ -74,19 +74,12 @@ public class FolderListFragment extends ListFragment implements SyncthingService public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + setHasOptionsMenu(true); setEmptyText(getString(R.string.folder_list_empty)); getListView().setOnItemClickListener(this); getListView().setOnItemLongClickListener(this); } - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - updateList(); - setHasOptionsMenu(true); - } - /** * Refreshes ListView by updating folders and info. * diff --git a/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java b/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java index 316118f7..64a8b2ec 100644 --- a/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java +++ b/src/main/java/com/nutomic/syncthingandroid/fragments/SettingsFragment.java @@ -8,22 +8,25 @@ import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.Preference; +import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; -import android.support.v4.app.NavUtils; import android.util.Log; -import android.view.MenuItem; import android.widget.Toast; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.activities.SyncthingActivity; +import com.nutomic.syncthingandroid.model.Config; import com.nutomic.syncthingandroid.model.Device; +import com.nutomic.syncthingandroid.model.Options; import com.nutomic.syncthingandroid.views.WifiSsidPreference; import com.nutomic.syncthingandroid.syncthing.RestApi; import com.nutomic.syncthingandroid.syncthing.SyncthingService; import java.util.List; -import java.util.TreeSet; import eu.chainfire.libsuperuser.Shell; @@ -33,89 +36,39 @@ public class SettingsFragment extends PreferenceFragment Preference.OnPreferenceClickListener { private static final String TAG = "SettingsFragment"; - - private static final String SYNCTHING_OPTIONS_KEY = "syncthing_options"; - private static final String SYNCTHING_GUI_KEY = "syncthing_gui"; - private static final String DEVICE_NAME_KEY = "deviceName"; - private static final String USAGE_REPORT_ACCEPTED = "urAccepted"; - private static final String ADDRESS = "address"; - private static final String USER = "user"; - // Note that this preference is seperate from the syncthing config value. While Syncthing - // stores a password hash, we store the plaintext password in the Android preferences. - private static final String PASSWORD = "web_gui_password"; - private static final String EXPORT_CONFIG = "export_config"; - private static final String IMPORT_CONFIG = "import_config"; - private static final String STTRACE = "sttrace"; - private static final String SYNCTHING_RESET = "streset"; - private static final String SYNCTHING_VERSION_KEY = "syncthing_version"; - private static final String APP_VERSION_KEY = "app_version"; + private static final String KEY_STTRACE = "sttrace"; + private static final String KEY_EXPORT_CONFIG = "export_config"; + private static final String KEY_IMPORT_CONFIG = "import_config"; + private static final String KEY_STRESET = "streset"; private CheckBoxPreference mAlwaysRunInBackground; private CheckBoxPreference mSyncOnlyCharging; private CheckBoxPreference mSyncOnlyWifi; private WifiSsidPreference mSyncOnlyOnSSIDs; + + private EditTextPreference mDeviceName; + private EditTextPreference mListenAddresses; + private EditTextPreference mMaxRecvKbps; + private EditTextPreference mMaxSendKbps; + private CheckBoxPreference mNatEnabled; + private CheckBoxPreference mLocalAnnounceEnabled; + private CheckBoxPreference mGlobalAnnounceEnabled; + private CheckBoxPreference mRelaysEnabled; + private EditTextPreference mGlobalAnnounceServers; + private EditTextPreference mAddress; + private EditTextPreference mUser; + private EditTextPreference mPassword; + private CheckBoxPreference mUrAccepted; + private CheckBoxPreference mUseRoot; - private CheckBoxPreference mKeepWakelock; - private PreferenceScreen mOptionsScreen; - private PreferenceScreen mGuiScreen; + + private Preference mSyncthingVersion; + private SyncthingService mSyncthingService; + private RestApi mApi; - @Override - public void onApiChange(SyncthingService.State currentState) { - boolean enabled = currentState == SyncthingService.State.ACTIVE; - mOptionsScreen.setEnabled(enabled); - mGuiScreen.setEnabled(enabled); - mUseRoot.setEnabled(enabled); - mKeepWakelock.setEnabled(enabled); - - if (currentState == SyncthingService.State.ACTIVE) { - Preference syncthingVersion = getPreferenceScreen().findPreference(SYNCTHING_VERSION_KEY); - syncthingVersion.setSummary(mSyncthingService.getApi().getVersion()); - RestApi api = mSyncthingService.getApi(); - for (int i = 0; i < mOptionsScreen.getPreferenceCount(); i++) { - final Preference pref = mOptionsScreen.getPreference(i); - pref.setOnPreferenceChangeListener(SettingsFragment.this); - String value; - switch (pref.getKey()) { - case DEVICE_NAME_KEY: - value = api.getLocalDevice().name; - break; - case USAGE_REPORT_ACCEPTED: - int setting = api.getUsageReportAccepted(); - value = Boolean.toString(setting == RestApi.USAGE_REPORTING_ACCEPTED); - break; - default: - value = api.getValue(RestApi.TYPE_OPTIONS, pref.getKey()); - } - applyPreference(pref, value); - } - - Preference address = mGuiScreen.findPreference(ADDRESS); - address.setOnPreferenceChangeListener(this); - applyPreference(address, api.getValue(RestApi.TYPE_GUI, ADDRESS)); - - Preference user = mGuiScreen.findPreference(USER); - user.setOnPreferenceChangeListener(this); - applyPreference(user, api.getValue(RestApi.TYPE_GUI, USER)); - - Preference password = mGuiScreen.findPreference(PASSWORD); - password.setOnPreferenceChangeListener(this); - } - } - - /** - * Applies the given value to the preference. - * - * If pref is an EditTextPreference, setText is used and the value shown as summary. If pref is - * a CheckBoxPreference, setChecked is used (by parsing value as Boolean). - */ - private void applyPreference(Preference pref, String value) { - if (pref instanceof EditTextPreference) { - ((EditTextPreference) pref).setText(value); - } else if (pref instanceof CheckBoxPreference) { - ((CheckBoxPreference) pref).setChecked(Boolean.parseBoolean(value)); - } - } + private Options mOptions; + private Config.Gui mGui; /** * Loads layout, sets version from Rest API. @@ -129,20 +82,59 @@ public class SettingsFragment extends PreferenceFragment ((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this); addPreferencesFromResource(R.xml.app_settings); - final PreferenceScreen screen = getPreferenceScreen(); - mAlwaysRunInBackground = (CheckBoxPreference) - findPreference(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND); - mSyncOnlyCharging = (CheckBoxPreference) - findPreference(SyncthingService.PREF_SYNC_ONLY_CHARGING); - mSyncOnlyWifi = (CheckBoxPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_WIFI); - mSyncOnlyOnSSIDs = (WifiSsidPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_WIFI_SSIDS); - mSyncOnlyOnSSIDs.setDefaultValue(new TreeSet()); // default to empty list - mUseRoot = (CheckBoxPreference) findPreference(SyncthingService.PREF_USE_ROOT); - mKeepWakelock = (CheckBoxPreference) findPreference(SyncthingService.PREF_USE_WAKE_LOCK); - Preference appVersion = screen.findPreference(APP_VERSION_KEY); - mOptionsScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_OPTIONS_KEY); - mGuiScreen = (PreferenceScreen) screen.findPreference(SYNCTHING_GUI_KEY); - Preference sttrace = findPreference(STTRACE); + PreferenceScreen screen = getPreferenceScreen(); + mAlwaysRunInBackground = + (CheckBoxPreference) findPreference(SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND); + mSyncOnlyCharging = + (CheckBoxPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_CHARGING); + mSyncOnlyWifi = + (CheckBoxPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_WIFI); + mSyncOnlyOnSSIDs = + (WifiSsidPreference) findPreference(SyncthingService.PREF_SYNC_ONLY_WIFI_SSIDS); + + mDeviceName = (EditTextPreference) findPreference("deviceName"); + mListenAddresses = (EditTextPreference) findPreference("listenAddresses"); + mMaxRecvKbps = (EditTextPreference) findPreference("maxRecvKbps"); + mMaxSendKbps = (EditTextPreference) findPreference("maxSendKbps"); + mNatEnabled = (CheckBoxPreference) findPreference("natEnabled"); + mLocalAnnounceEnabled = (CheckBoxPreference) findPreference("localAnnounceEnabled"); + mGlobalAnnounceEnabled = (CheckBoxPreference) findPreference("globalAnnounceEnabled"); + mRelaysEnabled = (CheckBoxPreference) findPreference("relaysEnabled"); + mGlobalAnnounceServers = (EditTextPreference) findPreference("globalAnnounceServers"); + mAddress = (EditTextPreference) findPreference("address"); + mUser = (EditTextPreference) findPreference("user"); + mPassword = (EditTextPreference) findPreference("password"); + mUrAccepted = (CheckBoxPreference) findPreference("urAccepted"); + + Preference exportConfig = findPreference("export_config"); + Preference importConfig = findPreference("import_config"); + + Preference stTrace = findPreference("sttrace"); + Preference stReset = findPreference("streset"); + + mUseRoot = (CheckBoxPreference) findPreference(SyncthingService.PREF_USE_ROOT); + Preference useWakelock = findPreference(SyncthingService.PREF_USE_WAKE_LOCK); + Preference foregroundService = findPreference("run_as_foreground_service"); + Preference useTor = findPreference("use_tor"); + + mSyncthingVersion = findPreference("syncthing_version"); + Preference appVersion = screen.findPreference("app_version"); + + setPreferenceCategoryChangeListener(findPreference("category_run_conditions"), this); + + setPreferenceCategoryChangeListener( + findPreference("category_syncthing_options"), this::onSyncthingPreferenceChange); + + exportConfig.setOnPreferenceClickListener(this); + importConfig.setOnPreferenceClickListener(this); + + stTrace.setOnPreferenceChangeListener(this); + stReset.setOnPreferenceClickListener(this); + + mUseRoot.setOnPreferenceChangeListener(this); + useWakelock.setOnPreferenceChangeListener(this::onRequireRestart); + foregroundService.setOnPreferenceChangeListener(this::onRequireRestart); + useTor.setOnPreferenceChangeListener(this::onRequireRestart); try { appVersion.setSummary(getActivity().getPackageManager() @@ -150,17 +142,193 @@ public class SettingsFragment extends PreferenceFragment } catch (PackageManager.NameNotFoundException e) { Log.d(TAG, "Failed to get app version name"); } + } - mAlwaysRunInBackground.setOnPreferenceChangeListener(this); - mSyncOnlyCharging.setOnPreferenceChangeListener(this); - mSyncOnlyWifi.setOnPreferenceChangeListener(this); - mSyncOnlyOnSSIDs.setOnPreferenceChangeListener(this); - mUseRoot.setOnPreferenceClickListener(this); - mKeepWakelock.setOnPreferenceClickListener(this); - screen.findPreference(EXPORT_CONFIG).setOnPreferenceClickListener(this); - screen.findPreference(IMPORT_CONFIG).setOnPreferenceClickListener(this); - screen.findPreference(SYNCTHING_RESET).setOnPreferenceClickListener(this); - sttrace.setOnPreferenceChangeListener(this); + @Override + public void onServiceConnected() { + mSyncthingService = ((SyncthingActivity) getActivity()).getService(); + mSyncthingService.registerOnApiChangeListener(this); + mGui = mSyncthingService.getApi().getGui(); + mOptions = mSyncthingService.getApi().getOptions(); + } + + @Override + public void onApiChange(SyncthingService.State currentState) { + boolean enabled = currentState == SyncthingService.State.ACTIVE; + getPreferenceScreen().setEnabled(enabled); + if (!enabled) + return; + + mApi = mSyncthingService.getApi(); + mSyncthingVersion.setSummary(mApi.getVersion()); + mOptions = mApi.getOptions(); + mGui = mApi.getGui(); + + Joiner joiner = Joiner.on(", "); + mDeviceName.setTitle(mApi.getLocalDevice().name); + mListenAddresses.setText(joiner.join(mOptions.listenAddresses)); + mMaxRecvKbps.setText(Integer.toString(mOptions.maxRecvKbps)); + mMaxSendKbps.setText(Integer.toString(mOptions.maxSendKbps)); + mNatEnabled.setChecked(mOptions.natEnabled); + mLocalAnnounceEnabled.setChecked(mOptions.localAnnounceEnabled); + mGlobalAnnounceEnabled.setChecked(mOptions.globalAnnounceEnabled); + mRelaysEnabled.setChecked(mOptions.relaysEnabled); + mGlobalAnnounceServers.setText(joiner.join(mOptions.globalAnnounceServers)); + mAddress.setText(mGui.address); + mUser.setText(mGui.user); + mPassword.setText(mGui.password); + mUrAccepted.setChecked(mOptions.getUsageReportValue() == Options.USAGE_REPORTING_ACCEPTED); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mSyncthingService.unregisterOnApiChangeListener(this); + } + + private void setPreferenceCategoryChangeListener( + Preference category, Preference.OnPreferenceChangeListener listener) { + PreferenceScreen ps = (PreferenceScreen) category; + for (int i = 0; i < ps.getPreferenceCount(); i++) { + Preference p = ps.getPreference(i); + p.setOnPreferenceChangeListener(listener); + } + } + + public boolean onSyncthingPreferenceChange(Preference preference, Object o) { + Device localDevice = mApi.getLocalDevice(); + localDevice.name = mDeviceName.getText(); + mApi.editDevice(localDevice); + + Splitter splitter = Splitter.on(",").trimResults().omitEmptyStrings(); + + mOptions.listenAddresses = Iterables.toArray( + splitter.split(mListenAddresses.getText()), String.class); + mOptions.maxRecvKbps = Integer.valueOf(mMaxRecvKbps.getText()); + mOptions.maxRecvKbps = Integer.valueOf(mMaxSendKbps.getText()); + mOptions.natEnabled = mNatEnabled.isChecked(); + mOptions.localAnnounceEnabled = mLocalAnnounceEnabled.isChecked(); + mOptions.globalAnnounceEnabled = mGlobalAnnounceEnabled.isChecked(); + mOptions.relaysEnabled = mRelaysEnabled.isChecked(); + mOptions.globalAnnounceServers = Iterables.toArray( + splitter.split(mGlobalAnnounceServers.getText()), String.class); + mGui.address = mAddress.getText(); + mGui.user = mUser.getText(); + mGui.password = mPassword.getText(); + mOptions.urAccepted = (mUrAccepted.isChecked()) + ? Options.USAGE_REPORTING_ACCEPTED + : Options.USAGE_REPORTING_DENIED; + + mApi.editOptions(mOptions, getActivity()); + mApi.editGui(mGui, getActivity()); + return true; + } + + public boolean onRequireRestart(Preference preference, Object o) { + mSyncthingService.getApi().requireRestart(getActivity()); + return true; + } + + /** + * Sends the updated value to {@link }RestApi}, and sets it as the summary + * for EditTextPreference. + */ + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + switch (preference.getKey()) { + case SyncthingService.PREF_ALWAYS_RUN_IN_BACKGROUND: + boolean value = (Boolean) o; + mAlwaysRunInBackground.setSummary((value) + ? R.string.always_run_in_background_enabled + : R.string.always_run_in_background_disabled); + mSyncOnlyCharging.setEnabled(value); + mSyncOnlyWifi.setEnabled(value); + mSyncOnlyOnSSIDs.setEnabled(mSyncOnlyWifi.isChecked()); + // Uncheck items when disabled, so it is clear they have no effect. + if (!value) { + mSyncOnlyCharging.setChecked(false); + mSyncOnlyWifi.setChecked(false); + } + break; + case SyncthingService.PREF_SYNC_ONLY_WIFI: + mSyncOnlyOnSSIDs.setEnabled((Boolean) o); + break; + case KEY_STTRACE: + if (((String) o).matches("[0-9a-z, ]*")) + mSyncthingService.getApi().requireRestart(getActivity()); + else { + Toast.makeText(getActivity(), R.string.toast_invalid_sttrace, Toast.LENGTH_SHORT) + .show(); + return false; + } + break; + } + + return true; + } + + @Override + public boolean onPreferenceClick(Preference preference) { + switch (preference.getKey()) { + case SyncthingService.PREF_USE_ROOT: + if (mUseRoot.isChecked()) { + // Only check preference after root was granted. + mUseRoot.setChecked(false); + new TestRootTask().execute(); + } else { + new Thread(new ChownFilesRunnable()).start(); + mSyncthingService.getApi().requireRestart(getActivity()); + } + return true; + case KEY_EXPORT_CONFIG: + new AlertDialog.Builder(getActivity()) + .setMessage(R.string.dialog_confirm_export) + .setPositiveButton(android.R.string.yes, (dialog, which) -> { + mSyncthingService.exportConfig(); + Toast.makeText(getActivity(), + getString(R.string.config_export_successful, + SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show(); + }) + .setNegativeButton(android.R.string.no, null) + .show(); + return true; + case KEY_IMPORT_CONFIG: + new AlertDialog.Builder(getActivity()) + .setMessage(R.string.dialog_confirm_import) + .setPositiveButton(android.R.string.yes, (dialog, which) -> { + if (mSyncthingService.importConfig()) { + Toast.makeText(getActivity(), + getString(R.string.config_imported_successful), + Toast.LENGTH_SHORT).show(); + // No need to restart, as we shutdown to import the config, and + // then have to start Syncthing again. + } else { + Toast.makeText(getActivity(), + getString(R.string.config_import_failed, + SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show(); + } + }) + .setNegativeButton(android.R.string.no, null) + .show(); + return true; + case KEY_STRESET: + final Intent intent = new Intent(getActivity(), SyncthingService.class) + .setAction(SyncthingService.ACTION_RESET); + + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.streset_title) + .setMessage(R.string.streset_question) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { + getActivity().startService(intent); + Toast.makeText(getActivity(), R.string.streset_done, Toast.LENGTH_LONG).show(); + }) + .setNegativeButton(android.R.string.no, (dialogInterface, i) -> { + }) + .show(); + return true; + default: + return false; + } } /** @@ -184,101 +352,6 @@ public class SettingsFragment extends PreferenceFragment } } - @Override - public void onServiceConnected() { - mSyncthingService = ((SyncthingActivity) getActivity()).getService(); - mSyncthingService.registerOnApiChangeListener(this); - } - - @Override - public void onDestroy() { - super.onDestroy(); - mSyncthingService.unregisterOnApiChangeListener(this); - } - - /** - * Handles ActionBar back selected. - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(getActivity()); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - /** - * Sends the updated value to {@link }RestApi}, and sets it as the summary - * for EditTextPreference. - */ - @Override - public boolean onPreferenceChange(Preference preference, Object o) { - boolean requireRestart = false; - - if (preference.equals(mAlwaysRunInBackground)) { - boolean value = (Boolean) o; - preference.setSummary((value) - ? R.string.always_run_in_background_enabled - : R.string.always_run_in_background_disabled); - mSyncOnlyCharging.setEnabled(value); - mSyncOnlyWifi.setEnabled(value); - mSyncOnlyOnSSIDs.setEnabled(mSyncOnlyWifi.isChecked()); - // Uncheck items when disabled, so it is clear they have no effect. - if (!value) { - mSyncOnlyCharging.setChecked(false); - mSyncOnlyWifi.setChecked(false); - } - } else if (preference.equals(mSyncOnlyWifi)) { - mSyncOnlyOnSSIDs.setEnabled((Boolean) o); - } else if (preference.getKey().equals(DEVICE_NAME_KEY)) { - Device old = mSyncthingService.getApi().getLocalDevice(); - Device updated = new Device(); - updated.addresses = old.addresses; - updated.compression = old.compression; - updated.deviceID = old.deviceID; - updated.introducer = old.introducer; - updated.name = (String) o; - mSyncthingService.getApi().editDevice(updated, getActivity(), null); - } else if (preference.getKey().equals(USAGE_REPORT_ACCEPTED)) { - int setting = ((Boolean) o) - ? RestApi.USAGE_REPORTING_ACCEPTED - : RestApi.USAGE_REPORTING_DENIED; - mSyncthingService.getApi().setUsageReportAccepted(setting, getActivity()); - } else if (mOptionsScreen.findPreference(preference.getKey()) != null) { - boolean isArray = preference.getKey().equals("listenAddress") || - preference.getKey().equals("globalAnnounceServers"); - mSyncthingService.getApi().setValue(RestApi.TYPE_OPTIONS, preference.getKey(), o, - isArray, getActivity()); - } else if (preference.getKey().equals(ADDRESS)) { - mSyncthingService.getApi().setValue( - RestApi.TYPE_GUI, preference.getKey(), o, false, getActivity()); - } else if (preference.getKey().equals(USER)) { - mSyncthingService.getApi().setValue( - RestApi.TYPE_GUI, preference.getKey(), o, false, getActivity()); - } else if (preference.getKey().equals(PASSWORD)) { - mSyncthingService.getApi().setValue( - RestApi.TYPE_GUI, "password", o, false, getActivity()); - } - - // Avoid any code injection. - if (preference.getKey().equals(STTRACE)) { - if (((String) o).matches("[0-9a-z, ]*")) - requireRestart = true; - else { - Toast.makeText(getActivity(), R.string.toast_invalid_sttrace, Toast.LENGTH_SHORT).show(); - return false; - } - } - - if (requireRestart) - mSyncthingService.getApi().requireRestart(getActivity()); - - return true; - } - /** * Changes the owner of syncthing files so they can be accessed without root. */ @@ -291,73 +364,4 @@ public class SettingsFragment extends PreferenceFragment } } - @Override - public boolean onPreferenceClick(Preference preference) { - switch (preference.getKey()) { - case SyncthingService.PREF_USE_ROOT: - if (mUseRoot.isChecked()) { - // Only check preference after root was granted. - mUseRoot.setChecked(false); - new TestRootTask().execute(); - } else { - new Thread(new ChownFilesRunnable()).start(); - mSyncthingService.getApi().requireRestart(getActivity()); - } - return true; - case SyncthingService.PREF_USE_WAKE_LOCK: - mSyncthingService.getApi().requireRestart(getActivity()); - return true; - case EXPORT_CONFIG: - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.dialog_confirm_export) - .setPositiveButton(android.R.string.yes, - (dialog, which) -> { - mSyncthingService.exportConfig(); - Toast.makeText(getActivity(), - getString(R.string.config_export_successful, - SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show(); - }) - .setNegativeButton(android.R.string.no, null) - .show(); - return true; - case IMPORT_CONFIG: - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.dialog_confirm_import) - .setPositiveButton(android.R.string.yes, - (dialog, which) -> { - if (mSyncthingService.importConfig()) { - Toast.makeText(getActivity(), - getString(R.string.config_imported_successful), - Toast.LENGTH_SHORT).show(); - // No need to restart, as we shutdown to import the config, and - // then have to start Syncthing again. - } else { - Toast.makeText(getActivity(), - getString(R.string.config_import_failed, - SyncthingService.EXPORT_PATH), Toast.LENGTH_LONG).show(); - } - }) - .setNegativeButton(android.R.string.no, null) - .show(); - return true; - case SYNCTHING_RESET: - final Intent intent = new Intent(getActivity(), SyncthingService.class) - .setAction(SyncthingService.ACTION_RESET); - - new AlertDialog.Builder(getActivity()) - .setTitle(R.string.streset_title) - .setMessage(R.string.streset_question) - .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { - getActivity().startService(intent); - Toast.makeText(getActivity(), R.string.streset_done, Toast.LENGTH_LONG).show(); - }) - .setNegativeButton(android.R.string.no, (dialogInterface, i) -> { - }) - .show(); - return true; - default: - return false; - } - } - } diff --git a/src/main/java/com/nutomic/syncthingandroid/http/RestTask.java b/src/main/java/com/nutomic/syncthingandroid/http/RestTask.java index c6e31015..f170c5dd 100644 --- a/src/main/java/com/nutomic/syncthingandroid/http/RestTask.java +++ b/src/main/java/com/nutomic/syncthingandroid/http/RestTask.java @@ -38,6 +38,11 @@ public abstract class RestTask extends private static final String TAG = "RestTask"; + /** + * The name of the HTTP header used for the syncthing API key. + */ + private static final String HEADER_API_KEY = "X-API-Key"; + public interface OnSuccessListener { public void onSuccess(String result); } @@ -67,7 +72,7 @@ public abstract class RestTask extends URL url = new URL(uriBuilder.build().toString()); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); - connection.setRequestProperty(RestApi.HEADER_API_KEY, mApiKey); + connection.setRequestProperty(HEADER_API_KEY, mApiKey); connection.setHostnameVerifier((h, s) -> true); connection.setSSLSocketFactory(getSslSocketFactory()); return connection; diff --git a/src/main/java/com/nutomic/syncthingandroid/model/Config.java b/src/main/java/com/nutomic/syncthingandroid/model/Config.java new file mode 100644 index 00000000..257a4440 --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/model/Config.java @@ -0,0 +1,23 @@ +package com.nutomic.syncthingandroid.model; + +import java.util.List; + +public class Config { + public int version; + public String[] ignoredDevices; + public List devices; + public List folders; + public Gui gui; + public Options options; + + public class Gui { + public boolean enabled; + public String address; + public String user; + public String password; + public boolean useTLS; + public String apiKey; + public boolean insecureAdminAccess; + public String theme; + } +} diff --git a/src/main/java/com/nutomic/syncthingandroid/model/Device.java b/src/main/java/com/nutomic/syncthingandroid/model/Device.java index 0c4f237e..22d5f84d 100644 --- a/src/main/java/com/nutomic/syncthingandroid/model/Device.java +++ b/src/main/java/com/nutomic/syncthingandroid/model/Device.java @@ -5,7 +5,7 @@ import android.text.TextUtils; import java.io.Serializable; import java.util.List; -public class Device implements Serializable { +public class Device { public String deviceID; public String name; public List addresses; diff --git a/src/main/java/com/nutomic/syncthingandroid/model/Folder.java b/src/main/java/com/nutomic/syncthingandroid/model/Folder.java index 647712f4..39247b32 100644 --- a/src/main/java/com/nutomic/syncthingandroid/model/Folder.java +++ b/src/main/java/com/nutomic/syncthingandroid/model/Folder.java @@ -6,7 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class Folder implements Serializable { +public class Folder { public String id; public String label; diff --git a/src/main/java/com/nutomic/syncthingandroid/model/Options.java b/src/main/java/com/nutomic/syncthingandroid/model/Options.java new file mode 100644 index 00000000..1752e465 --- /dev/null +++ b/src/main/java/com/nutomic/syncthingandroid/model/Options.java @@ -0,0 +1,50 @@ +package com.nutomic.syncthingandroid.model; + +public class Options { + public String[] listenAddresses; + public String[] globalAnnounceServers; + public boolean globalAnnounceEnabled; + public boolean localAnnounceEnabled; + public int localAnnouncePort; + public String localAnnounceMCAddr; + public int maxSendKbps; + public int maxRecvKbps; + public int reconnectionIntervalS; + public boolean relaysEnabled; + public int relayReconnectIntervalM; + public boolean startBrowser; + public boolean natEnabled; + public int natLeaseMinutes; + public int natRenewalMinutes; + public int natTimeoutSeconds; + public int urAccepted; + public String urUniqueId; + public String urURL; + public boolean urPostInsecurely; + public int urInitialDelayS; + public boolean restartOnWakeup; + public int autoUpgradeIntervalH; + public int keepTemporariesH; + public boolean cacheIgnoredFiles; + public int progressUpdateIntervalS; + public boolean symlinksEnabled; + public boolean limitBandwidthInLan; + public int minHomeDiskFreePct; + public String releasesURL; + public String[] alwaysLocalNets; + public boolean overwriteRemoteDeviceNamesOnConnect; + public int tempIndexMinBlocks; + + public static final int USAGE_REPORTING_UNDECIDED = 0; + public static final int USAGE_REPORTING_ACCEPTED = 2; + public static final int USAGE_REPORTING_DENIED = -1; + + public int getUsageReportValue() { + if (urAccepted > USAGE_REPORTING_ACCEPTED) + throw new RuntimeException("Inalid usage reporting value"); + + return (urAccepted == USAGE_REPORTING_ACCEPTED || urAccepted == USAGE_REPORTING_DENIED) + ? urAccepted + : USAGE_REPORTING_UNDECIDED; + } +} diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java index 30363657..562b7918 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/RestApi.java @@ -5,24 +5,26 @@ import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; -import android.support.annotation.NonNull; import android.util.Log; import com.google.common.base.Objects; +import com.google.common.reflect.TypeToken; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; import com.nutomic.syncthingandroid.BuildConfig; import com.nutomic.syncthingandroid.activities.RestartActivity; import com.nutomic.syncthingandroid.http.GetTask; import com.nutomic.syncthingandroid.http.PostTask; +import com.nutomic.syncthingandroid.model.Config; import com.nutomic.syncthingandroid.model.Connection; import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.Folder; import com.nutomic.syncthingandroid.model.Model; +import com.nutomic.syncthingandroid.model.Options; import com.nutomic.syncthingandroid.model.SystemInfo; import com.nutomic.syncthingandroid.model.SystemVersion; import com.nutomic.syncthingandroid.util.FolderObserver; @@ -31,10 +33,8 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.lang.reflect.Type; import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -49,51 +49,24 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, private static final String TAG = "RestApi"; - /** - * Parameter for {@link #getValue} or {@link #setValue} referring to "options" config item. - */ - public static final String TYPE_OPTIONS = "options"; - - /** - * Parameter for {@link #getValue} or {@link #setValue} referring to "gui" config item. - */ - public static final String TYPE_GUI = "gui"; - - /** - * The name of the HTTP header used for the syncthing API key. - */ - public static final String HEADER_API_KEY = "X-API-Key"; - /** * Key of the map element containing connection info for the local device, in the return * value of {@link #getConnections} */ public static final String TOTAL_STATS = "total"; - public static final int USAGE_REPORTING_UNDECIDED = 0; - public static final int USAGE_REPORTING_ACCEPTED = 2; - public static final int USAGE_REPORTING_DENIED = -1; - private static final List USAGE_REPORTING_DECIDED = - Arrays.asList(USAGE_REPORTING_ACCEPTED, USAGE_REPORTING_DENIED); - public interface OnConfigChangedListener { void onConfigChanged(); } private final Context mContext; - - private String mVersion; - private final URL mUrl; - private final String mApiKey; - private final String mHttpsCertPath; - private JSONObject mConfig; - + private String mVersion; + private Config mConfig; private String mLocalDeviceId; - private boolean mRestartPostponed = false; /** @@ -113,11 +86,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, */ private final HashMap mCachedModelInfo = new HashMap<>(); - /** - * Stores a hash map to resolve folders to paths for events. - */ - private final Map mCacheFolderPathLookup = new HashMap<>(); - public RestApi(Context context, URL url, String apiKey, OnApiAvailableListener apiListener, OnConfigChangedListener configListener) { mContext = context; @@ -159,12 +127,8 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, tryIsAvailable(); }).execute(); new GetTask(mUrl, GetTask.URI_CONFIG, mHttpsCertPath, mApiKey, result -> { - try { - mConfig = new JSONObject(result); - tryIsAvailable(); - } catch (JSONException e) { - Log.w(TAG, "Failed to parse config", e); - } + mConfig = new Gson().fromJson(result, Config.class); + tryIsAvailable(); }).execute(); getSystemInfo(info -> { mLocalDeviceId = info.myID; @@ -197,71 +161,12 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, * Stops syncthing and cancels notification. For use by {@link SyncthingService}. */ public void shutdown() { - // Happens in unit tests. - if (mContext == null) - return; - NotificationManager nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); nm.cancel(RestartActivity.NOTIFICATION_RESTART); mRestartPostponed = false; } - /** - * Gets a value from config, - * - * Booleans are returned as {@link }Boolean#toString}, arrays as space seperated string. - * - * @param name {@link #TYPE_OPTIONS} or {@link #TYPE_GUI} - * @param key The key to read from. - * @return The value as a String, or null on failure. - */ - public String getValue(String name, String key) { - // Happens if this functions is called before class is fully initialized. - if (mConfig == null) - return ""; - - try { - Object value = mConfig.getJSONObject(name).get(key); - return (value instanceof JSONArray) - ? ((JSONArray) value).join(", ").replace("\"", "").replace("\\", "") - : value.toString(); - } catch (JSONException e) { - Log.w(TAG, "Failed to get value for " + key, e); - return ""; - } - } - - /** - * Sets a value to config and sends it via Rest API. - *

- * Booleans must be passed as {@link Boolean}, arrays as space seperated string - * with isArray true. - * - * @param name {@link #TYPE_OPTIONS} or {@link #TYPE_GUI} - * @param key The key to write to. - * @param value The new value to set, either String, Boolean or Integer. - * @param isArray True if value is a space seperated String that should be converted to array. - */ - public void setValue(String name, String key, T value, boolean isArray, Activity activity) { - try { - mConfig.getJSONObject(name).put(key, (isArray) - ? new JSONArray(Arrays.asList(((String) value).split(","))) - : value); - requireRestart(activity); - } catch (JSONException e) { - Log.w(TAG, "Failed to set value for " + key, e); - } - } - - private List jsonToList(JSONArray array) throws JSONException { - ArrayList list = new ArrayList<>(array.length()); - for (int i = 0; i < array.length(); i++) { - list.add(array.getString(i)); - } - return list; - } - /** * Either shows a restart dialog, or only updates the config, depending on * {@link #mRestartPostponed}. @@ -277,16 +182,14 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, } /** - * Sends the current config to Syncthing and restarts it. - * - * This executes a restart immediately, and does not show a dialog. + * Immediately restarts Syncthing, without confirmation. */ - public void updateConfig() { + public void restart() { new PostTask(mUrl, PostTask.URI_CONFIG, mHttpsCertPath, mApiKey, result -> { - mContext.startService(new Intent(mContext, SyncthingService.class) - .setAction(SyncthingService.ACTION_RESTART)); + Intent intent = new Intent(mContext, SyncthingService.class) + .setAction(SyncthingService.ACTION_RESTART); + mContext.startService(intent); }).execute(mConfig.toString()); - } /** @@ -295,26 +198,89 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, * @param includeLocal True if the local device should be included in the result. */ public List getDevices(boolean includeLocal) { - if (mConfig == null) - return new ArrayList<>(); + List devices = deepCopy(mConfig.devices, new TypeToken>(){}.getType()); - try { - String json = mConfig.getJSONArray("devices").toString(); - List devices = new ArrayList<>(); - Collections.addAll(devices, new Gson().fromJson(json, Device[].class)); - - Iterator it = devices.iterator(); - while (it.hasNext()) { - Device device = it.next(); - boolean isLocalDevice = Objects.equal(mLocalDeviceId, device.deviceID); - if (!includeLocal && isLocalDevice) - it.remove(); - } - return devices; - } catch (JSONException e) { - Log.w(TAG, "Failed to read devices", e); - return new ArrayList<>(); + Iterator it = devices.iterator(); + while (it.hasNext()) { + Device device = it.next(); + boolean isLocalDevice = Objects.equal(mLocalDeviceId, device.deviceID); + if (!includeLocal && isLocalDevice) + it.remove(); } + return devices; + } + + public Options getOptions() { + return deepCopy(mConfig.options, Options.class); + } + + public Config.Gui getGui() { + return deepCopy(mConfig.gui, Config.Gui.class); + } + + /** + * Returns a deep copy of object. + * + * This method uses Gson and only works with objects that can be converted with Gson. + */ + public T deepCopy(T object, Type type) { + Gson gson = new Gson(); + return gson.fromJson(gson.toJson(object, type), type); + } + + public void removeDevice(String deviceId) { + Iterator it = mConfig.devices.iterator(); + while (it.hasNext()) { + Device d = it.next(); + if (d.deviceID.equals(deviceId)) { + it.remove(); + } + } + } + + public void removeFolder(String id) { + Iterator it = mConfig.folders.iterator(); + while (it.hasNext()) { + Folder f = it.next(); + if (f.id.equals(id)) { + it.remove(); + } + } + } + + public void addDevice(Device device, OnDeviceIdNormalizedListener listener) { + normalizeDeviceId(device.deviceID, ((normalizedId, error) -> { + if (error == null) { + mConfig.devices.add(device); + } + else { + listener.onDeviceIdNormalized(normalizedId, error); + } + })); + } + + public void addFolder(Folder folder) { + mConfig.folders.add(folder); + } + + public void editDevice(Device newDevice) { + removeDevice(newDevice.deviceID); + addDevice(newDevice, null); + } + + public void editFolder(Folder newFolder) { + removeFolder(newFolder.id); + addFolder(newFolder); + } + + public void editGui(Config.Gui newGui, Activity activity) { + mConfig.gui = newGui; + requireRestart(activity); + } + + public void editOptions(Options newOptions, Activity activity) { + mConfig.options = newOptions; + requireRestart(activity); } /** @@ -338,7 +304,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, */ public void getSystemInfo(final OnReceiveSystemInfoListener listener) { new GetTask(mUrl, GetTask.URI_SYSTEM, mHttpsCertPath, mApiKey, result -> { - listener.onReceiveSystemInfo(new Gson().fromJson(result, SystemInfo.class)); + listener.onReceiveSystemInfo(new Gson().fromJson(result, SystemInfo.class)); }).execute(); } @@ -349,12 +315,8 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, */ public void getSystemVersion(final OnReceiveSystemVersionListener listener) { new GetTask(mUrl, GetTask.URI_VERSION, mHttpsCertPath, mApiKey, result -> { - try { - SystemVersion systemVersion = new Gson().fromJson(result, SystemVersion.class); - listener.onReceiveSystemVersion(systemVersion); - } catch (JsonSyntaxException e) { - Log.w(TAG, "Failed to read system info", e); - } + SystemVersion systemVersion = new Gson().fromJson(result, SystemVersion.class); + listener.onReceiveSystemVersion(systemVersion); }).execute(); } @@ -362,16 +324,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, * Returns a list of all existing folders. */ public List getFolders() { - if (mConfig == null) - return new ArrayList<>(); - - try { - String foldersJson = mConfig.getJSONArray("folders").toString(); - return Arrays.asList(new Gson().fromJson(foldersJson, Folder[].class)); - } catch (JSONException e) { - Log.w(TAG, "Failed to read devices", e); - return new ArrayList<>(); - } + return deepCopy(mConfig.folders, new TypeToken>(){}.getType()); } /** @@ -520,28 +473,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, }).execute("folder", folderId); } - /** - * Refreshes the lookup table to convert folder names to paths for events. - */ - private String getPathForFolder(String folderName) { - synchronized(mCacheFolderPathLookup) { - if (!mCacheFolderPathLookup.containsKey(folderName)) { - mCacheFolderPathLookup.clear(); - for (Folder folder : getFolders()) { - mCacheFolderPathLookup.put(folder.id, folder.path); - } - } - - return mCacheFolderPathLookup.get(folderName); - } - } - - private void clearFolderCache() { - synchronized(mCacheFolderPathLookup) { - mCacheFolderPathLookup.clear(); - } - } - /** * Retrieves the events that have accumulated since the given event id. * @@ -567,7 +498,12 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, // Add folder path to data. if (data != null && data.has("folder")) { String folder = data.get("folder").getAsString(); - String folderPath = getPathForFolder(folder); + String folderPath = null; + for (Folder f : mConfig.folders) { + if (f.id.equals(folder)) { + folderPath = f.path; + } + } data.addProperty("folderpath", folderPath); } @@ -579,168 +515,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, "since", String.valueOf(sinceId), "limit", String.valueOf(limit)); } - /** - * Updates or creates the given device, depending on whether it already exists. - * - * @param device Settings of the device to edit. To create a device, pass a non-existant device ID. - * @param listener for the normalized device ID (may be null). - */ - public void editDevice(@NonNull final Device device, final Activity activity, - final OnDeviceIdNormalizedListener listener) { - normalizeDeviceId(device.deviceID, - (normalizedId, error) -> { - if (listener != null) listener.onDeviceIdNormalized(normalizedId, error); - if (normalizedId == null) - return; - - device.deviceID = normalizedId; - // If the device already exists, just update it. - boolean create = true; - for (Device n : getDevices(true)) { - if (n.deviceID.equals(device.deviceID)) { - create = false; - } - } - - try { - JSONArray devices = mConfig.getJSONArray("devices"); - JSONObject n = null; - if (create) { - n = new JSONObject(); - devices.put(n); - } else { - for (int i = 0; i < devices.length(); i++) { - JSONObject json = devices.getJSONObject(i); - if (device.deviceID.equals(json.getString("deviceID"))) { - n = devices.getJSONObject(i); - break; - } - } - } - n.put("deviceID", device.deviceID); - n.put("name", device.name); - n.put("addresses", new JSONArray(device.addresses)); - n.put("compression", device.compression); - n.put("introducer", device.introducer); - requireRestart(activity); - } catch (JSONException e) { - Log.w(TAG, "Failed to read devices", e); - } - } - ); - } - - /** - * Deletes the given device from syncthing. - */ - public boolean deleteDevice(Device device, Activity activity) { - try { - JSONArray devices = mConfig.getJSONArray("devices"); - - for (int i = 0; i < devices.length(); i++) { - JSONObject json = devices.getJSONObject(i); - if (device.deviceID.equals(json.getString("deviceID"))) { - mConfig.remove("devices"); - mConfig.put("devices", delete(devices, devices.getJSONObject(i))); - break; - } - } - requireRestart(activity); - } catch (JSONException e) { - Log.w(TAG, "Failed to edit folder", e); - return false; - } - return true; - } - - /** - * Updates or creates the given device. - */ - public void editFolder(Folder folder, boolean create, Activity activity) { - try { - JSONArray folders = mConfig.getJSONArray("folders"); - JSONObject r = null; - if (create) { - r = new JSONObject(); - folders.put(r); - } else { - for (int i = 0; i < folders.length(); i++) { - JSONObject json = folders.getJSONObject(i); - if (folder.id.equals(json.getString("id"))) { - r = folders.getJSONObject(i); - break; - } - } - } - r.put("path", folder.path); - r.put("label", folder.label); - r.put("id", folder.id); - r.put("ignorePerms", true); - r.put("type", folder.type); - - JSONArray devices = new JSONArray(); - for (String n : folder.getDevices()) { - JSONObject element = new JSONObject(); - element.put("deviceID", n); - devices.put(element); - } - r.put("devices", devices); - JSONObject versioning = new JSONObject(); - versioning.put("type", folder.versioning.type); - JSONObject params = new JSONObject(); - versioning.put("params", params); - for (String key : folder.versioning.params.keySet()) { - params.put(key, folder.versioning.params.get(key)); - } - r.put("rescanIntervalS", folder.rescanIntervalS); - r.put("versioning", versioning); - requireRestart(activity); - } catch (JSONException e) { - Log.w(TAG, "Failed to edit folder " + folder.id + " at " + folder.path, e); - return; - } - - clearFolderCache(); - } - - /** - * Deletes the given folder from syncthing. - */ - public boolean deleteFolder(Folder folder, Activity activity) { - try { - JSONArray folders = mConfig.getJSONArray("folders"); - - for (int i = 0; i < folders.length(); i++) { - JSONObject json = folders.getJSONObject(i); - if (folder.id.equals(json.getString("id"))) { - mConfig.remove("folders"); - mConfig.put("folders", delete(folders, folders.getJSONObject(i))); - break; - } - } - requireRestart(activity); - } catch (JSONException e) { - Log.w(TAG, "Failed to edit folder", e); - return false; - } - - clearFolderCache(); - return true; - } - - /** - * Replacement for {@link org.json.JSONArray#remove(int)}, which is not available on older APIs. - */ - private JSONArray delete(JSONArray array, JSONObject delete) throws JSONException { - JSONArray newArray = new JSONArray(); - for (int i = 0; i < array.length(); i++) { - if (!array.getJSONObject(i).equals(delete)) { - newArray.put(array.get(i)); - } - } - return newArray; - } - /** * Result listener for {@link #normalizeDeviceId(String, OnDeviceIdNormalizedListener)}. */ @@ -784,43 +558,10 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, public Device getLocalDevice() { for (Device d : getDevices(true)) { if (d.deviceID.equals(mLocalDeviceId)) { - return d; + return deepCopy(d, Device.class); } } - return new Device(); - } - - /** - * Returns value of usage reporting preference. - */ - public int getUsageReportAccepted() { - try { - int value = mConfig.getJSONObject(TYPE_OPTIONS).getInt("urAccepted"); - if (value > USAGE_REPORTING_ACCEPTED) - throw new RuntimeException("Inalid usage reporting value"); - if (!USAGE_REPORTING_DECIDED.contains(value)) - value = USAGE_REPORTING_UNDECIDED; - - return value; - } catch (JSONException e) { - Log.w(TAG, "Failed to read usage report value", e); - return USAGE_REPORTING_DENIED; - } - } - - /** - * Sets new value for usage reporting preference. - */ - public void setUsageReportAccepted(int value, Activity activity) { - if (BuildConfig.DEBUG && !USAGE_REPORTING_DECIDED.contains(value)) - throw new IllegalArgumentException("Invalid value for usage report"); - - try { - mConfig.getJSONObject(TYPE_OPTIONS).put("urAccepted", value); - } catch (JSONException e) { - Log.w(TAG, "Failed to set usage report value", e); - } - requireRestart(activity); + throw new RuntimeException(); } /** @@ -835,11 +576,9 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener, */ public void getUsageReport(final OnReceiveUsageReportListener listener) { new GetTask(mUrl, GetTask.URI_REPORT, mHttpsCertPath, mApiKey, result -> { - try { - listener.onReceiveUsageReport(new JSONObject(result).toString(4)); - } catch (JSONException e) { - throw new RuntimeException("Failed to prettify usage report", e); - } + JsonElement json = new JsonParser().parse(result); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + listener.onReceiveUsageReport(gson.toJson(json)); }).execute(); } diff --git a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java index 3b46ad40..a3895c19 100644 --- a/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java +++ b/src/main/java/com/nutomic/syncthingandroid/syncthing/SyncthingService.java @@ -343,8 +343,7 @@ public class SyncthingService extends Service implements return; } - mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second, - () -> { + mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second, () -> { mCurrentState = State.ACTIVE; onApiChange(); new Thread(() -> { diff --git a/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java b/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java index 8e8af0c7..a8070931 100644 --- a/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java +++ b/src/main/java/com/nutomic/syncthingandroid/util/ConfigXml.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.security.SecureRandom; -import java.util.Arrays; import java.util.Locale; import javax.xml.parsers.DocumentBuilder; diff --git a/src/main/java/com/nutomic/syncthingandroid/views/WifiSsidPreference.java b/src/main/java/com/nutomic/syncthingandroid/views/WifiSsidPreference.java index 1ad37cf5..a5f0fa97 100644 --- a/src/main/java/com/nutomic/syncthingandroid/views/WifiSsidPreference.java +++ b/src/main/java/com/nutomic/syncthingandroid/views/WifiSsidPreference.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.TreeSet; /** * MultiSelectListPreference which allows the user to select on which WiFi networks (based on SSID) @@ -34,10 +35,11 @@ public class WifiSsidPreference extends MultiSelectListPreference { public WifiSsidPreference(Context context, AttributeSet attrs) { super(context, attrs); + setDefaultValue(new TreeSet()); } public WifiSsidPreference(Context context) { - super(context); + this(context, null); } /** diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index e19265fa..1718885e 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -125,7 +125,6 @@ Настройки - Syncthing-Android Постоянна работа на заден план @@ -146,8 +145,7 @@ С нисък приоритет Без известия - Syncthing - Настройки на Syncthing + Настройки на Syncthing Име на устройството Адрес за слушане на синхронизиращия протокол Лимит на скоростта за сваляне (KiB/s) @@ -156,12 +154,11 @@ Локално откриване Сървър за глобално откриване Анонимен доклад за ползване на програмата - Потребителски интерфейс на Syncthing Адрес за свързване с потребителския интерфейс Потребител за потребителския интерфейс Парола за потребителския интерфейс Изнасяне на настройките - Експериментално + Експериментално Наистина ли желаете настройките да бъдат изнесени? Съществуващите файлове ще бъдат презаписани.\n\nВНИМАНИЕ! Програми могат да извлекат частния ви ключ от генерирания файл, а чрез него да свалят и синхронизират файлове. diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 611c2256..bde7593f 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -130,7 +130,6 @@ Nastavení - Syncthing-Android Vždy spuštěné na pozadí @@ -152,8 +151,7 @@ Nízká priorita Źádné - Syncthing - Nastavení Syncthing + Nastavení Syncthing Jméno přístroje Adresy naslouchání protokolu synchronizace Omezení příchozí rychlosti (KiB/s) @@ -164,12 +162,11 @@ Server globálního oznamování Povolit relaying Anonymní hlášení o používání - Syncthing GUI Adresy naslouchání GUI Přihlašovací jméno pro GUI Přihlašovací heslo pro GUI Zálohovat nastavení - Experimentální + Experimentální Udržovat CPU aktivní pokud běží Syncthing Použijte toto nastavení pokud zaznamenáváte neočekávaná ukončení spojení při běhu na baterii. Toto bude mít za následek vyšší spotřebu energie. Spustit službu na popředí diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index bcefb2d3..66e3341f 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -130,7 +130,6 @@ Einstellungen - Syncthing-Android Immer im Hintergrund ausführen @@ -152,8 +151,7 @@ Geringe Priorität Keine Priorität - Syncthing - Syncthing-Optionen + Syncthing-Optionen Gerätename Adresse(n) für das Synchronisierungsprotokoll Eingehende Datenrate Limite (KiB/s) @@ -164,12 +162,11 @@ Globaler Indexserver Weiterleitung aktivieren Anonymer Nutzungsbericht - Syncthing-Weboberfläche Adressen für Weboberfläche Nutzername für Weboberfläche Passwort für Weboberfläche Konfiguration exportieren - Experimentell + Experimentell Prozessor wach halten während Syncthing läuft. Nutze dieses Einstellung, wenn du unerwartete Verbindungsabbrüche hast, während du im Batteriebetrieb arbeitest. Das wird zu einem erhöhten Energieverbrauch führen. Führe Dienst mit Vordergrund-Priorität aus diff --git a/src/main/res/values-es-rMX/strings.xml b/src/main/res/values-es-rMX/strings.xml index c5f2090c..93944768 100644 --- a/src/main/res/values-es-rMX/strings.xml +++ b/src/main/res/values-es-rMX/strings.xml @@ -126,7 +126,6 @@ Configuración - Syncthing-Android Siempre ejecutar en el fondo @@ -147,8 +146,7 @@ Baja Prioridad Ninguna - Syncthing - Opciones de Syncthing + Opciones de Syncthing Nombre del dispositivo Direcciones de escucha del Protocolo de Sincronización Límite de Tasa de Entrada (KiB/s) @@ -157,12 +155,11 @@ Descubrimiento Local Servidor de Descubrimiento Global Reporte de uso anónimo - Interfaz gráfica de Syncthing Direcciones de escucha de la interfaz gráfica Autenticación del Usuario en la Interfaz Gráfica Autenticación de la Contraseña en la Interfaz Gráfica Exportar Configuración - Experimental + Experimental Mantener el CPU encendido mientras Syncthing está en ejecución Use estos ajustes si experimenta desconexiones inesperadas mientras opera con la batería. Esto resultará en un consumo elevado de batería. diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 74d06033..e04b88d0 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -130,7 +130,6 @@ Ajustes - Syncthing-Android Ejecutar siempre en segundo plano @@ -155,6 +154,7 @@ Syncthing Opciones de Syncthing Nombre del dispositivo + Opciones de Syncthing Direcciones de escucha del protocolo de sincronización Límite de tráfico de entrada (KiB/s) Límite de tráfico de salida (KiB/s) diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 728f8287..cdb20749 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -130,7 +130,6 @@ Paramètres - Syncthing-Android Fonctionner en tâche de fond. @@ -152,8 +151,7 @@ Discrète Aucune - Syncthing - Options Syncthing + Options Syncthing Nom de l\'appareil Adresse d\'écoute pour la synchro Limite du débit de réception (Ko/s) @@ -164,12 +162,11 @@ Serveur de découverte globale Activer le relayage Rapports anonymes d\'utilisation - Interface WEB Syncthing Adresse pour l\'interface WEB Nom d\'authentification pour l\'interface WEB Mot de passe d\'authentification pour l\'interface WEB Exporter la configuration - Expérimental + Expérimental Garder le CPU en éveil quand Syncthing fonctionne. Utilisez ce paramètre si vous rencontrez des déconnexions inattendues en fonctionnant sur batterie. Il en résultera une augmentation de la consommation de la batterie. Fonctionner avec prioriré de premier plan diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index e2af0f4e..f3cfb27b 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -111,7 +111,6 @@ Beállítások - Syncthing-Android Mindig fusson a háttérben @@ -123,13 +122,11 @@ Syncthing futtatása Superuser-ként Értesítés Értesítés típusának kiválasztása - Syncthing - Syncthing beállításai + Syncthing beállításai Eszköz név Globális felfedezés Helyi felfedezés Globális felfedező szerver - Syncthing GUI Beállítások mentése diff --git a/src/main/res/values-in/strings.xml b/src/main/res/values-in/strings.xml index 9b5745b9..e5c5c95c 100644 --- a/src/main/res/values-in/strings.xml +++ b/src/main/res/values-in/strings.xml @@ -90,7 +90,6 @@ - Syncthing diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 1247c3c9..4dde32ff 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -130,7 +130,6 @@ Impostazioni - Syncthing-Android Sempre in background @@ -152,8 +151,7 @@ Bassa priorità Nessuno - Syncthing - Opzioni di Syncthing + Opzioni di Syncthing Nome Dispositivo Indirizzi Protocollo Sincronizzazione Limite Velocità in Ingresso (KiB/s) @@ -164,12 +162,11 @@ Server Individuazione Globale Abilita Reindirizzamento Statistiche Anonime Utilizzo - Interfaccia Grafica di Syncthing Indirizzi Interfaccia Grafica Utente Interfaccia Grafica Password Autenticazione Utente Esporta Configurazione - Sperimentale + Sperimentale Mantieni attiva la CPU quando syncthing è in esecuzione Usa questa impostazione se rilevi disconnessioni inaspettate durante il funzionamento a batteria. Questo aumenterà il consumo della batteria. Esegui il servizio con la priorità di primo piano diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 5fc73921..9ba87a1f 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -130,7 +130,6 @@ 設定 - Syncthing-Android 常にバックグラウンドで実行 @@ -152,8 +151,7 @@ 優先度低 なし - Syncthing - 同期オプション + 同期オプション デバイス名 同期プロトコル リスンアドレス 着信レート制限 (KiB/秒) @@ -164,12 +162,11 @@ グローバル探索サーバー リレーを有効にする 匿名使用状況レポート - 同期中 GUI GUI リスンアドレス GUI 認証ユーザー GUI 認証パスワード 設定をエクスポート - 実験的 + 実験的 Syncthing の実行中、CPU をオンのままにする バッテリーで動作しているときに予期しない接続の切断が発生した場合、この設定を使用します。これは、バッテリー消費を増やします。 フォアグラウンドの優先度でサービスを実行する diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index 724af6ba..3fb888c6 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -130,7 +130,6 @@ 설정 - Syncthing-Android 백그라운드에서 항상 실행 @@ -152,8 +151,7 @@ 우선 순위 낮음 사용 안 함 - Syncthing - Syncthing 옵션 + Syncthing 옵션 기기명 동기화 프로토콜 수신 주소 다운로드 속도 제한 (KiB/s) @@ -164,12 +162,11 @@ 글로벌 탐색 서버 Relaying 활성화 익명 사용 기록 - Syncthing GUI 접속 대기 주소 GUI 인증 사용자 GUI 인증 비밀번호 설정 내보내기 - 실험적인 기능 + 실험적인 기능 Syncthing이 실행 중일 때 CPU를 깨어 있는 상태로 두기 만약 배터리로 사용중 예상하지 못한 접속 끊김이 발생 했다면 이 설정을 사용합니다. 이 기능은 배터리 소비를 늘립니다. 서비스를 포그라운드로 실행 diff --git a/src/main/res/values-nb/strings.xml b/src/main/res/values-nb/strings.xml index 8250e8e9..d4f844a0 100644 --- a/src/main/res/values-nb/strings.xml +++ b/src/main/res/values-nb/strings.xml @@ -128,7 +128,6 @@ Innstillinger - Syncthing-Android Kjør alltid i bakgrunnen @@ -150,8 +149,7 @@ Lav prioritet Ingen - Syncthing - Valg for Syncthing + Valg for Syncthing Enhetsnavn Lytteadresser for synkeprotokoll Grense for innkommende trafikk (KiB/s) @@ -162,12 +160,11 @@ Global søketjener Aktiver relésending Anonym bruksrapportering - Syncthing-GUI GUI Lytteadresser GUI Autentiseringsbruker GUI Autentiseringspassord Eksporter innstillinger - Eksperimentelt + Eksperimentelt Hold prosessoren våken mens Syncthing kjører Bruk denne innstillingen dersom du opplever uventede frakoblinger mens enheten går på batteri. Dette vil medføre økt batteriforbruk. Kjør tjenesten med forgrunnsprioritet diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 994329f9..9cc7010d 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -130,7 +130,6 @@ Instellingen - Syncthing-Android Altijd draaien in achtergrond @@ -152,8 +151,7 @@ Lage prioriteit Geen - Syncthing - Syncthing-opties + Syncthing-opties Apparaatnaam Synchronisatieprotocol luister-adressen Inkomende snelheidslimiet (KiB/s) @@ -164,12 +162,11 @@ Globale ontdekkingsserver Relayen inschakelen Anoniem gebruiksrapportage - Syncthing-GUI GUI luister-adressen GUI-authenticatie gebruiker GUI-authenticatie wachtwoord Configuratie exporteren - Experimenteel + Experimenteel Laat de CPU niet slapen wanneer Syncthing wordt uitgevoerd Gebruik deze instelling als je onverwacht losgekoppeld wordt wanneer je op batterij werkt. Dit zal leiden tot verhoogd batterijverbruik. Dienst uitvoeren met voorgrondprioriteit diff --git a/src/main/res/values-nn/strings.xml b/src/main/res/values-nn/strings.xml index 87e00bfb..a8833fac 100644 --- a/src/main/res/values-nn/strings.xml +++ b/src/main/res/values-nn/strings.xml @@ -128,7 +128,6 @@ Innstillingar - Syncthing-Android Køyr alltid i bakgrunnen @@ -150,8 +149,7 @@ Låg prioritet Ingen - Syncthing - Syncthing-innstillingar + Syncthing-innstillingar Einingsnamn Lytteadresse for synkroniseringsprotokoll Nedlastingsgrense (KiB/s) @@ -162,12 +160,11 @@ Tener for global søketeneste Aktiver Reléer Anonym rapportering av bruk - Grafisk grensesnitt for Syncthing Grensesnitt: lytteadresser Grensesnitt: Brukarnamn Grensesnitt: Passord Eksporter innstillingar - Eksperiment + Eksperiment Hald prosessoren vaken medan Syncthing køyrer Bruk denne innstillinga om du opplever uventa avkoplingar medan du køyrer på batteri. Dette vil føre til auka batteriforbruk. Køyr tenesten med forgrunnsprioritet diff --git a/src/main/res/values-pl-rPL/strings.xml b/src/main/res/values-pl-rPL/strings.xml index f962046a..b58a0b12 100644 --- a/src/main/res/values-pl-rPL/strings.xml +++ b/src/main/res/values-pl-rPL/strings.xml @@ -66,7 +66,6 @@ - Syncthing diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 1854feb7..eb4ff44a 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -130,7 +130,6 @@ Ustawienia - Syncthing-Android Działanie w tle @@ -146,8 +145,7 @@ Synchronizuje zawartość z uprawnieniami użytkownika Superuser Powiadomienie Wybiera rodzaj powiadomienia - Syncthing - Ustawienia Syncthing + Ustawienia Syncthing Nazwa urządzenia Adres nasłuchiwania protokołu Limit pobierania (KiB/s) @@ -165,6 +163,7 @@ Eksportuj ustawienia Eksperymentalne Utrzymywanie wybudzonego CPU podczas działania + Eksperymelne! Używaj, jeśli doświadczasz nieoczekiwanych rozłączeń w trakcie działania na baterii. Spowoduje to zwiększone jej zużycie. Pierwszoplanowy priorytet usługi Korzystanie z Tor diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index ceb0f59d..e38fa3c1 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -130,7 +130,6 @@ Configurações - Syncthing-Android Sempre rodar em segundo plano @@ -152,8 +151,7 @@ Baixa prioridade Nenhuma - Syncthing - Opções do Syncthing + Opções do Syncthing Nome do dispositivo Endereços de escuta do protocolo de sincronização Limite de velocidade de recepção (KiB/s) @@ -164,12 +162,11 @@ Descoberta global Habilitar retransmissão Relatório anônimo de uso - Interface do Syncthing Endereços de escuta da interface Nome de usuário da interface Senha da interface Exportar configuração - Configurações experimentais + Configurações experimentais Manter a CPU acordada Marque esta opção se você observar desconexões inesperadas enquanto a bateria do dispositivo não está sendo carregada. Isto resultará em maior uso da bateria. Rodar serviço com prioridade de primeiro plano diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index fc2c4684..eddf96e1 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -121,7 +121,6 @@ Configurações - Syncthing-Android Correr sempre em segundo plano @@ -140,8 +139,7 @@ Baixa prioridade Nenhuma - Syncthing - Opções do Syncthing + Opções do Syncthing Nome do dispositivo Endereços de escuta do protocolo de sincronização Limite de velocidade de recepção (KiB/s) @@ -150,7 +148,6 @@ Busca local Servidor da busca global Enviar relatórios anónimos de utilização - Interface gráfica do Syncthing Endereço de escuta da interface gráfica Utilizador da autenticação na interface gráfica Senha da autenticação na interface gráfica diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index b5735172..3dd4110f 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -127,7 +127,6 @@ Настройки - Настройки приложения Фоновый режим @@ -149,8 +148,7 @@ Низкий Никогда - Общие настройки Syncthing - Настройки устройства + Настройки устройства Имя устройства Адрес Ожидания Протокола Синхронизации Лимит Скачивания (КБ/с) @@ -161,7 +159,6 @@ Глобальный сервер обнаружения Включить релеи Анонимные Отчёты Использования - Syncthing GUI Адрес Ожидания GUI Пользователь Идентификации GUI Пароль Идентификации GUI diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index 3ea5bb00..65bac238 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -120,7 +120,6 @@ Nastavenia - Syncthing-Android Vždy bežať na pozadí @@ -139,8 +138,7 @@ S nízkou prioritou Žiadne - Syncthing - Nastavenia Syncthing + Nastavenia Syncthing Názov Zariadenia Adresa pre Sync Protokol Limit Rýchlosti Sťahovania (KiB/s) @@ -149,7 +147,6 @@ Lokálne Vyhľadávanie Adresa Serveru pre Globálne Vyhľadávanie Povoliť Anonymné Hlásenia o Používaní - Syncthing GUI Adresa pre GUI Užívateľské Meno pre GUI Užívateľské Heslo pre GUI diff --git a/src/main/res/values-sv-rSE/strings.xml b/src/main/res/values-sv-rSE/strings.xml index e6dad4bc..522a3861 100644 --- a/src/main/res/values-sv-rSE/strings.xml +++ b/src/main/res/values-sv-rSE/strings.xml @@ -134,7 +134,6 @@ Vänligen rapportera eventuella problem du stöter på via Github. Inställningar - Syncthing-Android Körs alltid i bakgrunden @@ -156,8 +155,7 @@ Vänligen rapportera eventuella problem du stöter på via Github. Låg prioritet Ingen - Syncthing - Syncthing alternativ + Syncthing alternativ Enhets namn Lyssnaradresser för synkroniseringsprotokoll Inkommande hastighetsbegränsning (KiB/s) @@ -168,12 +166,11 @@ Vänligen rapportera eventuella problem du stöter på via Github. Global upptäktsserver Aktivera reläa Anonym användningsrapportering - Syncting-GUI Lyssnaradresser för GUI GUI autentiseringsanvändare GUI autentiseringslösenord Exportera konfiguration - Experimentell + Experimentell Håll CPU vaken medan Syncthing körs Använd den här inställningen om du får oväntade frånkopplingar under batteridrift. Detta kommer att resultera i ökad batteriförbrukning . Kör tjänst med förgrundsprioritet diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index a0e5a905..b54186b5 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -130,7 +130,6 @@ Inställningar - Syncthing-Android Körs alltid i bakgrunden @@ -152,8 +151,7 @@ Låg prioritet Ingen - Syncthing - Syncthing alternativ + Syncthing alternativ Enhets namn Synkroniseringsprotokollets lyssnaradresser Inkommande hastighetsgräns (KiB/s) @@ -164,12 +162,11 @@ Global upptäktsserver Aktivera reläa Anonym användningsrapportering - Syncthing-GUI GUI lyssnaradresser GUI autentiseringsanvändare GUI autentiseringslösenord Exportera konfiguration - Experimentell + Experimentell Håll CPU:n vaken medan Syncthing körs Använd den här inställningen om du får oväntade frånkopplingar under batteridrift. Detta kommer att resultera i ökad batteriförbrukning. Kör tjänst med förgrundsprioritet diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 38baa171..dbedd3c4 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -121,7 +121,6 @@ Ayarlar - Syncthing-Android Arkaplanda herzaman çalış @@ -142,8 +141,7 @@ Düşük Öncelikli Hiçbiri - Syncthing - Syncthing Seçenekler + Syncthing Seçenekler Cihaz Adı Eşzamanlama Protokolü Dinleme/Bağlanma Adresleri Gelen Hız Sınırı (KiB/s) @@ -152,7 +150,6 @@ Yerel Bulunabilirlik Küresel Bulunabilirlik Sunucusu Anonim olarak Kullanım Raporlama - Syncthing Arayüzü Arayüz Dinleme Adresleri Arayüz Kullanıcı Doğrulaması için Kullanıcı Adı Arayüz Kullanıcı Doğrulaması için Kullanıcı Parolası diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml index 4dcfa06e..6bd0fbc5 100644 --- a/src/main/res/values-vi/strings.xml +++ b/src/main/res/values-vi/strings.xml @@ -126,7 +126,6 @@ Cài đặt - Syncthing-Android Luôn chạy nền @@ -147,8 +146,7 @@ Ưu tiên thấp Không thông báo - Syncthing - Tuỳ chọn Syncthing + Tuỳ chọn Syncthing Tên thiết bị Đồng bộ các đ.chỉ lắng nghe giao thức Giới hạn t.độ đầu vào (KiB/s) @@ -157,12 +155,11 @@ Dò tìm cục bộ Máy chủ dò tìm toàn cầu Báo cáo sử dụng ẩn danh - GUI Syncthing Các đ.chỉ lắng nghe GUI Người dùng xác thực GUI Mật khẩu xác thực GUI Xuất cấu hình - Thực nghiệm + Thực nghiệm Giữ CPU thức khi Syncthing đang chạy Dùng tuỳ chỉnh này nếu bạn bị mất kết nối bất ngờ khi đang dùng pin. Nó sẽ tiêu thụ nhiều pin hơn. diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 6eca66d2..d65cc86f 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -130,7 +130,6 @@ 设置 - Android 版 Syncthing 功能 总是在后台运行 @@ -152,8 +151,7 @@ 低优先级 隐藏 - Syncthing 通用功能 - 同步设置 + 同步设置 设备名称 同步协议监听地址 下载限速(KiB/s) @@ -164,12 +162,11 @@ 用以在互联网上寻找节点的发布服务器地址 启用中继 匿名使用报告 - Syncthing 管理页 管理页监听地址 管理页认证用户 管理页认证密码 导出设置 - 实验性 + 实验性 当 Syncthing 正在运行时保持 CPU 唤醒 使用此设置如果您在电池上运行时遇到意外的断开连接。这将会导致电量消耗的增加。 以前台优先级运行服务 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 53d893ee..c8af7dc1 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -113,14 +113,12 @@ 只在充電時同步 只透過 Wi-Fi 網路同步 使用進階的資料夾選擇器 - Syncthing - Syncthing 選項 + Syncthing 選項 同步通訊協定監聽位址 下載速率限制(KiB/s) 上傳速率限制(KiB/s) 全域探索 本地探索 - Syncthing GUI GUI 監聽位址 GUI 認證使用者名稱 GUI 認證密碼 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 896727d7..0e6d0fb7 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -217,12 +217,15 @@ Please report any problems you encounter via Github. - Settings - Syncthing-Android + Run Conditions + Behaviour + Syncthing Options + Backup + Debug + Experimental - Always run in background @@ -257,10 +260,6 @@ Please report any problems you encounter via Github. None - Syncthing - - Syncthing Options - Device Name Sync Protocol Listen Addresses @@ -275,14 +274,12 @@ Please report any problems you encounter via Github. Enable NAT Traversal - Global Discovery Server + Global Discovery Servers Enable Relaying Anonymous Usage Reporting - Syncthing GUI - GUI Listen Addresses GUI Authentication User @@ -291,8 +288,6 @@ Please report any problems you encounter via Github. Export Configuration - Experimental - Keep the CPU awake while Syncthing is running Use this setting if you experience unexpected disconnects while operating on battery. This will result in increased battery consumption. diff --git a/src/main/res/xml/app_settings.xml b/src/main/res/xml/app_settings.xml index fce6b987..858de9a9 100644 --- a/src/main/res/xml/app_settings.xml +++ b/src/main/res/xml/app_settings.xml @@ -1,8 +1,9 @@ - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - + + + + + + + + + - + - + - + - + - + - + - + - - - - - @@ -214,6 +219,6 @@ android:title="@string/app_version_title" style="?android:preferenceInformationStyle" /> - +