1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2025-01-07 10:42:07 +00:00

Allow changing suggested path for new folder (fixes #309) (#318)

* 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:
Catfriend1 2019-02-12 00:24:26 +01:00 committed by GitHub
parent ca17291561
commit 30efd903b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 128 additions and 35 deletions

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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_";

View file

@ -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);
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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"