mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-22 03:54:18 -04:00
Merge branch 'develop' into iadeelzafar/wifi-hotspot
This commit is contained in:
commit
7972774e2f
43
app/src/main/java/eu/mhutti1/utils/storage/Bytes.kt
Normal file
43
app/src/main/java/eu/mhutti1/utils/storage/Bytes.kt
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (C) 2018 Kiwix <android.kiwix.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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())
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
56
app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.kt
Normal file
56
app/src/main/java/eu/mhutti1/utils/storage/ExternalPaths.kt
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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"
|
||||
)
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
}
|
107
app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt
Normal file
107
app/src/main/java/eu/mhutti1/utils/storage/StorageDevice.kt
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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 java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
|
||||
const val LOCATION_EXTENSION = "storageLocationMarker"
|
||||
|
||||
data class StorageDevice(
|
||||
val file: File,
|
||||
val isInternal: Boolean
|
||||
) {
|
||||
|
||||
constructor(path: String, internal: Boolean) : this(File(path), internal)
|
||||
|
||||
init {
|
||||
if (file.exists()) {
|
||||
createLocationCode()
|
||||
}
|
||||
}
|
||||
|
||||
var isDuplicate = false
|
||||
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()
|
||||
}
|
||||
|
||||
// Create unique file to identify duplicate devices.
|
||||
private fun createLocationCode() {
|
||||
if (!getLocationCodeFromFolder(file)) {
|
||||
File(file.path, ".$LOCATION_EXTENSION").let { locationCode ->
|
||||
locationCode.createNewFile()
|
||||
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, ".$LOCATION_EXTENSION")
|
||||
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 parent = folder.parentFile
|
||||
if (parent == null) {
|
||||
isDuplicate = false
|
||||
return false
|
||||
}
|
||||
return getLocationCodeFromFolder(parent)
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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<StorageDevice> getStorageDevices(Context context, boolean writable) {
|
||||
ArrayList<StorageDevice> 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<StorageDevice> checkStorageValid(boolean writable,
|
||||
ArrayList<StorageDevice> storageDevices) {
|
||||
ArrayList<StorageDevice> activeDevices = new ArrayList<>();
|
||||
ArrayList<StorageDevice> 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<StorageDevice> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
119
app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt
Normal file
119
app/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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.util.ArrayList
|
||||
|
||||
object StorageDeviceUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun getStorageDevices(context: Context, writable: Boolean): List<StorageDevice> {
|
||||
val storageDevices = ArrayList<StorageDevice>().apply {
|
||||
add(environmentDevices(writable))
|
||||
addAll(externalMountPointDevices())
|
||||
addAll(externalFilesDirsDevices(context, writable))
|
||||
}
|
||||
return validate(storageDevices, writable)
|
||||
}
|
||||
|
||||
private fun externalFilesDirsDevices(
|
||||
context: Context,
|
||||
writable: Boolean
|
||||
) = ContextCompat.getExternalFilesDirs(context, "")
|
||||
.filterNotNull()
|
||||
.map { dir -> StorageDevice(generalisePath(dir.path, writable), false) }
|
||||
|
||||
private fun externalMountPointDevices(): Collection<StorageDevice> =
|
||||
ExternalPaths.possiblePaths.fold(mutableListOf(), { acc, path ->
|
||||
acc.apply {
|
||||
if (path.endsWith("*")) {
|
||||
addAll(devicesBeneath(File(path.substringBeforeLast("*"))))
|
||||
} else {
|
||||
add(StorageDevice(path, false))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
private fun devicesBeneath(directory: File) =
|
||||
directory.listFiles(FileFilter(File::isDirectory))
|
||||
?.map { dir -> StorageDevice(dir, false) }
|
||||
.orEmpty()
|
||||
|
||||
private fun environmentDevices(
|
||||
writable: Boolean
|
||||
) =
|
||||
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.substringBefore("/Android/data/")
|
||||
|
||||
// Amazingly file.canWrite() does not always return the correct value
|
||||
private fun canWrite(file: File): Boolean = "$file/test.txt".let {
|
||||
try {
|
||||
RandomAccessFile(it, "rw").use { randomAccessFile ->
|
||||
randomAccessFile.channel.use { channel ->
|
||||
channel.lock().use { fileLock ->
|
||||
fileLock.release()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ignore: Exception) {
|
||||
false
|
||||
} finally {
|
||||
File(it).delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun validate(
|
||||
storageDevices: ArrayList<StorageDevice>,
|
||||
writable: Boolean
|
||||
) = 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<StorageDevice>) {
|
||||
validatedDevices.forEach { recursiveDeleteStorageMarkers(it.file) }
|
||||
}
|
||||
|
||||
private fun recursiveDeleteStorageMarkers(file: File) {
|
||||
file.listFiles().forEach {
|
||||
when {
|
||||
it.isDirectory -> recursiveDeleteStorageMarkers(it)
|
||||
it.extension == LOCATION_EXTENSION -> it.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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.ArrayList;
|
||||
import org.kiwix.kiwixmobile.R;
|
||||
|
||||
class StorageSelectArrayAdapter extends ArrayAdapter<StorageDevice> {
|
||||
|
||||
private final String mInternal;
|
||||
|
||||
private final String mExternal;
|
||||
|
||||
public StorageSelectArrayAdapter(Context context, int resource, ArrayList<StorageDevice> 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.getSize() + " / " + device.getTotalSize());
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
TextView fileName;
|
||||
TextView fileSize;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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<StorageDevice>
|
||||
) : ArrayAdapter<StorageDevice>(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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2016 Isaac Hutt <mhutti1@gmail.com>
|
||||
*
|
||||
* 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
|
||||
}
|
||||
}
|
@ -30,6 +30,8 @@ import javax.inject.Inject
|
||||
class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
|
||||
|
||||
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<BookOnDiskEntity>) {
|
||||
fun migrationInsert(books: ArrayList<Book>) {
|
||||
insert(books.map { BookOnDisk(book = it, file = it.file) })
|
||||
}
|
||||
|
||||
private fun removeBooksThatDoNotExist(books: MutableList<BookOnDiskEntity>) {
|
||||
delete(books.filterNot { it.file.exists() })
|
||||
}
|
||||
|
||||
private fun delete(books: List<BookOnDiskEntity>) {
|
||||
box.remove(books)
|
||||
}
|
||||
}
|
||||
|
@ -26,15 +26,13 @@ import org.kiwix.kiwixmobile.database.newdb.entities.BookmarkEntity_
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) {
|
||||
fun getBookmarks(fromCurrentBook: Boolean): List<BookmarkItem> {
|
||||
return box.query {
|
||||
if (fromCurrentBook) {
|
||||
equal(BookmarkEntity_.zimId, ZimContentProvider.getId() ?: "")
|
||||
}
|
||||
order(BookmarkEntity_.bookmarkTitle)
|
||||
}.find()
|
||||
.map(::BookmarkItem)
|
||||
}
|
||||
fun getBookmarks(fromCurrentBook: Boolean) = box.query {
|
||||
if (fromCurrentBook) {
|
||||
equal(BookmarkEntity_.zimName, ZimContentProvider.getName() ?: "")
|
||||
}
|
||||
order(BookmarkEntity_.bookmarkTitle)
|
||||
}.find()
|
||||
.map(::BookmarkItem)
|
||||
|
||||
fun getCurrentZimBookmarksUrl() = box.query {
|
||||
equal(BookmarkEntity_.zimId, ZimContentProvider.getId() ?: "")
|
||||
|
@ -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) {
|
||||
findPreference(PREF_STORAGE).setSummary(storageDevice.getSize());
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -1,53 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
>
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:textAlignment="center"
|
||||
android:textSize="22sp"
|
||||
/>
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:textAlignment="center"
|
||||
android:textSize="22sp"
|
||||
/>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/device_list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
|
||||
</ListView>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<EditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ems="10"
|
||||
android:inputType="textPersonName"
|
||||
android:text="@string/slash"
|
||||
android:importantForAutofill="no"
|
||||
tools:targetApi="o"
|
||||
/>
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/plus"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
android:id="@+id/device_list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
@ -6,6 +6,4 @@
|
||||
<item>large</item>
|
||||
</string-array>
|
||||
|
||||
<string name="slash">/</string>
|
||||
<string name="plus">+</string>
|
||||
</resources>
|
||||
|
@ -29,7 +29,7 @@ class LanguageTest {
|
||||
@Nested
|
||||
inner class Equals {
|
||||
@Test
|
||||
@Suppress("UnusedEquals", "ReplaceCallWithBinaryOperator") //cannot == Unit
|
||||
@Suppress("UnusedEquals", "ReplaceCallWithBinaryOperator") // cannot == Unit
|
||||
fun `throws exception when object is not language item`() {
|
||||
assertThrows(ClassCastException::class.java) { language().equals(Unit) }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user