From b9b55a14432c8403e5290c25234df152126640dd Mon Sep 17 00:00:00 2001 From: Nicolas Nattis Date: Tue, 25 Jan 2022 22:02:07 -0300 Subject: [PATCH] Overhauls the settings screen --- .../filesystem/files/CryptUtil.java | 1082 +++++++++-------- .../ui/activities/PreferencesActivity.java | 330 ----- .../ui/activities/PreferencesActivity.kt | 158 +++ .../superclasses/PreferenceActivity.java | 4 +- .../superclasses/ThemedActivity.java | 24 - .../ui/dialogs/ColorPickerDialog.java | 12 + .../AppearancePrefsFragment.kt | 121 ++ ...ncedSearchPref.kt => BasePrefsFragment.kt} | 32 +- .../BehaviorPrefsFragment.kt | 70 ++ .../BookmarksPrefsFragment.kt | 239 ++++ .../preference_fragments/ColorPref.kt | 360 ------ .../ColorPrefsFragment.kt | 161 +++ .../preference_fragments/FoldersPref.kt | 246 ---- .../preference_fragments/PrefFrag.java | 441 ------- .../PreferencesConstants.java | 115 -- .../PreferencesConstants.kt | 99 ++ .../preference_fragments/PrefsFragment.kt | 83 ++ ...sPref.kt => QuickAccessesPrefsFragment.kt} | 45 +- .../SecurityPrefsFragment.kt | 178 +++ .../preference_fragments/UiPrefsFragment.kt | 79 ++ .../filemanager/ui/views/drawer/Drawer.java | 4 +- .../InvalidablePreferenceCategory.kt | 51 - .../views/preference/PathSwitchPreference.kt | 25 +- .../preference/SelectedColorsPreference.kt | 9 +- .../main/res/drawable/ic_baseline_add_24.xml | 10 + .../drawable/ic_baseline_brush_white_24.xml | 9 + .../drawable/ic_baseline_info_white_24.xml | 9 + .../drawable/ic_baseline_lock_white_24.xml | 9 + .../ic_baseline_settings_white_24.xml | 5 + .../ic_baseline_vertical_split_white_24.xml | 9 + .../res/drawable/ic_preference_appearance.xml | 14 + .../res/drawable/ic_preference_behavior.xml | 14 + .../main/res/drawable/ic_preference_info.xml | 14 + .../res/drawable/ic_preference_security.xml | 14 + .../main/res/drawable/ic_preference_ui.xml | 14 + ...prefsfrag.xml => activity_preferences.xml} | 4 +- app/src/main/res/values/colors.xml | 7 + app/src/main/res/values/strings.xml | 7 + app/src/main/res/xml/advancedsearch_prefs.xml | 13 - app/src/main/res/xml/appearance_prefs.xml | 50 + app/src/main/res/xml/behavior_prefs.xml | 65 + app/src/main/res/xml/bookmarks_prefs.xml | 18 + app/src/main/res/xml/color_prefs.xml | 34 +- app/src/main/res/xml/conficolor_prefs.xml | 26 - app/src/main/res/xml/fastaccess_prefs.xml | 34 - app/src/main/res/xml/folders_prefs.xml | 6 - app/src/main/res/xml/preferences.xml | 204 +--- app/src/main/res/xml/quickaccess_prefs.xml | 44 + app/src/main/res/xml/security_prefs.xml | 17 + app/src/main/res/xml/ui_prefs.xml | 50 + 50 files changed, 2250 insertions(+), 2418 deletions(-) delete mode 100644 app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.java create mode 100644 app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.kt create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/AppearancePrefsFragment.kt rename app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/{AdvancedSearchPref.kt => BasePrefsFragment.kt} (55%) create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BehaviorPrefsFragment.kt create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BookmarksPrefsFragment.kt delete mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPref.kt create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPrefsFragment.kt delete mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.kt delete mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefFrag.java delete mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.java create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefsFragment.kt rename app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/{QuickAccessPref.kt => QuickAccessesPrefsFragment.kt} (52%) create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/SecurityPrefsFragment.kt create mode 100644 app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/UiPrefsFragment.kt delete mode 100644 app/src/main/java/com/amaze/filemanager/ui/views/preference/InvalidablePreferenceCategory.kt create mode 100644 app/src/main/res/drawable/ic_baseline_add_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_brush_white_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_info_white_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_lock_white_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_settings_white_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_vertical_split_white_24.xml create mode 100644 app/src/main/res/drawable/ic_preference_appearance.xml create mode 100644 app/src/main/res/drawable/ic_preference_behavior.xml create mode 100644 app/src/main/res/drawable/ic_preference_info.xml create mode 100644 app/src/main/res/drawable/ic_preference_security.xml create mode 100644 app/src/main/res/drawable/ic_preference_ui.xml rename app/src/main/res/layout/{prefsfrag.xml => activity_preferences.xml} (94%) delete mode 100644 app/src/main/res/xml/advancedsearch_prefs.xml create mode 100644 app/src/main/res/xml/appearance_prefs.xml create mode 100644 app/src/main/res/xml/behavior_prefs.xml create mode 100644 app/src/main/res/xml/bookmarks_prefs.xml delete mode 100644 app/src/main/res/xml/conficolor_prefs.xml delete mode 100644 app/src/main/res/xml/fastaccess_prefs.xml delete mode 100644 app/src/main/res/xml/folders_prefs.xml create mode 100644 app/src/main/res/xml/quickaccess_prefs.xml create mode 100644 app/src/main/res/xml/security_prefs.xml create mode 100644 app/src/main/res/xml/ui_prefs.xml diff --git a/app/src/main/java/com/amaze/filemanager/filesystem/files/CryptUtil.java b/app/src/main/java/com/amaze/filemanager/filesystem/files/CryptUtil.java index a4798de2ae..d32f9b18b8 100644 --- a/app/src/main/java/com/amaze/filemanager/filesystem/files/CryptUtil.java +++ b/app/src/main/java/com/amaze/filemanager/filesystem/files/CryptUtil.java @@ -49,8 +49,8 @@ import com.amaze.filemanager.filesystem.HybridFile; import com.amaze.filemanager.filesystem.HybridFileParcelable; import com.amaze.filemanager.filesystem.MakeDirectoryOperation; -import com.amaze.filemanager.ui.fragments.preference_fragments.PrefFrag; import com.amaze.filemanager.utils.ProgressHandler; +import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants; import android.content.Context; import android.content.SharedPreferences; @@ -74,8 +74,8 @@ * service then calls the constructor which fires up the subsequent encryption/decryption process. * *

We differentiate between already encrypted files from new ones by encrypting the - * plaintext {@link PrefFrag#ENCRYPT_PASSWORD_MASTER} and {@link - * PrefFrag#ENCRYPT_PASSWORD_FINGERPRINT} against the path in database. At the time of decryption, + * plaintext {@link PreferencesConstants#ENCRYPT_PASSWORD_MASTER} and {@link + * PreferencesConstants#ENCRYPT_PASSWORD_FINGERPRINT} against the path in database. At the time of decryption, * we check for these values and either retrieve master password from preferences or fire up the * fingerprint sensor authentication. * @@ -90,593 +90,613 @@ */ public class CryptUtil { - private static final String ALGO_AES = "AES/GCM/NoPadding"; - private static final String ALGO_RSA = "RSA/ECB/PKCS1Padding"; - private static final String KEY_STORE_ANDROID = "AndroidKeyStore"; - private static final String KEY_ALIAS_AMAZE = "AmazeKey"; - private static final String PREFERENCE_KEY = "aes_key"; - // TODO: Generate a random IV every time, and keep track of it (in database against encrypted - // files) - private static final String IV = - BuildConfig.CRYPTO_IV; // 12 byte long IV supported by android for GCM - - public static final String CRYPT_EXTENSION = ".aze"; - - private ProgressHandler progressHandler; - private ArrayList failedOps; - - /** - * Constructor will start encryption process serially. Make sure to call with background thread. - * The result file of encryption will be in the same directory with a {@link #CRYPT_EXTENSION} - * extension - * - *

Make sure you're done with encrypting password for this file and map it with this file in - * database - * - *

Be sure to use constructors to encrypt/decrypt files only, and to call service through - * {@link ServiceWatcherUtil} and to initialize watchers beforehand - * - * @param sourceFile the file to encrypt - */ - public CryptUtil( - Context context, - HybridFileParcelable sourceFile, - ProgressHandler progressHandler, - ArrayList failedOps, - String targetFilename) - throws GeneralSecurityException, IOException { - - this.progressHandler = progressHandler; - this.failedOps = failedOps; - - // target encrypted file - HybridFile hFile = new HybridFile(sourceFile.getMode(), sourceFile.getParent(context)); - encrypt(context, sourceFile, hFile, targetFilename); - } - - /** - * Decrypt the file in specified path. Can be used to open the file (decrypt in cache) or simply - * decrypt the file in the same (or in a custom preference) directory Make sure to decrypt and - * check user provided passwords beforehand from database - * - *

Be sure to use constructors to encrypt/decrypt files only, and to call service through - * {@link ServiceWatcherUtil} and to initialize watchers beforehand - * - * @param baseFile the encrypted file - * @param targetPath the directory in which file is to be decrypted the source's parent in normal - * case - */ - public CryptUtil( - Context context, - HybridFileParcelable baseFile, - String targetPath, - ProgressHandler progressHandler, - ArrayList failedOps) - throws GeneralSecurityException, IOException { - - this.progressHandler = progressHandler; - this.failedOps = failedOps; - - HybridFile targetDirectory = new HybridFile(OpenMode.FILE, targetPath); - if (!targetPath.equals(context.getExternalCacheDir())) { - - // same file system as of base file - targetDirectory.setMode(baseFile.getMode()); + private static final String ALGO_AES = "AES/GCM/NoPadding"; + private static final String ALGO_RSA = "RSA/ECB/PKCS1Padding"; + private static final String KEY_STORE_ANDROID = "AndroidKeyStore"; + private static final String KEY_ALIAS_AMAZE = "AmazeKey"; + private static final String PREFERENCE_KEY = "aes_key"; + // TODO: Generate a random IV every time, and keep track of it (in database against encrypted + // files) + private static final String IV = + BuildConfig.CRYPTO_IV; // 12 byte long IV supported by android for GCM + + public static final String CRYPT_EXTENSION = ".aze"; + + private ProgressHandler progressHandler; + private ArrayList failedOps; + + /** + * Constructor will start encryption process serially. Make sure to call with background thread. + * The result file of encryption will be in the same directory with a {@link #CRYPT_EXTENSION} + * extension + * + *

Make sure you're done with encrypting password for this file and map it with this file in + * database + * + *

Be sure to use constructors to encrypt/decrypt files only, and to call service through + * {@link ServiceWatcherUtil} and to initialize watchers beforehand + * + * @param sourceFile the file to encrypt + */ + public CryptUtil( + Context context, + HybridFileParcelable sourceFile, + ProgressHandler progressHandler, + ArrayList failedOps, + String targetFilename) + throws GeneralSecurityException, IOException { + + this.progressHandler = progressHandler; + this.failedOps = failedOps; + + // target encrypted file + HybridFile hFile = new HybridFile(sourceFile.getMode(), sourceFile.getParent(context)); + encrypt(context, sourceFile, hFile, targetFilename); } - decrypt(context, baseFile, targetDirectory); - } - - /** - * Wrapper around handling decryption for directory tree - * - * @param sourceFile the source file to decrypt - * @param targetDirectory the target directory inside which we're going to decrypt - */ - private void decrypt( - final Context context, HybridFileParcelable sourceFile, HybridFile targetDirectory) - throws GeneralSecurityException, IOException { - if (progressHandler.getCancelled()) return; - if (sourceFile.isDirectory()) { - - final HybridFile hFile = - new HybridFile( - targetDirectory.getMode(), - targetDirectory.getPath(), - sourceFile.getName(context).replace(CRYPT_EXTENSION, ""), - sourceFile.isDirectory()); - MakeDirectoryOperation.mkdirs(context, hFile); - - sourceFile.forEachChildrenFile( - context, - sourceFile.isRoot(), - file -> { - try { - decrypt(context, file, hFile); - } catch (IOException | GeneralSecurityException e) { - throw new IllegalStateException(e); // throw unchecked exception, no throws needed - } - }); - } else { - - if (!sourceFile.getPath().endsWith(CRYPT_EXTENSION)) { - failedOps.add(sourceFile); - return; - } - - BufferedInputStream inputStream = - new BufferedInputStream( - sourceFile.getInputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - - HybridFile targetFile = - new HybridFile( - targetDirectory.getMode(), - targetDirectory.getPath(), - sourceFile.getName(context).replace(CRYPT_EXTENSION, ""), - sourceFile.isDirectory()); - - progressHandler.setFileName(sourceFile.getName(context)); - - BufferedOutputStream outputStream = - new BufferedOutputStream( - targetFile.getOutputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - aesDecrypt(inputStream, outputStream); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - rsaDecrypt(context, inputStream, outputStream); - } + /** + * Decrypt the file in specified path. Can be used to open the file (decrypt in cache) or simply + * decrypt the file in the same (or in a custom preference) directory Make sure to decrypt and + * check user provided passwords beforehand from database + * + *

Be sure to use constructors to encrypt/decrypt files only, and to call service through + * {@link ServiceWatcherUtil} and to initialize watchers beforehand + * + * @param baseFile the encrypted file + * @param targetPath the directory in which file is to be decrypted the source's parent in normal + * case + */ + public CryptUtil( + Context context, + HybridFileParcelable baseFile, + String targetPath, + ProgressHandler progressHandler, + ArrayList failedOps) + throws GeneralSecurityException, IOException { + + this.progressHandler = progressHandler; + this.failedOps = failedOps; + + HybridFile targetDirectory = new HybridFile(OpenMode.FILE, targetPath); + if (!targetPath.equals(context.getExternalCacheDir())) { + + // same file system as of base file + targetDirectory.setMode(baseFile.getMode()); + } + + decrypt(context, baseFile, targetDirectory); } - } - - /** - * Wrapper around handling encryption in directory tree - * - * @param sourceFile the source file to encrypt - * @param targetDirectory the target directory in which we're going to encrypt - */ - private void encrypt( - final Context context, - HybridFileParcelable sourceFile, - HybridFile targetDirectory, - String targetFilename) - throws GeneralSecurityException, IOException { - - if (progressHandler.getCancelled()) return; - if (sourceFile.isDirectory()) { - - // succeed #CRYPT_EXTENSION at end of directory/file name - final HybridFile hFile = - new HybridFile( - targetDirectory.getMode(), - targetDirectory.getPath(), - targetFilename, - sourceFile.isDirectory()); - MakeDirectoryOperation.mkdirs(context, hFile); - - sourceFile.forEachChildrenFile( - context, - sourceFile.isRoot(), - file -> { - try { - encrypt(context, file, hFile, file.getName(context).concat(CRYPT_EXTENSION)); - } catch (IOException | GeneralSecurityException e) { - throw new IllegalStateException(e); // throw unchecked exception, no throws needed - } - }); - } else { - - if (sourceFile.getName(context).endsWith(CRYPT_EXTENSION)) { - failedOps.add(sourceFile); - return; - } - - BufferedInputStream inputStream = - new BufferedInputStream( - sourceFile.getInputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - - // succeed #CRYPT_EXTENSION at end of directory/file name - HybridFile targetFile = - new HybridFile( - targetDirectory.getMode(), - targetDirectory.getPath(), - targetFilename, - sourceFile.isDirectory()); - - progressHandler.setFileName(sourceFile.getName(context)); - - BufferedOutputStream outputStream = - new BufferedOutputStream( - targetFile.getOutputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - aesEncrypt(inputStream, outputStream); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - rsaEncrypt(context, inputStream, outputStream); - } - } - } - /** Helper method to encrypt plain text password */ - @RequiresApi(api = Build.VERSION_CODES.M) - private static String aesEncryptPassword(String plainTextPassword) - throws GeneralSecurityException, IOException { + /** + * Wrapper around handling decryption for directory tree + * + * @param sourceFile the source file to decrypt + * @param targetDirectory the target directory inside which we're going to decrypt + */ + private void decrypt( + final Context context, HybridFileParcelable sourceFile, HybridFile targetDirectory) + throws GeneralSecurityException, IOException { + if (progressHandler.getCancelled()) return; + if (sourceFile.isDirectory()) { + + final HybridFile hFile = + new HybridFile( + targetDirectory.getMode(), + targetDirectory.getPath(), + sourceFile.getName(context).replace(CRYPT_EXTENSION, ""), + sourceFile.isDirectory()); + MakeDirectoryOperation.mkdirs(context, hFile); + + sourceFile.forEachChildrenFile( + context, + sourceFile.isRoot(), + file -> { + try { + decrypt(context, file, hFile); + } catch (IOException | GeneralSecurityException e) { + throw new IllegalStateException(e); // throw unchecked exception, no throws needed + } + }); + } else { + + if (!sourceFile.getPath().endsWith(CRYPT_EXTENSION)) { + failedOps.add(sourceFile); + return; + } - Cipher cipher = Cipher.getInstance(ALGO_AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); - cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), gcmParameterSpec); - byte[] encodedBytes = cipher.doFinal(plainTextPassword.getBytes()); + BufferedInputStream inputStream = + new BufferedInputStream( + sourceFile.getInputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - return Base64.encodeToString(encodedBytes, Base64.DEFAULT); - } + HybridFile targetFile = + new HybridFile( + targetDirectory.getMode(), + targetDirectory.getPath(), + sourceFile.getName(context).replace(CRYPT_EXTENSION, ""), + sourceFile.isDirectory()); - /** Helper method to decrypt cipher text password */ - @RequiresApi(api = Build.VERSION_CODES.M) - private static String aesDecryptPassword(String cipherPassword) - throws GeneralSecurityException, IOException { + progressHandler.setFileName(sourceFile.getName(context)); - Cipher cipher = Cipher.getInstance(ALGO_AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); - cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), gcmParameterSpec); - byte[] decryptedBytes = cipher.doFinal(Base64.decode(cipherPassword, Base64.DEFAULT)); + BufferedOutputStream outputStream = + new BufferedOutputStream( + targetFile.getOutputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - return new String(decryptedBytes); - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + aesDecrypt(inputStream, outputStream); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + rsaDecrypt(context, inputStream, outputStream); + } + } + } - /** - * Helper method to encrypt a file - * - * @param inputStream stream associated with the file to be encrypted - * @param outputStream stream associated with new output encrypted file - */ - @RequiresApi(api = Build.VERSION_CODES.M) - private void aesEncrypt(BufferedInputStream inputStream, BufferedOutputStream outputStream) - throws GeneralSecurityException, IOException { + /** + * Wrapper around handling encryption in directory tree + * + * @param sourceFile the source file to encrypt + * @param targetDirectory the target directory in which we're going to encrypt + */ + private void encrypt( + final Context context, + HybridFileParcelable sourceFile, + HybridFile targetDirectory, + String targetFilename) + throws GeneralSecurityException, IOException { + + if (progressHandler.getCancelled()) return; + if (sourceFile.isDirectory()) { + + // succeed #CRYPT_EXTENSION at end of directory/file name + final HybridFile hFile = + new HybridFile( + targetDirectory.getMode(), + targetDirectory.getPath(), + targetFilename, + sourceFile.isDirectory()); + MakeDirectoryOperation.mkdirs(context, hFile); + + sourceFile.forEachChildrenFile( + context, + sourceFile.isRoot(), + file -> { + try { + encrypt(context, file, hFile, file.getName(context).concat(CRYPT_EXTENSION)); + } catch (IOException | GeneralSecurityException e) { + throw new IllegalStateException(e); // throw unchecked exception, no throws needed + } + }); + } else { + + if (sourceFile.getName(context).endsWith(CRYPT_EXTENSION)) { + failedOps.add(sourceFile); + return; + } - Cipher cipher = Cipher.getInstance(ALGO_AES); + BufferedInputStream inputStream = + new BufferedInputStream( + sourceFile.getInputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); + // succeed #CRYPT_EXTENSION at end of directory/file name + HybridFile targetFile = + new HybridFile( + targetDirectory.getMode(), + targetDirectory.getPath(), + targetFilename, + sourceFile.isDirectory()); - cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), gcmParameterSpec); + progressHandler.setFileName(sourceFile.getName(context)); - byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; - int count; + BufferedOutputStream outputStream = + new BufferedOutputStream( + targetFile.getOutputStream(context), GenericCopyUtil.DEFAULT_BUFFER_SIZE); - CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + aesEncrypt(inputStream, outputStream); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + rsaEncrypt(context, inputStream, outputStream); + } + } + } - try { + /** + * Helper method to encrypt plain text password + */ + @RequiresApi(api = Build.VERSION_CODES.M) + private static String aesEncryptPassword(String plainTextPassword) + throws GeneralSecurityException, IOException { - while ((count = inputStream.read(buffer)) != -1) { - if (!progressHandler.getCancelled()) { - cipherOutputStream.write(buffer, 0, count); - ServiceWatcherUtil.position += count; - } else break; - } - } finally { + Cipher cipher = Cipher.getInstance(ALGO_AES); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); + cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), gcmParameterSpec); + byte[] encodedBytes = cipher.doFinal(plainTextPassword.getBytes()); - cipherOutputStream.flush(); - cipherOutputStream.close(); - inputStream.close(); + return Base64.encodeToString(encodedBytes, Base64.DEFAULT); } - } - - /** - * Helper method to decrypt file - * - * @param inputStream stream associated with encrypted file - * @param outputStream stream associated with new output decrypted file - */ - @RequiresApi(api = Build.VERSION_CODES.M) - private void aesDecrypt(BufferedInputStream inputStream, BufferedOutputStream outputStream) - throws GeneralSecurityException, IOException { - - Cipher cipher = Cipher.getInstance(ALGO_AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); - - cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), gcmParameterSpec); - CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); - - byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; - int count; - - try { - - while ((count = cipherInputStream.read(buffer)) != -1) { - if (!progressHandler.getCancelled()) { - outputStream.write(buffer, 0, count); - ServiceWatcherUtil.position += count; - } else break; - } - } finally { - - outputStream.flush(); - cipherInputStream.close(); - outputStream.close(); - } - } - - /** - * Gets a secret key from Android key store. If no key has been generated with a given alias then - * generate a new one - */ - @RequiresApi(api = Build.VERSION_CODES.M) - private static Key getSecretKey() throws GeneralSecurityException, IOException { - - KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); - keyStore.load(null); - - if (!keyStore.containsAlias(KEY_ALIAS_AMAZE)) { - KeyGenerator keyGenerator = - KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_ANDROID); - - KeyGenParameterSpec.Builder builder = - new KeyGenParameterSpec.Builder( - KEY_ALIAS_AMAZE, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT); - builder.setBlockModes(KeyProperties.BLOCK_MODE_GCM); - builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE); - builder.setRandomizedEncryptionRequired(false); - - keyGenerator.init(builder.build()); - return keyGenerator.generateKey(); - } else { - return keyStore.getKey(KEY_ALIAS_AMAZE, null); + + /** + * Helper method to decrypt cipher text password + */ + @RequiresApi(api = Build.VERSION_CODES.M) + private static String aesDecryptPassword(String cipherPassword) + throws GeneralSecurityException, IOException { + + Cipher cipher = Cipher.getInstance(ALGO_AES); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); + cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), gcmParameterSpec); + byte[] decryptedBytes = cipher.doFinal(Base64.decode(cipherPassword, Base64.DEFAULT)); + + return new String(decryptedBytes); } - } - @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - private void rsaEncrypt( - Context context, BufferedInputStream inputStream, BufferedOutputStream outputStream) - throws GeneralSecurityException, IOException { + /** + * Helper method to encrypt a file + * + * @param inputStream stream associated with the file to be encrypted + * @param outputStream stream associated with new output encrypted file + */ + @RequiresApi(api = Build.VERSION_CODES.M) + private void aesEncrypt(BufferedInputStream inputStream, BufferedOutputStream outputStream) + throws GeneralSecurityException, IOException { + + Cipher cipher = Cipher.getInstance(ALGO_AES); - Cipher cipher = Cipher.getInstance(ALGO_AES); - RSAKeygen keygen = new RSAKeygen(context); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); - IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); - cipher.init(Cipher.ENCRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); + cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), gcmParameterSpec); - byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; - int count; + byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; + int count; - CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); - try { + CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); - while ((count = inputStream.read(buffer)) != -1) { - if (!progressHandler.getCancelled()) { - cipherOutputStream.write(buffer, 0, count); - ServiceWatcherUtil.position += count; - } else break; - } - } finally { + try { + + while ((count = inputStream.read(buffer)) != -1) { + if (!progressHandler.getCancelled()) { + cipherOutputStream.write(buffer, 0, count); + ServiceWatcherUtil.position += count; + } else break; + } + } finally { - cipherOutputStream.flush(); - cipherOutputStream.close(); - inputStream.close(); + cipherOutputStream.flush(); + cipherOutputStream.close(); + inputStream.close(); + } } - } - @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - private void rsaDecrypt( - Context context, BufferedInputStream inputStream, BufferedOutputStream outputStream) - throws GeneralSecurityException, IOException { + /** + * Helper method to decrypt file + * + * @param inputStream stream associated with encrypted file + * @param outputStream stream associated with new output decrypted file + */ + @RequiresApi(api = Build.VERSION_CODES.M) + private void aesDecrypt(BufferedInputStream inputStream, BufferedOutputStream outputStream) + throws GeneralSecurityException, IOException { - Cipher cipher = Cipher.getInstance(ALGO_AES); - RSAKeygen keygen = new RSAKeygen(context); + Cipher cipher = Cipher.getInstance(ALGO_AES); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); - IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); - cipher.init(Cipher.DECRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); - CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); + cipher.init(Cipher.DECRYPT_MODE, getSecretKey(), gcmParameterSpec); + CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); - byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; - int count; + byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; + int count; - try { + try { - while ((count = cipherInputStream.read(buffer)) != -1) { - if (!progressHandler.getCancelled()) { - outputStream.write(buffer, 0, count); - ServiceWatcherUtil.position += count; - } else break; - } - } finally { + while ((count = cipherInputStream.read(buffer)) != -1) { + if (!progressHandler.getCancelled()) { + outputStream.write(buffer, 0, count); + ServiceWatcherUtil.position += count; + } else break; + } + } finally { - outputStream.flush(); - outputStream.close(); - cipherInputStream.close(); + outputStream.flush(); + cipherInputStream.close(); + outputStream.close(); + } } - } - - @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - private static String rsaEncryptPassword(Context context, String password) - throws GeneralSecurityException, IOException { - - Cipher cipher = Cipher.getInstance(ALGO_AES); - RSAKeygen keygen = new RSAKeygen(context); - - IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); - cipher.init(Cipher.ENCRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); - - return Base64.encodeToString(cipher.doFinal(password.getBytes()), Base64.DEFAULT); - } - - @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - private static String rsaDecryptPassword(Context context, String cipherText) - throws GeneralSecurityException, IOException { - - Cipher cipher = Cipher.getInstance(ALGO_AES); - RSAKeygen keygen = new RSAKeygen(context); - IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); - cipher.init(Cipher.DECRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); - byte[] decryptedBytes = cipher.doFinal(Base64.decode(cipherText, Base64.DEFAULT)); - - return new String(decryptedBytes); - } - - /** Method handles encryption of plain text on various APIs */ - public static String encryptPassword(Context context, String plainText) - throws GeneralSecurityException, IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return aesEncryptPassword(plainText); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - - return rsaEncryptPassword(context, plainText); - } else return plainText; - } - - /** Method handles decryption of cipher text on various APIs */ - public static String decryptPassword(Context context, String cipherText) - throws GeneralSecurityException, IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return aesDecryptPassword(cipherText); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - return rsaDecryptPassword(context, cipherText); - } else return cipherText; - } - - /** - * Method initializes a Cipher to be used by {@link - * android.hardware.fingerprint.FingerprintManager} - */ - public static Cipher initCipher(Context context) throws GeneralSecurityException, IOException { - Cipher cipher = null; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - cipher = Cipher.getInstance(ALGO_AES); - GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); - cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), gcmParameterSpec); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - cipher = Cipher.getInstance(ALGO_AES); - RSAKeygen keygen = new RSAKeygen(context); - - cipher.init(Cipher.ENCRYPT_MODE, keygen.getSecretKey()); + + /** + * Gets a secret key from Android key store. If no key has been generated with a given alias then + * generate a new one + */ + @RequiresApi(api = Build.VERSION_CODES.M) + private static Key getSecretKey() throws GeneralSecurityException, IOException { + + KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); + keyStore.load(null); + + if (!keyStore.containsAlias(KEY_ALIAS_AMAZE)) { + KeyGenerator keyGenerator = + KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_ANDROID); + + KeyGenParameterSpec.Builder builder = + new KeyGenParameterSpec.Builder( + KEY_ALIAS_AMAZE, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT); + builder.setBlockModes(KeyProperties.BLOCK_MODE_GCM); + builder.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE); + builder.setRandomizedEncryptionRequired(false); + + keyGenerator.init(builder.build()); + return keyGenerator.generateKey(); + } else { + return keyStore.getKey(KEY_ALIAS_AMAZE, null); + } } - return cipher; - } - /** Class responsible for generating key for API lower than M */ - static class RSAKeygen { + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + private void rsaEncrypt( + Context context, BufferedInputStream inputStream, BufferedOutputStream outputStream) + throws GeneralSecurityException, IOException { + + Cipher cipher = Cipher.getInstance(ALGO_AES); + RSAKeygen keygen = new RSAKeygen(context); - private Context context; + IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); + cipher.init(Cipher.ENCRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); - @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - RSAKeygen(Context context) { + byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; + int count; - this.context = context; + CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher); + try { + + while ((count = inputStream.read(buffer)) != -1) { + if (!progressHandler.getCancelled()) { + cipherOutputStream.write(buffer, 0, count); + ServiceWatcherUtil.position += count; + } else break; + } + } finally { - try { - generateKeyPair(context); - setKeyPreference(); - } catch (GeneralSecurityException | IOException e) { - e.printStackTrace(); - } + cipherOutputStream.flush(); + cipherOutputStream.close(); + inputStream.close(); + } } - /** Generates a RSA public/private key pair to encrypt AES key */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - private void generateKeyPair(Context context) throws GeneralSecurityException, IOException { - - KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); - keyStore.load(null); - - if (!keyStore.containsAlias(KEY_ALIAS_AMAZE)) { - // generate a RSA key pair to encrypt/decrypt AES key from preferences - Calendar start = Calendar.getInstance(); - Calendar end = Calendar.getInstance(); - end.add(Calendar.YEAR, 30); - - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", KEY_STORE_ANDROID); - - KeyPairGeneratorSpec spec = - new KeyPairGeneratorSpec.Builder(context) - .setAlias(KEY_ALIAS_AMAZE) - .setSubject(new X500Principal("CN=" + KEY_ALIAS_AMAZE)) - .setSerialNumber(BigInteger.TEN) - .setStartDate(start.getTime()) - .setEndDate(end.getTime()) - .build(); - - keyPairGenerator.initialize(spec); - keyPairGenerator.generateKeyPair(); - } - } + private void rsaDecrypt( + Context context, BufferedInputStream inputStream, BufferedOutputStream outputStream) + throws GeneralSecurityException, IOException { + + Cipher cipher = Cipher.getInstance(ALGO_AES); + RSAKeygen keygen = new RSAKeygen(context); - /** Encrypts AES key and set into preference */ - private void setKeyPreference() throws GeneralSecurityException, IOException { + IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); + cipher.init(Cipher.DECRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); + CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - String encodedAesKey = preferences.getString(PREFERENCE_KEY, null); + byte[] buffer = new byte[GenericCopyUtil.DEFAULT_BUFFER_SIZE]; + int count; - if (encodedAesKey == null) { - // generate encrypted aes key and save to preference + try { - byte[] key = new byte[16]; - SecureRandom secureRandom = new SecureRandom(); - secureRandom.nextBytes(key); + while ((count = cipherInputStream.read(buffer)) != -1) { + if (!progressHandler.getCancelled()) { + outputStream.write(buffer, 0, count); + ServiceWatcherUtil.position += count; + } else break; + } + } finally { - byte[] encryptedKey = encryptAESKey(key); - encodedAesKey = Base64.encodeToString(encryptedKey, Base64.DEFAULT); - preferences.edit().putString(PREFERENCE_KEY, encodedAesKey).apply(); - } + outputStream.flush(); + outputStream.close(); + cipherInputStream.close(); + } } - /** Encrypts randomly generated AES key using RSA public key */ - private byte[] encryptAESKey(byte[] secretKey) throws GeneralSecurityException, IOException { + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + private static String rsaEncryptPassword(Context context, String password) + throws GeneralSecurityException, IOException { - KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); - keyStore.load(null); - KeyStore.PrivateKeyEntry keyEntry = - (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS_AMAZE, null); - Cipher cipher = Cipher.getInstance(ALGO_RSA, "AndroidOpenSSL"); - cipher.init(Cipher.ENCRYPT_MODE, keyEntry.getCertificate().getPublicKey()); + Cipher cipher = Cipher.getInstance(ALGO_AES); + RSAKeygen keygen = new RSAKeygen(context); - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - CipherOutputStream outputStream = new CipherOutputStream(byteArrayOutputStream, cipher); - outputStream.write(secretKey); - outputStream.close(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); + cipher.init(Cipher.ENCRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); - return byteArrayOutputStream.toByteArray(); + return Base64.encodeToString(cipher.doFinal(password.getBytes()), Base64.DEFAULT); } - /** Decodes encrypted AES key from preference and decrypts using RSA private key */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) - private Key getSecretKey() throws GeneralSecurityException, IOException { - - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - String encodedString = preferences.getString(PREFERENCE_KEY, null); - if (encodedString != null) { - - return new SecretKeySpec( - decryptAESKey(Base64.decode(encodedString, Base64.DEFAULT)), "AES"); - } else { - generateKeyPair(context); - setKeyPreference(); - return getSecretKey(); - } + private static String rsaDecryptPassword(Context context, String cipherText) + throws GeneralSecurityException, IOException { + + Cipher cipher = Cipher.getInstance(ALGO_AES); + RSAKeygen keygen = new RSAKeygen(context); + IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); + cipher.init(Cipher.DECRYPT_MODE, keygen.getSecretKey(), ivParameterSpec); + byte[] decryptedBytes = cipher.doFinal(Base64.decode(cipherText, Base64.DEFAULT)); + + return new String(decryptedBytes); + } + + /** + * Method handles encryption of plain text on various APIs + */ + public static String encryptPassword(Context context, String plainText) + throws GeneralSecurityException, IOException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return aesEncryptPassword(plainText); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + + return rsaEncryptPassword(context, plainText); + } else return plainText; + } + + /** + * Method handles decryption of cipher text on various APIs + */ + public static String decryptPassword(Context context, String cipherText) + throws GeneralSecurityException, IOException { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return aesDecryptPassword(cipherText); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + return rsaDecryptPassword(context, cipherText); + } else return cipherText; } - /** Decrypts AES decoded key from preference using RSA private key */ - private byte[] decryptAESKey(byte[] encodedBytes) throws GeneralSecurityException, IOException { - - KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); - keyStore.load(null); - KeyStore.PrivateKeyEntry keyEntry = - (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS_AMAZE, null); - Cipher cipher = Cipher.getInstance(ALGO_RSA, "AndroidOpenSSL"); - cipher.init(Cipher.DECRYPT_MODE, keyEntry.getPrivateKey()); - - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(encodedBytes); - CipherInputStream inputStream = new CipherInputStream(byteArrayInputStream, cipher); - ArrayList bytes = new ArrayList<>(); - int nextByte; - while ((nextByte = inputStream.read()) != -1) { - bytes.add((byte) nextByte); - } - - byte[] decryptedBytes = new byte[bytes.size()]; - for (int i = 0; i < bytes.size(); i++) { - - decryptedBytes[i] = bytes.get(i).byteValue(); - } - return decryptedBytes; + /** + * Method initializes a Cipher to be used by {@link + * android.hardware.fingerprint.FingerprintManager} + */ + public static Cipher initCipher(Context context) throws GeneralSecurityException, IOException { + Cipher cipher = null; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + cipher = Cipher.getInstance(ALGO_AES); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, IV.getBytes()); + cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), gcmParameterSpec); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + cipher = Cipher.getInstance(ALGO_AES); + RSAKeygen keygen = new RSAKeygen(context); + + cipher.init(Cipher.ENCRYPT_MODE, keygen.getSecretKey()); + } + return cipher; + } + + /** + * Class responsible for generating key for API lower than M + */ + static class RSAKeygen { + + private Context context; + + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + RSAKeygen(Context context) { + + this.context = context; + + try { + generateKeyPair(context); + setKeyPreference(); + } catch (GeneralSecurityException | IOException e) { + e.printStackTrace(); + } + } + + /** + * Generates a RSA public/private key pair to encrypt AES key + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + private void generateKeyPair(Context context) throws GeneralSecurityException, IOException { + + KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); + keyStore.load(null); + + if (!keyStore.containsAlias(KEY_ALIAS_AMAZE)) { + // generate a RSA key pair to encrypt/decrypt AES key from preferences + Calendar start = Calendar.getInstance(); + Calendar end = Calendar.getInstance(); + end.add(Calendar.YEAR, 30); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", KEY_STORE_ANDROID); + + KeyPairGeneratorSpec spec = + new KeyPairGeneratorSpec.Builder(context) + .setAlias(KEY_ALIAS_AMAZE) + .setSubject(new X500Principal("CN=" + KEY_ALIAS_AMAZE)) + .setSerialNumber(BigInteger.TEN) + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + + keyPairGenerator.initialize(spec); + keyPairGenerator.generateKeyPair(); + } + } + + /** + * Encrypts AES key and set into preference + */ + private void setKeyPreference() throws GeneralSecurityException, IOException { + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + String encodedAesKey = preferences.getString(PREFERENCE_KEY, null); + + if (encodedAesKey == null) { + // generate encrypted aes key and save to preference + + byte[] key = new byte[16]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(key); + + byte[] encryptedKey = encryptAESKey(key); + encodedAesKey = Base64.encodeToString(encryptedKey, Base64.DEFAULT); + preferences.edit().putString(PREFERENCE_KEY, encodedAesKey).apply(); + } + } + + /** + * Encrypts randomly generated AES key using RSA public key + */ + private byte[] encryptAESKey(byte[] secretKey) throws GeneralSecurityException, IOException { + + KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); + keyStore.load(null); + KeyStore.PrivateKeyEntry keyEntry = + (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS_AMAZE, null); + Cipher cipher = Cipher.getInstance(ALGO_RSA, "AndroidOpenSSL"); + cipher.init(Cipher.ENCRYPT_MODE, keyEntry.getCertificate().getPublicKey()); + + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + CipherOutputStream outputStream = new CipherOutputStream(byteArrayOutputStream, cipher); + outputStream.write(secretKey); + outputStream.close(); + + return byteArrayOutputStream.toByteArray(); + } + + /** + * Decodes encrypted AES key from preference and decrypts using RSA private key + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) + private Key getSecretKey() throws GeneralSecurityException, IOException { + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + String encodedString = preferences.getString(PREFERENCE_KEY, null); + if (encodedString != null) { + + return new SecretKeySpec( + decryptAESKey(Base64.decode(encodedString, Base64.DEFAULT)), "AES"); + } else { + generateKeyPair(context); + setKeyPreference(); + return getSecretKey(); + } + } + + /** + * Decrypts AES decoded key from preference using RSA private key + */ + private byte[] decryptAESKey(byte[] encodedBytes) throws GeneralSecurityException, IOException { + + KeyStore keyStore = KeyStore.getInstance(KEY_STORE_ANDROID); + keyStore.load(null); + KeyStore.PrivateKeyEntry keyEntry = + (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS_AMAZE, null); + Cipher cipher = Cipher.getInstance(ALGO_RSA, "AndroidOpenSSL"); + cipher.init(Cipher.DECRYPT_MODE, keyEntry.getPrivateKey()); + + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(encodedBytes); + CipherInputStream inputStream = new CipherInputStream(byteArrayInputStream, cipher); + ArrayList bytes = new ArrayList<>(); + int nextByte; + while ((nextByte = inputStream.read()) != -1) { + bytes.add((byte) nextByte); + } + + byte[] decryptedBytes = new byte[bytes.size()]; + for (int i = 0; i < bytes.size(); i++) { + + decryptedBytes[i] = bytes.get(i).byteValue(); + } + return decryptedBytes; + } } - } } diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.java deleted file mode 100644 index eddcec00d4..0000000000 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.ui.activities; - -import static android.os.Build.VERSION.SDK_INT; - -import java.io.File; - -import com.afollestad.materialdialogs.folderselector.FolderChooserDialog; -import com.amaze.filemanager.R; -import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity; -import com.amaze.filemanager.ui.colors.ColorPreferenceHelper; -import com.amaze.filemanager.ui.fragments.preference_fragments.AdvancedSearchPref; -import com.amaze.filemanager.ui.fragments.preference_fragments.ColorPref; -import com.amaze.filemanager.ui.fragments.preference_fragments.FoldersPref; -import com.amaze.filemanager.ui.fragments.preference_fragments.PrefFrag; -import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants; -import com.amaze.filemanager.ui.fragments.preference_fragments.QuickAccessPref; -import com.amaze.filemanager.ui.theme.AppTheme; -import com.amaze.filemanager.utils.PreferenceUtils; -import com.amaze.filemanager.utils.Utils; -import com.readystatesoftware.systembartint.SystemBarTintManager; - -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Color; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.os.Parcelable; -import android.view.MenuItem; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.FrameLayout; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.FragmentTransaction; -import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.PreferenceManager; - -public class PreferencesActivity extends ThemedActivity - implements FolderChooserDialog.FolderCallback { - - // Start is the first activity you see - public static final int START_PREFERENCE = 0; - public static final int COLORS_PREFERENCE = 1; - public static final int FOLDERS_PREFERENCE = 2; - public static final int QUICKACCESS_PREFERENCE = 3; - public static final int ADVANCEDSEARCH_PREFERENCE = 4; - - private boolean restartActivity = false; - // The preference fragment currently selected - private int selectedItem = 0; - - private PreferenceFragmentCompat currentFragment; - - private static final String KEY_CURRENT_FRAG_OPEN = "current_frag_open"; - private static final int NUMBER_OF_PREFERENCES = 5; - - private Parcelable[] fragmentsListViewParcelables = new Parcelable[NUMBER_OF_PREFERENCES]; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.prefsfrag); - Toolbar toolbar = findViewById(R.id.toolbar); - invalidateRecentsColorAndIcon(); - setSupportActionBar(toolbar); - getSupportActionBar() - .setDisplayOptions( - androidx.appcompat.app.ActionBar.DISPLAY_HOME_AS_UP - | androidx.appcompat.app.ActionBar.DISPLAY_SHOW_TITLE); - - if (savedInstanceState != null) { - selectedItem = savedInstanceState.getInt(KEY_CURRENT_FRAG_OPEN, 0); - } else if (getIntent().getExtras() != null) { - selectItem(getIntent().getExtras().getInt(KEY_CURRENT_FRAG_OPEN)); - } else { - selectItem(0); - } - initStatusBarResources(findViewById(R.id.preferences)); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt(KEY_CURRENT_FRAG_OPEN, selectedItem); - } - - @Override - public void onBackPressed() { - if (currentFragment instanceof ColorPref) { - if (((ColorPref) currentFragment).onBackPressed()) return; - } - - if (selectedItem != START_PREFERENCE && restartActivity) { - restartActivity(this); - } else if (selectedItem != START_PREFERENCE) { - selectItem(START_PREFERENCE); - } else { - Intent in = new Intent(PreferencesActivity.this, MainActivity.class); - in.setAction(Intent.ACTION_MAIN); - in.setAction(Intent.CATEGORY_LAUNCHER); - this.startActivity(in); - this.finish(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - if (currentFragment != null && currentFragment.onOptionsItemSelected(item)) return true; - - if (selectedItem != START_PREFERENCE && restartActivity) { - restartActivity(this); - } else if (selectedItem != START_PREFERENCE) { - selectItem(START_PREFERENCE); - } else { - Intent in = new Intent(PreferencesActivity.this, MainActivity.class); - in.setAction(Intent.ACTION_MAIN); - in.setAction(Intent.CATEGORY_LAUNCHER); - - final int enter_anim = android.R.anim.fade_in; - final int exit_anim = android.R.anim.fade_out; - Activity activity = this; - activity.overridePendingTransition(enter_anim, exit_anim); - activity.finish(); - activity.overridePendingTransition(enter_anim, exit_anim); - activity.startActivity(in); - } - return true; - } - return false; - } - - /** - * This is a hack, each PreferenceFragment has a ListView that loses it's state (specifically the - * scrolled position) when the user accesses another PreferenceFragment. To prevent this, the - * Activity saves the ListView's state, so that it can be restored when the user returns to the - * PreferenceFragment. - * - *

We cannot use the normal save/restore state functions because they only get called when the - * OS kills the fragment, not the user. See https://stackoverflow.com/a/12793395/3124150 for a - * better explanation. - * - *

We cannot save the Parcelable in the fragment because the fragment is destroyed. - */ - public void saveListViewState(int prefFragment, Parcelable listViewState) { - fragmentsListViewParcelables[prefFragment] = listViewState; - } - - /** This is a hack see {@link PreferencesActivity#saveListViewState(int, Parcelable)} */ - public Parcelable restoreListViewState(int prefFragment) { - return fragmentsListViewParcelables[prefFragment]; - } - - public void setRestartActivity() { - restartActivity = true; - } - - public boolean getRestartActivity() { - return restartActivity; - } - - public void invalidateRecentsColorAndIcon() { - if (SDK_INT >= 21) { - @ColorInt - int primaryColor = - ColorPreferenceHelper.getPrimary(getCurrentColorPreference(), MainActivity.currentTab); - - ActivityManager.TaskDescription taskDescription = - new ActivityManager.TaskDescription( - "Amaze", - ((BitmapDrawable) getResources().getDrawable(R.mipmap.ic_launcher)).getBitmap(), - primaryColor); - setTaskDescription(taskDescription); - } - } - - public void invalidateToolbarColor() { - @ColorInt - int primaryColor = - ColorPreferenceHelper.getPrimary(getCurrentColorPreference(), MainActivity.currentTab); - getSupportActionBar().setBackgroundDrawable(new ColorDrawable(primaryColor)); - } - - public void invalidateNavBar() { - @ColorInt - int primaryColor = - ColorPreferenceHelper.getPrimary(getCurrentColorPreference(), MainActivity.currentTab); - - if (SDK_INT == 20 || SDK_INT == 19) { - SystemBarTintManager tintManager = new SystemBarTintManager(this); - tintManager.setStatusBarTintEnabled(true); - tintManager.setStatusBarTintColor(primaryColor); - - FrameLayout.MarginLayoutParams p = - (ViewGroup.MarginLayoutParams) findViewById(R.id.preferences).getLayoutParams(); - SystemBarTintManager.SystemBarConfig config = tintManager.getConfig(); - p.setMargins(0, config.getStatusBarHeight(), 0, 0); - } else if (SDK_INT >= 21) { - boolean colourednavigation = getBoolean(PreferencesConstants.PREFERENCE_COLORED_NAVIGATION); - Window window = getWindow(); - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - int tabStatusColor = PreferenceUtils.getStatusColor(primaryColor); - window.setStatusBarColor(tabStatusColor); - if (colourednavigation) { - window.setNavigationBarColor(tabStatusColor); - } else if (window.getNavigationBarColor() != Color.BLACK) { - window.setNavigationBarColor(Color.BLACK); - } - } - - if (getAppTheme().equals(AppTheme.BLACK)) - getWindow().getDecorView().setBackgroundColor(Utils.getColor(this, android.R.color.black)); - } - - /** - * This 'elegantly' destroys the activity and recreates it so that the different widgets and texts - * change their inner states's colors. - */ - public void restartActivity(final Activity activity) { - if (activity == null) throw new NullPointerException(); - - final int enter_anim = android.R.anim.fade_in; - final int exit_anim = android.R.anim.fade_out; - activity.overridePendingTransition(enter_anim, exit_anim); - activity.finish(); - activity.overridePendingTransition(enter_anim, exit_anim); - if (selectedItem != START_PREFERENCE) { - Intent i = activity.getIntent(); - i.putExtra(KEY_CURRENT_FRAG_OPEN, selectedItem); - } - activity.startActivity(activity.getIntent()); - } - - /** - * When a Preference (that requires an independent fragment) is selected this is called. - * - * @param item the Preference in question - */ - public void selectItem(int item) { - selectedItem = item; - switch (item) { - case START_PREFERENCE: - loadPrefFragment(new PrefFrag(), R.string.setting); - break; - case COLORS_PREFERENCE: - loadPrefFragment(new ColorPref(), R.string.color_title); - break; - case FOLDERS_PREFERENCE: - loadPrefFragment(new FoldersPref(), R.string.sidebar_bookmarks_title); - break; - case QUICKACCESS_PREFERENCE: - loadPrefFragment(new QuickAccessPref(), R.string.sidebar_quick_access_title); - break; - case ADVANCEDSEARCH_PREFERENCE: - loadPrefFragment(new AdvancedSearchPref(), R.string.advanced_search); - break; - } - } - - private void loadPrefFragment(PreferenceFragmentCompat fragment, @StringRes int titleBarName) { - currentFragment = fragment; - - FragmentTransaction t = getSupportFragmentManager().beginTransaction(); - t.replace(R.id.prefsfragment, fragment); - t.commit(); - getSupportActionBar().setTitle(titleBarName); - } - - /** - * Update preference key with selected path. - * - * @see PrefFrag - * @see FolderChooserDialog - * @see com.afollestad.materialdialogs.folderselector.FolderChooserDialog.FolderCallback - * @param dialog - * @param folder selected folder - */ - @Override - public void onFolderSelection(@NonNull FolderChooserDialog dialog, @NonNull File folder) { - // Just to be safe - if (folder.exists() && folder.isDirectory()) { - // Write settings to preferences - SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); - sharedPref.edit().putString(dialog.getTag(), folder.getAbsolutePath()).apply(); - } - dialog.dismiss(); - } - - /** - * Do nothing other than dismissing the folder selection dialog. - * - * @see FolderChooserDialog - * @see com.afollestad.materialdialogs.folderselector.FolderChooserDialog.FolderCallback - * @param dialog - */ - @Override - public void onFolderChooserDismissed(@NonNull FolderChooserDialog dialog) { - dialog.dismiss(); - } -} diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.kt b/app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.kt new file mode 100644 index 0000000000..da2006c0bf --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/PreferencesActivity.kt @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.activities + +import android.content.Intent +import android.graphics.Color +import android.os.Build +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.appcompat.app.ActionBar +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.commit +import com.afollestad.materialdialogs.folderselector.FolderChooserDialog +import com.amaze.filemanager.R +import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity +import com.amaze.filemanager.ui.colors.ColorPreferenceHelper +import com.amaze.filemanager.ui.fragments.preference_fragments.BasePrefsFragment +import com.amaze.filemanager.ui.fragments.preference_fragments.PrefsFragment +import com.amaze.filemanager.ui.theme.AppTheme +import com.amaze.filemanager.utils.PreferenceUtils +import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants +import com.amaze.filemanager.utils.Utils +import com.readystatesoftware.systembartint.SystemBarTintManager +import java.io.File + +class PreferencesActivity : ThemedActivity(), FolderChooserDialog.FolderCallback { + val layout: View + get() = _layout!! + private var _layout: View? = null + + override fun onCreate(savedInstanceState: Bundle?) { + var savedInstanceState = savedInstanceState + if (savedInstanceState == null && intent.hasExtra("savedInstanceState")) { + savedInstanceState = intent.getBundleExtra("savedInstanceState") + } + + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_preferences) + + _layout = findViewById(R.id.activity_preferences) + + val toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + supportActionBar?.displayOptions = ActionBar.DISPLAY_HOME_AS_UP or ActionBar.DISPLAY_SHOW_TITLE + initStatusBarResources(layout) + + if (savedInstanceState == null) { + val fragment = PrefsFragment() + supportFragmentManager.beginTransaction().replace(R.id.preferences_container, fragment).commit() + } + } + + override fun onBackPressed() { + if (supportFragmentManager.backStackEntryCount > 0) { + supportFragmentManager.popBackStack() + return + } + + val x = Intent(this, MainActivity::class.java) + x.action = Intent.ACTION_MAIN + x.action = Intent.CATEGORY_LAUNCHER + finish() + startActivity(x) + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == android.R.id.home) { + onBackPressed() + true + } else false + } + + override fun recreate() { + val bundle = Bundle() + onSaveInstanceState(bundle) + val intent = Intent(this, javaClass) + intent.putExtra("savedInstanceState", bundle) + + finish() + startActivity(intent) + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) + } + + fun pushFragment(fragment: BasePrefsFragment) { + supportFragmentManager.commit { + setCustomAnimations( + R.anim.slide_in, + android.R.anim.fade_out, + android.R.anim.fade_in, + R.anim.slide_out, + ) + + replace(R.id.preferences_container, fragment) + supportActionBar?.title = getString(fragment.title) + addToBackStack(null) + } + } + + fun invalidateNavBar() { + val primaryColor = ColorPreferenceHelper.getPrimary(currentColorPreference, MainActivity.currentTab) + if (Build.VERSION.SDK_INT == 20 || Build.VERSION.SDK_INT == 19) { + val tintManager = SystemBarTintManager(this) + tintManager.isStatusBarTintEnabled = true + tintManager.setStatusBarTintColor(primaryColor) + val p = findViewById(R.id.activity_preferences).layoutParams as ViewGroup.MarginLayoutParams + val config = tintManager.config + p.setMargins(0, config.statusBarHeight, 0, 0) + } else if (Build.VERSION.SDK_INT >= 21) { + val colouredNavigation = getBoolean(PreferencesConstants.PREFERENCE_COLORED_NAVIGATION) + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + val tabStatusColor = PreferenceUtils.getStatusColor(primaryColor) + window.statusBarColor = tabStatusColor + if (colouredNavigation) { + window.navigationBarColor = tabStatusColor + } else if (window.navigationBarColor != Color.BLACK) { + window.navigationBarColor = Color.BLACK + } + } + if (appTheme == AppTheme.BLACK) { + window.decorView.setBackgroundColor(Utils.getColor(this, android.R.color.black)) + } + } + + override fun onFolderSelection(dialog: FolderChooserDialog, folder: File) { + supportFragmentManager.fragments.lastOrNull { it is BasePrefsFragment }?.let { + (it as BasePrefsFragment).onFolderSelection(dialog, folder) + } + } + + override fun onFolderChooserDismissed(dialog: FolderChooserDialog) { + supportFragmentManager.fragments.lastOrNull { it is BasePrefsFragment }?.let { + (it as BasePrefsFragment).onFolderChooserDismissed(dialog) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PreferenceActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PreferenceActivity.java index 81adb40295..42c7f05ac2 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PreferenceActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/PreferenceActivity.java @@ -57,9 +57,9 @@ public class PreferenceActivity extends BasicActivity { @Override public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + + super.onCreate(savedInstanceState); } public SharedPreferences getPrefs() { diff --git a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/ThemedActivity.java b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/ThemedActivity.java index d2ed1aa786..8a3a04f750 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/ThemedActivity.java +++ b/app/src/main/java/com/amaze/filemanager/ui/activities/superclasses/ThemedActivity.java @@ -32,9 +32,7 @@ import com.amaze.filemanager.utils.PreferenceUtils; import com.readystatesoftware.systembartint.SystemBarTintManager; -import android.app.Activity; import android.app.ActivityManager; -import android.content.res.Configuration; import android.graphics.drawable.BitmapDrawable; import android.os.Build; import android.os.Bundle; @@ -45,7 +43,6 @@ import android.widget.FrameLayout; import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; @@ -85,27 +82,6 @@ public void onCreate(Bundle savedInstanceState) { setTheme(); } - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - if (getPrefs().getString(PreferencesConstants.FRAGMENT_THEME, "4").equals("4")) { - getUtilsProvider().getThemeManager().setAppTheme(AppTheme.getTheme(this, 4)); - restartPC(this); - } - } - - public static void restartPC(final Activity activity) { - if (activity == null) return; - - final int enter_anim = android.R.anim.fade_in; - final int exit_anim = android.R.anim.fade_out; - activity.overridePendingTransition(enter_anim, exit_anim); - activity.finish(); - activity.overridePendingTransition(enter_anim, exit_anim); - activity.startActivity(activity.getIntent()); - } - /** * Set status bar and navigation bar colors based on sdk * diff --git a/app/src/main/java/com/amaze/filemanager/ui/dialogs/ColorPickerDialog.java b/app/src/main/java/com/amaze/filemanager/ui/dialogs/ColorPickerDialog.java index 4abd87a38e..57d222caf3 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/dialogs/ColorPickerDialog.java +++ b/app/src/main/java/com/amaze/filemanager/ui/dialogs/ColorPickerDialog.java @@ -284,6 +284,18 @@ public void onDialogClosed(boolean positiveResult) { } } + public static int getTitle(int i) { + if (i == RANDOM_INDEX) { + return R.string.random; + } else if (i == CUSTOM_INDEX) { + return R.string.custom; + } else if (i >= 0 && i < COLORS.length) { + return COLORS[i].first; + } else { + return COLORS[0].first; + } + } + private int getColor(int i, int pos) { return Utils.getColor(getContext(), COLORS[i].second[pos]); } diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/AppearancePrefsFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/AppearancePrefsFragment.kt new file mode 100644 index 0000000000..6ec3aa7dfc --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/AppearancePrefsFragment.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.fragments.preference_fragments + +import android.os.Build +import android.os.Bundle +import androidx.preference.Preference +import com.afollestad.materialdialogs.MaterialDialog +import com.amaze.filemanager.R +import com.amaze.filemanager.ui.theme.AppTheme + +class AppearancePrefsFragment : BasePrefsFragment() { + override val title = R.string.appearance; + + private var gridColumnPref: Preference? = null + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.appearance_prefs, rootKey) + + val themePref = findPreference(PreferencesConstants.FRAGMENT_THEME)!! + val themes = resources.getStringArray(R.array.theme) + val currentTheme = activity.prefs.getString(PreferencesConstants.FRAGMENT_THEME, "4")!!.toInt() + themePref.summary = themes[currentTheme] + themePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { + val builder = MaterialDialog.Builder(activity) + builder.items(*themes) + .itemsCallbackSingleChoice(currentTheme) + { dialog, _, which, _ -> + val editor = activity.prefs.edit() + editor.putString(PreferencesConstants.FRAGMENT_THEME, which.toString()) + editor.apply() + + activity.utilsProvider.themeManager.appTheme = AppTheme.getTheme(activity, which) + activity.recreate() + + dialog.dismiss() + true + } + .title(R.string.theme) + .build() + .show() + + true + } + + val navPref = findPreference(PreferencesConstants.PREFERENCE_COLORED_NAVIGATION)!! + if (Build.VERSION.SDK_INT >= 21) { + navPref.isEnabled = true + navPref.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.invalidateNavBar() + + true + } + } + + val colorPrefs = findPreference(PreferencesConstants.PREFERENCE_SELECT_COLOR_CONFIG)!! + colorPrefs.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.pushFragment(ColorPrefsFragment()) + + true + } + + val gridColumnItems = resources.getStringArray(R.array.columns) + gridColumnPref = findPreference(PreferencesConstants.PREFERENCE_GRID_COLUMNS) + updateGridColumnSummary() + gridColumnPref!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + val builder = MaterialDialog.Builder(activity) + builder.theme(activity.utilsProvider.appTheme.getMaterialDialogTheme(activity)) + builder.title(R.string.gridcolumnno) + var current = + activity.prefs.getString(PreferencesConstants.PREFERENCE_GRID_COLUMNS, "-1")!!.toInt() - 1 + if (current < 0) current = 0 + builder + .items(*gridColumnItems) + .itemsCallbackSingleChoice(current) + { dialog, _, which, _ -> + val editor = activity.prefs.edit() + editor.putString( + PreferencesConstants.PREFERENCE_GRID_COLUMNS, + if (which != 0) gridColumnItems[which] else "-1") + editor.apply() + dialog.dismiss() + updateGridColumnSummary() + true + } + builder.build().show() + + true + } + } + + private fun updateGridColumnSummary() { + val gridColumnItems = resources.getStringArray(R.array.columns) + + activity.prefs.getString(PreferencesConstants.PREFERENCE_GRID_COLUMNS, "-1")?.let { + if (it == "-1") { + gridColumnPref!!.summary = gridColumnItems[0] + } else { + gridColumnPref!!.summary = it + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/AdvancedSearchPref.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BasePrefsFragment.kt similarity index 55% rename from app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/AdvancedSearchPref.kt rename to app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BasePrefsFragment.kt index 32f76b3ad0..9bef9a6484 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/AdvancedSearchPref.kt +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BasePrefsFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , * Emmanuel Messulam, Raymond Lai and Contributors. * * This file is part of Amaze File Manager. @@ -20,16 +20,28 @@ package com.amaze.filemanager.ui.fragments.preference_fragments -import android.os.Bundle import androidx.preference.PreferenceFragmentCompat -import com.amaze.filemanager.R +import com.afollestad.materialdialogs.folderselector.FolderChooserDialog +import com.amaze.filemanager.ui.activities.PreferencesActivity +import java.io.File -/** @author Emmanuel on 8/5/2017, at 11:30. - */ -class AdvancedSearchPref : PreferenceFragmentCompat() { +abstract class BasePrefsFragment : PreferenceFragmentCompat(), FolderChooserDialog.FolderCallback { + protected val activity: PreferencesActivity + get() = requireActivity() as PreferencesActivity + + abstract val title: Int + + override fun onResume() { + super.onResume() + + activity.supportActionBar?.title = getString(title) + } + + override fun onFolderSelection(dialog: FolderChooserDialog, folder: File) { + dialog.dismiss() + } - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.advancedsearch_prefs) + override fun onFolderChooserDismissed(dialog: FolderChooserDialog) { + dialog.dismiss() } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BehaviorPrefsFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BehaviorPrefsFragment.kt new file mode 100644 index 0000000000..efe5c6fdf9 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BehaviorPrefsFragment.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.fragments.preference_fragments + +import android.os.Bundle +import android.os.Environment +import androidx.preference.Preference +import com.afollestad.materialdialogs.folderselector.FolderChooserDialog +import com.amaze.filemanager.R +import com.amaze.filemanager.application.AppConfig +import com.amaze.filemanager.ui.dialogs.OpenFileDialogFragment.Companion.clearPreferences +import java.io.File + +class BehaviorPrefsFragment : BasePrefsFragment(), FolderChooserDialog.FolderCallback { + override val title = R.string.behavior + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.behavior_prefs, rootKey) + + findPreference("clear_open_file")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + clearPreferences(activity.prefs) + AppConfig.toast(getActivity(), activity.getString(R.string.done)) + true + } + + findPreference(PreferencesConstants.PREFERENCE_ZIP_EXTRACT_PATH)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + FolderChooserDialog.Builder(activity) + .tag(PreferencesConstants.PREFERENCE_ZIP_EXTRACT_PATH) + .goUpLabel(getString(R.string.folder_go_up_one_level)) + .chooseButton(R.string.choose_folder) + .cancelButton(R.string.cancel) + .initialPath( + activity.prefs.getString( + PreferencesConstants.PREFERENCE_ZIP_EXTRACT_PATH, + Environment.getExternalStorageDirectory().path)) + .build() + .show(activity) + true + } + } + + override fun onFolderSelection(dialog: FolderChooserDialog, folder: File) { + // Just to be safe + if (folder.exists() && folder.isDirectory) { + // Write settings to preferences + val e = activity.prefs.edit() + e.putString(dialog.tag, folder.absolutePath) + e.apply() + } + dialog.dismiss() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BookmarksPrefsFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BookmarksPrefsFragment.kt new file mode 100644 index 0000000000..49ad2e23bb --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/BookmarksPrefsFragment.kt @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.fragments.preference_fragments + +import android.os.Bundle +import android.text.Editable +import android.view.LayoutInflater +import android.widget.EditText +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import com.afollestad.materialdialogs.DialogAction +import com.afollestad.materialdialogs.MaterialDialog +import com.amaze.filemanager.R +import com.amaze.filemanager.application.AppConfig +import com.amaze.filemanager.database.UtilsHandler +import com.amaze.filemanager.database.models.OperationData +import com.amaze.filemanager.databinding.DialogTwoedittextsBinding +import com.amaze.filemanager.filesystem.files.FileUtils +import com.amaze.filemanager.ui.views.preference.PathSwitchPreference +import com.amaze.filemanager.utils.DataUtils +import com.amaze.filemanager.utils.SimpleTextWatcher +import java.util.HashMap + +class BookmarksPrefsFragment : BasePrefsFragment() { + override val title = R.string.show_bookmarks_pref + + companion object { + private val dataUtils = DataUtils.getInstance()!! + } + + private val position: MutableMap = HashMap() + private var bookmarksList: PreferenceCategory? = null + + private val itemOnEditListener = { it: PathSwitchPreference -> + showEditDialog(it) + } + + private val itemOnDeleteListener = { it: PathSwitchPreference -> + showDeleteDialog(it) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.bookmarks_prefs, rootKey) + + findPreference("add_bookmarks")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + showCreateDialog() + + true + } + + bookmarksList = findPreference("bookmarks_list") + reload() + } + + private fun reload() { + for (p in position) { + bookmarksList!!.removePreference(p.key) + } + + position.clear() + for (i in dataUtils.books.indices) { + val p = PathSwitchPreference(activity) + p.title = dataUtils.books[i][0] + p.summary = dataUtils.books[i][1] + p.onDelete = itemOnDeleteListener + p.onEdit = itemOnEditListener + position[p] = i + bookmarksList!!.addPreference(p) + } + } + + private fun showCreateDialog() { + val fabSkin = activity.accent + val utilsHandler = AppConfig.getInstance().utilsHandler + val dialogBinding = DialogTwoedittextsBinding.inflate(LayoutInflater.from(requireContext())) + + val v = dialogBinding.root + dialogBinding.textInput1.hint = getString(R.string.name) + dialogBinding.textInput2.hint = getString(R.string.directory) + val txtShortcutName = dialogBinding.text1 + val txtShortcutPath = dialogBinding.text2 + + val dialog = MaterialDialog.Builder(requireActivity()) + .title(R.string.create_bookmark) + .theme(activity.appTheme.getMaterialDialogTheme(activity.applicationContext)) + .positiveColor(fabSkin) + .positiveText(R.string.create) + .negativeColor(fabSkin) + .negativeText(android.R.string.cancel) + .customView(v, false) + .build() + dialog.getActionButton(DialogAction.POSITIVE).isEnabled = false + disableButtonIfTitleEmpty(txtShortcutName, dialog) + disableButtonIfNotPath(txtShortcutPath, dialog) + dialog.getActionButton(DialogAction.POSITIVE) + .setOnClickListener { + val p = PathSwitchPreference(getActivity()) + p.title = txtShortcutName.text + p.summary = txtShortcutPath.text + p.onDelete = itemOnDeleteListener + p.onEdit = itemOnEditListener + position[p] = dataUtils.books.size + bookmarksList!!.addPreference(p) + val values = arrayOf( + txtShortcutName.text.toString(), + txtShortcutPath.text.toString() + ) + dataUtils.addBook(values) + utilsHandler.saveToDatabase( + OperationData( + UtilsHandler.Operation.BOOKMARKS, + txtShortcutName.text.toString(), + txtShortcutPath.text.toString() + ) + ) + dialog.dismiss() + } + dialog.show() + } + + private fun showEditDialog(p: PathSwitchPreference) { + val fabSkin = activity.accent + val utilsHandler = AppConfig.getInstance().utilsHandler + val dialogBinding = DialogTwoedittextsBinding.inflate(LayoutInflater.from(requireContext())) + + val v = dialogBinding.root + dialogBinding.textInput1.hint = getString(R.string.name) + dialogBinding.textInput2.hint = getString(R.string.directory) + val editText1 = dialogBinding.text1 + val editText2 = dialogBinding.text2 + editText1.setText(p.title) + editText2.setText(p.summary) + + val dialog = MaterialDialog.Builder(activity) + .title(R.string.edit_bookmark) + .theme(activity.appTheme.getMaterialDialogTheme(activity.applicationContext)) + .positiveColor(fabSkin) + .positiveText(getString(R.string.edit).uppercase()) // TODO: 29/4/2017 don't use toUpperCase() + .negativeColor(fabSkin) + .negativeText(android.R.string.cancel) + .customView(v, false) + .build() + dialog.getActionButton(DialogAction.POSITIVE).isEnabled = + FileUtils.isPathAccessible(editText2.text.toString(), activity.prefs) + disableButtonIfTitleEmpty(editText1, dialog) + disableButtonIfNotPath(editText2, dialog) + dialog.getActionButton(DialogAction.POSITIVE) + .setOnClickListener { + val oldName = p.title.toString() + val oldPath = p.summary.toString() + dataUtils.removeBook(position[p]!!) + position.remove(p) + bookmarksList!!.removePreference(p) + p.title = editText1.text + p.summary = editText2.text + position[p] = position.size + bookmarksList!!.addPreference(p) + val values = arrayOf(editText1.text.toString(), editText2.text.toString()) + dataUtils.addBook(values) + AppConfig.getInstance() + .runInBackground { + utilsHandler.renameBookmark( + oldName, + oldPath, + editText1.text.toString(), + editText2.text.toString() + ) + } + dialog.dismiss() + } + dialog.show() + } + + private fun showDeleteDialog(p: PathSwitchPreference) { + val fabSkin = activity.accent + val utilsHandler = AppConfig.getInstance().utilsHandler + + val dialog = MaterialDialog.Builder(activity) + .title(R.string.question_delete_bookmark) + .theme(activity.appTheme.getMaterialDialogTheme(activity.applicationContext)) + .positiveColor(fabSkin) + .positiveText(getString(R.string.delete).uppercase()) // TODO: 29/4/2017 don't use toUpperCase(), 20/9,2017 why not? + .negativeColor(fabSkin) + .negativeText(android.R.string.cancel) + .build() + dialog.getActionButton(DialogAction.POSITIVE) + .setOnClickListener { + dataUtils.removeBook(position[p]!!) + utilsHandler.removeFromDatabase( + OperationData( + UtilsHandler.Operation.BOOKMARKS, + p.title.toString(), + p.summary.toString() + ) + ) + bookmarksList!!.removePreference(p) + position.remove(p) + dialog.dismiss() + } + dialog.show() + } + + private fun disableButtonIfNotPath(path: EditText, dialog: MaterialDialog) { + path.addTextChangedListener( + object : SimpleTextWatcher() { + override fun afterTextChanged(s: Editable) { + dialog.getActionButton(DialogAction.POSITIVE).isEnabled = + FileUtils.isPathAccessible(s.toString(), activity.prefs) + } + }) + } + + private fun disableButtonIfTitleEmpty(title: EditText, dialog: MaterialDialog) { + title.addTextChangedListener( + object : SimpleTextWatcher() { + override fun afterTextChanged(s: Editable) { + dialog.getActionButton(DialogAction.POSITIVE).isEnabled = title.length() > 0 + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPref.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPref.kt deleted file mode 100644 index 4e4901a21a..0000000000 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPref.kt +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.ui.fragments.preference_fragments - -import android.content.SharedPreferences -import android.graphics.Color -import android.os.Build -import android.os.Bundle -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.View -import androidx.annotation.ColorInt -import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager -import com.afollestad.materialdialogs.MaterialDialog -import com.afollestad.materialdialogs.Theme -import com.amaze.filemanager.adapters.ColorAdapter -import com.amaze.filemanager.application.AppConfig -import com.amaze.filemanager.databinding.DialogGridBinding -import com.amaze.filemanager.ui.activities.PreferencesActivity -import com.amaze.filemanager.ui.colors.ColorPreference -import com.amaze.filemanager.ui.colors.UserColorPreferences -import com.amaze.filemanager.ui.dialogs.ColorPickerDialog -import com.amaze.filemanager.ui.views.preference.InvalidablePreferenceCategory -import com.amaze.filemanager.ui.views.preference.SelectedColorsPreference - -/** - * This class uses two sections, so that there doesn't need to be two different Fragments. For - * sections info check switchSections() below. - * - * - * Created by Arpit on 21-06-2015 edited by Emmanuel Messulam @gmail.com> - */ -@Suppress("TooManyFunctions") -class ColorPref : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener { - - private var currentSection = SECTION_0 - private var dialog: MaterialDialog? = null - private var sharedPref: SharedPreferences? = null - - private val activity: PreferencesActivity - get() = requireActivity() as PreferencesActivity - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - sharedPref = PreferenceManager.getDefaultSharedPreferences(requireActivity()) - if (savedInstanceState == null) { - loadSection0() - reloadListeners() - } else { - onRestoreInstanceState(savedInstanceState) - activity.supportFragmentManager.findFragmentByTag(KEY_SELECT_COLOR_CONFIG)?.apply { - (this as ColorPickerDialog).setListener(createColorPickerDialogListener()) - } - } - } - - override fun onPause() { - dialog?.dismiss() - super.onPause() - } - - /** Deal with the "up" button going to last fragment, instead of section 0. */ - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home && currentSection != SECTION_0) { - switchSections() - return true - } - return super.onOptionsItemSelected(item) - } - - /** - * Handle back button behaviour, returns to first section of colour preferences as necessary - */ - fun onBackPressed(): Boolean { - return if (currentSection != SECTION_0) { - switchSections() - true // dealt with click - } else { - false - } - } - - override fun onPreferenceClick(preference: Preference): Boolean { - when (preference.key) { - KEY_COLOREDNAV -> activity.invalidateNavBar() - PreferencesConstants.PREFERENCE_SKIN, - PreferencesConstants.PREFERENCE_SKIN_TWO, - PreferencesConstants.PREFERENCE_ACCENT, - PreferencesConstants.PREFERENCE_ICON_SKIN -> { - colorChangeDialog(preference.key) - return false - } - KEY_SELECT_COLOR_CONFIG -> { - switchSections() - return true - } - } - return false - } - - override fun onDisplayPreferenceDialog(preference: Preference) { - if (preference is SelectedColorsPreference) { - openSelectColorDialog(preference) - } else { - super.onDisplayPreferenceDialog(preference) - } - } - - private fun colorChangeDialog(colorPrefKey: String) { - val userColorPreferences = activity.currentColorPreference - if (userColorPreferences != null) { - @ColorInt val currentColor = when (colorPrefKey) { - PreferencesConstants.PREFERENCE_SKIN -> userColorPreferences.primaryFirstTab - PreferencesConstants.PREFERENCE_SKIN_TWO -> userColorPreferences.primarySecondTab - PreferencesConstants.PREFERENCE_ACCENT -> userColorPreferences.accent - PreferencesConstants.PREFERENCE_ICON_SKIN -> userColorPreferences.iconSkin - else -> 0 - } - - val adapter = ColorAdapter( - getActivity(), - ColorPreference.availableColors, - currentColor - ) { selectedColor: Int -> - @ColorInt var primaryFirst = userColorPreferences.primaryFirstTab - @ColorInt var primarySecond = userColorPreferences.primarySecondTab - @ColorInt var accent = userColorPreferences.accent - @ColorInt var iconSkin = userColorPreferences.iconSkin - when (colorPrefKey) { - PreferencesConstants.PREFERENCE_SKIN -> primaryFirst = selectedColor - PreferencesConstants.PREFERENCE_SKIN_TWO -> primarySecond = selectedColor - PreferencesConstants.PREFERENCE_ACCENT -> accent = selectedColor - PreferencesConstants.PREFERENCE_ICON_SKIN -> iconSkin = selectedColor - } - activity - .colorPreference - .saveColorPreferences( - sharedPref, - UserColorPreferences(primaryFirst, primarySecond, accent, iconSkin) - ) - dialog?.dismiss() - invalidateEverything() - } - val v = DialogGridBinding.inflate(LayoutInflater.from(requireContext())).root.also { - it.adapter = adapter - it.onItemClickListener = adapter - } - val fab_skin = activity.accent - - dialog = MaterialDialog.Builder(activity) - .positiveText(com.amaze.filemanager.R.string.cancel) - .title(com.amaze.filemanager.R.string.choose_color) - .theme(activity.appTheme.getMaterialDialogTheme(activity.applicationContext)) - .autoDismiss(true) - .positiveColor(fab_skin) - .neutralColor(fab_skin) - .neutralText(com.amaze.filemanager.R.string.default_string) - .onNeutral { _, _ -> - activity.setRestartActivity() - activity - .colorPreference - .saveColorPreferences(sharedPref, userColorPreferences) - invalidateEverything() - }.customView(v, false) - .show() - } - } - - private fun switchSections() { - preferenceScreen.removeAll() - if (currentSection == SECTION_0) { - currentSection = SECTION_1 - loadSection1() - } else if (currentSection == SECTION_1) { - currentSection = SECTION_0 - loadSection0() - } - reloadListeners() - } - - private fun loadSection0() { - if ((getActivity() as PreferencesActivity?)!!.restartActivity) { - (getActivity() as PreferencesActivity?)!!.restartActivity(getActivity()) - } - addPreferencesFromResource(com.amaze.filemanager.R.xml.color_prefs) - if (Build.VERSION.SDK_INT >= 21) { - findPreference(KEY_COLOREDNAV)!!.isEnabled = true - } - } - - private fun loadSection1() { - addPreferencesFromResource(com.amaze.filemanager.R.xml.conficolor_prefs) - findPreference(KEY_PRESELECTED_CONFIGS).let { selectedColors -> - invalidateColorPreference(selectedColors) - selectedColors?.onPreferenceClickListener = this - } - checkCustomization() - (findPreference(KEY_CUSTOMIZE) as InvalidablePreferenceCategory?) - ?.invalidate(activity.accent) - } - - private fun openSelectColorDialog(pref: SelectedColorsPreference) { - val dialog = ColorPickerDialog.newInstance( - pref.key, - activity.currentColorPreference, - activity.appTheme - ) - dialog.setListener(createColorPickerDialogListener()) - (findPreference(KEY_CUSTOMIZE) as InvalidablePreferenceCategory?) - ?.invalidate(activity.accent) - dialog.setTargetFragment(this, 0) - dialog.show(parentFragmentManager, KEY_SELECT_COLOR_CONFIG) - } - - private fun createColorPickerDialogListener() = ColorPickerDialog.OnAcceptedConfig { - activity.setRestartActivity() - checkCustomization() - invalidateEverything() - sharedPref!!.getInt( - PreferencesConstants.PREFERENCE_COLOR_CONFIG, - ColorPickerDialog.NO_DATA - ).let { colorPickerPref -> - if (colorPickerPref == ColorPickerDialog.RANDOM_INDEX) { - AppConfig.toast(getActivity(), com.amaze.filemanager.R.string.set_random) - } - } - } - - private fun checkCustomization() { - ( - sharedPref!!.getInt( - PreferencesConstants.PREFERENCE_COLOR_CONFIG, - ColorPickerDialog.NO_DATA - ) == ColorPickerDialog.CUSTOM_INDEX - ).apply { - findPreference(PreferencesConstants.PREFERENCE_SKIN)?.isEnabled = - this - findPreference(PreferencesConstants.PREFERENCE_SKIN_TWO)?.isEnabled = - this - findPreference(PreferencesConstants.PREFERENCE_ACCENT)?.isEnabled = - this - findPreference(PreferencesConstants.PREFERENCE_ICON_SKIN)?.isEnabled = - this - } - } - - private fun reloadListeners() { - for ( - PREFERENCE_KEY in if (currentSection == SECTION_0) { - PREFERENCE_KEYS_SECTION_0 - } else { - PREFERENCE_KEYS_SECTION_1 - } - ) { - findPreference(PREFERENCE_KEY)!!.onPreferenceClickListener = this - } - } - - private fun invalidateEverything() { - activity.invalidateRecentsColorAndIcon() - activity.invalidateToolbarColor() - activity.invalidateNavBar() - if (currentSection == SECTION_1) { - findPreference(KEY_PRESELECTED_CONFIGS).let { - selectedColors -> - if (selectedColors != null) { - invalidateColorPreference(selectedColors) - selectedColors.invalidateColors() - } - } - (findPreference(KEY_CUSTOMIZE) as InvalidablePreferenceCategory?) - ?.invalidate(activity.accent) - } - } - - private fun invalidateColorPreference(selectedColors: SelectedColorsPreference?) { - val colorPickerPref = sharedPref!!.getInt( - PreferencesConstants.PREFERENCE_COLOR_CONFIG, - ColorPickerDialog.NO_DATA - ) - val isColor = ( - colorPickerPref != ColorPickerDialog.CUSTOM_INDEX && - colorPickerPref != ColorPickerDialog.RANDOM_INDEX - ) - if (isColor) { - selectedColors!!.setColorsVisibility(View.VISIBLE) - val userColorPreferences = activity.currentColorPreference - - selectedColors.setColors( - userColorPreferences.primaryFirstTab, - userColorPreferences.primarySecondTab, - userColorPreferences.accent, - userColorPreferences.iconSkin - ) - - val theme = activity.appTheme.getMaterialDialogTheme(activity.applicationContext) - - if (theme == Theme.LIGHT) { - selectedColors.setDividerColor(Color.WHITE) - } else { - selectedColors.setDividerColor(Color.BLACK) - } - } else { - selectedColors!!.setColorsVisibility(View.GONE) - } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putInt(KEY_SECTION, currentSection) - } - - private fun onRestoreInstanceState(inState: Bundle) { - currentSection = inState.getInt(KEY_SECTION, SECTION_0) - if (currentSection == SECTION_0) { - loadSection0() - reloadListeners() - } else { - loadSection1() - reloadListeners() - } - } - - companion object { - private const val SECTION_0 = 0 - private const val SECTION_1 = 1 - private const val KEY_PRESELECTED_CONFIGS = "preselectedconfigs" - private const val KEY_COLOREDNAV = "colorednavigation" - private const val KEY_SELECT_COLOR_CONFIG = "selectcolorconfig" - private val PREFERENCE_KEYS_SECTION_0 = arrayOf(KEY_COLOREDNAV, KEY_SELECT_COLOR_CONFIG) - private val PREFERENCE_KEYS_SECTION_1 = arrayOf( - KEY_PRESELECTED_CONFIGS, - PreferencesConstants.PREFERENCE_SKIN, - PreferencesConstants.PREFERENCE_SKIN_TWO, - PreferencesConstants.PREFERENCE_ACCENT, - PreferencesConstants.PREFERENCE_ICON_SKIN - ) - private const val KEY_SECTION = "section" - private const val KEY_CUSTOMIZE = "category" - } -} diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPrefsFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPrefsFragment.kt new file mode 100644 index 0000000000..26aa072c2e --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/ColorPrefsFragment.kt @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.fragments.preference_fragments + +import android.os.Bundle +import android.view.LayoutInflater +import androidx.annotation.ColorInt +import androidx.preference.Preference +import com.afollestad.materialdialogs.MaterialDialog +import com.amaze.filemanager.R +import com.amaze.filemanager.adapters.ColorAdapter +import com.amaze.filemanager.application.AppConfig +import com.amaze.filemanager.databinding.DialogGridBinding +import com.amaze.filemanager.ui.colors.ColorPreference +import com.amaze.filemanager.ui.colors.UserColorPreferences +import com.amaze.filemanager.ui.dialogs.ColorPickerDialog + +class ColorPrefsFragment : BasePrefsFragment() { + override val title = R.string.color_title + + private var dialog: MaterialDialog? = null + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.color_prefs, rootKey) + + val showColorChangeDialogListener = Preference.OnPreferenceClickListener { + showColorChangeDialog(it.key) + + true + } + + val colorPickerPref = activity.prefs.getInt( + PreferencesConstants.PREFERENCE_COLOR_CONFIG, + ColorPickerDialog.NO_DATA + ) + + val skin = findPreference(PreferencesConstants.PREFERENCE_SKIN)!! + val skinTwo = findPreference(PreferencesConstants.PREFERENCE_SKIN_TWO)!! + val accent = findPreference(PreferencesConstants.PREFERENCE_ACCENT)!! + val icon = findPreference(PreferencesConstants.PREFERENCE_ICON_SKIN)!! + + if (colorPickerPref != ColorPickerDialog.CUSTOM_INDEX) { + skin.isEnabled = false + skinTwo.isEnabled = false + accent.isEnabled = false + icon.isEnabled = false + } else { + skin.onPreferenceClickListener = showColorChangeDialogListener + skinTwo.onPreferenceClickListener = showColorChangeDialogListener + accent.onPreferenceClickListener = showColorChangeDialogListener + icon.onPreferenceClickListener = showColorChangeDialogListener + } + } + + override fun onDisplayPreferenceDialog(preference: Preference) { + if (preference.key == PreferencesConstants.PRESELECTED_CONFIGS) { + showPreselectedColorsConfigDialog(); + } else { + super.onDisplayPreferenceDialog(preference) + } + } + + private fun showPreselectedColorsConfigDialog() { + val dialog = ColorPickerDialog.newInstance( + PreferencesConstants.PRESELECTED_CONFIGS, + activity.currentColorPreference, + activity.appTheme + ) + dialog.setListener { + val colorPickerPref = activity.prefs.getInt( + PreferencesConstants.PREFERENCE_COLOR_CONFIG, + ColorPickerDialog.NO_DATA + ) + if (colorPickerPref == ColorPickerDialog.RANDOM_INDEX) { + AppConfig.toast(getActivity(), R.string.set_random) + } + + activity.recreate() + } + dialog.setTargetFragment(this, 0) + dialog.show(parentFragmentManager, PreferencesConstants.PREFERENCE_SELECT_COLOR_CONFIG) + } + + private fun showColorChangeDialog(colorPrefKey: String) { + val currentColorPreference = activity.currentColorPreference ?: return + + @ColorInt val currentColor = when (colorPrefKey) { + PreferencesConstants.PREFERENCE_SKIN -> currentColorPreference.primaryFirstTab + PreferencesConstants.PREFERENCE_SKIN_TWO -> currentColorPreference.primarySecondTab + PreferencesConstants.PREFERENCE_ACCENT -> currentColorPreference.accent + PreferencesConstants.PREFERENCE_ICON_SKIN -> currentColorPreference.iconSkin + else -> 0 + } + + val adapter = ColorAdapter( + activity, + ColorPreference.availableColors, + currentColor + ) { selectedColor: Int -> + @ColorInt var primaryFirst = currentColorPreference.primaryFirstTab + @ColorInt var primarySecond = currentColorPreference.primarySecondTab + @ColorInt var accent = currentColorPreference.accent + @ColorInt var iconSkin = currentColorPreference.iconSkin + when (colorPrefKey) { + PreferencesConstants.PREFERENCE_SKIN -> primaryFirst = selectedColor + PreferencesConstants.PREFERENCE_SKIN_TWO -> primarySecond = selectedColor + PreferencesConstants.PREFERENCE_ACCENT -> accent = selectedColor + PreferencesConstants.PREFERENCE_ICON_SKIN -> iconSkin = selectedColor + } + activity + .colorPreference + .saveColorPreferences( + activity.prefs, + UserColorPreferences(primaryFirst, primarySecond, accent, iconSkin) + ) + dialog?.dismiss() + activity.recreate() + } + + val v = DialogGridBinding.inflate(LayoutInflater.from(requireContext())).root.also { + it.adapter = adapter + it.onItemClickListener = adapter + } + + val fabSkin = activity.accent + + dialog = MaterialDialog.Builder(activity) + .positiveText(com.amaze.filemanager.R.string.cancel) + .title(com.amaze.filemanager.R.string.choose_color) + .theme(activity.appTheme.getMaterialDialogTheme(activity.applicationContext)) + .autoDismiss(true) + .positiveColor(fabSkin) + .neutralColor(fabSkin) + .neutralText(com.amaze.filemanager.R.string.default_string) + .onNeutral { _, _ -> + activity + .colorPreference + .saveColorPreferences(activity.prefs, currentColorPreference) + activity.recreate() + }.customView(v, false) + .show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.kt deleted file mode 100644 index 6695319fd6..0000000000 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/FoldersPref.kt +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.ui.fragments.preference_fragments - -import android.content.SharedPreferences -import android.os.Bundle -import android.text.Editable -import android.view.LayoutInflater -import android.widget.EditText -import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager -import com.afollestad.materialdialogs.DialogAction -import com.afollestad.materialdialogs.MaterialDialog -import com.amaze.filemanager.R -import com.amaze.filemanager.application.AppConfig -import com.amaze.filemanager.database.UtilsHandler -import com.amaze.filemanager.database.models.OperationData -import com.amaze.filemanager.databinding.DialogTwoedittextsBinding -import com.amaze.filemanager.filesystem.files.FileUtils -import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity -import com.amaze.filemanager.ui.views.preference.PathSwitchPreference -import com.amaze.filemanager.utils.DataUtils -import com.amaze.filemanager.utils.SimpleTextWatcher -import java.util.* - -/** @author Emmanuel on 17/4/2017, at 22:49. - */ -class FoldersPref : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener { - - private var sharedPrefs: SharedPreferences? = null - private val position: MutableMap = HashMap() - private var dataUtils: DataUtils? = null - private var utilsHandler: UtilsHandler? = null - - private val mainActivity: ThemedActivity - get() = requireActivity() as ThemedActivity - - private var _dialogBinding: DialogTwoedittextsBinding? = null - private val dialogBinding: DialogTwoedittextsBinding - get() = _dialogBinding!! - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - utilsHandler = AppConfig.getInstance().utilsHandler - dataUtils = DataUtils.getInstance() - _dialogBinding = DialogTwoedittextsBinding.inflate(LayoutInflater.from(requireContext())) - - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.folders_prefs) - sharedPrefs = PreferenceManager.getDefaultSharedPreferences(mainActivity) - findPreference(PreferencesConstants.PREFERENCE_SHORTCUT)!! - .onPreferenceClickListener = this - for (i in dataUtils!!.books.indices) { - val p = PathSwitchPreference(activity) - p.title = dataUtils!!.books[i][0] - p.summary = dataUtils!!.books[i][1] - p.onPreferenceClickListener = this - position[p] = i - preferenceScreen.addPreference(p) - } - } - - override fun onResume() { - super.onResume() - onCreate(null) - } - - override fun onPreferenceClick(preference: Preference): Boolean { - if (preference is PathSwitchPreference) { - when (preference.lastItemClicked) { - PathSwitchPreference.EDIT -> loadEditDialog(preference) - PathSwitchPreference.DELETE -> loadDeleteDialog(preference) - else -> { - } - } - } else if (preference.key == PreferencesConstants.PREFERENCE_SHORTCUT) { - if (preferenceScreen.preferenceCount - >= findPreference(PreferencesConstants.PREFERENCE_SHORTCUT)!!.order - ) - findPreference(PreferencesConstants.PREFERENCE_SHORTCUT) - ?.order = preferenceScreen.preferenceCount + 10 - loadCreateDialog() - } - return false - } - - private fun loadCreateDialog() { - val fab_skin = mainActivity.accent - val v = dialogBinding.root - dialogBinding.textInput1.hint = getString(R.string.name) - dialogBinding.textInput2.hint = getString(R.string.directory) - val txtShortcutName = dialogBinding.text1 - val txtShortcutPath = dialogBinding.text2 - - val dialog = MaterialDialog.Builder(requireActivity()) - .title(R.string.create_bookmark) - .theme(mainActivity.appTheme.getMaterialDialogTheme(mainActivity.applicationContext)) - .positiveColor(fab_skin) - .positiveText(R.string.create) - .negativeColor(fab_skin) - .negativeText(android.R.string.cancel) - .customView(v, false) - .build() - dialog.getActionButton(DialogAction.POSITIVE).isEnabled = false - disableButtonIfTitleEmpty(txtShortcutName, dialog) - disableButtonIfNotPath(txtShortcutPath, dialog) - dialog.getActionButton(DialogAction.POSITIVE) - .setOnClickListener { - val p = PathSwitchPreference(getActivity()) - p.title = txtShortcutName.text - p.summary = txtShortcutPath.text - p.onPreferenceClickListener = this@FoldersPref - position[p] = dataUtils!!.books.size - preferenceScreen.addPreference(p) - val values = arrayOf( - txtShortcutName.text.toString(), - txtShortcutPath.text.toString() - ) - dataUtils!!.addBook(values) - utilsHandler!!.saveToDatabase( - OperationData( - UtilsHandler.Operation.BOOKMARKS, - txtShortcutName.text.toString(), - txtShortcutPath.text.toString() - ) - ) - dialog.dismiss() - } - dialog.show() - } - - private fun loadEditDialog(p: PathSwitchPreference) { - val fab_skin = mainActivity.accent - val v = dialogBinding.root - dialogBinding.textInput1.hint = getString(R.string.name) - dialogBinding.textInput2.hint = getString(R.string.directory) - val editText1 = dialogBinding.text1 - val editText2 = dialogBinding.text2 - editText1.setText(p.title) - editText2.setText(p.summary) - - val dialog = MaterialDialog.Builder(mainActivity) - .title(R.string.edit_bookmark) - .theme(mainActivity.appTheme.getMaterialDialogTheme(mainActivity.applicationContext)) - .positiveColor(fab_skin) - .positiveText(getString(R.string.edit).toUpperCase()) // TODO: 29/4/2017 don't use toUpperCase() - .negativeColor(fab_skin) - .negativeText(android.R.string.cancel) - .customView(v, false) - .build() - dialog.getActionButton(DialogAction.POSITIVE).isEnabled = - FileUtils.isPathAccessible(editText2.text.toString(), sharedPrefs) - disableButtonIfTitleEmpty(editText1, dialog) - disableButtonIfNotPath(editText2, dialog) - dialog.getActionButton(DialogAction.POSITIVE) - .setOnClickListener { - val oldName = p.title.toString() - val oldPath = p.summary.toString() - dataUtils!!.removeBook(position[p]!!) - position.remove(p) - preferenceScreen.removePreference(p) - p.title = editText1.text - p.summary = editText2.text - position[p] = position.size - preferenceScreen.addPreference(p) - val values = arrayOf(editText1.text.toString(), editText2.text.toString()) - dataUtils!!.addBook(values) - AppConfig.getInstance() - .runInBackground { - utilsHandler!!.renameBookmark( - oldName, - oldPath, - editText1.text.toString(), - editText2.text.toString() - ) - } - dialog.dismiss() - } - dialog.show() - } - - private fun loadDeleteDialog(p: PathSwitchPreference) { - val fab_skin = mainActivity.accent - - val dialog = MaterialDialog.Builder(mainActivity) - .title(R.string.question_delete_bookmark) - .theme(mainActivity.appTheme.getMaterialDialogTheme(mainActivity.applicationContext)) - .positiveColor(fab_skin) - .positiveText(getString(R.string.delete).toUpperCase()) // TODO: 29/4/2017 don't use toUpperCase(), 20/9,2017 why not? - .negativeColor(fab_skin) - .negativeText(android.R.string.cancel) - .build() - dialog.getActionButton(DialogAction.POSITIVE) - .setOnClickListener { - dataUtils!!.removeBook(position[p]!!) - utilsHandler!!.removeFromDatabase( - OperationData( - UtilsHandler.Operation.BOOKMARKS, - p.title.toString(), - p.summary.toString() - ) - ) - preferenceScreen.removePreference(p) - position.remove(p) - dialog.dismiss() - } - dialog.show() - } - - private fun disableButtonIfNotPath(path: EditText, dialog: MaterialDialog) { - path.addTextChangedListener( - object : SimpleTextWatcher() { - override fun afterTextChanged(s: Editable) { - dialog.getActionButton(DialogAction.POSITIVE).isEnabled = - FileUtils.isPathAccessible(s.toString(), sharedPrefs) - } - }) - } - - private fun disableButtonIfTitleEmpty(title: EditText, dialog: MaterialDialog) { - title.addTextChangedListener( - object : SimpleTextWatcher() { - override fun afterTextChanged(s: Editable) { - dialog.getActionButton(DialogAction.POSITIVE).isEnabled = title.length() > 0 - } - }) - } -} diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefFrag.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefFrag.java deleted file mode 100644 index 4cc1db4609..0000000000 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefFrag.java +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.ui.fragments.preference_fragments; - -import static com.amaze.filemanager.R.string.feedback; -import static com.amaze.filemanager.ui.activities.PreferencesActivity.START_PREFERENCE; -import static com.amaze.filemanager.utils.Utils.EMAIL_SUPPORT; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.util.List; - -import com.afollestad.materialdialogs.MaterialDialog; -import com.afollestad.materialdialogs.folderselector.FolderChooserDialog; -import com.amaze.filemanager.R; -import com.amaze.filemanager.application.AppConfig; -import com.amaze.filemanager.filesystem.files.CryptUtil; -import com.amaze.filemanager.ui.activities.AboutActivity; -import com.amaze.filemanager.ui.activities.PreferencesActivity; -import com.amaze.filemanager.ui.activities.superclasses.BasicActivity; -import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity; -import com.amaze.filemanager.ui.dialogs.OpenFileDialogFragment; -import com.amaze.filemanager.ui.provider.UtilitiesProvider; -import com.amaze.filemanager.ui.theme.AppTheme; -import com.amaze.filemanager.ui.views.preference.CheckBox; -import com.amaze.filemanager.utils.Utils; - -import android.Manifest; -import android.app.Activity; -import android.app.KeyguardManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.hardware.fingerprint.FingerprintManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Parcelable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ListView; -import android.widget.Toast; - -import androidx.annotation.Nullable; -import androidx.core.app.ActivityCompat; -import androidx.preference.Preference; -import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.PreferenceManager; - -public class PrefFrag extends PreferenceFragmentCompat - implements Preference.OnPreferenceClickListener { - - private static final String[] PREFERENCE_KEYS = { - PreferencesConstants.PREFERENCE_GRID_COLUMNS, - PreferencesConstants.FRAGMENT_THEME, - PreferencesConstants.PREFERENCE_ROOTMODE, - PreferencesConstants.PREFERENCE_SHOW_HIDDENFILES, - PreferencesConstants.FRAGMENT_FEEDBACK, - PreferencesConstants.FRAGMENT_ABOUT, - PreferencesConstants.FRAGMENT_COLORS, - PreferencesConstants.FRAGMENT_FOLDERS, - PreferencesConstants.FRAGMENT_QUICKACCESSES, - PreferencesConstants.FRAGMENT_ADVANCED_SEARCH, - PreferencesConstants.PREFERENCE_ZIP_EXTRACT_PATH, - PreferencesConstants.PREFERENCE_CLEAR_OPEN_FILE, - PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE - }; - - private UtilitiesProvider utilsProvider; - private SharedPreferences sharedPref; - /** This is a hack see {@link PreferencesActivity#saveListViewState(int, Parcelable)} */ - private ListView listView; - - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - utilsProvider = ((BasicActivity) getActivity()).getUtilsProvider(); - - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.preferences); - - sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); - - for (String PREFERENCE_KEY : PREFERENCE_KEYS) { - findPreference(PREFERENCE_KEY).setOnPreferenceClickListener(this); - } - - // crypt master password - final Preference masterPasswordPreference = - findPreference(PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD); - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 - || sharedPref.getBoolean(PreferencesConstants.PREFERENCE_CRYPT_FINGERPRINT, false)) { - // encryption feature not available - masterPasswordPreference.setEnabled(false); - } - masterPasswordPreference.setOnPreferenceClickListener(this); - - CheckBox checkBoxFingerprint = - (CheckBox) findPreference(PreferencesConstants.PREFERENCE_CRYPT_FINGERPRINT); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - - // finger print sensor - FingerprintManager fingerprintManager = null; - - final KeyguardManager keyguardManager = - (KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - fingerprintManager = - (FingerprintManager) getActivity().getSystemService(Context.FINGERPRINT_SERVICE); - if (fingerprintManager != null && fingerprintManager.isHardwareDetected()) { - checkBoxFingerprint.setEnabled(true); - } - } - - FingerprintManager finalFingerprintManager = fingerprintManager; - checkBoxFingerprint.setOnPreferenceChangeListener( - (preference, newValue) -> { - if (ActivityCompat.checkSelfPermission( - getActivity(), Manifest.permission.USE_FINGERPRINT) - != PackageManager.PERMISSION_GRANTED) { - Toast.makeText( - getActivity(), - getResources().getString(R.string.crypt_fingerprint_no_permission), - Toast.LENGTH_LONG) - .show(); - return false; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M - && finalFingerprintManager != null - && !finalFingerprintManager.hasEnrolledFingerprints()) { - Toast.makeText( - getActivity(), - getResources().getString(R.string.crypt_fingerprint_not_enrolled), - Toast.LENGTH_LONG) - .show(); - return false; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M - && keyguardManager != null - && !keyguardManager.isKeyguardSecure()) { - Toast.makeText( - getActivity(), - getResources().getString(R.string.crypt_fingerprint_no_security), - Toast.LENGTH_LONG) - .show(); - return false; - } - - masterPasswordPreference.setEnabled(false); - return true; - }); - } else { - - // fingerprint manager class not defined in the framework - checkBoxFingerprint.setEnabled(false); - } - } - - @Override - public boolean onPreferenceClick(Preference preference) { - final String[] sort; - final String[] dragToMoveArray; - MaterialDialog.Builder builder; - MaterialDialog.Builder dragDialogBuilder; - - switch (preference.getKey()) { - case PreferencesConstants.PREFERENCE_CLEAR_OPEN_FILE: - OpenFileDialogFragment.Companion.clearPreferences(sharedPref); - AppConfig.toast(getActivity(), getActivity().getString(R.string.done)); - return true; - case PreferencesConstants.PREFERENCE_GRID_COLUMNS: - sort = getResources().getStringArray(R.array.columns); - builder = new MaterialDialog.Builder(getActivity()); - builder.theme(utilsProvider.getAppTheme().getMaterialDialogTheme(requireContext())); - builder.title(R.string.gridcolumnno); - int current = - Integer.parseInt( - sharedPref.getString(PreferencesConstants.PREFERENCE_GRID_COLUMNS, "-1")); - current = current == -1 ? 0 : current; - if (current != 0) current = current - 1; - builder - .items(sort) - .itemsCallbackSingleChoice( - current, - (dialog, view, which, text) -> { - sharedPref - .edit() - .putString( - PreferencesConstants.PREFERENCE_GRID_COLUMNS, - "" + (which != 0 ? sort[which] : "" + -1)) - .apply(); - dialog.dismiss(); - return true; - }); - builder.build().show(); - return true; - case PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE: - dragToMoveArray = getResources().getStringArray(R.array.dragAndDropPreference); - dragDialogBuilder = new MaterialDialog.Builder(getActivity()); - dragDialogBuilder.theme( - utilsProvider.getAppTheme().getMaterialDialogTheme(requireContext())); - dragDialogBuilder.title(R.string.drag_and_drop_preference); - int currentDragPreference = - sharedPref.getInt( - PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE, - PreferencesConstants.PREFERENCE_DRAG_DEFAULT); - dragDialogBuilder - .items(dragToMoveArray) - .itemsCallbackSingleChoice( - currentDragPreference, - (dialog, view, which, text) -> { - sharedPref - .edit() - .putInt(PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE, which) - .apply(); - sharedPref - .edit() - .putString(PreferencesConstants.PREFERENCE_DRAG_AND_DROP_REMEMBERED, null) - .apply(); - dialog.dismiss(); - return true; - }); - dragDialogBuilder.build().show(); - return true; - case PreferencesConstants.FRAGMENT_THEME: - sort = getResources().getStringArray(R.array.theme); - current = Integer.parseInt(sharedPref.getString(PreferencesConstants.FRAGMENT_THEME, "4")); - builder = new MaterialDialog.Builder(getActivity()); - // builder.theme(utilsProvider.getAppTheme().getMaterialDialogTheme()); - builder - .items(sort) - .itemsCallbackSingleChoice( - current, - (dialog, view, which, text) -> { - utilsProvider - .getThemeManager() - .setAppTheme(AppTheme.getTheme(requireContext(), which)); - sharedPref - .edit() - .putString(PreferencesConstants.FRAGMENT_THEME, Integer.toString(which)) - .apply(); - dialog.dismiss(); - restartPC(getActivity()); - return true; - }); - builder.title(R.string.theme); - builder.build().show(); - return true; - case PreferencesConstants.FRAGMENT_FEEDBACK: - Intent emailIntent = Utils.buildEmailIntent(null, EMAIL_SUPPORT); - - PackageManager packageManager = getActivity().getPackageManager(); - List activities = - packageManager.queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY); - boolean isIntentSafe = activities.size() > 0; - - if (isIntentSafe) - startActivity(Intent.createChooser(emailIntent, getResources().getString(feedback))); - else - Toast.makeText( - getActivity(), - getResources().getString(R.string.send_email_to) + " " + EMAIL_SUPPORT, - Toast.LENGTH_LONG) - .show(); - return false; - case PreferencesConstants.FRAGMENT_ABOUT: - startActivity(new Intent(getActivity(), AboutActivity.class)); - return false; - /*FROM HERE BE FRAGMENTS*/ - case PreferencesConstants.FRAGMENT_COLORS: - ((PreferencesActivity) getActivity()).selectItem(PreferencesActivity.COLORS_PREFERENCE); - return true; - case PreferencesConstants.FRAGMENT_FOLDERS: - ((PreferencesActivity) getActivity()).selectItem(PreferencesActivity.FOLDERS_PREFERENCE); - return true; - case PreferencesConstants.FRAGMENT_QUICKACCESSES: - ((PreferencesActivity) getActivity()) - .selectItem(PreferencesActivity.QUICKACCESS_PREFERENCE); - return true; - case PreferencesConstants.FRAGMENT_ADVANCED_SEARCH: - ((PreferencesActivity) getActivity()) - .selectItem(PreferencesActivity.ADVANCEDSEARCH_PREFERENCE); - return true; - case PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD: - MaterialDialog.Builder masterPasswordDialogBuilder = - new MaterialDialog.Builder(getActivity()); - masterPasswordDialogBuilder.title( - getResources().getString(R.string.crypt_pref_master_password_title)); - - String decryptedPassword = null; - try { - String preferencePassword = - sharedPref.getString( - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT); - if (!preferencePassword.equals( - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT)) { - - // password is set, try to decrypt - decryptedPassword = CryptUtil.decryptPassword(getActivity(), preferencePassword); - } else { - // no password set in preferences, just leave the field empty - decryptedPassword = ""; - } - } catch (GeneralSecurityException | IOException e) { - e.printStackTrace(); - } - - masterPasswordDialogBuilder.input( - getResources().getString(R.string.authenticate_password), - decryptedPassword, - true, - (dialog, input) -> {}); - masterPasswordDialogBuilder.theme( - utilsProvider.getAppTheme().getMaterialDialogTheme(requireContext())); - masterPasswordDialogBuilder.positiveText(getResources().getString(R.string.ok)); - masterPasswordDialogBuilder.negativeText(getResources().getString(R.string.cancel)); - masterPasswordDialogBuilder.positiveColor(((ThemedActivity) getActivity()).getAccent()); - masterPasswordDialogBuilder.negativeColor(((ThemedActivity) getActivity()).getAccent()); - - masterPasswordDialogBuilder.onPositive( - (dialog, which) -> { - try { - - String inputText = dialog.getInputEditText().getText().toString(); - if (!inputText.equals( - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT)) { - - sharedPref - .edit() - .putString( - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, - CryptUtil.encryptPassword( - getActivity(), dialog.getInputEditText().getText().toString())) - .apply(); - } else { - // empty password, remove the preference - sharedPref - .edit() - .putString(PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, "") - .apply(); - } - } catch (GeneralSecurityException | IOException e) { - e.printStackTrace(); - sharedPref - .edit() - .putString( - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, - PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT) - .apply(); - } - }); - - masterPasswordDialogBuilder.onNegative((dialog, which) -> dialog.cancel()); - - masterPasswordDialogBuilder.build().show(); - return true; - // If there is no directory set, default to storage root (/storage/sdcard...) - case PreferencesConstants.PREFERENCE_ZIP_EXTRACT_PATH: - new FolderChooserDialog.Builder(getActivity()) - .tag(PreferencesConstants.PREFERENCE_ZIP_EXTRACT_PATH) - .goUpLabel(getString(R.string.folder_go_up_one_level)) - .chooseButton(R.string.choose_folder) - .cancelButton(R.string.cancel) - .initialPath( - sharedPref.getString( - PreferencesConstants.PREFERENCE_ZIP_EXTRACT_PATH, - Environment.getExternalStorageDirectory().getPath())) - .build() - .show((PreferencesActivity) getActivity()); - return true; - } - - return false; - } - - public static void restartPC(final Activity activity) { - if (activity == null) return; - - final int enter_anim = android.R.anim.fade_in; - final int exit_anim = android.R.anim.fade_out; - activity.overridePendingTransition(enter_anim, exit_anim); - activity.finish(); - activity.overridePendingTransition(enter_anim, exit_anim); - activity.startActivity(activity.getIntent()); - } - - @Override - public View onCreateView( - LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View v = super.onCreateView(inflater, container, savedInstanceState); - /** This is a hack see {@link PreferencesActivity#saveListViewState(int, Parcelable)} */ - listView = v.findViewById(android.R.id.list); - return v; - } - - @Override - public void onPause() { - super.onPause(); - - if (listView != null) { - /** This is a hack see {@link PreferencesActivity#saveListViewState(int, Parcelable)} */ - ((PreferencesActivity) getActivity()) - .saveListViewState(START_PREFERENCE, listView.onSaveInstanceState()); - } - } - - @Override - public void onResume() { - super.onResume(); - - if (listView != null) { - /** This is a hack see {@link PreferencesActivity#saveListViewState(int, Parcelable)} */ - Parcelable restored = - ((PreferencesActivity) getActivity()).restoreListViewState(START_PREFERENCE); - if (restored != null) { - listView.onRestoreInstanceState(restored); - } - } - } -} diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.java b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.java deleted file mode 100644 index 6d23aa3ae1..0000000000 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.ui.fragments.preference_fragments; - -public class PreferencesConstants { - // START fragments - public static final String FRAGMENT_THEME = "theme"; - public static final String FRAGMENT_COLORS = "colors"; - public static final String FRAGMENT_FOLDERS = "sidebar_folders"; - public static final String FRAGMENT_QUICKACCESSES = "sidebar_quickaccess"; - public static final String FRAGMENT_ADVANCED_SEARCH = "advancedsearch"; - public static final String FRAGMENT_ABOUT = "about"; - public static final String FRAGMENT_FEEDBACK = "feedback"; - // END fragments - - // START preferences.xml constants - public static final String PREFERENCE_INTELLI_HIDE_TOOLBAR = "intelliHideToolbar"; - public static final String PREFERENCE_SHOW_FILE_SIZE = "showFileSize"; - public static final String PREFERENCE_SHOW_PERMISSIONS = "showPermissions"; - public static final String PREFERENCE_SHOW_DIVIDERS = "showDividers"; - public static final String PREFERENCE_SHOW_HEADERS = "showHeaders"; - public static final String PREFERENCE_SHOW_GOBACK_BUTTON = "goBack_checkbox"; - public static final String PREFERENCE_SHOW_SIDEBAR_FOLDERS = "sidebar_folders_enable"; - public static final String PREFERENCE_SHOW_SIDEBAR_QUICKACCESSES = "sidebar_quickaccess_enable"; - public static final String PREFERENCE_ENABLE_MARQUEE_FILENAME = "enableMarqueeFilename"; - public static final String PREFERENCE_ROOT_LEGACY_LISTING = "legacyListing"; - public static final String PREFERENCE_DRAG_AND_DROP_PREFERENCE = "dragAndDropPreference"; - public static final String PREFERENCE_DRAG_AND_DROP_REMEMBERED = "dragOperationRemembered"; - - public static final String PREFERENCE_CLEAR_OPEN_FILE = "clear_open_file"; - public static final String PREFERENCE_BOOKMARKS_ADDED = "books_added"; - public static final String PREFERENCE_TEXTEDITOR_NEWSTACK = "texteditor_newstack"; - public static final String PREFERENCE_SHOW_HIDDENFILES = "showHidden"; - public static final String PREFERENCE_SHOW_LAST_MODIFIED = "showLastModified"; - public static final String PREFERENCE_USE_CIRCULAR_IMAGES = "circularimages"; - public static final String PREFERENCE_ROOTMODE = "rootmode"; - public static final String PREFERENCE_CHANGEPATHS = "typeablepaths"; - public static final String PREFERENCE_GRID_COLUMNS = "columns"; - public static final String PREFERENCE_SHOW_THUMB = "showThumbs"; - - public static final String PREFERENCE_CRYPT_MASTER_PASSWORD = "crypt_password"; - public static final String PREFERENCE_CRYPT_FINGERPRINT = "crypt_fingerprint"; - public static final String PREFERENCE_CRYPT_WARNING_REMEMBER = "crypt_remember"; - - public static final String ENCRYPT_PASSWORD_FINGERPRINT = "fingerprint"; - public static final String ENCRYPT_PASSWORD_MASTER = "master"; - - public static final String PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT = ""; - public static final boolean PREFERENCE_CRYPT_FINGERPRINT_DEFAULT = false; - public static final boolean PREFERENCE_CRYPT_WARNING_REMEMBER_DEFAULT = false; - - public static final String PREFERENCE_ZIP_EXTRACT_PATH = "extractpath"; - // END preferences.xml constants - - // START color_prefs.xml constants - public static final String PREFERENCE_SKIN = "skin"; - public static final String PREFERENCE_SKIN_TWO = "skin_two"; - public static final String PREFERENCE_ACCENT = "accent_skin"; - public static final String PREFERENCE_ICON_SKIN = "icon_skin"; - public static final String PREFERENCE_CURRENT_TAB = "current_tab"; - public static final String PREFERENCE_COLORIZE_ICONS = "coloriseIcons"; - public static final String PREFERENCE_COLORED_NAVIGATION = "colorednavigation"; - public static final String PREFERENCE_RANDOM_COLOR = "random_checkbox"; - // END color_prefs.xml constants - - // START folders_prefs.xml constants - public static final String PREFERENCE_SHORTCUT = "add_shortcut"; - // END folders_prefs.xml constants - - // START random preferences - public static final String PREFERENCE_DIRECTORY_SORT_MODE = "dirontop"; - public static final String PREFERENCE_DRAWER_HEADER_PATH = "drawer_header_path"; - public static final String PREFERENCE_URI = "URI"; - public static final String PREFERENCE_HIDEMODE = "hidemode"; - public static final String PREFERENCE_VIEW = "view"; - public static final String PREFERENCE_NEED_TO_SET_HOME = "needtosethome"; - - /** The value is an int with values RANDOM_INDEX, CUSTOM_INDEX, NO_DATA or [0, ...] */ - public static final String PREFERENCE_COLOR_CONFIG = "color config"; - - public static final String PREFERENCE_SAVED_PATHS = "savepaths"; - // END random preferences - - // START sort preferences - public static final String PREFERENCE_SORTBY_ONLY_THIS = "sortby_only_this"; - public static final String PREFERENCE_APPLIST_SORTBY = "AppsListFragment.sortBy"; - public static final String PREFERENCE_APPLIST_ISASCENDING = "AppsListFragment.isAscending"; - // END sort preferences - - // START drag and drop preferences - public static final int PREFERENCE_DRAG_DEFAULT = 0; - public static final int PREFERENCE_DRAG_TO_SELECT = 1; - public static final int PREFERENCE_DRAG_TO_MOVE_COPY = 2; - public static final String PREFERENCE_DRAG_REMEMBER_COPY = "copy"; - public static final String PREFERENCE_DRAG_REMEMBER_MOVE = "move"; - // END drag and drop preferences -} diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt new file mode 100644 index 0000000000..fb81f8fec5 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PreferencesConstants.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.amaze.filemanager.ui.fragments.preference_fragments + +object PreferencesConstants { + // appearance_prefs.xml + const val FRAGMENT_THEME = "theme" + const val PREFERENCE_USE_CIRCULAR_IMAGES = "circularimages" + const val PREFERENCE_SHOW_DIVIDERS = "showDividers" + const val PREFERENCE_SHOW_HEADERS = "showHeaders" + const val PREFERENCE_COLORIZE_ICONS = "coloriseIcons" + const val PREFERENCE_COLORED_NAVIGATION = "colorednavigation" + const val PREFERENCE_SELECT_COLOR_CONFIG = "selectcolorconfig" + const val PREFERENCE_INTELLI_HIDE_TOOLBAR = "intelliHideToolbar" + const val PREFERENCE_GRID_COLUMNS = "columns" + const val PREFERENCE_ENABLE_MARQUEE_FILENAME = "enableMarqueeFilename" + + // color_prefs.xml + const val PREFERENCE_SKIN = "skin" + const val PREFERENCE_SKIN_TWO = "skin_two" + const val PREFERENCE_ACCENT = "accent_skin" + const val PREFERENCE_ICON_SKIN = "icon_skin" + const val PRESELECTED_CONFIGS = "preselectedconfigs" + + /** The value is an int with values RANDOM_INDEX, CUSTOM_INDEX, NO_DATA or [0, ...] */ + const val PREFERENCE_COLOR_CONFIG = "color config" + + // ui_prefs.xml + const val PREFERENCE_SHOW_THUMB = "showThumbs" + const val PREFERENCE_SHOW_FILE_SIZE = "showFileSize" + const val PREFERENCE_SHOW_PERMISSIONS = "showPermissions" + const val PREFERENCE_SHOW_GOBACK_BUTTON = "goBack_checkbox" + const val PREFERENCE_SHOW_HIDDENFILES = "showHidden" + const val PREFERENCE_SHOW_LAST_MODIFIED = "showLastModified" + const val PREFERENCE_DRAG_AND_DROP_PREFERENCE = "dragAndDropPreference" + const val PREFERENCE_DRAG_AND_DROP_REMEMBERED = "dragOperationRemembered" + + // drag and drop + const val PREFERENCE_DRAG_DEFAULT = 0 + const val PREFERENCE_DRAG_TO_SELECT = 1 + const val PREFERENCE_DRAG_TO_MOVE_COPY = 2 + const val PREFERENCE_DRAG_REMEMBER_COPY = "copy" + const val PREFERENCE_DRAG_REMEMBER_MOVE = "move" // END drag and drop preferences + + // bookmarks_prefs.xml + const val PREFERENCE_SHOW_SIDEBAR_FOLDERS = "sidebar_bookmarks_enable" + + // quickaccess_prefs.xml + const val PREFERENCE_SHOW_SIDEBAR_QUICKACCESSES = "sidebar_quickaccess_enable" + + // behavior_prefs.xml + const val PREFERENCE_ROOT_LEGACY_LISTING = "legacyListing" + const val PREFERENCE_ROOTMODE = "rootmode" + const val PREFERENCE_CHANGEPATHS = "typeablepaths" + const val PREFERENCE_SAVED_PATHS = "savepaths" + const val PREFERENCE_ZIP_EXTRACT_PATH = "extractpath" + const val PREFERENCE_TEXTEDITOR_NEWSTACK = "texteditor_newstack" + + // security_prefs.xml + const val PREFERENCE_CRYPT_FINGERPRINT = "crypt_fingerprint" + const val PREFERENCE_CRYPT_MASTER_PASSWORD = "crypt_password" + const val PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT = "" + const val PREFERENCE_CRYPT_FINGERPRINT_DEFAULT = false + + const val PREFERENCE_CRYPT_WARNING_REMEMBER = "crypt_remember" + const val ENCRYPT_PASSWORD_FINGERPRINT = "fingerprint" + const val ENCRYPT_PASSWORD_MASTER = "master" + const val PREFERENCE_CRYPT_WARNING_REMEMBER_DEFAULT = false + + // others + const val PREFERENCE_CURRENT_TAB = ""; + const val PREFERENCE_BOOKMARKS_ADDED = "books_added" + const val PREFERENCE_DIRECTORY_SORT_MODE = "dirontop" + const val PREFERENCE_DRAWER_HEADER_PATH = "drawer_header_path" + const val PREFERENCE_URI = "URI" + const val PREFERENCE_VIEW = "view" + const val PREFERENCE_NEED_TO_SET_HOME = "needtosethome" + + const val PREFERENCE_SORTBY_ONLY_THIS = "sortby_only_this" + const val PREFERENCE_APPLIST_SORTBY = "AppsListFragment.sortBy" + const val PREFERENCE_APPLIST_ISASCENDING = "AppsListFragment.isAscending" +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefsFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefsFragment.kt new file mode 100644 index 0000000000..be742e58d8 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/PrefsFragment.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.fragments.preference_fragments + +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.widget.Toast +import androidx.preference.Preference +import com.amaze.filemanager.R +import com.amaze.filemanager.ui.activities.AboutActivity +import com.amaze.filemanager.utils.Utils + +class PrefsFragment : BasePrefsFragment() { + override val title = R.string.setting + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.preferences, rootKey) + + findPreference("appearance")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.pushFragment(AppearancePrefsFragment()) + true + } + + findPreference("ui")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.pushFragment(UiPrefsFragment()) + true + } + + findPreference("behavior")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.pushFragment(BehaviorPrefsFragment()) + true + } + + findPreference("security")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.pushFragment(SecurityPrefsFragment()) + true + } + + findPreference("about")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + startActivity(Intent(activity, AboutActivity::class.java)) + false + } + + findPreference("feedback")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + val emailIntent = Utils.buildEmailIntent(null, Utils.EMAIL_SUPPORT) + + val packageManager = activity.packageManager + val activities: List<*> = packageManager.queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY) + val isIntentSafe = activities.isNotEmpty() + + if (isIntentSafe) { + startActivity(Intent.createChooser(emailIntent, resources.getString(R.string.feedback))) + } else { + Toast.makeText( + getActivity(), + resources.getString(R.string.send_email_to) + " " + Utils.EMAIL_SUPPORT, + Toast.LENGTH_LONG) + .show() + } + + false + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/QuickAccessPref.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/QuickAccessesPrefsFragment.kt similarity index 52% rename from app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/QuickAccessPref.kt rename to app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/QuickAccessesPrefsFragment.kt index a6a71fb1a0..66e7b88493 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/QuickAccessPref.kt +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/QuickAccessesPrefsFragment.kt @@ -20,52 +20,41 @@ package com.amaze.filemanager.ui.fragments.preference_fragments -import android.content.SharedPreferences import android.os.Bundle import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager import androidx.preference.SwitchPreference import com.amaze.filemanager.R import com.amaze.filemanager.utils.TinyDB -import java.util.* -/** @author Emmanuel on 17/4/2017, at 23:17. - */ -class QuickAccessPref : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener { +class QuickAccessesPrefsFragment : BasePrefsFragment() { + override val title = R.string.show_quick_access_pref companion object { const val KEY = "quick access array" val KEYS = arrayOf( - "fastaccess", "recent", "image", "video", "audio", "documents", "apks" + "fastaccess", "recent", "image", "video", "audio", "documents", "apks" ) val DEFAULT = arrayOf(true, true, true, true, true, true, true) - private var prefPos: Map = HashMap() - init { - prefPos = KEYS.withIndex().associate { - Pair(it.value, it.index) - } + val prefPos: Map = KEYS.withIndex().associate { + Pair(it.value, it.index) } } - private var preferences: SharedPreferences? = null - private var currentValue: Array? = null - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.quickaccess_prefs, rootKey) + + val currentValue = TinyDB.getBooleanArray(activity.prefs, KEY, DEFAULT)!! - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.fastaccess_prefs) - preferences = PreferenceManager.getDefaultSharedPreferences(activity) - currentValue = TinyDB.getBooleanArray(preferences!!, KEY, DEFAULT) - for (i in 0 until preferenceScreen.preferenceCount) { - preferenceScreen.getPreference(i).onPreferenceClickListener = this + val onChange = Preference.OnPreferenceClickListener { + currentValue[prefPos[it.key]!!] = (it as SwitchPreference).isChecked + TinyDB.putBooleanArray(activity.prefs, KEY, currentValue!!) + + true } - } - override fun onPreferenceClick(preference: Preference): Boolean { - currentValue!![prefPos[preference.key]!!] = (preference as SwitchPreference).isChecked - TinyDB.putBooleanArray(preferences!!, KEY, currentValue!!) - return true + for (k in KEYS) { + findPreference(k)!!.onPreferenceClickListener = onChange + } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/SecurityPrefsFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/SecurityPrefsFragment.kt new file mode 100644 index 0000000000..e08c55fc33 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/SecurityPrefsFragment.kt @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014-2020 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.fragments.preference_fragments + +import android.Manifest +import android.app.KeyguardManager +import android.content.Context +import android.content.pm.PackageManager +import android.hardware.fingerprint.FingerprintManager +import android.os.Build +import android.os.Bundle +import android.widget.Toast +import androidx.core.app.ActivityCompat +import androidx.preference.Preference +import com.afollestad.materialdialogs.MaterialDialog +import com.amaze.filemanager.R +import com.amaze.filemanager.filesystem.files.CryptUtil +import com.amaze.filemanager.ui.activities.superclasses.ThemedActivity +import com.amaze.filemanager.ui.views.preference.CheckBox +import java.io.IOException +import java.security.GeneralSecurityException + +class SecurityPrefsFragment : BasePrefsFragment() { + override val title = R.string.security + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.security_prefs, rootKey) + + val masterPasswordPreference = findPreference(PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD)!! + val checkBoxFingerprint = findPreference(PreferencesConstants.PREFERENCE_CRYPT_FINGERPRINT)!! + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 + || activity.prefs.getBoolean(PreferencesConstants.PREFERENCE_CRYPT_FINGERPRINT, false)) { + // encryption feature not available + masterPasswordPreference.isEnabled = false + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + + // finger print sensor + var fingerprintManager: FingerprintManager? = null + val keyguardManager = activity.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager? + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + fingerprintManager = activity.getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager? + if (fingerprintManager != null && fingerprintManager.isHardwareDetected) { + checkBoxFingerprint.isEnabled = true + } + } + + checkBoxFingerprint.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ -> + if (ActivityCompat.checkSelfPermission( + activity, Manifest.permission.USE_FINGERPRINT) + != PackageManager.PERMISSION_GRANTED) { + Toast.makeText( + getActivity(), + resources.getString(R.string.crypt_fingerprint_no_permission), + Toast.LENGTH_LONG) + .show() + false + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && fingerprintManager != null && !fingerprintManager.hasEnrolledFingerprints()) { + Toast.makeText( + getActivity(), + resources.getString(R.string.crypt_fingerprint_not_enrolled), + Toast.LENGTH_LONG) + .show() + false + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && keyguardManager != null && !keyguardManager.isKeyguardSecure) { + Toast.makeText( + getActivity(), + resources.getString(R.string.crypt_fingerprint_no_security), + Toast.LENGTH_LONG) + .show() + false + } else { + masterPasswordPreference.isEnabled = false + true + } + } + } else { + + // fingerprint manager class not defined in the framework + checkBoxFingerprint.isEnabled = false + } + + masterPasswordPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { + val masterPasswordDialogBuilder = MaterialDialog.Builder(activity) + masterPasswordDialogBuilder.title( + resources.getString(R.string.crypt_pref_master_password_title)) + + var decryptedPassword: String? = null + try { + val preferencePassword = activity.prefs.getString( + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT)!! + decryptedPassword = if (preferencePassword != PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT) { + + // password is set, try to decrypt + CryptUtil.decryptPassword(getActivity(), preferencePassword) + } else { + // no password set in preferences, just leave the field empty + "" + } + } catch (e: GeneralSecurityException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() + } + + masterPasswordDialogBuilder.input( + resources.getString(R.string.authenticate_password), + decryptedPassword, + true + ) { _, _ -> } + masterPasswordDialogBuilder.theme( + activity.utilsProvider.appTheme.getMaterialDialogTheme(requireContext())) + masterPasswordDialogBuilder.positiveText(resources.getString(R.string.ok)) + masterPasswordDialogBuilder.negativeText(resources.getString(R.string.cancel)) + masterPasswordDialogBuilder.positiveColor((getActivity() as ThemedActivity?)!!.accent) + masterPasswordDialogBuilder.negativeColor((getActivity() as ThemedActivity?)!!.accent) + + masterPasswordDialogBuilder.onPositive { dialog, _ -> + try { + val inputText = dialog.inputEditText!!.text.toString() + if (inputText != PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT) { + val editor = activity.prefs.edit() + editor.putString( + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, + CryptUtil.encryptPassword( + getActivity(), dialog.inputEditText!!.text.toString())) + editor.apply() + } else { + val editor = activity.prefs.edit() + editor.putString(PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, "") + editor.apply() + } + } catch (e: GeneralSecurityException) { + e.printStackTrace() + val editor = activity.prefs.edit() + editor.putString( + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT) + editor.apply() + } catch (e: IOException) { + e.printStackTrace() + val editor = activity.prefs.edit() + editor.putString( + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD, + PreferencesConstants.PREFERENCE_CRYPT_MASTER_PASSWORD_DEFAULT) + editor.apply() + } + } + + masterPasswordDialogBuilder.onNegative { dialog, _ -> dialog.cancel() } + + masterPasswordDialogBuilder.build().show() + + true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/UiPrefsFragment.kt b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/UiPrefsFragment.kt new file mode 100644 index 0000000000..8a618b9e15 --- /dev/null +++ b/app/src/main/java/com/amaze/filemanager/ui/fragments/preference_fragments/UiPrefsFragment.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , + * Emmanuel Messulam, Raymond Lai and Contributors. + * + * This file is part of Amaze File Manager. + * + * Amaze File Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.amaze.filemanager.ui.fragments.preference_fragments + +import android.os.Bundle +import androidx.preference.Preference +import com.afollestad.materialdialogs.MaterialDialog +import com.amaze.filemanager.R + +class UiPrefsFragment : BasePrefsFragment() { + override val title = R.string.ui + + private var dragAndDropPref: Preference? = null + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.ui_prefs, rootKey) + + findPreference("sidebar_bookmarks")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.pushFragment(BookmarksPrefsFragment()) + true + } + + findPreference("sidebar_quick_access")!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + activity.pushFragment(QuickAccessesPrefsFragment()) + true + } + + val dragToMoveArray = resources.getStringArray(R.array.dragAndDropPreference) + dragAndDropPref = findPreference(PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE) + updateDragAndDropPreferenceSummary() + dragAndDropPref?.onPreferenceClickListener = Preference.OnPreferenceClickListener { + val dragDialogBuilder = MaterialDialog.Builder(activity) + dragDialogBuilder.theme( + activity.utilsProvider.appTheme.getMaterialDialogTheme(requireContext())) + dragDialogBuilder.title(R.string.drag_and_drop_preference) + val currentDragPreference: Int = activity.prefs.getInt( + PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE, + PreferencesConstants.PREFERENCE_DRAG_DEFAULT) + dragDialogBuilder + .items(*dragToMoveArray) + .itemsCallbackSingleChoice(currentDragPreference) + { dialog, _, which, _ -> + val editor = activity.prefs.edit() + editor.putInt(PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE, which) + editor.putString(PreferencesConstants.PREFERENCE_DRAG_AND_DROP_REMEMBERED, null) + editor.apply() + dialog.dismiss() + updateDragAndDropPreferenceSummary() + true + } + dragDialogBuilder.build().show() + true + } + } + + private fun updateDragAndDropPreferenceSummary() { + val value = activity.prefs.getInt(PreferencesConstants.PREFERENCE_DRAG_AND_DROP_PREFERENCE, PreferencesConstants.PREFERENCE_DRAG_DEFAULT) + val dragToMoveArray = resources.getStringArray(R.array.dragAndDropPreference) + dragAndDropPref?.summary = dragToMoveArray[value] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java index c8b066a44a..a79aa89d18 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java +++ b/app/src/main/java/com/amaze/filemanager/ui/views/drawer/Drawer.java @@ -49,7 +49,7 @@ import com.amaze.filemanager.ui.fragments.FtpServerFragment; import com.amaze.filemanager.ui.fragments.MainFragment; import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants; -import com.amaze.filemanager.ui.fragments.preference_fragments.QuickAccessPref; +import com.amaze.filemanager.ui.fragments.preference_fragments.QuickAccessesPrefsFragment; import com.amaze.filemanager.ui.theme.AppTheme; import com.amaze.filemanager.utils.Billing; import com.amaze.filemanager.utils.BookSorter; @@ -420,7 +420,7 @@ public void refreshDrawer() { Boolean[] quickAccessPref = TinyDB.getBooleanArray( - mainActivity.getPrefs(), QuickAccessPref.KEY, QuickAccessPref.Companion.getDEFAULT()); + mainActivity.getPrefs(), QuickAccessesPrefsFragment.KEY, QuickAccessesPrefsFragment.Companion.getDEFAULT()); if (mainActivity.getBoolean(PREFERENCE_SHOW_SIDEBAR_QUICKACCESSES)) { if (quickAccessPref[0]) { diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/preference/InvalidablePreferenceCategory.kt b/app/src/main/java/com/amaze/filemanager/ui/views/preference/InvalidablePreferenceCategory.kt deleted file mode 100644 index 6913705eac..0000000000 --- a/app/src/main/java/com/amaze/filemanager/ui/views/preference/InvalidablePreferenceCategory.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2014-2021 Arpit Khurana , Vishal Nehra , - * Emmanuel Messulam, Raymond Lai and Contributors. - * - * This file is part of Amaze File Manager. - * - * Amaze File Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.amaze.filemanager.ui.views.preference - -import android.content.Context -import android.util.AttributeSet -import androidx.annotation.ColorInt -import androidx.appcompat.widget.AppCompatTextView -import androidx.preference.PreferenceCategory -import androidx.preference.PreferenceViewHolder -import com.amaze.filemanager.utils.PreferenceUtils - -/** @author Emmanuel on 15/10/2017, at 20:46. - */ -class InvalidablePreferenceCategory(context: Context?, attrs: AttributeSet?) : - PreferenceCategory(context, attrs) { - - private var titleColor = 0 - - override fun onBindViewHolder(holder: PreferenceViewHolder?) { - super.onBindViewHolder(holder) - val title: AppCompatTextView = holder?.findViewById(android.R.id.title) as AppCompatTextView - title.setTextColor(titleColor) - } - - /** - * notify change of title colour as necessary - */ - fun invalidate(@ColorInt accentColor: Int) { - titleColor = PreferenceUtils.getStatusColor(accentColor) - notifyChanged() - } -} diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/preference/PathSwitchPreference.kt b/app/src/main/java/com/amaze/filemanager/ui/views/preference/PathSwitchPreference.kt index 1afe1f2d2b..f34bfef760 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/views/preference/PathSwitchPreference.kt +++ b/app/src/main/java/com/amaze/filemanager/ui/views/preference/PathSwitchPreference.kt @@ -33,32 +33,21 @@ class PathSwitchPreference(context: Context?) : Preference(context) { var lastItemClicked = -1 private set + var onEdit: (PathSwitchPreference) -> Unit = {} + var onDelete: (PathSwitchPreference) -> Unit = {} + init { widgetLayoutResource = R.layout.namepathswitch_preference } override fun onBindViewHolder(holder: PreferenceViewHolder?) { - holder?.itemView.let { view -> - setListener(view, R.id.edit, EDIT) - setListener(view, R.id.delete, DELETE) - view?.setOnClickListener(null) + holder?.itemView?.let { view -> + view.findViewById(R.id.edit).setOnClickListener { onEdit(this) } + view.findViewById(R.id.delete).setOnClickListener { onDelete(this) } + view.setOnClickListener(null) } // Keep this before things that need changing what's on screen super.onBindViewHolder(holder) } - - private fun setListener(v: View?, @IdRes id: Int, elem: Int): View.OnClickListener { - val l = View.OnClickListener { - lastItemClicked = elem - onPreferenceClickListener.onPreferenceClick(this) - } - v?.findViewById(id)?.setOnClickListener(l) - return l - } - - companion object { - const val EDIT = 0 - const val DELETE = 1 - } } diff --git a/app/src/main/java/com/amaze/filemanager/ui/views/preference/SelectedColorsPreference.kt b/app/src/main/java/com/amaze/filemanager/ui/views/preference/SelectedColorsPreference.kt index 57a3c3044d..0225360274 100644 --- a/app/src/main/java/com/amaze/filemanager/ui/views/preference/SelectedColorsPreference.kt +++ b/app/src/main/java/com/amaze/filemanager/ui/views/preference/SelectedColorsPreference.kt @@ -31,6 +31,7 @@ import androidx.preference.PreferenceViewHolder import com.amaze.filemanager.R import com.amaze.filemanager.ui.dialogs.ColorPickerDialog import com.amaze.filemanager.ui.views.CircularColorsView +import com.amaze.filemanager.ui.fragments.preference_fragments.PreferencesConstants /** * This is the external notification that shows some text and a CircularColorsView. @@ -63,7 +64,13 @@ class SelectedColorsPreference(context: Context?, attrs: AttributeSet?) : } } - override fun getSummary(): CharSequence = "" + override fun getSummary(): CharSequence { + val colorPickerPref = sharedPreferences.getInt( + PreferencesConstants.PREFERENCE_COLOR_CONFIG, + ColorPickerDialog.NO_DATA + ) + return context.getString(ColorPickerDialog.getTitle(colorPickerPref)) + } override fun onGetDefaultValue(a: TypedArray?, index: Int): Any { return a!!.getString(index)!! diff --git a/app/src/main/res/drawable/ic_baseline_add_24.xml b/app/src/main/res/drawable/ic_baseline_add_24.xml new file mode 100644 index 0000000000..eb232541d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_add_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_brush_white_24.xml b/app/src/main/res/drawable/ic_baseline_brush_white_24.xml new file mode 100644 index 0000000000..7bda920f14 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_brush_white_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_info_white_24.xml b/app/src/main/res/drawable/ic_baseline_info_white_24.xml new file mode 100644 index 0000000000..9f62d94da8 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_info_white_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_lock_white_24.xml b/app/src/main/res/drawable/ic_baseline_lock_white_24.xml new file mode 100644 index 0000000000..c3c7ac2041 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_lock_white_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_settings_white_24.xml b/app/src/main/res/drawable/ic_baseline_settings_white_24.xml new file mode 100644 index 0000000000..b240b83007 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_settings_white_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_vertical_split_white_24.xml b/app/src/main/res/drawable/ic_baseline_vertical_split_white_24.xml new file mode 100644 index 0000000000..eed2e0bf72 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_vertical_split_white_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_preference_appearance.xml b/app/src/main/res/drawable/ic_preference_appearance.xml new file mode 100644 index 0000000000..c132c36371 --- /dev/null +++ b/app/src/main/res/drawable/ic_preference_appearance.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_preference_behavior.xml b/app/src/main/res/drawable/ic_preference_behavior.xml new file mode 100644 index 0000000000..a85f58d963 --- /dev/null +++ b/app/src/main/res/drawable/ic_preference_behavior.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_preference_info.xml b/app/src/main/res/drawable/ic_preference_info.xml new file mode 100644 index 0000000000..b515fbf91c --- /dev/null +++ b/app/src/main/res/drawable/ic_preference_info.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_preference_security.xml b/app/src/main/res/drawable/ic_preference_security.xml new file mode 100644 index 0000000000..d9aad669f0 --- /dev/null +++ b/app/src/main/res/drawable/ic_preference_security.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_preference_ui.xml b/app/src/main/res/drawable/ic_preference_ui.xml new file mode 100644 index 0000000000..7197378302 --- /dev/null +++ b/app/src/main/res/drawable/ic_preference_ui.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/prefsfrag.xml b/app/src/main/res/layout/activity_preferences.xml similarity index 94% rename from app/src/main/res/layout/prefsfrag.xml rename to app/src/main/res/layout/activity_preferences.xml index 319155fd82..32c30756a1 100644 --- a/app/src/main/res/layout/prefsfrag.xml +++ b/app/src/main/res/layout/activity_preferences.xml @@ -20,7 +20,7 @@ --> @@ -36,7 +36,7 @@ app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c81c639985..8261564231 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -121,4 +121,11 @@ #F4511E + + #673ab7 + #f9a825 + #009688 + #da4336 + #757575 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 423d70b789..62205ec2a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -734,6 +734,13 @@ You only need to do this once, until the next time you select a new location for \n\nAmaze File Manager needs this permission too. After pressing "Grant"here, please select Allow access to manage all files option on the next screen. \n\nCanceling this dialog will exit the app. User apps + Appearance + Behavior + Customize the theme, colors and appearance of the file manager + Control what information to show, customize the sidebar, and toggle UI preferences + Modify behaviors such as advanced search and whether to open files as a new task + Set up password and encryption + Files recently accessed through Amaze Recently created or modified files diff --git a/app/src/main/res/xml/advancedsearch_prefs.xml b/app/src/main/res/xml/advancedsearch_prefs.xml deleted file mode 100644 index bfe829b582..0000000000 --- a/app/src/main/res/xml/advancedsearch_prefs.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/appearance_prefs.xml b/app/src/main/res/xml/appearance_prefs.xml new file mode 100644 index 0000000000..f855254f70 --- /dev/null +++ b/app/src/main/res/xml/appearance_prefs.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/behavior_prefs.xml b/app/src/main/res/xml/behavior_prefs.xml new file mode 100644 index 0000000000..1ef28f64e3 --- /dev/null +++ b/app/src/main/res/xml/behavior_prefs.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/bookmarks_prefs.xml b/app/src/main/res/xml/bookmarks_prefs.xml new file mode 100644 index 0000000000..0f3998900f --- /dev/null +++ b/app/src/main/res/xml/bookmarks_prefs.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/color_prefs.xml b/app/src/main/res/xml/color_prefs.xml index 7dd26118ec..400afa7997 100644 --- a/app/src/main/res/xml/color_prefs.xml +++ b/app/src/main/res/xml/color_prefs.xml @@ -1,20 +1,24 @@ - + + - - + app:key="skin" + app:summary="@string/primary_color_summary" + app:title="@string/primary_color_title" /> + + + \ No newline at end of file diff --git a/app/src/main/res/xml/conficolor_prefs.xml b/app/src/main/res/xml/conficolor_prefs.xml deleted file mode 100644 index 6b468c9a30..0000000000 --- a/app/src/main/res/xml/conficolor_prefs.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/fastaccess_prefs.xml b/app/src/main/res/xml/fastaccess_prefs.xml deleted file mode 100644 index 97016fa11d..0000000000 --- a/app/src/main/res/xml/fastaccess_prefs.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/folders_prefs.xml b/app/src/main/res/xml/folders_prefs.xml deleted file mode 100644 index 326e1264ab..0000000000 --- a/app/src/main/res/xml/folders_prefs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index ff96a1d2a8..b78c38a5ae 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -17,168 +17,46 @@ along with this program. If not, see . --> - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/xml/quickaccess_prefs.xml b/app/src/main/res/xml/quickaccess_prefs.xml new file mode 100644 index 0000000000..fd6cd0cc65 --- /dev/null +++ b/app/src/main/res/xml/quickaccess_prefs.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/security_prefs.xml b/app/src/main/res/xml/security_prefs.xml new file mode 100644 index 0000000000..3ecf06d4ba --- /dev/null +++ b/app/src/main/res/xml/security_prefs.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/ui_prefs.xml b/app/src/main/res/xml/ui_prefs.xml new file mode 100644 index 0000000000..df44fda939 --- /dev/null +++ b/app/src/main/res/xml/ui_prefs.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file