mirror of https://github.com/tasks/tasks
Convert FileHelper to Kotlin
parent
81904e5888
commit
e6926f5943
@ -1,288 +0,0 @@
|
||||
package org.tasks.files;
|
||||
|
||||
import static android.content.ContentResolver.SCHEME_CONTENT;
|
||||
import static android.provider.DocumentsContract.EXTRA_INITIAL_URI;
|
||||
import static androidx.core.content.FileProvider.getUriForFile;
|
||||
import static com.google.common.collect.Iterables.any;
|
||||
import static com.todoroo.astrid.utility.Constants.FILE_PROVIDER_AUTHORITY;
|
||||
import static org.tasks.Strings.isNullOrEmpty;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.tasks.R;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class FileHelper {
|
||||
|
||||
public static final int MAX_FILENAME_LENGTH = 40;
|
||||
|
||||
public static Intent newFilePickerIntent(Activity activity, Uri initial, String... mimeTypes) {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
|
||||
intent.putExtra("android.content.extra.FANCY", true);
|
||||
intent.putExtra("android.content.extra.SHOW_FILESIZE", true);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
setInitialUri(activity, intent, initial);
|
||||
if (mimeTypes.length == 1) {
|
||||
intent.setType(mimeTypes[0]);
|
||||
} else {
|
||||
intent.setType("*/*");
|
||||
if (mimeTypes.length > 1) {
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
|
||||
}
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static void newDirectoryPicker(Fragment fragment, int rc, @Nullable Uri initial) {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(
|
||||
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
||||
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
|
||||
intent.putExtra("android.content.extra.FANCY", true);
|
||||
intent.putExtra("android.content.extra.SHOW_FILESIZE", true);
|
||||
setInitialUri(fragment.getContext(), intent, initial);
|
||||
fragment.startActivityForResult(intent, rc);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private static void setInitialUri(Context context, Intent intent, Uri uri) {
|
||||
if (uri == null || !uri.getScheme().equals(SCHEME_CONTENT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
intent.putExtra(EXTRA_INITIAL_URI, DocumentFile.fromTreeUri(context, uri).getUri());
|
||||
} catch (Exception e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void delete(Context context, Uri uri) {
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (uri.getScheme()) {
|
||||
case "content":
|
||||
DocumentFile documentFile = DocumentFile.fromSingleUri(context, uri);
|
||||
documentFile.delete();
|
||||
break;
|
||||
case "file":
|
||||
delete(new File(uri.getPath()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void delete(File... files) {
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
delete(file.listFiles());
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable static String getFilename(Context context, Uri uri) {
|
||||
switch (uri.getScheme()) {
|
||||
case ContentResolver.SCHEME_FILE:
|
||||
return uri.getLastPathSegment();
|
||||
case SCHEME_CONTENT:
|
||||
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
try {
|
||||
return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getExtension(Context context, Uri uri) {
|
||||
if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
|
||||
String mimeType = context.getContentResolver().getType(uri);
|
||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||
if (!isNullOrEmpty(extension)) {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(uri.getPath());
|
||||
if (!isNullOrEmpty(extension)) {
|
||||
return extension;
|
||||
}
|
||||
|
||||
return Files.getFileExtension(getFilename(context, uri));
|
||||
}
|
||||
|
||||
public static String getMimeType(Context context, Uri uri) {
|
||||
String mimeType = context.getContentResolver().getType(uri);
|
||||
if (!isNullOrEmpty(mimeType)) {
|
||||
return mimeType;
|
||||
}
|
||||
String extension = getExtension(context, uri);
|
||||
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
}
|
||||
|
||||
public static void startActionView(Activity context, Uri uri) {
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String mimeType = getMimeType(context, uri);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
if (uri.getScheme().equals(SCHEME_CONTENT)) {
|
||||
uri = copyToUri(context, Uri.fromFile(context.getExternalCacheDir()), uri);
|
||||
}
|
||||
Uri share = getUriForFile(context, FILE_PROVIDER_AUTHORITY, new File(uri.getPath()));
|
||||
intent.setDataAndType(share, mimeType);
|
||||
grantReadPermissions(intent);
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(context, R.string.no_application_found, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private static void grantReadPermissions(Intent intent) {
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
|
||||
public static Uri newFile(
|
||||
Context context, Uri destination, String mimeType, String baseName, String extension)
|
||||
throws IOException {
|
||||
String filename = getNonCollidingFileName(context, destination, baseName, extension);
|
||||
switch (destination.getScheme()) {
|
||||
case "content":
|
||||
DocumentFile tree = DocumentFile.fromTreeUri(context, destination);
|
||||
DocumentFile f1 = tree.createFile(mimeType, filename);
|
||||
if (f1 == null) {
|
||||
throw new FileNotFoundException("Failed to create " + filename);
|
||||
}
|
||||
return f1.getUri();
|
||||
case "file":
|
||||
File dir = new File(destination.getPath());
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException("Failed to create %s" + dir.getAbsolutePath());
|
||||
}
|
||||
File f2 = new File(dir.getAbsolutePath() + File.separator + filename);
|
||||
if (f2.createNewFile()) {
|
||||
return Uri.fromFile(f2);
|
||||
}
|
||||
throw new FileNotFoundException("Failed to create " + filename);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI scheme: " + destination.getScheme());
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri copyToUri(Context context, Uri destination, Uri input) {
|
||||
String filename = getFilename(context, input);
|
||||
return copyToUri(context, destination, input, Files.getNameWithoutExtension(filename));
|
||||
}
|
||||
|
||||
public static Uri copyToUri(Context context, Uri destination, Uri input, String basename) {
|
||||
try {
|
||||
Uri output =
|
||||
newFile(
|
||||
context,
|
||||
destination,
|
||||
getMimeType(context, input),
|
||||
basename,
|
||||
getExtension(context, input));
|
||||
copyStream(context, input, output);
|
||||
return output;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyStream(Context context, Uri input, Uri output) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
try {
|
||||
InputStream inputStream = contentResolver.openInputStream(input);
|
||||
OutputStream outputStream = contentResolver.openOutputStream(output);
|
||||
ByteStreams.copy(inputStream, outputStream);
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getNonCollidingFileName(
|
||||
Context context, Uri uri, String baseName, String extension) {
|
||||
int tries = 1;
|
||||
if (!extension.startsWith(".")) {
|
||||
extension = "." + extension;
|
||||
}
|
||||
String tempName = baseName;
|
||||
switch (uri.getScheme()) {
|
||||
case SCHEME_CONTENT:
|
||||
DocumentFile dir = DocumentFile.fromTreeUri(context, uri);
|
||||
List<DocumentFile> documentFiles = Arrays.asList(dir.listFiles());
|
||||
while (true) {
|
||||
String result = tempName + extension;
|
||||
if (any(documentFiles, f -> f.getName().equals(result))) {
|
||||
tempName = baseName + "-" + tries;
|
||||
tries++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ContentResolver.SCHEME_FILE:
|
||||
File f = new File(uri.getPath(), baseName + extension);
|
||||
while (f.exists()) {
|
||||
tempName = baseName + "-" + tries; // $NON-NLS-1$
|
||||
f = new File(uri.getPath(), tempName + extension);
|
||||
tries++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return tempName + extension;
|
||||
}
|
||||
|
||||
public static String uri2String(@Nullable Uri uri) {
|
||||
if (uri == null) {
|
||||
return "";
|
||||
}
|
||||
if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
|
||||
return new File(uri.getPath()).getAbsolutePath();
|
||||
}
|
||||
return uri.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,265 @@
|
||||
package org.tasks.files
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.OpenableColumns
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.common.collect.Iterables
|
||||
import com.google.common.io.ByteStreams
|
||||
import com.google.common.io.Files
|
||||
import com.todoroo.astrid.utility.Constants
|
||||
import org.tasks.R
|
||||
import org.tasks.Strings.isNullOrEmpty
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
object FileHelper {
|
||||
const val MAX_FILENAME_LENGTH = 40
|
||||
fun newFilePickerIntent(activity: Activity?, initial: Uri?, vararg mimeTypes: String?): Intent {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
intent.putExtra("android.content.extra.SHOW_ADVANCED", true)
|
||||
intent.putExtra("android.content.extra.FANCY", true)
|
||||
intent.putExtra("android.content.extra.SHOW_FILESIZE", true)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
setInitialUri(activity, intent, initial)
|
||||
if (mimeTypes.size == 1) {
|
||||
intent.type = mimeTypes[0]
|
||||
} else {
|
||||
intent.type = "*/*"
|
||||
if (mimeTypes.size > 1) {
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
||||
}
|
||||
}
|
||||
return intent
|
||||
}
|
||||
|
||||
fun newDirectoryPicker(fragment: Fragment, rc: Int, initial: Uri?) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
intent.addFlags(
|
||||
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
|
||||
intent.putExtra("android.content.extra.SHOW_ADVANCED", true)
|
||||
intent.putExtra("android.content.extra.FANCY", true)
|
||||
intent.putExtra("android.content.extra.SHOW_FILESIZE", true)
|
||||
setInitialUri(fragment.context, intent, initial)
|
||||
fragment.startActivityForResult(intent, rc)
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private fun setInitialUri(context: Context?, intent: Intent, uri: Uri?) {
|
||||
if (uri == null || uri.scheme != ContentResolver.SCHEME_CONTENT) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, DocumentFile.fromTreeUri(context!!, uri)!!.uri)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun delete(context: Context?, uri: Uri?) {
|
||||
if (uri == null) {
|
||||
return
|
||||
}
|
||||
when (uri.scheme) {
|
||||
"content" -> {
|
||||
val documentFile = DocumentFile.fromSingleUri(context!!, uri)
|
||||
documentFile!!.delete()
|
||||
}
|
||||
"file" -> delete(File(uri.path))
|
||||
}
|
||||
}
|
||||
|
||||
private fun delete(vararg files: File) {
|
||||
if (files == null) {
|
||||
return
|
||||
}
|
||||
for (file in files) {
|
||||
if (file.isDirectory) {
|
||||
delete(*file.listFiles())
|
||||
} else {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFilename(context: Context, uri: Uri): String? {
|
||||
when (uri.scheme) {
|
||||
ContentResolver.SCHEME_FILE -> return uri.lastPathSegment
|
||||
ContentResolver.SCHEME_CONTENT -> {
|
||||
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return try {
|
||||
cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||
} finally {
|
||||
cursor.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getExtension(context: Context, uri: Uri): String? {
|
||||
if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
|
||||
val mimeType = context.contentResolver.getType(uri)
|
||||
val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
|
||||
if (!isNullOrEmpty(extension)) {
|
||||
return extension
|
||||
}
|
||||
}
|
||||
val extension = MimeTypeMap.getFileExtensionFromUrl(uri.path)
|
||||
return if (!isNullOrEmpty(extension)) {
|
||||
extension
|
||||
} else Files.getFileExtension(getFilename(context, uri)!!)
|
||||
}
|
||||
|
||||
fun getMimeType(context: Context, uri: Uri): String? {
|
||||
val mimeType = context.contentResolver.getType(uri)
|
||||
if (!isNullOrEmpty(mimeType)) {
|
||||
return mimeType
|
||||
}
|
||||
val extension = getExtension(context, uri)
|
||||
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
|
||||
}
|
||||
|
||||
fun startActionView(context: Activity, uri: Uri?) {
|
||||
var uri = uri ?: return
|
||||
val mimeType = getMimeType(context, uri)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
|
||||
uri = copyToUri(context, Uri.fromFile(context.externalCacheDir), uri)
|
||||
}
|
||||
val share = FileProvider.getUriForFile(context, Constants.FILE_PROVIDER_AUTHORITY, File(uri.path))
|
||||
intent.setDataAndType(share, mimeType)
|
||||
grantReadPermissions(intent)
|
||||
val packageManager = context.packageManager
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
context.startActivity(intent)
|
||||
} else {
|
||||
Toast.makeText(context, R.string.no_application_found, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun grantReadPermissions(intent: Intent) {
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun newFile(
|
||||
context: Context, destination: Uri, mimeType: String?, baseName: String, extension: String?): Uri {
|
||||
val filename = getNonCollidingFileName(context, destination, baseName, extension)
|
||||
return when (destination.scheme) {
|
||||
"content" -> {
|
||||
val tree = DocumentFile.fromTreeUri(context, destination)
|
||||
val f1 = tree!!.createFile(mimeType!!, filename)
|
||||
?: throw FileNotFoundException("Failed to create $filename")
|
||||
f1.uri
|
||||
}
|
||||
"file" -> {
|
||||
val dir = File(destination.path)
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw IOException("Failed to create %s" + dir.absolutePath)
|
||||
}
|
||||
val f2 = File(dir.absolutePath + File.separator + filename)
|
||||
if (f2.createNewFile()) {
|
||||
return Uri.fromFile(f2)
|
||||
}
|
||||
throw FileNotFoundException("Failed to create $filename")
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown URI scheme: " + destination.scheme)
|
||||
}
|
||||
}
|
||||
|
||||
fun copyToUri(context: Context, destination: Uri, input: Uri): Uri {
|
||||
val filename = getFilename(context, input)
|
||||
return copyToUri(context, destination, input, Files.getNameWithoutExtension(filename!!))
|
||||
}
|
||||
|
||||
fun copyToUri(context: Context, destination: Uri, input: Uri, basename: String): Uri {
|
||||
return try {
|
||||
val output = newFile(
|
||||
context,
|
||||
destination,
|
||||
getMimeType(context, input),
|
||||
basename,
|
||||
getExtension(context, input))
|
||||
copyStream(context, input, output)
|
||||
output
|
||||
} catch (e: IOException) {
|
||||
throw IllegalStateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun copyStream(context: Context, input: Uri?, output: Uri?) {
|
||||
val contentResolver = context.contentResolver
|
||||
try {
|
||||
val inputStream = contentResolver.openInputStream(input!!)
|
||||
val outputStream = contentResolver.openOutputStream(output!!)
|
||||
ByteStreams.copy(inputStream!!, outputStream!!)
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
} catch (e: IOException) {
|
||||
throw IllegalStateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNonCollidingFileName(
|
||||
context: Context, uri: Uri, baseName: String, extension: String?): String {
|
||||
var extension = extension
|
||||
var tries = 1
|
||||
if (!extension!!.startsWith(".")) {
|
||||
extension = ".$extension"
|
||||
}
|
||||
var tempName = baseName
|
||||
when (uri.scheme) {
|
||||
ContentResolver.SCHEME_CONTENT -> {
|
||||
val dir = DocumentFile.fromTreeUri(context, uri)
|
||||
val documentFiles = Arrays.asList(*dir!!.listFiles())
|
||||
while (true) {
|
||||
val result = tempName + extension
|
||||
if (Iterables.any(documentFiles) { f: DocumentFile? -> f!!.name == result }) {
|
||||
tempName = "$baseName-$tries"
|
||||
tries++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
ContentResolver.SCHEME_FILE -> {
|
||||
var f = File(uri.path, baseName + extension)
|
||||
while (f.exists()) {
|
||||
tempName = "$baseName-$tries" // $NON-NLS-1$
|
||||
f = File(uri.path, tempName + extension)
|
||||
tries++
|
||||
}
|
||||
}
|
||||
}
|
||||
return tempName + extension
|
||||
}
|
||||
|
||||
fun uri2String(uri: Uri?): String {
|
||||
if (uri == null) {
|
||||
return ""
|
||||
}
|
||||
return if (uri.scheme == ContentResolver.SCHEME_FILE) {
|
||||
File(uri.path).absolutePath
|
||||
} else uri.toString()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue