Turned SettingsFragment into activity

This commit is contained in:
Felix Ableitner 2016-11-10 23:55:55 +09:00
parent 9e9397be26
commit 4f41eb7b06
11 changed files with 509 additions and 506 deletions

View File

@ -50,7 +50,7 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<activity android:name=".activities.SettingsActivity"
<activity android:name=".activities.SyncthingSettingsActivity"
android:label="@string/settings_title"
android:parentActivityName=".activities.MainActivity">
<meta-data
@ -60,14 +60,14 @@
<activity
android:name=".activities.LogActivity"
android:label="@string/log_title"
android:parentActivityName=".activities.SettingsActivity">
android:parentActivityName=".activities.SyncthingSettingsActivity">
<intent-filter>
<action android:name=".activities.LogActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.SettingsActivity" />
android:value=".activities.SyncthingSettingsActivity" />
</activity>
<activity
android:name=".activities.FolderPickerActivity"
@ -79,7 +79,12 @@
<activity android:name=".activities.RestartActivity"
android:theme="@style/Theme.Syncthing.Translucent"
android:launchMode="singleTop"/>
<activity android:name=".activities.SettingsActivity"
android:parentActivityName=".activities.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity" />
</activity>
<service android:name=".service.SyncthingService" />
<receiver android:name=".receiver.NetworkReceiver">

View File

@ -290,9 +290,7 @@ public class MainActivity extends SyncthingActivity
.setPositiveButton(R.string.syncthing_disabled_change_settings,
(dialogInterface, i) -> {
finish();
Intent intent = new Intent(MainActivity.this, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_APP_SETTINGS);
startActivity(intent);
startActivity(new Intent(this, SettingsActivity.class));
}
)
.setNegativeButton(R.string.exit,

View File

@ -1,106 +1,396 @@
package com.nutomic.syncthingandroid.activities;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.util.Log;
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.fragments.DeviceFragment;
import com.nutomic.syncthingandroid.fragments.FolderFragment;
import com.nutomic.syncthingandroid.fragments.SettingsFragment;
import com.nutomic.syncthingandroid.model.Config;
import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.model.Options;
import com.nutomic.syncthingandroid.service.RestApi;
import com.nutomic.syncthingandroid.service.SyncthingService;
import com.nutomic.syncthingandroid.views.WifiSsidPreference;
import java.security.InvalidParameterException;
import java.util.List;
import eu.chainfire.libsuperuser.Shell;
/**
* General Activity used by all PreferenceFragments.
*/
public class SettingsActivity extends SyncthingActivity {
public static final String ACTION_APP_SETTINGS =
"com.nutomic.syncthingandroid.activities.SettingsActivity.APP_SETTINGS";
public static final String ACTION_DEVICE_SETTINGS =
"com.nutomic.syncthingandroid.activities.SettingsActivity.DEVICE_SETTINGS";
public static final String ACTION_FOLDER_SETTINGS =
"com.nutomic.syncthingandroid.activities.SettingsActivity.FOLDER_SETTINGS";
/**
* Must be set for {@link #ACTION_DEVICE_SETTINGS} and
* {@link #ACTION_FOLDER_SETTINGS} to determine if an existing folder/device should be
* edited or a new one created.
*/
public static final String EXTRA_IS_CREATE =
"com.nutomic.syncthingandroid.activities.SettingsActivity.IS_CREATE";
private Fragment mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
FragmentManager fm = getFragmentManager();
if (savedInstanceState != null) {
mFragment = fm.getFragment(savedInstanceState,
savedInstanceState.getString("fragment_name"));
} else {
String action = getIntent().getAction();
if (action == null) {
action = ACTION_APP_SETTINGS;
}
switch (action) {
case ACTION_APP_SETTINGS:
setTitle(R.string.settings_title);
mFragment = new SettingsFragment();
break;
case ACTION_DEVICE_SETTINGS:
assertHasCreateExtra();
mFragment = new DeviceFragment();
break;
case ACTION_FOLDER_SETTINGS:
assertHasCreateExtra();
mFragment = new FolderFragment();
break;
default:
throw new IllegalArgumentException(
"You must provide the requested fragment type as an extra.");
}
}
fm.beginTransaction()
.replace(R.id.content, mFragment)
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
public static class SettingsFragment extends PreferenceFragment
implements SyncthingActivity.OnServiceConnectedListener,
SyncthingService.OnApiChangeListener, Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener {
private static final String TAG = "SettingsFragment";
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 Preference mSyncthingVersion;
private SyncthingService mSyncthingService;
private RestApi mApi;
private Options mOptions;
private Config.Gui mGui;
/**
* Loads layout, sets version from Rest API.
*
* Manual target API as we manually check if ActionBar is available (for ActionBar back button).
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String fragmentClassName = mFragment.getClass().getName();
outState.putString("fragment_name", fragmentClassName);
FragmentManager fm = getFragmentManager();
fm.putFragment(outState, fragmentClassName, mFragment);
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
private void assertHasCreateExtra() {
if (!getIntent().hasExtra(EXTRA_IS_CREATE))
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
}
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
public boolean getIsCreate() {
return getIntent().getBooleanExtra(EXTRA_IS_CREATE, false);
addPreferencesFromResource(R.xml.app_settings);
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);
mSyncOnlyCharging.setEnabled(mAlwaysRunInBackground.isChecked());
mSyncOnlyWifi.setEnabled(mAlwaysRunInBackground.isChecked());
mSyncOnlyOnSSIDs.setEnabled(mSyncOnlyWifi.isChecked());
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");
mSyncOnlyOnSSIDs.setEnabled(mSyncOnlyWifi.isChecked());
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()
.getPackageInfo(getActivity().getPackageName(), 0).versionName);
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "Failed to get app version name");
}
}
@Override
public void onBackPressed() {
if (getIsCreate() && (mFragment instanceof DeviceFragment || mFragment instanceof FolderFragment)) {
new AlertDialog.Builder(this)
.setMessage(R.string.dialog_discard_changes)
.setPositiveButton(android.R.string.ok, (dialog, which) -> finish())
.setNegativeButton(android.R.string.cancel, null)
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
if (mSyncthingService.getApi().isConfigLoaded()) {
mGui = mSyncthingService.getApi().getGui();
mOptions = mSyncthingService.getApi().getOptions();
}
}
@Override
public void onApiChange(SyncthingService.State currentState) {
boolean syncthingActive = currentState == SyncthingService.State.ACTIVE;
boolean enableAllPrefs = syncthingActive && mSyncthingService.getApi().isConfigLoaded();
PreferenceScreen ps = getPreferenceScreen();
for (int i = 0; i < ps.getPreferenceCount(); i++) {
Preference p = ps.getPreference(i);
p.setEnabled(enableAllPrefs || "category_run_conditions".equals(p.getKey()));
}
if (!enableAllPrefs)
return;
mApi = mSyncthingService.getApi();
mSyncthingVersion.setSummary(mApi.getVersion());
mOptions = mApi.getOptions();
mGui = mApi.getGui();
Joiner joiner = Joiner.on(", ");
mDeviceName.setText(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) {
Splitter splitter = Splitter.on(",").trimResults().omitEmptyStrings();
switch (preference.getKey()) {
case "deviceName":
Device localDevice = mApi.getLocalDevice();
localDevice.name = (String) o;
mApi.editDevice(localDevice);
break;
case "listenAddresses":
mOptions.listenAddresses = Iterables.toArray(splitter.split((String) o), String.class);
break;
case "maxRecvKbps": mOptions.maxRecvKbps = (int) o; break;
case "maxSendKbps": mOptions.maxRecvKbps = (int) o; break;
case "natEnabled": mOptions.natEnabled = (boolean) o; break;
case "localAnnounceEnabled": mOptions.localAnnounceEnabled = (boolean) o; break;
case "globalAnnounceEnabled": mOptions.globalAnnounceEnabled = (boolean) o; break;
case "relaysEnabled": mOptions.relaysEnabled = (boolean) o; break;
case "globalAnnounceServers":
mOptions.globalAnnounceServers = Iterables.toArray(splitter.split((String) o), String.class);
break;
case "address": mGui.address = (String) o; break;
case "user": mGui.user = (String) o; break;
case "password": mGui.password = (String) o; break;
case "urAccepted":
mOptions.urAccepted = ((boolean) o)
? Options.USAGE_REPORTING_ACCEPTED
: Options.USAGE_REPORTING_DENIED;
break;
default: throw new InvalidParameterException();
}
mApi.editSettings(mGui, mOptions, getActivity());
return true;
}
public boolean onRequireRestart(Preference preference, Object o) {
mSyncthingService.getApi().showRestartDialog(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().showRestartDialog(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().showRestartDialog(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;
}
}
/**
* Enables or disables {@link #mUseRoot} preference depending whether root is available.
*/
private class TestRootTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
return Shell.SU.available();
}
@Override
protected void onPostExecute(Boolean haveRoot) {
if (haveRoot) {
mSyncthingService.getApi().showRestartDialog(getActivity());
mUseRoot.setChecked(true);
} else {
Toast.makeText(getActivity(), R.string.toast_root_denied, Toast.LENGTH_SHORT)
.show();
}
else {
finish();
}
}
/**
* Changes the owner of syncthing files so they can be accessed without root.
*/
private class ChownFilesRunnable implements Runnable {
@Override
public void run() {
String f = getActivity().getFilesDir().getAbsolutePath();
List<String> out = Shell.SU.run("chown -R --reference=" + f + " " + f);
Log.i(TAG, "Changed owner of syncthing files, output: " + out);
}
}
}
}

View File

@ -0,0 +1,96 @@
package com.nutomic.syncthingandroid.activities;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.fragments.DeviceFragment;
import com.nutomic.syncthingandroid.fragments.FolderFragment;
/**
* General Activity used by all PreferenceFragments.
*/
public class SyncthingSettingsActivity extends SyncthingActivity {
public static final String ACTION_DEVICE_SETTINGS =
"com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity.DEVICE_SETTINGS";
public static final String ACTION_FOLDER_SETTINGS =
"com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity.FOLDER_SETTINGS";
/**
* Must be set for {@link #ACTION_DEVICE_SETTINGS} and
* {@link #ACTION_FOLDER_SETTINGS} to determine if an existing folder/device should be
* edited or a new one created.
*/
public static final String EXTRA_IS_CREATE =
"com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity.IS_CREATE";
private Fragment mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
FragmentManager fm = getFragmentManager();
if (savedInstanceState != null) {
mFragment = fm.getFragment(savedInstanceState,
savedInstanceState.getString("fragment_name"));
} else {
String action = getIntent().getAction();
switch (action) {
case ACTION_DEVICE_SETTINGS:
assertHasCreateExtra();
mFragment = new DeviceFragment();
break;
case ACTION_FOLDER_SETTINGS:
assertHasCreateExtra();
mFragment = new FolderFragment();
break;
default:
throw new IllegalArgumentException(
"You must provide the requested fragment type as an extra.");
}
}
fm.beginTransaction()
.replace(R.id.content, mFragment)
.commit();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String fragmentClassName = mFragment.getClass().getName();
outState.putString("fragment_name", fragmentClassName);
FragmentManager fm = getFragmentManager();
fm.putFragment(outState, fragmentClassName, mFragment);
}
private void assertHasCreateExtra() {
if (!getIntent().hasExtra(EXTRA_IS_CREATE))
throw new IllegalArgumentException("EXTRA_IS_CREATE must be set");
}
public boolean getIsCreate() {
return getIntent().getBooleanExtra(EXTRA_IS_CREATE, false);
}
@Override
public void onBackPressed() {
if (getIsCreate() && (mFragment instanceof DeviceFragment || mFragment instanceof FolderFragment)) {
new AlertDialog.Builder(this)
.setMessage(R.string.dialog_discard_changes)
.setPositiveButton(android.R.string.ok, (dialog, which) -> finish())
.setNegativeButton(android.R.string.cancel, null)
.show();
}
else {
finish();
}
}
}

View File

@ -24,7 +24,7 @@ import com.google.gson.Gson;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.SettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
import com.nutomic.syncthingandroid.model.Connection;
import com.nutomic.syncthingandroid.model.Device;
@ -149,7 +149,7 @@ public class DeviceFragment extends Fragment implements View.OnClickListener {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SettingsActivity activity = (SettingsActivity) getActivity();
SyncthingSettingsActivity activity = (SyncthingSettingsActivity) getActivity();
mIsCreateMode = activity.getIsCreate();
activity.registerOnServiceConnectedListener(this::onServiceConnected);
activity.setTitle(mIsCreateMode ? R.string.add_device : R.string.edit_device);

View File

@ -11,7 +11,7 @@ import android.widget.AdapterView;
import android.widget.ListView;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.SettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.service.SyncthingService;
@ -98,9 +98,9 @@ public class DeviceListFragment extends ListFragment implements SyncthingService
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
intent.setAction(SettingsActivity.ACTION_DEVICE_SETTINGS);
intent.putExtra(SettingsActivity.EXTRA_IS_CREATE, false);
Intent intent = new Intent(getActivity(), SyncthingSettingsActivity.class);
intent.setAction(SyncthingSettingsActivity.ACTION_DEVICE_SETTINGS);
intent.putExtra(SyncthingSettingsActivity.EXTRA_IS_CREATE, false);
intent.putExtra(DeviceFragment.EXTRA_DEVICE_ID, mAdapter.getItem(i).deviceID);
startActivity(intent);
}
@ -114,9 +114,9 @@ public class DeviceListFragment extends ListFragment implements SyncthingService
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_device:
Intent intent = new Intent(getActivity(), SettingsActivity.class)
.setAction(SettingsActivity.ACTION_DEVICE_SETTINGS)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true);
Intent intent = new Intent(getActivity(), SyncthingSettingsActivity.class)
.setAction(SyncthingSettingsActivity.ACTION_DEVICE_SETTINGS)
.putExtra(SyncthingSettingsActivity.EXTRA_IS_CREATE, true);
startActivity(intent);
return true;
default:

View File

@ -14,6 +14,7 @@ import com.google.common.base.Optional;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.MainActivity;
import com.nutomic.syncthingandroid.activities.SettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity;
import com.nutomic.syncthingandroid.activities.WebGuiActivity;
import com.nutomic.syncthingandroid.model.Connection;
import com.nutomic.syncthingandroid.model.SystemInfo;
@ -208,8 +209,7 @@ public class DrawerFragment extends Fragment implements View.OnClickListener {
mActivity.closeDrawer();
break;
case R.id.drawerActionSettings:
startActivity(new Intent(mActivity, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_APP_SETTINGS));
startActivity(new Intent(mActivity, SettingsActivity.class));
mActivity.closeDrawer();
break;
case R.id.drawerActionRestart:

View File

@ -29,7 +29,7 @@ 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;
import com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
import com.nutomic.syncthingandroid.fragments.dialog.KeepVersionsDialogFragment;
import com.nutomic.syncthingandroid.model.Device;
@ -149,7 +149,7 @@ public class FolderFragment extends Fragment
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SettingsActivity activity = (SettingsActivity) getActivity();
SyncthingSettingsActivity activity = (SyncthingSettingsActivity) getActivity();
mIsCreateMode = activity.getIsCreate();
activity.setTitle(mIsCreateMode ? R.string.create_folder : R.string.edit_folder);
activity.registerOnServiceConnectedListener(this);

View File

@ -11,7 +11,7 @@ import android.view.View;
import android.widget.AdapterView;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.SettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingActivity;
import com.nutomic.syncthingandroid.model.Folder;
import com.nutomic.syncthingandroid.service.SyncthingService;
@ -107,9 +107,9 @@ public class FolderListFragment extends ListFragment implements SyncthingService
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), SettingsActivity.class)
.setAction(SettingsActivity.ACTION_FOLDER_SETTINGS)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, false)
Intent intent = new Intent(getActivity(), SyncthingSettingsActivity.class)
.setAction(SyncthingSettingsActivity.ACTION_FOLDER_SETTINGS)
.putExtra(SyncthingSettingsActivity.EXTRA_IS_CREATE, false)
.putExtra(FolderFragment.EXTRA_FOLDER_ID, mAdapter.getItem(i).id);
startActivity(intent);
}
@ -135,9 +135,9 @@ public class FolderListFragment extends ListFragment implements SyncthingService
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_folder:
Intent intent = new Intent(getActivity(), SettingsActivity.class)
.setAction(SettingsActivity.ACTION_FOLDER_SETTINGS)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true);
Intent intent = new Intent(getActivity(), SyncthingSettingsActivity.class)
.setAction(SyncthingSettingsActivity.ACTION_FOLDER_SETTINGS)
.putExtra(SyncthingSettingsActivity.EXTRA_IS_CREATE, true);
startActivity(intent);
return true;
default:

View File

@ -1,386 +0,0 @@
package com.nutomic.syncthingandroid.fragments;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.util.Log;
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.service.RestApi;
import com.nutomic.syncthingandroid.service.SyncthingService;
import com.nutomic.syncthingandroid.views.WifiSsidPreference;
import java.security.InvalidParameterException;
import java.util.List;
import eu.chainfire.libsuperuser.Shell;
public class SettingsFragment extends PreferenceFragment
implements SyncthingActivity.OnServiceConnectedListener,
SyncthingService.OnApiChangeListener, Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener {
private static final String TAG = "SettingsFragment";
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 Preference mSyncthingVersion;
private SyncthingService mSyncthingService;
private RestApi mApi;
private Options mOptions;
private Config.Gui mGui;
/**
* Loads layout, sets version from Rest API.
*
* Manual target API as we manually check if ActionBar is available (for ActionBar back button).
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((SyncthingActivity) getActivity()).registerOnServiceConnectedListener(this);
addPreferencesFromResource(R.xml.app_settings);
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);
mSyncOnlyCharging.setEnabled(mAlwaysRunInBackground.isChecked());
mSyncOnlyWifi.setEnabled(mAlwaysRunInBackground.isChecked());
mSyncOnlyOnSSIDs.setEnabled(mSyncOnlyWifi.isChecked());
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");
mSyncOnlyOnSSIDs.setEnabled(mSyncOnlyWifi.isChecked());
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()
.getPackageInfo(getActivity().getPackageName(), 0).versionName);
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "Failed to get app version name");
}
}
@Override
public void onServiceConnected() {
mSyncthingService = ((SyncthingActivity) getActivity()).getService();
mSyncthingService.registerOnApiChangeListener(this);
if (mSyncthingService.getApi().isConfigLoaded()) {
mGui = mSyncthingService.getApi().getGui();
mOptions = mSyncthingService.getApi().getOptions();
}
}
@Override
public void onApiChange(SyncthingService.State currentState) {
boolean syncthingActive = currentState == SyncthingService.State.ACTIVE;
boolean enableAllPrefs = syncthingActive && mSyncthingService.getApi().isConfigLoaded();
PreferenceScreen ps = getPreferenceScreen();
for (int i = 0; i < ps.getPreferenceCount(); i++) {
Preference p = ps.getPreference(i);
p.setEnabled(enableAllPrefs || "category_run_conditions".equals(p.getKey()));
}
if (!enableAllPrefs)
return;
mApi = mSyncthingService.getApi();
mSyncthingVersion.setSummary(mApi.getVersion());
mOptions = mApi.getOptions();
mGui = mApi.getGui();
Joiner joiner = Joiner.on(", ");
mDeviceName.setText(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) {
Splitter splitter = Splitter.on(",").trimResults().omitEmptyStrings();
switch (preference.getKey()) {
case "deviceName":
Device localDevice = mApi.getLocalDevice();
localDevice.name = (String) o;
mApi.editDevice(localDevice);
break;
case "listenAddresses":
mOptions.listenAddresses = Iterables.toArray(splitter.split((String) o), String.class);
break;
case "maxRecvKbps": mOptions.maxRecvKbps = (int) o; break;
case "maxSendKbps": mOptions.maxRecvKbps = (int) o; break;
case "natEnabled": mOptions.natEnabled = (boolean) o; break;
case "localAnnounceEnabled": mOptions.localAnnounceEnabled = (boolean) o; break;
case "globalAnnounceEnabled": mOptions.globalAnnounceEnabled = (boolean) o; break;
case "relaysEnabled": mOptions.relaysEnabled = (boolean) o; break;
case "globalAnnounceServers":
mOptions.globalAnnounceServers = Iterables.toArray(splitter.split((String) o), String.class);
break;
case "address": mGui.address = (String) o; break;
case "user": mGui.user = (String) o; break;
case "password": mGui.password = (String) o; break;
case "urAccepted":
mOptions.urAccepted = ((boolean) o)
? Options.USAGE_REPORTING_ACCEPTED
: Options.USAGE_REPORTING_DENIED;
break;
default: throw new InvalidParameterException();
}
mApi.editSettings(mGui, mOptions, getActivity());
return true;
}
public boolean onRequireRestart(Preference preference, Object o) {
mSyncthingService.getApi().showRestartDialog(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().showRestartDialog(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().showRestartDialog(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;
}
}
/**
* Enables or disables {@link #mUseRoot} preference depending whether root is available.
*/
private class TestRootTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
return Shell.SU.available();
}
@Override
protected void onPostExecute(Boolean haveRoot) {
if (haveRoot) {
mSyncthingService.getApi().showRestartDialog(getActivity());
mUseRoot.setChecked(true);
} else {
Toast.makeText(getActivity(), R.string.toast_root_denied, Toast.LENGTH_SHORT)
.show();
}
}
}
/**
* Changes the owner of syncthing files so they can be accessed without root.
*/
private class ChownFilesRunnable implements Runnable {
@Override
public void run() {
String f = getActivity().getFilesDir().getAbsolutePath();
List<String> out = Shell.SU.run("chown -R --reference=" + f + " " + f);
Log.i(TAG, "Changed owner of syncthing files, output: " + out);
}
}
}

View File

@ -14,7 +14,7 @@ import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.SettingsActivity;
import com.nutomic.syncthingandroid.activities.SyncthingSettingsActivity;
import com.nutomic.syncthingandroid.fragments.DeviceFragment;
import com.nutomic.syncthingandroid.fragments.FolderFragment;
import com.nutomic.syncthingandroid.model.Device;
@ -94,9 +94,9 @@ public class EventProcessor implements SyncthingService.OnWebGuiAvailableListene
String deviceId = (String) event.data.get("device");
Log.d(TAG, "Unknwon device " + deviceId + " wants to connect");
Intent intent = new Intent(mContext, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_DEVICE_SETTINGS)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true)
Intent intent = new Intent(mContext, SyncthingSettingsActivity.class)
.setAction(SyncthingSettingsActivity.ACTION_DEVICE_SETTINGS)
.putExtra(SyncthingSettingsActivity.EXTRA_IS_CREATE, true)
.putExtra(DeviceFragment.EXTRA_DEVICE_ID, deviceId);
// HACK: Use a random, deterministic ID to make multiple PendingIntents
// distinguishable
@ -114,9 +114,9 @@ public class EventProcessor implements SyncthingService.OnWebGuiAvailableListene
String folderLabel = (String) event.data.get("folderLabel");
Log.d(TAG, "Device " + deviceId + " wants to share folder " + folderId);
intent = new Intent(mContext, SettingsActivity.class)
.setAction(SettingsActivity.ACTION_FOLDER_SETTINGS)
.putExtra(SettingsActivity.EXTRA_IS_CREATE, true)
intent = new Intent(mContext, SyncthingSettingsActivity.class)
.setAction(SyncthingSettingsActivity.ACTION_FOLDER_SETTINGS)
.putExtra(SyncthingSettingsActivity.EXTRA_IS_CREATE, true)
.putExtra(FolderFragment.EXTRA_DEVICE_ID, deviceId)
.putExtra(FolderFragment.EXTRA_FOLDER_ID, folderId)
.putExtra(FolderFragment.EXTRA_FOLDER_LABEL, folderLabel);