From 8ae692cfbe04e8bec5c503db95c1ed6b2e8512f0 Mon Sep 17 00:00:00 2001 From: Sean Mac Gillicuddy Date: Wed, 14 Aug 2019 16:14:05 +0100 Subject: [PATCH 1/5] #1333 Why create an empty text.text and hidden .storageLocationMarker? --- .../java/eu/mhutti1/utils/storage/Bytes.kt | 43 +++++ .../mhutti1/utils/storage/ExternalPaths.java | 59 ------- .../eu/mhutti1/utils/storage/ExternalPaths.kt | 56 ++++++ .../mhutti1/utils/storage/StorageDevice.java | 164 ------------------ .../eu/mhutti1/utils/storage/StorageDevice.kt | 112 ++++++++++++ .../utils/storage/StorageDeviceUtils.java | 131 -------------- .../utils/storage/StorageDeviceUtils.kt | 119 +++++++++++++ .../storage/StorageSelectArrayAdapter.java | 6 +- .../settings/KiwixSettingsActivity.java | 2 +- 9 files changed, 334 insertions(+), 358 deletions(-) create mode 100644 app/src/main/java/eu/mhutti1/utils/storage/Bytes.kt delete mode 100644 app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.java create mode 100644 app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.kt delete mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.java create mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt delete mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.java create mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt diff --git a/app/src/main/java/eu/mhutti1/utils/storage/Bytes.kt b/app/src/main/java/eu/mhutti1/utils/storage/Bytes.kt new file mode 100644 index 000000000..34c0c66d1 --- /dev/null +++ b/app/src/main/java/eu/mhutti1/utils/storage/Bytes.kt @@ -0,0 +1,43 @@ +/* + * Kiwix Android + * Copyright (C) 2018 Kiwix + * + * This program 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 eu.mhutti1.utils.storage + +import java.text.DecimalFormat + +const val Kb = 1 * 1024L +const val Mb = Kb * 1024 +const val Gb = Mb * 1024 +const val Tb = Gb * 1024 +const val Pb = Tb * 1024 +const val Eb = Pb * 1024 + +inline class Bytes(val size: Long) { + val humanReadable + get() = when { + size < Kb -> "${floatForm(size)} byte" + size < Mb -> "${floatForm(size / Kb)} KB" + size < Gb -> "${floatForm(size / Mb)} MB" + size < Tb -> "${floatForm(size / Gb)} GB" + size < Pb -> "${floatForm(size / Tb)} TB" + size < Eb -> "${floatForm(size / Pb)} PB" + size >= Eb -> "${floatForm(size / Eb)} EB" + else -> "???" + } + + private fun floatForm(d: Long) = DecimalFormat("#.#").format(d.toDouble()) +} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.java b/app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.java deleted file mode 100644 index d6994a4fc..000000000 --- a/app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2016 Isaac Hutt - * - * This program 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 - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -package eu.mhutti1.utils.storage; - -import android.annotation.SuppressLint; - -class ExternalPaths { - - @SuppressLint("SdCardPath") private static final String[] paths = { - "/storage/sdcard0", - "/storage/sdcard1", - "/storage/extsdcard", - "/storage/extSdCard", - "/storage/sdcard0/external_sdcard", - "/mnt/sdcard/external_sd", - "/mnt/external_sd", - "/mnt/media_rw/*", - "/removable/microsd", - "/mnt/emmc", - "/storage/external_SD", - "/storage/ext_sd", - "/storage/removable/sdcard1", - "/data/sdext", - "/data/sdext2", - "/data/sdext3", - "/data/sdext2", - "/data/sdext3", - "/data/sdext4", - "/sdcard", - "/sdcard1", - "/sdcard2", - "/storage/microsd", - "/mnt/extsd", - "/extsd", - "/mnt/sdcard", - "/misc/android", - }; - - public static String[] getPossiblePaths() { - return paths; - } -} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.kt b/app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.kt new file mode 100644 index 000000000..9da96562f --- /dev/null +++ b/app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2016 Isaac Hutt + * + * This program 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package eu.mhutti1.utils.storage + +import android.annotation.SuppressLint + +internal object ExternalPaths { + + @SuppressLint("SdCardPath") + val possiblePaths = arrayOf( + "/storage/sdcard0", + "/storage/sdcard1", + "/storage/extsdcard", + "/storage/extSdCard", + "/storage/sdcard0/external_sdcard", + "/mnt/sdcard/external_sd", + "/mnt/external_sd", + "/mnt/media_rw/*", + "/removable/microsd", + "/mnt/emmc", + "/storage/external_SD", + "/storage/ext_sd", + "/storage/removable/sdcard1", + "/data/sdext", + "/data/sdext2", + "/data/sdext3", + "/data/sdext2", + "/data/sdext3", + "/data/sdext4", + "/sdcard", + "/sdcard1", + "/sdcard2", + "/storage/microsd", + "/mnt/extsd", + "/extsd", + "/mnt/sdcard", + "/misc/android" + ) +} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.java b/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.java deleted file mode 100644 index 0aa65af62..000000000 --- a/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2016 Isaac Hutt - * - * This program 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 - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -package eu.mhutti1.utils.storage; - -import android.os.Build; -import android.os.StatFs; -import android.util.Log; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.text.DecimalFormat; - -public class StorageDevice { - - // File object containing device path - private final File mFile; - - private final boolean mInternal; - - private boolean mDuplicate = true; - - public StorageDevice(String path, boolean internal) { - mFile = new File(path); - mInternal = internal; - if (mFile.exists()) { - createLocationCode(); - } - } - - public StorageDevice(File file, boolean internal) { - mFile = file; - mInternal = internal; - if (mFile.exists()) { - createLocationCode(); - } - } - - // Get device path - public String getName() { - return mFile.getPath(); - } - - // Get available space on device - public String getSize() { - return bytesToHuman(getAvailableBytes()); - } - - private Long getAvailableBytes() { - StatFs statFs = new StatFs(mFile.getPath()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - return statFs.getBlockSizeLong() * statFs.getAvailableBlocksLong(); - } else { - return (long) statFs.getBlockSize() * (long) statFs.getAvailableBlocks(); - } - } - - public String getTotalSize() { - return bytesToHuman(getTotalBytes()); - } - - // Get total space on device - private Long getTotalBytes() { - StatFs statFs = new StatFs((mFile.getPath())); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - return statFs.getBlockSizeLong() * statFs.getBlockCountLong(); - } else { - return (long) statFs.getBlockSize() * (long) statFs.getBlockCount(); - } - } - - // Convert bytes to human readable form - private static String bytesToHuman(long size) { - long Kb = 1 * 1024; - long Mb = Kb * 1024; - long Gb = Mb * 1024; - long Tb = Gb * 1024; - long Pb = Tb * 1024; - long Eb = Pb * 1024; - - if (size < Kb) return floatForm(size) + " byte"; - if (size >= Kb && size < Mb) return floatForm((double) size / Kb) + " KB"; - if (size >= Mb && size < Gb) return floatForm((double) size / Mb) + " MB"; - if (size >= Gb && size < Tb) return floatForm((double) size / Gb) + " GB"; - if (size >= Tb && size < Pb) return floatForm((double) size / Tb) + " TB"; - if (size >= Pb && size < Eb) return floatForm((double) size / Pb) + " PB"; - if (size >= Eb) return floatForm((double) size / Eb) + " EB"; - - return "???"; - } - - public boolean isInternal() { - return mInternal; - } - - public File getPath() { - return mFile; - } - - private static String floatForm(double d) { - return new DecimalFormat("#.#").format(d); - } - - // Create unique file to identify duplicate devices. - private void createLocationCode() { - if (!getLocationCodeFromFolder(mFile)) { - File locationCode = new File(mFile.getPath(), ".storageLocationMarker"); - try { - locationCode.createNewFile(); - FileWriter fw = new FileWriter(locationCode); - fw.write(mFile.getPath()); - fw.close(); - } catch (IOException e) { - Log.d("android-storage-devices", "Unable to create marker file, duplicates may be listed"); - } - } - } - - // Check if there is already a device code in our path - private boolean getLocationCodeFromFolder(File folder) { - File locationCode = new File(folder.getPath(), ".storageLocationMarker"); - if (locationCode.exists()) { - try ( BufferedReader br = new BufferedReader(new FileReader(locationCode))){ - if (br.readLine().equals(mFile.getPath())) { - mDuplicate = false; - } else { - mDuplicate = true; - return true; - } - } catch (Exception e) { - return true; - } - } - String path = folder.getPath(); - String parent = path.substring(0, path.lastIndexOf("/")); - if (parent.equals("")) { - mDuplicate = false; - return false; - } - return getLocationCodeFromFolder(new File(parent)); - } - - public boolean isDuplicate() { - return mDuplicate; - } -} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt b/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt new file mode 100644 index 000000000..f55cfd162 --- /dev/null +++ b/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2016 Isaac Hutt + * + * This program 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package eu.mhutti1.utils.storage + +import android.os.Build +import android.os.StatFs +import android.util.Log +import java.io.BufferedReader +import java.io.File +import java.io.FileReader +import java.io.FileWriter +import java.io.IOException + +data class StorageDevice( + val file: File, + val isInternal: Boolean +) { + + var isDuplicate = true + private set + + val name: String + get() = file.path + + val availableSpace: String + get() = Bytes(availableBytes).humanReadable + + private val availableBytes: Long + get() = StatFs(file.path).let { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) + it.blockSizeLong * it.availableBlocksLong + else + it.blockSize.toLong() * it.availableBlocks.toLong() + } + + val totalSize: String + get() = Bytes(totalBytes).humanReadable + + // Get total space on device + private val totalBytes: Long + get() = StatFs(file.path).let { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) + it.blockSizeLong * it.blockCountLong + else + it.blockSize.toLong() * it.blockCount.toLong() + } + + constructor(path: String, internal: Boolean) : this(File(path), internal) + + init { + if (file.exists()) { + createLocationCode() + } + } + + // Create unique file to identify duplicate devices. + private fun createLocationCode() { + if (!getLocationCodeFromFolder(file)) { + val locationCode = File(file.path, ".storageLocationMarker") + try { + locationCode.createNewFile() + val fw = FileWriter(locationCode) + fw.write(file.path) + fw.close() + } catch (e: IOException) { + Log.d("android-storage-devices", "Unable to create marker file, duplicates may be listed") + } + } + } + + // Check if there is already a device code in our path + private fun getLocationCodeFromFolder(folder: File): Boolean { + val locationCode = File(folder.path, ".storageLocationMarker") + if (locationCode.exists()) { + try { + BufferedReader(FileReader(locationCode)).use { br -> + if (br.readLine() == file.path) { + isDuplicate = false + } else { + isDuplicate = true + return@getLocationCodeFromFolder true + } + } + } catch (e: Exception) { + return true + } + } + val parentFile = folder.parentFile + if (parentFile == null) { + isDuplicate = false + return false + } + return getLocationCodeFromFolder(parentFile) + } +} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.java b/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.java deleted file mode 100644 index 68a79d4b8..000000000 --- a/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2016 Isaac Hutt - * - * This program 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 - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -package eu.mhutti1.utils.storage; - -import android.content.Context; -import android.os.Environment; -import androidx.core.content.ContextCompat; -import java.io.File; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.util.ArrayList; - -public class StorageDeviceUtils { - - public static ArrayList getStorageDevices(Context context, boolean writable) { - ArrayList storageDevices = new ArrayList<>(); - - // Add as many possible mount points as we know about - - // Only add this device if its very likely that we have missed a users sd card - if (Environment.isExternalStorageEmulated()) { - // This is our internal storage directory - storageDevices.add(new StorageDevice( - generalisePath(Environment.getExternalStorageDirectory().getPath(), writable), true)); - } else { - // This is an external storage directory - storageDevices.add(new StorageDevice( - generalisePath(Environment.getExternalStorageDirectory().getPath(), writable), false)); - } - - // These are possible manufacturer sdcard mount points - - String[] paths = ExternalPaths.getPossiblePaths(); - - for (String path : paths) { - if (path.endsWith("*")) { - File root = new File(path.substring(0, path.length() - 1)); - File[] directories = root.listFiles(file -> file.isDirectory()); - if (directories != null) { - for (File dir : directories) { - storageDevices.add(new StorageDevice(dir, false)); - } - } - } else { - storageDevices.add(new StorageDevice(path, false)); - } - } - - // Iterate through any sdcards manufacturers may have specified - for (File file : ContextCompat.getExternalFilesDirs(context, "")) { - if (file != null) { - storageDevices.add(new StorageDevice(generalisePath(file.getPath(), writable), false)); - } - } - - // Check all devices exist, we can write to them if required and they are not duplicates - return checkStorageValid(writable, storageDevices); - } - - // Remove app specific path from directories so that we can search them from the top - private static String generalisePath(String path, boolean writable) { - if (writable) { - return path; - } - int endIndex = path.lastIndexOf("/Android/data/"); - if (endIndex != -1) { - return path.substring(0, endIndex); - } - return path; - } - - private static ArrayList checkStorageValid(boolean writable, - ArrayList storageDevices) { - ArrayList activeDevices = new ArrayList<>(); - ArrayList devicePaths = new ArrayList<>(); - for (StorageDevice device : storageDevices) { - if (existsAndIsDirAndWritableIfRequiredAndNotDuplicate(writable, devicePaths, device)) { - activeDevices.add(device); - devicePaths.add(device); - } - } - return activeDevices; - } - - private static boolean existsAndIsDirAndWritableIfRequiredAndNotDuplicate(boolean writable, - ArrayList devicePaths, StorageDevice device) { - final File devicePath = device.getPath(); - return devicePath.exists() - && devicePath.isDirectory() - && (canWrite(devicePath) || !writable) - && !device.isDuplicate() - && !devicePaths.contains(device); - } - - // Amazingly file.canWrite() does not always return the correct value - private static boolean canWrite(File file) { - final String filePath = file + "/test.txt"; - try { - RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw"); - FileChannel fileChannel = randomAccessFile.getChannel(); - FileLock fileLock = fileChannel.lock(); - fileLock.release(); - fileChannel.close(); - randomAccessFile.close(); - return true; - } catch (Exception ex) { - return false; - } finally { - new File(filePath).delete(); - } - } -} - diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt b/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt new file mode 100644 index 000000000..bd55565af --- /dev/null +++ b/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2016 Isaac Hutt + * + * This program 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package eu.mhutti1.utils.storage + +import android.content.Context +import android.os.Environment +import androidx.core.content.ContextCompat +import java.io.File +import java.io.FileFilter +import java.io.RandomAccessFile +import java.nio.channels.FileLock +import java.util.ArrayList + +object StorageDeviceUtils { + + @JvmStatic + fun getStorageDevices(context: Context, writable: Boolean): List { + val storageDevices = ArrayList() + + storageDevices.add(environmentDevices(Environment.getExternalStorageDirectory().path, writable)) + storageDevices.addAll(externalMountPointDevices()) + storageDevices.addAll(externalFilesDirsDevices(context, writable)) + + return validate(storageDevices, writable).also(::deleteStorageMarkers) + } + + private fun externalFilesDirsDevices( + context: Context, + writable: Boolean + ) = ContextCompat.getExternalFilesDirs(context, "") + .filterNotNull() + .map { dir -> StorageDevice(generalisePath(dir.path, writable), false) } + + private fun externalMountPointDevices(): Collection { + val storageDevices = ArrayList() + for (path in ExternalPaths.possiblePaths) { + if (path.endsWith("*")) { + val root = File(path.substringBeforeLast("*")) + root.listFiles(FileFilter(File::isDirectory)) + ?.mapTo(storageDevices) { dir -> StorageDevice(dir, false) } + } else { + storageDevices.add(StorageDevice(path, false)) + } + } + return storageDevices + } + + private fun environmentDevices( + environmentPath: String, + writable: Boolean + ) = + // This is our internal storage directory + if (Environment.isExternalStorageEmulated()) + StorageDevice(generalisePath(environmentPath, writable), true) + // This is an external storage directory + else StorageDevice(generalisePath(environmentPath, writable), false) + + // Remove app specific path from directories so that we can search them from the top + private fun generalisePath(path: String, writable: Boolean) = + if (writable) path + else path.substringBeforeLast("/Android/data/") + + // Amazingly file.canWrite() does not always return the correct value + private fun canWrite(file: File) = "$file/test.txt".let { + try { + RandomAccessFile(it, "rw").use { randomAccessFile -> + randomAccessFile.channel.use { channel -> + channel.lock().also(FileLock::release) + true + } + } + false + } finally { + File(it).delete() + } + } + + private fun validate( + storageDevices: ArrayList, + writable: Boolean + ): List { + return storageDevices.asSequence().distinct() + .filter { it.file.exists() } + .filter { it.file.isDirectory } + .filter { (!writable || canWrite(it.file)) } + .filterNot(StorageDevice::isDuplicate).toList() + } + + private fun deleteStorageMarkers(validatedDevices: List) { + validatedDevices.forEach { recursiveDeleteStorageMarkers(it.file) } + } + + private fun recursiveDeleteStorageMarkers(file: File) { + file.listFiles().forEach { + if (it.isDirectory) { + recursiveDeleteStorageMarkers(it) + } else if (it.extension == "storageMarker") { + it.delete() + } + } + } +} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java index 394cff47c..186f8e52a 100644 --- a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java +++ b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java @@ -25,7 +25,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; -import java.util.ArrayList; +import java.util.List; import org.kiwix.kiwixmobile.R; class StorageSelectArrayAdapter extends ArrayAdapter { @@ -34,7 +34,7 @@ class StorageSelectArrayAdapter extends ArrayAdapter { private final String mExternal; - public StorageSelectArrayAdapter(Context context, int resource, ArrayList devices, + public StorageSelectArrayAdapter(Context context, int resource, List devices, String internal, String external) { super(context, resource, devices); mInternal = internal; @@ -60,7 +60,7 @@ class StorageSelectArrayAdapter extends ArrayAdapter { } else { holder.fileName.setText(mExternal); } - holder.fileSize.setText(device.getSize() + " / " + device.getTotalSize()); + holder.fileSize.setText(device.getAvailableSpace() + " / " + device.getTotalSize()); return convertView; } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java index 653ef3d26..eceb09693 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java @@ -342,7 +342,7 @@ public class KiwixSettingsActivity extends BaseActivity { @Override public void selectionCallback(StorageDevice storageDevice) { - findPreference(PREF_STORAGE).setSummary(storageDevice.getSize()); + findPreference(PREF_STORAGE).setSummary(storageDevice.getAvailableSpace()); sharedPreferenceUtil.putPrefStorage(storageDevice.getName()); if (storageDevice.isInternal()) { findPreference(PREF_STORAGE).setTitle(getResources().getString(R.string.internal_storage)); From a2bd77e98ecb71a55bef337517d9ac692ea43d2d Mon Sep 17 00:00:00 2001 From: Sean Mac Gillicuddy Date: Thu, 15 Aug 2019 13:48:31 +0100 Subject: [PATCH 2/5] #1333 cleanup and extension correction --- .../eu/mhutti1/utils/storage/StorageDevice.kt | 39 ++++----- .../utils/storage/StorageDeviceUtils.kt | 82 +++++++++---------- 2 files changed, 58 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt b/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt index f55cfd162..01214c164 100644 --- a/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt +++ b/app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt @@ -21,19 +21,27 @@ package eu.mhutti1.utils.storage import android.os.Build import android.os.StatFs -import android.util.Log import java.io.BufferedReader import java.io.File import java.io.FileReader import java.io.FileWriter -import java.io.IOException + +const val LOCATION_EXTENSION = "storageLocationMarker" data class StorageDevice( val file: File, val isInternal: Boolean ) { - var isDuplicate = true + constructor(path: String, internal: Boolean) : this(File(path), internal) + + init { + if (file.exists()) { + createLocationCode() + } + } + + var isDuplicate = false private set val name: String @@ -62,32 +70,19 @@ data class StorageDevice( it.blockSize.toLong() * it.blockCount.toLong() } - constructor(path: String, internal: Boolean) : this(File(path), internal) - - init { - if (file.exists()) { - createLocationCode() - } - } - // Create unique file to identify duplicate devices. private fun createLocationCode() { if (!getLocationCodeFromFolder(file)) { - val locationCode = File(file.path, ".storageLocationMarker") - try { + File(file.path, ".$LOCATION_EXTENSION").let { locationCode -> locationCode.createNewFile() - val fw = FileWriter(locationCode) - fw.write(file.path) - fw.close() - } catch (e: IOException) { - Log.d("android-storage-devices", "Unable to create marker file, duplicates may be listed") + FileWriter(locationCode).use { it.write(file.path) } } } } // Check if there is already a device code in our path private fun getLocationCodeFromFolder(folder: File): Boolean { - val locationCode = File(folder.path, ".storageLocationMarker") + val locationCode = File(folder.path, ".$LOCATION_EXTENSION") if (locationCode.exists()) { try { BufferedReader(FileReader(locationCode)).use { br -> @@ -102,11 +97,11 @@ data class StorageDevice( return true } } - val parentFile = folder.parentFile - if (parentFile == null) { + val parent = folder.parentFile + if (parent == null) { isDuplicate = false return false } - return getLocationCodeFromFolder(parentFile) + return getLocationCodeFromFolder(parent) } } diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt b/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt index bd55565af..d31d031ca 100644 --- a/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt +++ b/app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt @@ -25,20 +25,18 @@ import androidx.core.content.ContextCompat import java.io.File import java.io.FileFilter import java.io.RandomAccessFile -import java.nio.channels.FileLock import java.util.ArrayList object StorageDeviceUtils { @JvmStatic fun getStorageDevices(context: Context, writable: Boolean): List { - val storageDevices = ArrayList() - - storageDevices.add(environmentDevices(Environment.getExternalStorageDirectory().path, writable)) - storageDevices.addAll(externalMountPointDevices()) - storageDevices.addAll(externalFilesDirsDevices(context, writable)) - - return validate(storageDevices, writable).also(::deleteStorageMarkers) + val storageDevices = ArrayList().apply { + add(environmentDevices(writable)) + addAll(externalMountPointDevices()) + addAll(externalFilesDirsDevices(context, writable)) + } + return validate(storageDevices, writable) } private fun externalFilesDirsDevices( @@ -48,44 +46,47 @@ object StorageDeviceUtils { .filterNotNull() .map { dir -> StorageDevice(generalisePath(dir.path, writable), false) } - private fun externalMountPointDevices(): Collection { - val storageDevices = ArrayList() - for (path in ExternalPaths.possiblePaths) { - if (path.endsWith("*")) { - val root = File(path.substringBeforeLast("*")) - root.listFiles(FileFilter(File::isDirectory)) - ?.mapTo(storageDevices) { dir -> StorageDevice(dir, false) } - } else { - storageDevices.add(StorageDevice(path, false)) + private fun externalMountPointDevices(): Collection = + ExternalPaths.possiblePaths.fold(mutableListOf(), { acc, path -> + acc.apply { + if (path.endsWith("*")) { + addAll(devicesBeneath(File(path.substringBeforeLast("*")))) + } else { + add(StorageDevice(path, false)) + } } - } - return storageDevices - } + }) + + private fun devicesBeneath(directory: File) = + directory.listFiles(FileFilter(File::isDirectory)) + ?.map { dir -> StorageDevice(dir, false) } + .orEmpty() private fun environmentDevices( - environmentPath: String, writable: Boolean ) = - // This is our internal storage directory - if (Environment.isExternalStorageEmulated()) - StorageDevice(generalisePath(environmentPath, writable), true) - // This is an external storage directory - else StorageDevice(generalisePath(environmentPath, writable), false) + StorageDevice( + generalisePath(Environment.getExternalStorageDirectory().path, writable), + Environment.isExternalStorageEmulated() + ) // Remove app specific path from directories so that we can search them from the top private fun generalisePath(path: String, writable: Boolean) = if (writable) path - else path.substringBeforeLast("/Android/data/") + else path.substringBefore("/Android/data/") // Amazingly file.canWrite() does not always return the correct value - private fun canWrite(file: File) = "$file/test.txt".let { + private fun canWrite(file: File): Boolean = "$file/test.txt".let { try { RandomAccessFile(it, "rw").use { randomAccessFile -> randomAccessFile.channel.use { channel -> - channel.lock().also(FileLock::release) - true + channel.lock().use { fileLock -> + fileLock.release() + true + } } } + } catch (ignore: Exception) { false } finally { File(it).delete() @@ -95,13 +96,13 @@ object StorageDeviceUtils { private fun validate( storageDevices: ArrayList, writable: Boolean - ): List { - return storageDevices.asSequence().distinct() - .filter { it.file.exists() } - .filter { it.file.isDirectory } - .filter { (!writable || canWrite(it.file)) } - .filterNot(StorageDevice::isDuplicate).toList() - } + ) = storageDevices.asSequence().distinct() + .filter { it.file.exists() } + .filter { it.file.isDirectory } + .filter { canWrite(it.file) || !writable } + .filterNot(StorageDevice::isDuplicate) + .toList() + .also(StorageDeviceUtils::deleteStorageMarkers) private fun deleteStorageMarkers(validatedDevices: List) { validatedDevices.forEach { recursiveDeleteStorageMarkers(it.file) } @@ -109,10 +110,9 @@ object StorageDeviceUtils { private fun recursiveDeleteStorageMarkers(file: File) { file.listFiles().forEach { - if (it.isDirectory) { - recursiveDeleteStorageMarkers(it) - } else if (it.extension == "storageMarker") { - it.delete() + when { + it.isDirectory -> recursiveDeleteStorageMarkers(it) + it.extension == LOCATION_EXTENSION -> it.delete() } } } From ccbce17793eac80e1348633b31c6aae8ecdd300d Mon Sep 17 00:00:00 2001 From: Sean Mac Gillicuddy Date: Thu, 15 Aug 2019 14:08:46 +0100 Subject: [PATCH 3/5] #1376 Unable to delete old zim files after re-install --- .../kiwix/kiwixmobile/database/newdb/dao/NewBookDao.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/database/newdb/dao/NewBookDao.kt b/app/src/main/java/org/kiwix/kiwixmobile/database/newdb/dao/NewBookDao.kt index fbef5c8b5..d2f1a5b40 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/database/newdb/dao/NewBookDao.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/database/newdb/dao/NewBookDao.kt @@ -30,6 +30,8 @@ import javax.inject.Inject class NewBookDao @Inject constructor(private val box: Box) { fun books() = box.asFlowable() + .doOnNext(::removeBooksThatDoNotExist) + .map { books -> books.filter { it.file.exists() } } .map { it.map(::BookOnDisk) } fun getBooks() = box.all.map(::BookOnDisk) @@ -52,4 +54,12 @@ class NewBookDao @Inject constructor(private val box: Box) { fun migrationInsert(books: ArrayList) { insert(books.map { BookOnDisk(book = it, file = it.file) }) } + + private fun removeBooksThatDoNotExist(books: MutableList) { + delete(books.filterNot { it.file.exists() }) + } + + private fun delete(books: List) { + box.remove(books) + } } From 0785df2bb2b77db9d994b89927a2db1a602d4429 Mon Sep 17 00:00:00 2001 From: Sean Mac Gillicuddy Date: Thu, 15 Aug 2019 15:39:34 +0100 Subject: [PATCH 4/5] #1165 New external storage disappears on screen rotation --- .../storage/StorageSelectArrayAdapter.java | 72 ----------- .../storage/StorageSelectArrayAdapter.kt | 62 ++++++++++ .../utils/storage/StorageSelectDialog.java | 114 ------------------ .../utils/storage/StorageSelectDialog.kt | 74 ++++++++++++ .../settings/KiwixSettingsActivity.java | 34 +++--- .../library_view/LibraryFragment.kt | 26 +--- .../main/res/layout/storage_select_dialog.xml | 62 +++------- .../res/values/strings_nontranslatable.xml | 2 - .../kiwixmobile/zim_manager/LanguageTest.kt | 2 +- 9 files changed, 173 insertions(+), 275 deletions(-) delete mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java create mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.kt delete mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.java create mode 100644 app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.kt diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java deleted file mode 100644 index 186f8e52a..000000000 --- a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2016 Isaac Hutt - * - * This program 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 - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -package eu.mhutti1.utils.storage; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; -import java.util.List; -import org.kiwix.kiwixmobile.R; - -class StorageSelectArrayAdapter extends ArrayAdapter { - - private final String mInternal; - - private final String mExternal; - - public StorageSelectArrayAdapter(Context context, int resource, List devices, - String internal, String external) { - super(context, resource, devices); - mInternal = internal; - mExternal = external; - } - - @SuppressLint("SetTextI18n") @Override - public View getView(int position, View convertView, ViewGroup parent) { - - ViewHolder holder; - if (convertView == null) { - convertView = View.inflate(getContext(), R.layout.device_item, null); - holder = new ViewHolder(); - holder.fileName = convertView.findViewById(R.id.file_name); - holder.fileSize = convertView.findViewById(R.id.file_size); - convertView.setTag(holder); - } else { - holder = (ViewHolder) convertView.getTag(); - } - StorageDevice device = getItem(position); - if (device.isInternal()) { - holder.fileName.setText(mInternal); - } else { - holder.fileName.setText(mExternal); - } - holder.fileSize.setText(device.getAvailableSpace() + " / " + device.getTotalSize()); - - return convertView; - } - - class ViewHolder { - TextView fileName; - TextView fileSize; - } -} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.kt b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.kt new file mode 100644 index 000000000..601d39ddc --- /dev/null +++ b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectArrayAdapter.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2016 Isaac Hutt + * + * This program 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package eu.mhutti1.utils.storage + +import android.annotation.SuppressLint +import android.content.Context +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.device_item.file_name +import kotlinx.android.synthetic.main.device_item.file_size +import org.kiwix.kiwixmobile.R +import org.kiwix.kiwixmobile.R.string +import org.kiwix.kiwixmobile.extensions.inflate + +internal class StorageSelectArrayAdapter( + context: Context, + devices: List +) : ArrayAdapter(context, 0, devices) { + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var convertView = convertView + + val holder: ViewHolder + if (convertView == null) { + convertView = parent.inflate(R.layout.device_item, false) + holder = ViewHolder(convertView) + convertView.tag = holder + } else { + holder = convertView.tag as ViewHolder + } + holder.bind(getItem(position)!!) + + return convertView + } + + @SuppressLint("SetTextI18n") + internal inner class ViewHolder(override val containerView: View) : LayoutContainer { + fun bind(device: StorageDevice) { + file_name.setText(if (device.isInternal) string.internal_storage else string.external_storage) + file_size.text = device.availableSpace + " / " + device.totalSize + } + } +} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.java b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.java deleted file mode 100644 index d4302d8e8..000000000 --- a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2016 Isaac Hutt - * - * This program 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 - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -package eu.mhutti1.utils.storage; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TextView; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.FragmentManager; -import java.io.File; -import org.kiwix.kiwixmobile.R; - -public class StorageSelectDialog extends DialogFragment implements ListView.OnItemClickListener { - - // Activities/Fragments can create instances of a StorageSelectDialog and bind a listener to get its result - - public static final String STORAGE_DIALOG_THEME = "THEME"; - - public static final String STORAGE_DIALOG_INTERNAL = "INTERNAL"; - - public static final String STORAGE_DIALOG_EXTERNAL = "EXTERNAL"; - - private StorageSelectArrayAdapter mAdapter; - - private OnSelectListener mOnSelectListener; - private String mTitle; - - private String mInternal = "Internal"; - - private String mExternal = "External"; - - @Override - public void onCreate(Bundle savedInstanceState) { - if (getArguments() != null) { - // Set string values - mInternal = getArguments().getString(STORAGE_DIALOG_INTERNAL, mInternal); - mExternal = getArguments().getString(STORAGE_DIALOG_EXTERNAL, mExternal); - // Set the theme to a supplied value - if (getArguments().containsKey(STORAGE_DIALOG_THEME)) { - setStyle(DialogFragment.STYLE_NORMAL, getArguments().getInt(STORAGE_DIALOG_THEME)); - } - } - super.onCreate(savedInstanceState); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.storage_select_dialog, container, false); - TextView title = rootView.findViewById(R.id.title); - title.setText(mTitle); - ListView listView = rootView.findViewById(R.id.device_list); - mAdapter = new StorageSelectArrayAdapter(getActivity(), 0, - StorageDeviceUtils.getStorageDevices(getActivity(), true), mInternal, mExternal); - listView.setAdapter(mAdapter); - listView.setOnItemClickListener(this); - Button button = rootView.findViewById(R.id.button); - final EditText editText = rootView.findViewById(R.id.editText); - button.setOnClickListener(view -> { - if (editText.getText().length() != 0) { - String path = editText.getText().toString(); - if (new File(path).exists()) { - mAdapter.add(new StorageDevice(path, false)); - } - } - }); - return rootView; - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (mOnSelectListener != null) { - mOnSelectListener.selectionCallback(mAdapter.getItem(position)); - } - dismiss(); - } - - public void setOnSelectListener(OnSelectListener selectListener) { - mOnSelectListener = selectListener; - } - - public interface OnSelectListener { - void selectionCallback(StorageDevice s); - } - - @Override - public void show(FragmentManager fm, String text) { - mTitle = text; - super.show(fm, text); - } -} diff --git a/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.kt b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.kt new file mode 100644 index 000000000..96aa8a32a --- /dev/null +++ b/app/src/main/java/eu/mhutti1/utils/storage/StorageSelectDialog.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2016 Isaac Hutt + * + * This program 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 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package eu.mhutti1.utils.storage + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView.OnItemClickListener +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.FragmentManager +import kotlinx.android.synthetic.main.storage_select_dialog.device_list +import kotlinx.android.synthetic.main.storage_select_dialog.title +import org.kiwix.kiwixmobile.R +import org.kiwix.kiwixmobile.utils.StyleUtils + +class StorageSelectDialog : DialogFragment() { + + private var onSelectAction: ((StorageDevice) -> Unit)? = null + private var mAdapter: StorageSelectArrayAdapter? = null + + private var mTitle: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + setStyle(STYLE_NORMAL, StyleUtils.dialogStyle()) + super.onCreate(savedInstanceState) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = inflater.inflate(R.layout.storage_select_dialog, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + title.text = mTitle + mAdapter = StorageSelectArrayAdapter( + activity!!, + StorageDeviceUtils.getStorageDevices(activity!!, true) + ) + device_list.adapter = mAdapter + device_list.onItemClickListener = OnItemClickListener { _, _, position, _ -> + onSelectAction?.invoke(mAdapter!!.getItem(position)!!) + dismiss() + } + } + + override fun show(fm: FragmentManager, text: String) { + mTitle = text + super.show(fm, text) + } + + fun setOnSelectListener(onSelectAction: (StorageDevice) -> Unit) { + this.onSelectAction = onSelectAction + } +} diff --git a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java index eceb09693..2fcf947f8 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixSettingsActivity.java @@ -43,6 +43,7 @@ import java.io.File; import java.util.List; import java.util.Locale; import javax.inject.Inject; +import kotlin.Unit; import org.kiwix.kiwixmobile.BuildConfig; import org.kiwix.kiwixmobile.KiwixApplication; import org.kiwix.kiwixmobile.R; @@ -50,7 +51,6 @@ import org.kiwix.kiwixmobile.base.BaseActivity; import org.kiwix.kiwixmobile.main.MainActivity; import org.kiwix.kiwixmobile.utils.LanguageUtils; import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil; -import org.kiwix.kiwixmobile.utils.StyleUtils; import org.kiwix.kiwixmobile.zim_manager.library_view.LibraryUtils; import static org.kiwix.kiwixmobile.utils.Constants.EXTRA_WEBVIEWS_LIST; @@ -81,9 +81,9 @@ public class KiwixSettingsActivity extends BaseActivity { allHistoryCleared = false; getFragmentManager() - .beginTransaction(). - replace(R.id.content_frame, new PrefsFragment()) - .commit(); + .beginTransaction(). + replace(R.id.content_frame, new PrefsFragment()) + .commit(); setUpToolbar(); } @@ -110,8 +110,8 @@ public class KiwixSettingsActivity extends BaseActivity { } public static class PrefsFragment extends PreferenceFragment implements - SettingsContract.View, - SharedPreferences.OnSharedPreferenceChangeListener, StorageSelectDialog.OnSelectListener { + SettingsContract.View, + SharedPreferences.OnSharedPreferenceChangeListener { @Inject SettingsPresenter presenter; @@ -329,29 +329,25 @@ public class KiwixSettingsActivity extends BaseActivity { public void openFolderSelect() { StorageSelectDialog dialogFragment = new StorageSelectDialog(); - Bundle b = new Bundle(); - b.putString(StorageSelectDialog.STORAGE_DIALOG_INTERNAL, - getResources().getString(R.string.internal_storage)); - b.putString(StorageSelectDialog.STORAGE_DIALOG_EXTERNAL, - getResources().getString(R.string.external_storage)); - b.putInt(StorageSelectDialog.STORAGE_DIALOG_THEME, StyleUtils.dialogStyle()); - dialogFragment.setArguments(b); - dialogFragment.setOnSelectListener(this); - dialogFragment.show(((AppCompatActivity) getActivity()).getSupportFragmentManager(), getResources().getString(R.string.pref_storage)); + dialogFragment.setOnSelectListener(storageDevice -> { + selectionCallback(storageDevice); + return Unit.INSTANCE; + }); + dialogFragment.show(((AppCompatActivity) getActivity()).getSupportFragmentManager(), + getResources().getString(R.string.pref_storage)); } - @Override - public void selectionCallback(StorageDevice storageDevice) { + private void selectionCallback(StorageDevice storageDevice) { findPreference(PREF_STORAGE).setSummary(storageDevice.getAvailableSpace()); sharedPreferenceUtil.putPrefStorage(storageDevice.getName()); if (storageDevice.isInternal()) { findPreference(PREF_STORAGE).setTitle(getResources().getString(R.string.internal_storage)); sharedPreferenceUtil.putPrefStorageTitle( - getResources().getString(R.string.internal_storage)); + getResources().getString(R.string.internal_storage)); } else { findPreference(PREF_STORAGE).setTitle(getResources().getString(R.string.external_storage)); sharedPreferenceUtil.putPrefStorageTitle( - getResources().getString(R.string.external_storage)); + getResources().getString(R.string.external_storage)); } } } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt index d3120f312..baf14ff52 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt @@ -17,7 +17,6 @@ */ package org.kiwix.kiwixmobile.zim_manager.library_view -import android.annotation.SuppressLint import android.net.ConnectivityManager import android.os.Bundle import android.view.LayoutInflater @@ -49,7 +48,6 @@ import org.kiwix.kiwixmobile.utils.DialogShower import org.kiwix.kiwixmobile.utils.KiwixDialog.YesNoDialog.WifiOnly import org.kiwix.kiwixmobile.utils.NetworkUtils import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil -import org.kiwix.kiwixmobile.utils.StyleUtils import org.kiwix.kiwixmobile.utils.TestingUtils import org.kiwix.kiwixmobile.zim_manager.NetworkState import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED @@ -210,23 +208,9 @@ class LibraryFragment : BaseFragment() { private fun notEnoughSpaceAvailable(item: BookItem) = spaceAvailable < item.book.size.toLong() * 1024f - @SuppressLint("ImplicitSamInstance") - private fun showStorageSelectDialog() { - StorageSelectDialog() - .apply { - arguments = Bundle().apply { - putString( - StorageSelectDialog.STORAGE_DIALOG_INTERNAL, - this@LibraryFragment.getString(string.internal_storage) - ) - putString( - StorageSelectDialog.STORAGE_DIALOG_EXTERNAL, - this@LibraryFragment.getString(string.external_storage) - ) - putInt(StorageSelectDialog.STORAGE_DIALOG_THEME, StyleUtils.dialogStyle()) - } - setOnSelectListener(::storeDeviceInPreferences) - } - .show(fragmentManager, getString(string.pref_storage)) - } + private fun showStorageSelectDialog() = StorageSelectDialog() + .apply { + setOnSelectListener(::storeDeviceInPreferences) + } + .show(fragmentManager!!, getString(string.pref_storage)) } diff --git a/app/src/main/res/layout/storage_select_dialog.xml b/app/src/main/res/layout/storage_select_dialog.xml index 7e75b03f5..0ea93c35a 100644 --- a/app/src/main/res/layout/storage_select_dialog.xml +++ b/app/src/main/res/layout/storage_select_dialog.xml @@ -1,53 +1,23 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="10dp" + > + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:textAlignment="center" + android:textSize="22sp" + /> - - - - - -