mirror of
https://github.com/syncthing/syncthing-android.git
synced 2025-01-07 10:42:07 +00:00
* Add pref: Suggested new folder root (fixes #309) * Fix NPE in offline config reader (fixes #316) after folder has been created while Syncthing was not running * Fix language pref and root pref * Fix action bar not showing on sub-prefs screen (fixes #317) on Android < 7 * Add DATA, MEDIA to FileUtils#getExternalFilesDirUri (fixes #309) * SettingsActivity: Remove pref "SuggestNewFolderRoot" on API < 21 * Update Constants PREF_SUGGEST_NEW_FOLDER_ROOT_DATA, PREF_SUGGEST_NEW_FOLDER_ROOT_MEDIA * Update FolderActivity#onPathViewClick (fixes #309) to respect PREF_SUGGEST_NEW_FOLDER_ROOT_DATA * Imported de translation
This commit is contained in:
parent
ca17291561
commit
30efd903b5
8 changed files with 128 additions and 35 deletions
|
@ -41,6 +41,7 @@ import com.nutomic.syncthingandroid.service.SyncthingServiceBinder;
|
|||
import com.nutomic.syncthingandroid.SyncthingApp;
|
||||
import com.nutomic.syncthingandroid.util.ConfigRouter;
|
||||
import com.nutomic.syncthingandroid.util.FileUtils;
|
||||
import com.nutomic.syncthingandroid.util.FileUtils.ExternalStorageDirType;
|
||||
import com.nutomic.syncthingandroid.util.TextWatcherAdapter;
|
||||
import com.nutomic.syncthingandroid.util.Util;
|
||||
|
||||
|
@ -293,13 +294,20 @@ public class FolderActivity extends SyncthingActivity {
|
|||
@SuppressLint("InlinedAPI")
|
||||
private void onPathViewClick() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
// API < 21
|
||||
startActivityForResult(FolderPickerActivity.createIntent(this, mFolder.path, null),
|
||||
FolderPickerActivity.DIRECTORY_REQUEST_CODE);
|
||||
return;
|
||||
}
|
||||
String prefSuggestNewFolderRoot = mPreferences.getString(Constants.PREF_SUGGEST_NEW_FOLDER_ROOT, Constants.PREF_SUGGEST_NEW_FOLDER_ROOT_DATA);
|
||||
|
||||
// This has to be android.net.Uri as it implements a Parcelable.
|
||||
android.net.Uri externalFilesDirUri = FileUtils.getExternalFilesDirUri(FolderActivity.this);
|
||||
android.net.Uri externalFilesDirUri = FileUtils.getExternalFilesDirUri(
|
||||
FolderActivity.this,
|
||||
prefSuggestNewFolderRoot.equals(Constants.PREF_SUGGEST_NEW_FOLDER_ROOT_DATA) ?
|
||||
ExternalStorageDirType.DATA :
|
||||
ExternalStorageDirType.MEDIA
|
||||
);
|
||||
|
||||
// Display storage access framework directory picker UI.
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
|
|
|
@ -154,7 +154,6 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
private Dialog mCurrentPrefScreenDialog = null;
|
||||
|
||||
private Preference mCategoryRunConditions;
|
||||
private CheckBoxPreference mStartServiceOnBoot;
|
||||
private ListPreference mPowerSource;
|
||||
private CheckBoxPreference mRunOnMobileData;
|
||||
private CheckBoxPreference mRunOnWifi;
|
||||
|
@ -163,6 +162,13 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
private WifiSsidPreference mWifiSsidWhitelist;
|
||||
private CheckBoxPreference mRunInFlightMode;
|
||||
|
||||
/* Behaviour */
|
||||
private CheckBoxPreference mStartServiceOnBoot;
|
||||
private CheckBoxPreference mUseRoot;
|
||||
private ListPreference mSuggestNewFolderRoot;
|
||||
private Languages mLanguages;
|
||||
|
||||
/* Syncthing Options */
|
||||
private Preference mCategorySyncthingOptions;
|
||||
private EditTextPreference mDeviceName;
|
||||
private EditTextPreference mListenAddresses;
|
||||
|
@ -179,7 +185,6 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
private CheckBoxPreference mUrAccepted;
|
||||
|
||||
/* Experimental options */
|
||||
private CheckBoxPreference mUseRoot;
|
||||
private CheckBoxPreference mUseWakelock;
|
||||
private CheckBoxPreference mUseTor;
|
||||
private EditTextPreference mSocksProxyAddress;
|
||||
|
@ -248,14 +253,10 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
categoryBehaviour.removePreference(languagePref);
|
||||
} else {
|
||||
Languages languages = new Languages(getActivity());
|
||||
languagePref.setDefaultValue(Languages.USE_SYSTEM_DEFAULT);
|
||||
languagePref.setEntries(languages.getAllNames());
|
||||
languagePref.setEntryValues(languages.getSupportedLocales());
|
||||
languagePref.setOnPreferenceChangeListener((p, o) -> {
|
||||
languages.forceChangeLanguage(getActivity(), (String) o);
|
||||
return false;
|
||||
});
|
||||
mLanguages = new Languages(getActivity());
|
||||
languagePref.setDefaultValue(mLanguages.USE_SYSTEM_DEFAULT);
|
||||
languagePref.setEntries(mLanguages.getAllNames());
|
||||
languagePref.setEntryValues(mLanguages.getSupportedLocales());
|
||||
}
|
||||
PreferenceScreen screen = getPreferenceScreen();
|
||||
|
||||
|
@ -294,6 +295,14 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
(CheckBoxPreference) findPreference(Constants.PREF_START_SERVICE_ON_BOOT);
|
||||
mUseRoot =
|
||||
(CheckBoxPreference) findPreference(Constants.PREF_USE_ROOT);
|
||||
mSuggestNewFolderRoot =
|
||||
(ListPreference) findPreference(Constants.PREF_SUGGEST_NEW_FOLDER_ROOT);
|
||||
screen.findPreference(Constants.PREF_SUGGEST_NEW_FOLDER_ROOT).setSummary(mSuggestNewFolderRoot.getEntry());
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
// Remove preference as FileUtils#getExternalFilesDirUri is only supported on API 21+ (LOLLIPOP+).
|
||||
categoryBehaviour.removePreference(mSuggestNewFolderRoot);
|
||||
}
|
||||
setPreferenceCategoryChangeListener(categoryBehaviour, this::onBehaviourPreferenceChange);
|
||||
|
||||
/* Syncthing options */
|
||||
mDeviceName = (EditTextPreference) findPreference("deviceName");
|
||||
|
@ -350,7 +359,6 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
mUseWakelock.setChecked(false);
|
||||
}
|
||||
|
||||
mUseRoot.setOnPreferenceClickListener(this);
|
||||
mUseWakelock.setOnPreferenceChangeListener(this);
|
||||
mUseTor.setOnPreferenceChangeListener(this);
|
||||
|
||||
|
@ -557,6 +565,27 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean onBehaviourPreferenceChange(Preference preference, Object o) {
|
||||
switch (preference.getKey()) {
|
||||
case Constants.PREF_USE_ROOT:
|
||||
if ((Boolean) o) {
|
||||
new TestRootTask(this).execute();
|
||||
} else {
|
||||
new Thread(() -> Util.fixAppDataPermissions(getActivity())).start();
|
||||
mPendingConfig = true;
|
||||
}
|
||||
break;
|
||||
case Constants.PREF_SUGGEST_NEW_FOLDER_ROOT:
|
||||
mSuggestNewFolderRoot.setValue(o.toString());
|
||||
preference.setSummary(mSuggestNewFolderRoot.getEntry());
|
||||
break;
|
||||
case Languages.PREFERENCE_LANGUAGE:
|
||||
mLanguages.forceChangeLanguage(getActivity(), (String) o);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onSyncthingPreferenceChange(Preference preference, Object o) {
|
||||
Splitter splitter = Splitter.on(",").trimResults().omitEmptyStrings();
|
||||
switch (preference.getKey()) {
|
||||
|
@ -710,16 +739,6 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
public boolean onPreferenceClick(Preference preference) {
|
||||
final Intent intent;
|
||||
switch (preference.getKey()) {
|
||||
case Constants.PREF_USE_ROOT:
|
||||
if (mUseRoot.isChecked()) {
|
||||
// Only check preference after root was granted.
|
||||
mUseRoot.setChecked(false);
|
||||
new TestRootTask(this).execute();
|
||||
} else {
|
||||
new Thread(() -> Util.fixAppDataPermissions(getActivity())).start();
|
||||
mPendingConfig = true;
|
||||
}
|
||||
return true;
|
||||
case KEY_OPEN_ISSUE_TRACKER:
|
||||
intent = new Intent(getActivity(), WebViewActivity.class);
|
||||
intent.putExtra(WebViewActivity.EXTRA_WEB_URL, getString(R.string.issue_tracker_url));
|
||||
|
@ -829,13 +848,15 @@ public class SettingsActivity extends SyncthingActivity {
|
|||
if (settingsFragment == null) {
|
||||
return;
|
||||
}
|
||||
settingsFragment.mUseRoot.setOnPreferenceChangeListener(null);
|
||||
settingsFragment.mUseRoot.setChecked(haveRoot);
|
||||
if (haveRoot) {
|
||||
settingsFragment.mPendingConfig = true;
|
||||
settingsFragment.mUseRoot.setChecked(true);
|
||||
} else {
|
||||
Toast.makeText(settingsFragment.getActivity(), R.string.toast_root_denied, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
settingsFragment.mUseRoot.setOnPreferenceChangeListener(settingsFragment::onBehaviourPreferenceChange);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,14 +27,23 @@ public class Constants {
|
|||
public static final String PREF_RUN_IN_FLIGHT_MODE = "run_in_flight_mode";
|
||||
|
||||
// Preferences - Behaviour
|
||||
public static final String PREF_USE_ROOT = "use_root";
|
||||
public static final String PREF_USE_ROOT = "use_root";
|
||||
|
||||
public static final String PREF_SUGGEST_NEW_FOLDER_ROOT = "suggest_new_folder_root";
|
||||
public static final String PREF_SUGGEST_NEW_FOLDER_ROOT_DATA = "external_android_data";
|
||||
public static final String PREF_SUGGEST_NEW_FOLDER_ROOT_MEDIA = "external_android_media";
|
||||
|
||||
|
||||
// Preferences - Troubleshooting
|
||||
public static final String PREF_ENVIRONMENT_VARIABLES = "environment_variables";
|
||||
public static final String PREF_DEBUG_FACILITIES_ENABLED = "debug_facilities_enabled";
|
||||
public static final String PREF_USE_WAKE_LOCK = "wakelock_while_binary_running";
|
||||
|
||||
// Preferences - Experimental
|
||||
public static final String PREF_USE_TOR = "use_tor";
|
||||
public static final String PREF_SOCKS_PROXY_ADDRESS = "socks_proxy_address";
|
||||
public static final String PREF_HTTP_PROXY_ADDRESS = "http_proxy_address";
|
||||
public static final String PREF_BROADCAST_SERVICE_CONTROL = "broadcast_service_control";
|
||||
public static final String PREF_USE_WAKE_LOCK = "wakelock_while_binary_running";
|
||||
|
||||
// Preferences - per Folder and Device Sync Conditions
|
||||
public static final String PREF_OBJECT_PREFIX_FOLDER = "sc_folder_";
|
||||
|
|
|
@ -47,6 +47,11 @@ public class FileUtils {
|
|||
private static final String PRIMARY_VOLUME_NAME = "primary";
|
||||
private static final String HOME_VOLUME_NAME = "home";
|
||||
|
||||
public enum ExternalStorageDirType {
|
||||
DATA,
|
||||
MEDIA
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@TargetApi(21)
|
||||
public static String getAbsolutePathFromSAFUri(Context context, @Nullable final Uri safResultUri) {
|
||||
|
@ -155,24 +160,37 @@ public class FileUtils {
|
|||
* has been blocked since Android 7+, we need to build the Uri
|
||||
* manually after discovering the first external storage.
|
||||
* This is crucial to assist the user finding a writeable folder
|
||||
* to use syncthing's two way sync feature.
|
||||
* to use Syncthing's two way sync feature.
|
||||
* API for getExternalFilesDirs(): 19+ (KITKAT+)
|
||||
* API for getExternalMediaDirs(): 21+ (LOLLIPOP+)
|
||||
*/
|
||||
@TargetApi(19)
|
||||
public static android.net.Uri getExternalFilesDirUri(Context context) {
|
||||
@TargetApi(21)
|
||||
public static android.net.Uri getExternalFilesDirUri(Context context, ExternalStorageDirType extDirType) {
|
||||
try {
|
||||
/**
|
||||
* Determine the app's private data folder on external storage if present.
|
||||
* e.g. "/storage/abcd-efgh/Android/[PACKAGE_NAME]/files"
|
||||
*/
|
||||
ArrayList<File> externalFilesDir = new ArrayList<>();
|
||||
externalFilesDir.addAll(Arrays.asList(context.getExternalFilesDirs(null)));
|
||||
externalFilesDir.remove(context.getExternalFilesDir(null));
|
||||
switch(extDirType ){
|
||||
case DATA:
|
||||
externalFilesDir.addAll(Arrays.asList(context.getExternalFilesDirs(null)));
|
||||
externalFilesDir.remove(context.getExternalFilesDir(null));
|
||||
break;
|
||||
case MEDIA:
|
||||
externalFilesDir.addAll(Arrays.asList(context.getExternalMediaDirs()));
|
||||
if (externalFilesDir.size() > 0) {
|
||||
externalFilesDir.remove(externalFilesDir.get(0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
externalFilesDir.remove(null); // getExternalFilesDirs may return null for an ejected SDcard.
|
||||
if (externalFilesDir.size() == 0) {
|
||||
Log.w(TAG, "Could not determine app's private files directory on external storage.");
|
||||
return null;
|
||||
}
|
||||
String absPath = externalFilesDir.get(0).getAbsolutePath();
|
||||
// Log.v(TAG, "getExternalFilesDirUri: absPath=" + absPath);
|
||||
String[] segments = absPath.split("/");
|
||||
if (segments.length < 2) {
|
||||
Log.w(TAG, "Could not extract volumeId from app's private files path '" + absPath + "'");
|
||||
|
@ -180,11 +198,20 @@ public class FileUtils {
|
|||
}
|
||||
// Extract the volumeId, e.g. "abcd-efgh"
|
||||
String volumeId = segments[2];
|
||||
// Build the content Uri for our private "files" folder.
|
||||
return android.net.Uri.parse(
|
||||
"content://com.android.externalstorage.documents/document/" +
|
||||
volumeId + "%3AAndroid%2Fdata%2F" +
|
||||
context.getPackageName() + "%2Ffiles");
|
||||
switch(extDirType ){
|
||||
case DATA:
|
||||
// Build the content Uri for our private ".../data/[PKG_NAME]/files" folder.
|
||||
return android.net.Uri.parse(
|
||||
"content://com.android.externalstorage.documents/document/" +
|
||||
volumeId + "%3AAndroid%2Fdata%2F" +
|
||||
context.getPackageName() + "%2Ffiles");
|
||||
case MEDIA:
|
||||
// Build the content Uri for our private ".../media/[PKG_NAME]" folder.
|
||||
return android.net.Uri.parse(
|
||||
"content://com.android.externalstorage.documents/document/" +
|
||||
volumeId + "%3AAndroid%2Fmedia%2F" +
|
||||
context.getPackageName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "getExternalFilesDirUri exception", e);
|
||||
}
|
||||
|
|
|
@ -439,6 +439,13 @@ Bitte melden Sie auftretende Probleme via GitHub.</string>
|
|||
|
||||
<string name="use_root_summary">Wenn Syncthing unter dem Root-Benutzer ausgeführt wird, hat es Schreibzugriff auf Ordner, die Android normalerweise auf schreibgeschützten Zugriff beschränkt. Verwende diese Funktion mit Bedacht.</string>
|
||||
|
||||
<string name="suggest_new_folder_root_title">Wähle Stamm für neue Ordner</string>
|
||||
|
||||
<string-array name="suggest_new_folder_root_entries">
|
||||
<item>[Ext_Speicher]/Android/data</item>
|
||||
<item>[Ext_Speicher]/Android/media</item>
|
||||
</string-array>
|
||||
|
||||
<string name="preference_language_title">Sprache</string>
|
||||
|
||||
<string name="preference_language_summary">Anwendungssprache ändern</string>
|
||||
|
|
|
@ -15,4 +15,10 @@
|
|||
<item>battery_power</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Preference screen -->
|
||||
<string-array name="suggest_new_folder_root_values">
|
||||
<item>external_android_data</item>
|
||||
<item>external_android_media</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -442,6 +442,13 @@ Please report any problems you encounter via Github.</string>
|
|||
|
||||
<string name="use_root_summary">Running Syncthing as root allows it to write to folders Android normally restricts to be readonly accessed. Use this feature cautious.</string>
|
||||
|
||||
<string name="suggest_new_folder_root_title">Select new folder root</string>
|
||||
|
||||
<string-array name="suggest_new_folder_root_entries">
|
||||
<item>[external_storage]/Android/data</item>
|
||||
<item>[external_storage]/Android/media</item>
|
||||
</string-array>
|
||||
|
||||
<string name="preference_language_title">Language</string>
|
||||
|
||||
<string name="preference_language_summary">Change the app language</string>
|
||||
|
|
|
@ -90,6 +90,14 @@
|
|||
android:summary="@string/use_root_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<ListPreference
|
||||
android:key="suggest_new_folder_root"
|
||||
android:title="@string/suggest_new_folder_root_title"
|
||||
android:entryValues="@array/suggest_new_folder_root_values"
|
||||
android:entries="@array/suggest_new_folder_root_entries"
|
||||
android:summary="@null"
|
||||
android:defaultValue="external_android_data" />
|
||||
|
||||
<ListPreference
|
||||
android:key="pref_current_language"
|
||||
android:title="@string/preference_language_title"
|
||||
|
|
Loading…
Reference in a new issue