diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 67e8d56..0684340 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,19 +8,23 @@ android { applicationId = "com.prangesoftwaresolutions.audioanchor" minSdk = 21 targetSdk = 35 - versionCode = 31 - versionName = "2.4.0" + versionCode = 32 + versionName = "2.5.0" } buildTypes { release { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") + resValue("string", "app_package_uri", + "package:" + android.defaultConfig.applicationId) } debug { applicationIdSuffix = ".debug" versionNameSuffix = "-DEBUG" isDebuggable = true + resValue("string", "app_package_uri", + "package:" + android.defaultConfig.applicationId + applicationIdSuffix) } } compileOptions { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d141796..8484820 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ android:maxSdkVersion="32" /> + diff --git a/app/src/main/java/com/prangesoftwaresolutions/audioanchor/activities/DirectoryActivity.java b/app/src/main/java/com/prangesoftwaresolutions/audioanchor/activities/DirectoryActivity.java index da2f57c..6b5ba81 100644 --- a/app/src/main/java/com/prangesoftwaresolutions/audioanchor/activities/DirectoryActivity.java +++ b/app/src/main/java/com/prangesoftwaresolutions/audioanchor/activities/DirectoryActivity.java @@ -4,6 +4,7 @@ import android.content.ContentUris; import android.content.CursorLoader; import android.content.Loader; +import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.Environment; @@ -19,6 +20,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import androidx.preference.PreferenceManager; import com.nambimobile.widgets.efab.FabOption; import com.prangesoftwaresolutions.audioanchor.R; @@ -135,7 +137,12 @@ public void onDestroyActionMode(ActionMode actionMode) { } private void addDirectory(boolean isParentDirectory) { - File baseDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC); + String baseDirectoryPref = PreferenceManager.getDefaultSharedPreferences(this).getString( + getString(R.string.settings_directory_picker_initial_path_key), getString(R.string.settings_directory_picker_initial_path_default)); + File baseDirectory = baseDirectoryPref.isBlank() + ? Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) + : new File(baseDirectoryPref); + FileDialog fileDialog = new FileDialog(this, baseDirectory, true, null, this); fileDialog.addDirectoryListener(directory -> { Directory.Type directoryType = isParentDirectory ? Directory.Type.PARENT_DIR : Directory.Type.SUB_DIR; @@ -144,6 +151,12 @@ private void addDirectory(boolean isParentDirectory) { mSynchronizer.addDirectory(newDirectory); } }); + fileDialog.addDefaultDirectoryListener(directory -> { + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); + editor.putString(getString(R.string.settings_directory_picker_initial_path_key), directory.getAbsolutePath()); + editor.apply(); + fileDialog.showDialog(); + }); fileDialog.showDialog(); } diff --git a/app/src/main/java/com/prangesoftwaresolutions/audioanchor/dialogs/FileDialog.java b/app/src/main/java/com/prangesoftwaresolutions/audioanchor/dialogs/FileDialog.java index a1ba1ca..8b92384 100644 --- a/app/src/main/java/com/prangesoftwaresolutions/audioanchor/dialogs/FileDialog.java +++ b/app/src/main/java/com/prangesoftwaresolutions/audioanchor/dialogs/FileDialog.java @@ -4,7 +4,10 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; +import android.os.Build; import android.os.Environment; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import androidx.annotation.NonNull; import android.util.Log; import android.view.View; @@ -47,6 +50,7 @@ public interface DirectorySelectedListener { private final ListenerList fileListenerList = new ListenerList<>(); private final ListenerList dirListenerList = new ListenerList<>(); + private final ListenerList defaultDirListenerList = new ListenerList<>(); private final Activity activity; private final boolean mSelectDirectory; private String fileEndsWith; @@ -56,9 +60,22 @@ public FileDialog(Activity activity, File initialPath, boolean selectDirectory, this.activity = activity; setFileEndsWith(fileEndsWith); mSelectDirectory = selectDirectory; + mContext = context; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + StorageManager storageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); + for (StorageVolume v: storageManager.getStorageVolumes()) { + File volumePath = v.getDirectory(); + if (volumePath != null && volumePath.canRead()) { + ensureReachabilityOfPath(volumePath); + } + } + } else { + ensureReachabilityOfPath(Environment.getExternalStorageDirectory()); + } + if (!initialPath.exists()) initialPath = Environment.getExternalStorageDirectory(); loadFileList(initialPath); - mContext = context; } /** @@ -86,6 +103,10 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { Log.d(TAG, currentPath.getPath()); fireDirectorySelectedEvent(currentPath); }); + builder.setNeutralButton(R.string.dialog_msg_set_default_dir, (dialog1, which) -> { + Log.d(TAG, currentPath.getPath()); + fireDefaultDirectorySelectedEvent(currentPath); + }); } builder.setNegativeButton(R.string.dialog_msg_cancel, (dialog1, which) -> { if (dialog1 != null) { @@ -131,6 +152,14 @@ public void removeDirectoryListener(DirectorySelectedListener listener) { dirListenerList.remove(listener); } + public void addDefaultDirectoryListener(DirectorySelectedListener listener) { + defaultDirListenerList.add(listener); + } + + public void removeDefaultDirectoryListener(DirectorySelectedListener listener) { + defaultDirListenerList.remove(listener); + } + /** * Show file dialog */ @@ -146,6 +175,10 @@ private void fireDirectorySelectedEvent(final File directory) { dirListenerList.fireEvent(listener -> listener.directorySelected(directory)); } + private void fireDefaultDirectorySelectedEvent(final File directory) { + defaultDirListenerList.fireEvent(listener -> listener.directorySelected(directory)); + } + private void loadFileList(File path) { this.currentPath = path; List fileList = new ArrayList<>(); @@ -200,6 +233,20 @@ private File getChosenFile(String fileChosen) { private void setFileEndsWith(String fileEndsWith) { this.fileEndsWith = fileEndsWith != null ? fileEndsWith.toLowerCase() : null; } + + private void ensureReachabilityOfPath(File path) { + Log.i("FileDialog.java", "Ensure reachability of " + path); + String filename = path.getName(); + File parent = path.getParentFile(); + while (parent != null) { + HashSet childDirs = childDirectories.get(parent.getAbsolutePath()); + if (childDirs == null) childDirs = new HashSet<>(); + childDirs.add(filename); + childDirectories.put(parent.getAbsolutePath(), childDirs); + filename = parent.getName(); + parent = parent.getParentFile(); + } + } } class ListenerList { diff --git a/app/src/main/res/values/donttranslate.xml b/app/src/main/res/values/donttranslate.xml index 26a380c..82e25b0 100644 --- a/app/src/main/res/values/donttranslate.xml +++ b/app/src/main/res/values/donttranslate.xml @@ -73,6 +73,9 @@ notification_backward_button notification_forward_button %d s + manage_external_storage + directory_picker_initial_path + album_id diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c2923a..265f8ba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,6 +100,9 @@ Notification backward button Notification forward button Skip interval for %s + Request \"All files access\" + Initial directory picker path + Title @@ -141,7 +144,8 @@ You can select a single directory that contains audio files or a parent directory that contains several directories containing audio files. - Select directory + Select + Set default Enter the number of minutes for which the player should keep playing OK Cancel diff --git a/app/src/main/res/xml-v26/settings.xml b/app/src/main/res/xml-v26/settings.xml index d53f027..561add7 100644 --- a/app/src/main/res/xml-v26/settings.xml +++ b/app/src/main/res/xml-v26/settings.xml @@ -178,6 +178,13 @@ android:key="@string/settings_keep_deleted_key" android:title="@string/settings_keep_deleted_label" android:singleLineTitle="false" /> + + diff --git a/app/src/main/res/xml-v30/settings.xml b/app/src/main/res/xml-v30/settings.xml new file mode 100644 index 0000000..e386898 --- /dev/null +++ b/app/src/main/res/xml-v30/settings.xml @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index aa6d92c..d285ddd 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -154,6 +154,12 @@ android:defaultValue="@string/settings_keep_deleted_default" android:key="@string/settings_keep_deleted_key" android:title="@string/settings_keep_deleted_label" /> + +