diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 608ca22835..b22e23ad2a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -92,6 +92,13 @@ + + + + + + + diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java index 3d129a9fdc..9e775f2260 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/FileUtil.java @@ -110,157 +110,155 @@ public static final void writeUriToStorage( @NonNull final String currentPath) { MaybeOnSubscribe> writeUri = - (MaybeOnSubscribe>) - emitter -> { - List retval = new ArrayList<>(); - - for (Uri uri : uris) { - - BufferedInputStream bufferedInputStream = null; - try { - bufferedInputStream = - new BufferedInputStream(contentResolver.openInputStream(uri)); - } catch (FileNotFoundException e) { - emitter.onError(e); - return; - } - - BufferedOutputStream bufferedOutputStream = null; + emitter -> { + List retval = new ArrayList<>(); + + for (Uri uri : uris) { + + BufferedInputStream bufferedInputStream = null; + try { + bufferedInputStream = new BufferedInputStream(contentResolver.openInputStream(uri)); + } catch (FileNotFoundException e) { + emitter.onError(e); + return; + } + + BufferedOutputStream bufferedOutputStream = null; + + try { + DocumentFile documentFile = DocumentFile.fromSingleUri(mainActivity, uri); + String filename = documentFile.getName(); + if (filename == null) { + filename = uri.getLastPathSegment(); + + // For cleaning up slashes. Back in #1217 there is a case of + // Uri.getLastPathSegment() end up with a full file path + if (filename.contains("/")) + filename = filename.substring(filename.lastIndexOf('/') + 1); + } - try { - DocumentFile documentFile = DocumentFile.fromSingleUri(mainActivity, uri); - String filename = documentFile.getName(); - if (filename == null) { - filename = uri.getLastPathSegment(); + String finalFilePath = currentPath + "/" + filename; + DataUtils dataUtils = DataUtils.getInstance(); - // For cleaning up slashes. Back in #1217 there is a case of - // Uri.getLastPathSegment() end up with a full file path - if (filename.contains("/")) - filename = filename.substring(filename.lastIndexOf('/') + 1); - } + HybridFile hFile = new HybridFile(OpenMode.UNKNOWN, currentPath); + hFile.generateMode(mainActivity); - String finalFilePath = currentPath + "/" + filename; - DataUtils dataUtils = DataUtils.getInstance(); - - HybridFile hFile = new HybridFile(OpenMode.UNKNOWN, currentPath); - hFile.generateMode(mainActivity); - - switch (hFile.getMode()) { - case FILE: - case ROOT: - File targetFile = new File(finalFilePath); - if (!FileProperties.isWritableNormalOrSaf( - targetFile.getParentFile(), mainActivity.getApplicationContext())) { - emitter.onError(new NotAllowedException()); - return; - } - - DocumentFile targetDocumentFile = - ExternalSdCardOperation.getDocumentFile( - targetFile, false, mainActivity.getApplicationContext()); - - // Fallback, in case getDocumentFile() didn't properly return a - // DocumentFile - // instance - if (targetDocumentFile == null) { - targetDocumentFile = DocumentFile.fromFile(targetFile); - } - - // Lazy check... and in fact, different apps may pass in URI in different - // formats, so we could only check filename matches - // FIXME?: Prompt overwrite instead of simply blocking - if (targetDocumentFile.exists() && targetDocumentFile.length() > 0) { - emitter.onError(new OperationWouldOverwriteException()); - return; - } - - bufferedOutputStream = - new BufferedOutputStream( - contentResolver.openOutputStream(targetDocumentFile.getUri())); - retval.add(targetFile.getPath()); - break; - case SMB: - SmbFile targetSmbFile = SmbUtil.create(finalFilePath); - if (targetSmbFile.exists()) { - emitter.onError(new OperationWouldOverwriteException()); - return; - } else { - OutputStream outputStream = targetSmbFile.getOutputStream(); - bufferedOutputStream = new BufferedOutputStream(outputStream); - retval.add(HybridFile.parseAndFormatUriForDisplay(targetSmbFile.getPath())); - } - break; - case SFTP: - // FIXME: implement support - AppConfig.toast(mainActivity, mainActivity.getString(R.string.not_allowed)); - emitter.onError(new NotImplementedError()); - return; - case DROPBOX: - case BOX: - case ONEDRIVE: - case GDRIVE: - OpenMode mode = hFile.getMode(); - - CloudStorage cloudStorage = dataUtils.getAccount(mode); - String path = CloudUtil.stripPath(mode, finalFilePath); - cloudStorage.upload(path, bufferedInputStream, documentFile.length(), true); - retval.add(path); - break; - case OTG: - DocumentFile documentTargetFile = - OTGUtil.getDocumentFile(finalFilePath, mainActivity, true); - - if (documentTargetFile.exists()) { - emitter.onError(new OperationWouldOverwriteException()); - return; - } - - bufferedOutputStream = - new BufferedOutputStream( - contentResolver.openOutputStream(documentTargetFile.getUri()), - GenericCopyUtil.DEFAULT_BUFFER_SIZE); - - retval.add(documentTargetFile.getUri().getPath()); - break; - default: - return; + switch (hFile.getMode()) { + case FILE: + case ROOT: + File targetFile = new File(finalFilePath); + if (!FileProperties.isWritableNormalOrSaf( + targetFile.getParentFile(), mainActivity.getApplicationContext())) { + emitter.onError(new NotAllowedException()); + return; } - int count = 0; - byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; + DocumentFile targetDocumentFile = + ExternalSdCardOperation.getDocumentFile( + targetFile, false, mainActivity.getApplicationContext()); - while (count != -1) { - count = bufferedInputStream.read(buffer); - if (count != -1) { + // Fallback, in case getDocumentFile() didn't properly return a + // DocumentFile + // instance + if (targetDocumentFile == null) { + targetDocumentFile = DocumentFile.fromFile(targetFile); + } - bufferedOutputStream.write(buffer, 0, count); - } + // Lazy check... and in fact, different apps may pass in URI in different + // formats, so we could only check filename matches + // FIXME?: Prompt overwrite instead of simply blocking + if (targetDocumentFile.exists() && targetDocumentFile.length() > 0) { + emitter.onError(new OperationWouldOverwriteException()); + return; } - bufferedOutputStream.flush(); - } catch (IOException e) { - emitter.onError(e); + bufferedOutputStream = + new BufferedOutputStream( + contentResolver.openOutputStream(targetDocumentFile.getUri())); + retval.add(targetFile.getPath()); + break; + case SMB: + SmbFile targetSmbFile = SmbUtil.create(finalFilePath); + if (targetSmbFile.exists()) { + emitter.onError(new OperationWouldOverwriteException()); + return; + } else { + OutputStream outputStream = targetSmbFile.getOutputStream(); + bufferedOutputStream = new BufferedOutputStream(outputStream); + retval.add(HybridFile.parseAndFormatUriForDisplay(targetSmbFile.getPath())); + } + break; + case SFTP: + // FIXME: implement support + AppConfig.toast(mainActivity, mainActivity.getString(R.string.not_allowed)); + emitter.onError(new NotImplementedError()); return; - } finally { - try { - if (bufferedInputStream != null) { - bufferedInputStream.close(); - } - if (bufferedOutputStream != null) { - bufferedOutputStream.close(); - } - } catch (IOException e) { - emitter.onError(e); + case DROPBOX: + case BOX: + case ONEDRIVE: + case GDRIVE: + OpenMode mode = hFile.getMode(); + + CloudStorage cloudStorage = dataUtils.getAccount(mode); + String path = CloudUtil.stripPath(mode, finalFilePath); + cloudStorage.upload(path, bufferedInputStream, documentFile.length(), true); + retval.add(path); + break; + case OTG: + DocumentFile documentTargetFile = + OTGUtil.getDocumentFile(finalFilePath, mainActivity, true); + + if (documentTargetFile.exists()) { + emitter.onError(new OperationWouldOverwriteException()); + return; } - } + + bufferedOutputStream = + new BufferedOutputStream( + contentResolver.openOutputStream(documentTargetFile.getUri()), + GenericCopyUtil.DEFAULT_BUFFER_SIZE); + + retval.add(documentTargetFile.getUri().getPath()); + break; + default: + return; } - if (retval.size() > 0) { - emitter.onSuccess(retval); - } else { - emitter.onError(new Exception()); + int count = 0; + byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; + + while (count != -1) { + count = bufferedInputStream.read(buffer); + if (count != -1) { + + bufferedOutputStream.write(buffer, 0, count); + } + } + bufferedOutputStream.flush(); + + } catch (IOException e) { + emitter.onError(e); + return; + } finally { + try { + if (bufferedInputStream != null) { + bufferedInputStream.close(); + } + if (bufferedOutputStream != null) { + bufferedOutputStream.close(); + } + } catch (IOException e) { + emitter.onError(e); } - }; + } + } + + if (retval.size() > 0) { + emitter.onSuccess(retval); + } else { + emitter.onError(new Exception()); + } + }; Maybe.create(writeUri) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java index 9849515f1b..284d9dcb2d 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/MainActivity.java @@ -191,6 +191,7 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; +import androidx.appcompat.widget.AppCompatEditText; import androidx.arch.core.util.Function; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.fragment.app.Fragment; @@ -651,6 +652,16 @@ private void checkForExternalIntent(Intent intent) { ArrayList arrayList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); initFabToSave(arrayList); + // disable screen rotation just for convenience purpose + // TODO: Support screen rotation when saving a file + Utils.disableScreenRotation(this); + } else if (actionIntent.equals(Intent.ACTION_CREATE_DOCUMENT)) { + // FIXME: There may be DocumentsContract.EXTRA_INITIAL_URI in the Bundle, but we are ignoring + // it for now. We will provide the full path anyway + String filename = intent.getStringExtra(Intent.EXTRA_TITLE); + initFabToSaveWith(filename); + mReturnIntent = true; + // disable screen rotation just for convenience purpose // TODO: Support screen rotation when saving a file Utils.disableScreenRotation(this); @@ -667,6 +678,15 @@ private void initFabToSave(final ArrayList uris) { () -> saveExternalIntent(uris)); } + private void initFabToSaveWith(@NonNull final String filename) { + Utils.showThemedSnackbar( + this, + getString(R.string.select_save_location), + BaseTransientBottomBar.LENGTH_INDEFINITE, + R.string.save, + () -> createDocumentByIntent(filename)); + } + private void saveExternalIntent(final ArrayList uris) { executeWithMainFragment( mainFragment -> { @@ -724,6 +744,29 @@ private void saveExternalIntentExtras() { }); } + private void createDocumentByIntent(@NonNull String filename) { + MaterialDialog saveAsDialog = + GeneralDialogCreation.showNameDialog( + this, + mainActivity.getResources().getString(R.string.entername), + filename, + getString(R.string.intent_save_as), + mainActivity.getResources().getString(R.string.save), + mainActivity.getResources().getString(android.R.string.cancel), + null, + (dialog, which) -> { + AppCompatEditText textfield = + dialog.getCustomView().findViewById(R.id.singleedittext_input); + executeWithMainFragment( + mainFragment -> { + mainFragment.returnIntentResultForSaveFile(textfield.getText().toString()); + return null; + }); + }, + new MainActivityHelper.FilenameValidator(false)); + saveAsDialog.show(); + } + public void clearFabActionItems() { floatingActionButton.removeActionItemById(R.id.menu_new_folder); floatingActionButton.removeActionItemById(R.id.menu_new_file); @@ -1567,12 +1610,10 @@ protected void onActivityResult(int requestCode, int responseCode, Intent intent // After confirmation, update stored value of folder. // Persist access permissions. - if (SDK_INT >= KITKAT) { - getContentResolver() - .takePersistableUriPermission( - treeUri, - Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - } + getContentResolver() + .takePersistableUriPermission( + treeUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); executeWithMainFragment( mainFragment -> { @@ -2245,14 +2286,14 @@ this, getResources().getString(R.string.cloud_error_fdroid), Toast.LENGTH_LONG) args.putInt(ARGS_KEY_LOADER, service.ordinal()); // check if we already had done some work on the loader - Loader loader = getSupportLoaderManager().getLoader(REQUEST_CODE_CLOUD_LIST_KEY); + Loader loader = LoaderManager.getInstance(this).getLoader(REQUEST_CODE_CLOUD_LIST_KEY); if (loader != null && loader.isStarted()) { // making sure that loader is not started - getSupportLoaderManager().destroyLoader(REQUEST_CODE_CLOUD_LIST_KEY); + LoaderManager.getInstance(this).destroyLoader(REQUEST_CODE_CLOUD_LIST_KEY); } - getSupportLoaderManager().initLoader(REQUEST_CODE_CLOUD_LIST_KEY, args, this); + LoaderManager.getInstance(this).initLoader(REQUEST_CODE_CLOUD_LIST_KEY, args, this); } } catch (CloudPluginException e) { LOG.warn("failure when adding cloud plugin connections", e); @@ -2344,7 +2385,7 @@ this, getResources().getString(R.string.cloud_error_plugin), Toast.LENGTH_LONG) } @Override - public void onLoadFinished(Loader loader, final Cursor data) { + public void onLoadFinished(@NonNull Loader loader, final Cursor data) { if (data == null) { Toast.makeText( this, @@ -2376,7 +2417,7 @@ public void onLoadFinished(Loader loader, final Cursor data) { } @Override - public void onLoaderReset(Loader loader) { + public void onLoaderReset(@NonNull Loader loader) { // For passing code check } @@ -2421,67 +2462,62 @@ private void initLeftRightAndTopDragListeners(boolean destroy, boolean shouldInv */ @Override public void onFolderSelection(@NonNull FolderChooserDialog dialog, @NonNull File folder) { - switch (dialog.getTag()) { - case FtpServerFragment.TAG: - FtpServerFragment ftpServerFragment = (FtpServerFragment) getFragmentAtFrame(); - if (folder.exists() && folder.isDirectory()) { - if (FileUtils.isRunningAboveStorage(folder.getAbsolutePath())) { - if (!isRootExplorer()) { - AlertDialog.show( - this, - R.string.ftp_server_root_unavailable, - R.string.error, - android.R.string.ok, - null, - false); - } else { - MaterialDialog confirmDialog = - GeneralDialogCreation.showBasicDialog( - this, - R.string.ftp_server_root_filesystem_warning, - R.string.warning, - android.R.string.ok, - android.R.string.cancel); - confirmDialog - .getActionButton(DialogAction.POSITIVE) - .setOnClickListener( - v -> { - ftpServerFragment.changeFTPServerPath(folder.getPath()); - Toast.makeText(this, R.string.ftp_path_change_success, Toast.LENGTH_SHORT) - .show(); - confirmDialog.dismiss(); - }); - confirmDialog - .getActionButton(DialogAction.NEGATIVE) - .setOnClickListener(v -> confirmDialog.dismiss()); - confirmDialog.show(); - } + if (FtpServerFragment.TAG.equals(dialog.getTag())) { + FtpServerFragment ftpServerFragment = (FtpServerFragment) getFragmentAtFrame(); + if (folder.exists() && folder.isDirectory()) { + if (FileUtils.isRunningAboveStorage(folder.getAbsolutePath())) { + if (!isRootExplorer()) { + AlertDialog.show( + this, + R.string.ftp_server_root_unavailable, + R.string.error, + android.R.string.ok, + null, + false); } else { - ftpServerFragment.changeFTPServerPath(folder.getPath()); - Toast.makeText(this, R.string.ftp_path_change_success, Toast.LENGTH_SHORT).show(); + MaterialDialog confirmDialog = + GeneralDialogCreation.showBasicDialog( + this, + R.string.ftp_server_root_filesystem_warning, + R.string.warning, + android.R.string.ok, + android.R.string.cancel); + confirmDialog + .getActionButton(DialogAction.POSITIVE) + .setOnClickListener( + v -> { + ftpServerFragment.changeFTPServerPath(folder.getPath()); + Toast.makeText(this, R.string.ftp_path_change_success, Toast.LENGTH_SHORT) + .show(); + confirmDialog.dismiss(); + }); + confirmDialog + .getActionButton(DialogAction.NEGATIVE) + .setOnClickListener(v -> confirmDialog.dismiss()); + confirmDialog.show(); } } else { - // try to get parent - String pathParentFilePath = folder.getParent(); - if (pathParentFilePath == null) { - dialog.dismiss(); - return; - } - File pathParentFile = new File(pathParentFilePath); - if (pathParentFile.exists() && pathParentFile.isDirectory()) { - ftpServerFragment.changeFTPServerPath(pathParentFile.getPath()); - Toast.makeText(this, R.string.ftp_path_change_success, Toast.LENGTH_SHORT).show(); - } else { - // don't have access, print error - Toast.makeText(this, R.string.ftp_path_change_error_invalid, Toast.LENGTH_SHORT).show(); - } + ftpServerFragment.changeFTPServerPath(folder.getPath()); + Toast.makeText(this, R.string.ftp_path_change_success, Toast.LENGTH_SHORT).show(); } - dialog.dismiss(); - break; - default: - dialog.dismiss(); - break; + } else { + // try to get parent + String pathParentFilePath = folder.getParent(); + if (pathParentFilePath == null) { + dialog.dismiss(); + return; + } + File pathParentFile = new File(pathParentFilePath); + if (pathParentFile.exists() && pathParentFile.isDirectory()) { + ftpServerFragment.changeFTPServerPath(pathParentFile.getPath()); + Toast.makeText(this, R.string.ftp_path_change_success, Toast.LENGTH_SHORT).show(); + } else { + // don't have access, print error + Toast.makeText(this, R.string.ftp_path_change_error_invalid, Toast.LENGTH_SHORT).show(); + } + } } + dialog.dismiss(); } /** @@ -2521,7 +2557,6 @@ private void executeWithMainFragment(@NonNull Function lambd executeWithMainFragment(lambda, false); } - @Nullable private void executeWithMainFragment( @NonNull Function lambda, boolean showToastIfMainFragmentIsNull) { final MainFragment mainFragment = getCurrentMainFragment(); diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java index c8a11f114b..94e9e2c7c0 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/MainFragment.java @@ -41,6 +41,7 @@ import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; +import com.amaze.filemanager.BuildConfig; import com.amaze.filemanager.R; import com.amaze.filemanager.adapters.RecyclerAdapter; import com.amaze.filemanager.adapters.data.LayoutElementParcelable; @@ -86,6 +87,7 @@ import com.amaze.filemanager.utils.Utils; import com.google.android.material.appbar.AppBarLayout; +import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -119,7 +121,6 @@ import androidx.core.content.pm.ShortcutManagerCompat; import androidx.core.graphics.drawable.IconCompat; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.ViewModelProvider; import androidx.preference.PreferenceManager; @@ -553,8 +554,23 @@ public void returnIntentResults(HybridFileParcelable baseFile) { LOG.debug("pickup file"); intent.setDataAndType(mediaStoreUri, MimeTypes.getExtension(baseFile.getPath())); } - getActivity().setResult(FragmentActivity.RESULT_OK, intent); - getActivity().finish(); + requireActivity().setResult(Activity.RESULT_OK, intent); + requireActivity().finish(); + } + + public void returnIntentResultForSaveFile(@NonNull String filename) { + requireMainActivity().mReturnIntent = false; + Uri uri = Utils.getUriForLocalFile(requireContext(), new File(getCurrentPath(), filename)); + requireContext() + .grantUriPermission( + BuildConfig.APPLICATION_ID, + uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + intent.setData(uri); + requireActivity().setResult(Activity.RESULT_OK, intent); + requireActivity().finish(); } LoadFilesListTask loadFilesListTask; diff --git a/app/src/main/java/com/amaze/filemanager/utils/MainActivityActionMode.kt b/app/src/main/java/com/amaze/filemanager/utils/MainActivityActionMode.kt index e84b8f3034..92d8703062 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/MainActivityActionMode.kt +++ b/app/src/main/java/com/amaze/filemanager/utils/MainActivityActionMode.kt @@ -24,6 +24,7 @@ import android.content.Intent import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Build +import android.os.Build.VERSION_CODES import android.view.Menu import android.view.MenuItem import android.view.View @@ -85,8 +86,13 @@ class MainActivityActionMode(private val mainActivityReference: WeakReference= Build.VERSION_CODES.JELLY_BEAN - ) { + if (mainActivity.mReturnIntent) { showOption(R.id.openmulti, menu) } // tv.setText(checkedItems.size()); @@ -154,14 +162,14 @@ class MainActivityActionMode(private val mainActivityReference: WeakReference= 16) showOption( + showOption( R.id.openmulti, menu ) } } else { showOption(R.id.share, menu) - if (mainActivity.mReturnIntent && Build.VERSION.SDK_INT >= 16) { + if (mainActivity.mReturnIntent) { showOption( R.id.openmulti, menu @@ -188,15 +196,13 @@ class MainActivityActionMode(private val mainActivityReference: WeakReference= Build.VERSION_CODES.JELLY_BEAN - ) { + if (mainActivity.mReturnIntent) { showOption(R.id.openmulti, menu) } } else { hideOption(R.id.openparent, menu) hideOption(R.id.addshortcut, menu) - if (mainActivity.mReturnIntent && Build.VERSION.SDK_INT >= 16) { + if (mainActivity.mReturnIntent) { showOption( R.id.openmulti, menu @@ -277,17 +283,20 @@ class MainActivityActionMode(private val mainActivityReference: WeakReference { - if (checkedItems.size > 100) Toast.makeText( - mainActivity, - mainActivity.resources.getString(R.string.share_limit), - Toast.LENGTH_SHORT - ) - .show() else { + if (checkedItems.size > 100) { + Toast.makeText( + mainActivity, + mainActivity.resources.getString(R.string.share_limit), + Toast.LENGTH_SHORT + ) + .show() + } else { mainActivity.currentMainFragment?.mainFragmentViewModel?.also { mainFragmentViewModel -> when (checkedItems[0].mode) { OpenMode.DROPBOX, OpenMode.BOX, OpenMode.GDRIVE, - OpenMode.ONEDRIVE -> + OpenMode.ONEDRIVE + -> FileUtils.shareCloudFiles( checkedItems, checkedItems[0].mode, @@ -372,8 +381,11 @@ class MainActivityActionMode(private val mainActivityReference: WeakReference if (!mainFragmentViewModel.results) { adapter.toggleChecked(false, mainFragmentViewModel.currentPath) - } else adapter.toggleChecked(false) + } else { + adapter.toggleChecked(false) + } mainActivity .updateViews( ColorDrawable( if (MainActivity.currentTab == 1) { mainFragmentViewModel.primaryTwoColor - } else mainFragmentViewModel.primaryColor + } else { + mainFragmentViewModel.primaryColor + } ) ) } diff --git a/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java b/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java index 7a927ec5c1..ccccf7e37c 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java +++ b/app/src/main/java/com/amaze/filemanager/utils/MainActivityHelper.java @@ -213,37 +213,7 @@ public void mkfile(final OpenMode openMode, final String path, final MainFragmen ma); dialog.dismiss(); }, - (text) -> { - boolean isValidFilename = FileProperties.isValidFilename(text); - - // The redundant equalsIgnoreCase() is needed since ".txt" itself does not end with .txt - // (i.e. recommended as ".txt.txt" - if (text.length() > 0) { - if (!isValidFilename || text.startsWith(" ")) { - return new WarnableTextInputValidator.ReturnState( - WarnableTextInputValidator.ReturnState.STATE_ERROR, R.string.invalid_name); - } else { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mainActivity); - if (text.startsWith(".") - && !prefs.getBoolean(PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES, false)) { - return new WarnableTextInputValidator.ReturnState( - WarnableTextInputValidator.ReturnState.STATE_WARNING, - R.string.create_hidden_file_warn); - } else if (!text.toLowerCase() - .endsWith( - AppConstants.NEW_FILE_DELIMITER.concat( - AppConstants.NEW_FILE_EXTENSION_TXT))) { - return new WarnableTextInputValidator.ReturnState( - WarnableTextInputValidator.ReturnState.STATE_WARNING, - R.string.create_file_suggest_txt_extension); - } - } - } else { - return new WarnableTextInputValidator.ReturnState( - WarnableTextInputValidator.ReturnState.STATE_ERROR, R.string.field_empty); - } - return new WarnableTextInputValidator.ReturnState(); - }); + new FilenameValidator()); } private void mk( @@ -826,4 +796,52 @@ public static void addSearchFragment( fragment.setArguments(args); fragmentManager.beginTransaction().add(fragment, MainActivity.TAG_ASYNC_HELPER).commit(); } + + public static class FilenameValidator implements WarnableTextInputValidator.OnTextValidate { + + private boolean recommendTxtExtension; + + public FilenameValidator() { + this(true); + } + + public FilenameValidator(boolean recommendTxtExtension) { + this.recommendTxtExtension = recommendTxtExtension; + } + + @Override + public WarnableTextInputValidator.ReturnState isTextValid(String text) { + boolean isValidFilename = FileProperties.isValidFilename(text); + + // The redundant equalsIgnoreCase() is needed since ".txt" itself does not end with .txt + // (i.e. recommended as ".txt.txt" + if (text.length() > 0) { + if (!isValidFilename || text.startsWith(" ")) { + return new WarnableTextInputValidator.ReturnState( + WarnableTextInputValidator.ReturnState.STATE_ERROR, R.string.invalid_name); + } else { + SharedPreferences prefs = + PreferenceManager.getDefaultSharedPreferences(AppConfig.getInstance()); + if (text.startsWith(".") + && !prefs.getBoolean(PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES, false)) { + return new WarnableTextInputValidator.ReturnState( + WarnableTextInputValidator.ReturnState.STATE_WARNING, + R.string.create_hidden_file_warn); + } else if (recommendTxtExtension + && !text.toLowerCase() + .endsWith( + AppConstants.NEW_FILE_DELIMITER.concat( + AppConstants.NEW_FILE_EXTENSION_TXT))) { + return new WarnableTextInputValidator.ReturnState( + WarnableTextInputValidator.ReturnState.STATE_WARNING, + R.string.create_file_suggest_txt_extension); + } + } + } else { + return new WarnableTextInputValidator.ReturnState( + WarnableTextInputValidator.ReturnState.STATE_ERROR, R.string.field_empty); + } + return new WarnableTextInputValidator.ReturnState(); + } + } } diff --git a/app/src/main/java/com/amaze/filemanager/utils/Utils.java b/app/src/main/java/com/amaze/filemanager/utils/Utils.java index ea6a951fcd..c778ad65d8 100644 --- a/app/src/main/java/com/amaze/filemanager/utils/Utils.java +++ b/app/src/main/java/com/amaze/filemanager/utils/Utils.java @@ -239,18 +239,25 @@ private static String sanitizeInputOnce(String input) { .replaceAll(INPUT_INTENT_BLACKLIST_COLON, ""); } - /** Returns uri associated to specific basefile */ + public static Uri getUriForLocalFile(@NonNull Context context, @NonNull File localFile) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return FileProvider.getUriForFile(context, context.getPackageName(), localFile); + } else { + return Uri.fromFile(localFile); + } + } + + /** + * Returns uri associated to specific basefile + * + *

FIXME: what if the basefile's path is not local path? + */ public static Uri getUriForBaseFile( @NonNull Context context, @NonNull HybridFileParcelable baseFile) { switch (baseFile.getMode()) { case FILE: case ROOT: - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - return FileProvider.getUriForFile( - context, context.getPackageName(), new File(baseFile.getPath())); - } else { - return Uri.fromFile(new File(baseFile.getPath())); - } + return getUriForLocalFile(context, new File(baseFile.getPath())); case OTG: return OTGUtil.getDocumentFile(baseFile.getPath(), context, true).getUri(); case SMB: