From 1f772935f2468c2a2914f7221f67cc03bf330ae2 Mon Sep 17 00:00:00 2001 From: Catfriend1 Date: Tue, 30 Oct 2018 21:50:48 +0100 Subject: [PATCH] Recent changes - open file/dir on click (#118) * Initialize DiskEventData for tests * Add FileUtils#getMimeTypeFromFileExtension * Open file when user clicks on it in RecentChangesActivity * Add RestApi#getFolderByID * Add Constants#ENABLE_TEST_DATA * Add strings * Move open folder to FileUtils#openFolder * Add FileUtils#openFolder, FileUtils#openFile * Fix UI glitch Make clear to the user that the folder label is not part of the physical full file path and name. * Open file/folder on item click Add test data * Imported translations * Fix lint * Update APK version to 0.14.51.13 / 4176 * Update README.md, whatsnew * Turn off test data mode * Remove unused import --- README.md | 4 +- app/build.gradle | 4 +- .../activities/RecentChangesActivity.java | 82 +- .../syncthingandroid/model/DiskEvent.java | 2 +- .../syncthingandroid/service/Constants.java | 3 + .../syncthingandroid/service/RestApi.java | 21 + .../syncthingandroid/util/FileUtils.java | 712 ++++++++++++++++++ .../views/ChangeListAdapter.java | 2 +- .../views/FoldersAdapter.java | 71 +- app/src/main/play/en-GB/whatsnew | 3 +- app/src/main/res/values-ca-rES/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 2 + app/src/main/res/values-fi/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values/strings.xml | 2 + 22 files changed, 824 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index ee0bd359..279969d8 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ [![License: MPLv2](https://img.shields.io/badge/License-MPLv2-blue.svg)](https://opensource.org/licenses/MPL-2.0) - + # Major enhancements in this fork are: - Individual sync conditions can be applied per device and per folder (for expert users). -- Recent changes UI. +- Recent changes UI, click to open files. - UI explains why syncthing is running or not running according to the run conditions set in preferences. - "Battery eater" problem is fixed. - Android 8 and 9 support. diff --git a/app/build.gradle b/app/build.gradle index 4f1762fe..6a53b9b1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,8 +37,8 @@ android { applicationId "com.github.catfriend1.syncthingandroid" minSdkVersion 16 targetSdkVersion 26 - versionCode 4175 - versionName "0.14.51.12" + versionCode 4176 + versionName "0.14.51.13" testApplicationId 'com.github.catfriend1.syncthingandroid.test' testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' playAccountConfig = playAccountConfigs.defaultAccountConfig diff --git a/app/src/main/java/com/nutomic/syncthingandroid/activities/RecentChangesActivity.java b/app/src/main/java/com/nutomic/syncthingandroid/activities/RecentChangesActivity.java index 3738c619..16ef5d7e 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/activities/RecentChangesActivity.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/activities/RecentChangesActivity.java @@ -9,20 +9,28 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; + import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.DiskEvent; +import com.nutomic.syncthingandroid.model.Folder; import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.SyncthingService; import com.nutomic.syncthingandroid.service.SyncthingServiceBinder; +import com.nutomic.syncthingandroid.util.FileUtils; import com.nutomic.syncthingandroid.views.ChangeListAdapter; import com.nutomic.syncthingandroid.views.ChangeListAdapter.ItemClickListener; import java.io.File; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static com.nutomic.syncthingandroid.service.Constants.ENABLE_TEST_DATA; + /** * Holds a RecyclerView that shows recent changes to files and folders. */ @@ -55,11 +63,38 @@ public class RecentChangesActivity extends SyncthingActivity @Override public void onItemClick(DiskEvent diskEvent) { Log.v(TAG, "User clicked item with title \'" + diskEvent.data.path + "\'"); - /** - * Future improvement: - * Collapse texts to the first three lines and open a DialogFragment - * if the user clicks an item from the list. - */ + switch (diskEvent.data.action) { + case "deleted": + return; + } + if (mServiceState != SyncthingService.State.ACTIVE) { + return; + } + SyncthingService syncthingService = getService(); + if (syncthingService == null) { + Log.e(TAG, "onItemClick: syncthingService == null"); + return; + } + RestApi restApi = syncthingService.getApi(); + if (restApi == null) { + Log.e(TAG, "onItemClick: restApi == null"); + return; + } + Folder folder = restApi.getFolderByID(diskEvent.data.folderID); + if (folder == null) { + Log.e(TAG, "onItemClick: folder == null"); + return; + } + switch (diskEvent.data.type) { + case "dir": + FileUtils.openFolder(RecentChangesActivity.this, folder.path + File.separator + diskEvent.data.path); + break; + case "file": + FileUtils.openFile(RecentChangesActivity.this, folder.path + File.separator + diskEvent.data.path); + break; + default: + Log.e(TAG, "onItemClick: Unknown diskEvent.data.type=[" + diskEvent.data.type + "]"); + } } } ); @@ -100,17 +135,20 @@ public class RecentChangesActivity extends SyncthingActivity } SyncthingService syncthingService = getService(); if (syncthingService == null) { - Log.e(TAG, "syncthingService == null"); + Log.e(TAG, "onTimerEvent: syncthingService == null"); return; } RestApi restApi = syncthingService.getApi(); if (restApi == null) { - Log.e(TAG, "restApi == null"); + Log.e(TAG, "onTimerEvent: restApi == null"); return; } mDevices = restApi.getDevices(true); Log.v(TAG, "Querying disk events"); restApi.getDiskEvents(DISK_EVENT_LIMIT, this::onReceiveDiskEvents); + if (ENABLE_TEST_DATA) { + onReceiveDiskEvents(new ArrayList()); + } } private void onReceiveDiskEvents(List diskEvents) { @@ -119,6 +157,26 @@ public class RecentChangesActivity extends SyncthingActivity return; } + if (ENABLE_TEST_DATA) { + DiskEvent fakeDiskEvent = new DiskEvent(); + fakeDiskEvent.id = 2; + fakeDiskEvent.globalID = 84; + fakeDiskEvent.time = "2018-10-28T14:08:01.6183215+01:00"; + fakeDiskEvent.type = "RemoteChangeDetected"; + fakeDiskEvent.data.action = "added"; + fakeDiskEvent.data.folder = "abcd-efgh"; + fakeDiskEvent.data.folderID = "abcd-efgh"; + fakeDiskEvent.data.label = "label_abcd-efgh"; + fakeDiskEvent.data.modifiedBy = "SRV01"; + fakeDiskEvent.data.path = "document1.txt"; + fakeDiskEvent.data.type = "file"; + diskEvents.add(fakeDiskEvent); + fakeDiskEvent = deepCopy(fakeDiskEvent, new TypeToken(){}.getType()); + fakeDiskEvent.id = 1; + fakeDiskEvent.data.action = "deleted"; + diskEvents.add(fakeDiskEvent); + } + mRecentChangeAdapter.clear(); for (DiskEvent diskEvent : diskEvents) { if (diskEvent.data != null) { @@ -136,4 +194,14 @@ public class RecentChangesActivity extends SyncthingActivity } mRecentChangeAdapter.notifyDataSetChanged(); } + + /** + * Returns a deep copy of object. + * + * This method uses Gson and only works with objects that can be converted with Gson. + */ + private T deepCopy(T object, Type type) { + Gson gson = new Gson(); + return gson.fromJson(gson.toJson(object, type), type); + } } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/model/DiskEvent.java b/app/src/main/java/com/nutomic/syncthingandroid/model/DiskEvent.java index 01ff74da..d7fb3a00 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/model/DiskEvent.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/model/DiskEvent.java @@ -11,5 +11,5 @@ public class DiskEvent { // type = {"LocalChangeDetected", "RemoteChangeDetected"} public String type = ""; - public DiskEventData data; + public DiskEventData data = new DiskEventData(); } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java b/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java index d2a64d80..f59d0a03 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/service/Constants.java @@ -8,6 +8,9 @@ import java.util.concurrent.TimeUnit; public class Constants { + // Always set ENABLE_TEST_DATA to false before building debug or release APK's. + public static final Boolean ENABLE_TEST_DATA = false; + public static final String FILENAME_SYNCTHING_BINARY = "libsyncthing.so"; // Preferences - Run conditions diff --git a/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java b/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java index bd47db1f..ec69ac21 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/service/RestApi.java @@ -56,6 +56,8 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.inject.Inject; +import static com.nutomic.syncthingandroid.service.Constants.ENABLE_TEST_DATA; + /** * Provides functions to interact with the syncthing REST API. */ @@ -429,6 +431,25 @@ public class RestApi { return folders; } + public final Folder getFolderByID(String folderID) { + if (ENABLE_TEST_DATA && folderID.equals("abcd-efgh")) { + final Folder folder = new Folder(); + folder.id = "abcd-efgh"; + folder.label = "label_abcd-efgh"; + folder.path = "/storage/emulated/0/testdata"; + folder.type = Constants.FOLDER_TYPE_SEND_RECEIVE; + return folder; + } + + final List folders = getFolders(); + for (Folder folder : folders) { + if (folder.id.equals(folderID)) { + return folder; + } + } + return null; + } + /** * This is only used for new folder creation, see {@link FolderActivity}. */ diff --git a/app/src/main/java/com/nutomic/syncthingandroid/util/FileUtils.java b/app/src/main/java/com/nutomic/syncthingandroid/util/FileUtils.java index 835b2198..7f113c30 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/util/FileUtils.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/util/FileUtils.java @@ -2,7 +2,11 @@ package com.nutomic.syncthingandroid.util; import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Environment; @@ -10,6 +14,10 @@ import android.os.storage.StorageManager; import android.provider.DocumentsContract; import android.support.annotation.Nullable; import android.util.Log; +import android.webkit.MimeTypeMap; +import android.widget.Toast; + +import com.nutomic.syncthingandroid.R; import java.io.File; import java.io.IOException; @@ -18,6 +26,8 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; +import java.util.Locale; /** * Utils for dealing with Storage Access Framework URIs. @@ -219,4 +229,706 @@ public class FileUtils { .map(java.nio.file.Path::toFile) .forEach(File::delete); } + + /** + * Derives the mime type from file extension. + */ + public static String getMimeTypeFromFileExtension(String fileExtension) { + HashMap mimeTypes = new HashMap(); + mimeTypes.put("323", "text/h323"); + mimeTypes.put("3g2", "video/3gpp2"); + mimeTypes.put("3gp", "video/3gpp"); + mimeTypes.put("3gp2", "video/3gpp2"); + mimeTypes.put("3gpp", "video/3gpp"); + mimeTypes.put("7z", "application/x-7z-compressed"); + mimeTypes.put("aa", "audio/audible"); + mimeTypes.put("aac", "audio/aac"); + mimeTypes.put("aaf", "application/octet-stream"); + mimeTypes.put("aax", "audio/vnd.audible.aax"); + mimeTypes.put("ac3", "audio/ac3"); + mimeTypes.put("aca", "application/octet-stream"); + mimeTypes.put("accda", "application/msaccess.addin"); + mimeTypes.put("accdb", "application/msaccess"); + mimeTypes.put("accdc", "application/msaccess.cab"); + mimeTypes.put("accde", "application/msaccess"); + mimeTypes.put("accdr", "application/msaccess.runtime"); + mimeTypes.put("accdt", "application/msaccess"); + mimeTypes.put("accdw", "application/msaccess.webapplication"); + mimeTypes.put("accft", "application/msaccess.ftemplate"); + mimeTypes.put("acx", "application/internet-property-stream"); + mimeTypes.put("addin", "text/xml"); + mimeTypes.put("ade", "application/msaccess"); + mimeTypes.put("adobebridge", "application/x-bridge-url"); + mimeTypes.put("adp", "application/msaccess"); + mimeTypes.put("adt", "audio/vnd.dlna.adts"); + mimeTypes.put("adts", "audio/aac"); + mimeTypes.put("afm", "application/octet-stream"); + mimeTypes.put("ai", "application/postscript"); + mimeTypes.put("aif", "audio/aiff"); + mimeTypes.put("aifc", "audio/aiff"); + mimeTypes.put("aiff", "audio/aiff"); + mimeTypes.put("air", "application/vnd.adobe.air-application-installer-package+zip"); + mimeTypes.put("amc", "application/mpeg"); + mimeTypes.put("anx", "application/annodex"); + mimeTypes.put("apk", "application/vnd.android.package-archive"); + mimeTypes.put("application", "application/x-ms-application"); + mimeTypes.put("art", "image/x-jg"); + mimeTypes.put("asa", "application/xml"); + mimeTypes.put("asax", "application/xml"); + mimeTypes.put("ascx", "application/xml"); + mimeTypes.put("asd", "application/octet-stream"); + mimeTypes.put("asf", "video/x-ms-asf"); + mimeTypes.put("ashx", "application/xml"); + mimeTypes.put("asi", "application/octet-stream"); + mimeTypes.put("asm", "text/plain"); + mimeTypes.put("asmx", "application/xml"); + mimeTypes.put("aspx", "application/xml"); + mimeTypes.put("asr", "video/x-ms-asf"); + mimeTypes.put("asx", "video/x-ms-asf"); + mimeTypes.put("atom", "application/atom+xml"); + mimeTypes.put("au", "audio/basic"); + mimeTypes.put("avi", "video/x-msvideo"); + mimeTypes.put("axa", "audio/annodex"); + mimeTypes.put("axs", "application/olescript"); + mimeTypes.put("axv", "video/annodex"); + mimeTypes.put("bas", "text/plain"); + mimeTypes.put("bcpio", "application/x-bcpio"); + mimeTypes.put("bin", "application/octet-stream"); + mimeTypes.put("bmp", "image/bmp"); + mimeTypes.put("c", "text/plain"); + mimeTypes.put("cab", "application/octet-stream"); + mimeTypes.put("caf", "audio/x-caf"); + mimeTypes.put("calx", "application/vnd.ms-office.calx"); + mimeTypes.put("cat", "application/vnd.ms-pki.seccat"); + mimeTypes.put("cc", "text/plain"); + mimeTypes.put("cd", "text/plain"); + mimeTypes.put("cdda", "audio/aiff"); + mimeTypes.put("cdf", "application/x-cdf"); + mimeTypes.put("cer", "application/x-x509-ca-cert"); + mimeTypes.put("cfg", "text/plain"); + mimeTypes.put("chm", "application/octet-stream"); + mimeTypes.put("class", "application/x-java-applet"); + mimeTypes.put("clp", "application/x-msclip"); + mimeTypes.put("cmd", "text/plain"); + mimeTypes.put("cmx", "image/x-cmx"); + mimeTypes.put("cnf", "text/plain"); + mimeTypes.put("cod", "image/cis-cod"); + mimeTypes.put("config", "application/xml"); + mimeTypes.put("contact", "text/x-ms-contact"); + mimeTypes.put("coverage", "application/xml"); + mimeTypes.put("cpio", "application/x-cpio"); + mimeTypes.put("cpp", "text/plain"); + mimeTypes.put("crd", "application/x-mscardfile"); + mimeTypes.put("crl", "application/pkix-crl"); + mimeTypes.put("crt", "application/x-x509-ca-cert"); + mimeTypes.put("cs", "text/plain"); + mimeTypes.put("csdproj", "text/plain"); + mimeTypes.put("csh", "application/x-csh"); + mimeTypes.put("csproj", "text/plain"); + mimeTypes.put("css", "text/css"); + mimeTypes.put("csv", "text/csv"); + mimeTypes.put("cur", "application/octet-stream"); + mimeTypes.put("cxx", "text/plain"); + mimeTypes.put("dat", "application/octet-stream"); + mimeTypes.put("datasource", "application/xml"); + mimeTypes.put("dbproj", "text/plain"); + mimeTypes.put("dcr", "application/x-director"); + mimeTypes.put("def", "text/plain"); + mimeTypes.put("deploy", "application/octet-stream"); + mimeTypes.put("der", "application/x-x509-ca-cert"); + mimeTypes.put("dgml", "application/xml"); + mimeTypes.put("dib", "image/bmp"); + mimeTypes.put("dif", "video/x-dv"); + mimeTypes.put("dir", "application/x-director"); + mimeTypes.put("disco", "text/xml"); + mimeTypes.put("divx", "video/divx"); + mimeTypes.put("dll", "application/x-msdownload"); + mimeTypes.put("dll.config", "text/xml"); + mimeTypes.put("dlm", "text/dlm"); + mimeTypes.put("dng", "image/x-adobe-dng"); + mimeTypes.put("doc", "application/msword"); + mimeTypes.put("docm", "application/vnd.ms-word.document.macroEnabled.12"); + mimeTypes.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + mimeTypes.put("dot", "application/msword"); + mimeTypes.put("dotm", "application/vnd.ms-word.template.macroEnabled.12"); + mimeTypes.put("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"); + mimeTypes.put("dsp", "application/octet-stream"); + mimeTypes.put("dsw", "text/plain"); + mimeTypes.put("dtd", "text/xml"); + mimeTypes.put("dtsconfig", "text/xml"); + mimeTypes.put("dv", "video/x-dv"); + mimeTypes.put("dvi", "application/x-dvi"); + mimeTypes.put("dwf", "drawing/x-dwf"); + mimeTypes.put("dwp", "application/octet-stream"); + mimeTypes.put("dxr", "application/x-director"); + mimeTypes.put("eml", "message/rfc822"); + mimeTypes.put("emz", "application/octet-stream"); + mimeTypes.put("eot", "application/vnd.ms-fontobject"); + mimeTypes.put("eps", "application/postscript"); + mimeTypes.put("etl", "application/etl"); + mimeTypes.put("etx", "text/x-setext"); + mimeTypes.put("evy", "application/envoy"); + mimeTypes.put("exe", "application/octet-stream"); + mimeTypes.put("exe.config", "text/xml"); + mimeTypes.put("fdf", "application/vnd.fdf"); + mimeTypes.put("fif", "application/fractals"); + mimeTypes.put("filters", "application/xml"); + mimeTypes.put("fla", "application/octet-stream"); + mimeTypes.put("flac", "audio/flac"); + mimeTypes.put("flr", "x-world/x-vrml"); + mimeTypes.put("flv", "video/x-flv"); + mimeTypes.put("fsscript", "application/fsharp-script"); + mimeTypes.put("fsx", "application/fsharp-script"); + mimeTypes.put("generictest", "application/xml"); + mimeTypes.put("gif", "image/gif"); + mimeTypes.put("group", "text/x-ms-group"); + mimeTypes.put("gsm", "audio/x-gsm"); + mimeTypes.put("gtar", "application/x-gtar"); + mimeTypes.put("gz", "application/x-gzip"); + mimeTypes.put("h", "text/plain"); + mimeTypes.put("hdf", "application/x-hdf"); + mimeTypes.put("hdml", "text/x-hdml"); + mimeTypes.put("hhc", "application/x-oleobject"); + mimeTypes.put("hhk", "application/octet-stream"); + mimeTypes.put("hhp", "application/octet-stream"); + mimeTypes.put("hlp", "application/winhlp"); + mimeTypes.put("hpp", "text/plain"); + mimeTypes.put("hqx", "application/mac-binhex40"); + mimeTypes.put("hta", "application/hta"); + mimeTypes.put("htc", "text/x-component"); + mimeTypes.put("htm", "text/html"); + mimeTypes.put("html", "text/html"); + mimeTypes.put("htt", "text/webviewhtml"); + mimeTypes.put("hxa", "application/xml"); + mimeTypes.put("hxc", "application/xml"); + mimeTypes.put("hxd", "application/octet-stream"); + mimeTypes.put("hxe", "application/xml"); + mimeTypes.put("hxf", "application/xml"); + mimeTypes.put("hxh", "application/octet-stream"); + mimeTypes.put("hxi", "application/octet-stream"); + mimeTypes.put("hxk", "application/xml"); + mimeTypes.put("hxq", "application/octet-stream"); + mimeTypes.put("hxr", "application/octet-stream"); + mimeTypes.put("hxs", "application/octet-stream"); + mimeTypes.put("hxt", "text/html"); + mimeTypes.put("hxv", "application/xml"); + mimeTypes.put("hxw", "application/octet-stream"); + mimeTypes.put("hxx", "text/plain"); + mimeTypes.put("i", "text/plain"); + mimeTypes.put("ico", "image/x-icon"); + mimeTypes.put("ics", "text/calendar"); + mimeTypes.put("idl", "text/plain"); + mimeTypes.put("ief", "image/ief"); + mimeTypes.put("iii", "application/x-iphone"); + mimeTypes.put("inc", "text/plain"); + mimeTypes.put("inf", "application/octet-stream"); + mimeTypes.put("ini", "text/plain"); + mimeTypes.put("inl", "text/plain"); + mimeTypes.put("ins", "application/x-internet-signup"); + mimeTypes.put("ipa", "application/x-itunes-ipa"); + mimeTypes.put("ipg", "application/x-itunes-ipg"); + mimeTypes.put("ipproj", "text/plain"); + mimeTypes.put("ipsw", "application/x-itunes-ipsw"); + mimeTypes.put("iqy", "text/x-ms-iqy"); + mimeTypes.put("isp", "application/x-internet-signup"); + mimeTypes.put("ite", "application/x-itunes-ite"); + mimeTypes.put("itlp", "application/x-itunes-itlp"); + mimeTypes.put("itms", "application/x-itunes-itms"); + mimeTypes.put("itpc", "application/x-itunes-itpc"); + mimeTypes.put("ivf", "video/x-ivf"); + mimeTypes.put("jar", "application/java-archive"); + mimeTypes.put("java", "application/octet-stream"); + mimeTypes.put("jck", "application/liquidmotion"); + mimeTypes.put("jcz", "application/liquidmotion"); + mimeTypes.put("jfif", "image/pjpeg"); + mimeTypes.put("jnlp", "application/x-java-jnlp-file"); + mimeTypes.put("jpb", "application/octet-stream"); + mimeTypes.put("jpe", "image/jpeg"); + mimeTypes.put("jpeg", "image/jpeg"); + mimeTypes.put("jpg", "image/jpeg"); + mimeTypes.put("js", "application/javascript"); + mimeTypes.put("json", "application/json"); + mimeTypes.put("jsx", "text/jscript"); + mimeTypes.put("jsxbin", "text/plain"); + mimeTypes.put("latex", "application/x-latex"); + mimeTypes.put("library-ms", "application/windows-library+xml"); + mimeTypes.put("lit", "application/x-ms-reader"); + mimeTypes.put("loadtest", "application/xml"); + mimeTypes.put("lpk", "application/octet-stream"); + mimeTypes.put("lsf", "video/x-la-asf"); + mimeTypes.put("lst", "text/plain"); + mimeTypes.put("lsx", "video/x-la-asf"); + mimeTypes.put("lzh", "application/octet-stream"); + mimeTypes.put("m13", "application/x-msmediaview"); + mimeTypes.put("m14", "application/x-msmediaview"); + mimeTypes.put("m1v", "video/mpeg"); + mimeTypes.put("m2t", "video/vnd.dlna.mpeg-tts"); + mimeTypes.put("m2ts", "video/vnd.dlna.mpeg-tts"); + mimeTypes.put("m2v", "video/mpeg"); + mimeTypes.put("m3u", "audio/x-mpegurl"); + mimeTypes.put("m3u8", "audio/x-mpegurl"); + mimeTypes.put("m4a", "audio/m4a"); + mimeTypes.put("m4b", "audio/m4b"); + mimeTypes.put("m4p", "audio/m4p"); + mimeTypes.put("m4r", "audio/x-m4r"); + mimeTypes.put("m4v", "video/x-m4v"); + mimeTypes.put("mac", "image/x-macpaint"); + mimeTypes.put("mak", "text/plain"); + mimeTypes.put("man", "application/x-troff-man"); + mimeTypes.put("manifest", "application/x-ms-manifest"); + mimeTypes.put("map", "text/plain"); + mimeTypes.put("master", "application/xml"); + mimeTypes.put("mda", "application/msaccess"); + mimeTypes.put("mdb", "application/x-msaccess"); + mimeTypes.put("mde", "application/msaccess"); + mimeTypes.put("mdp", "application/octet-stream"); + mimeTypes.put("me", "application/x-troff-me"); + mimeTypes.put("mfp", "application/x-shockwave-flash"); + mimeTypes.put("mht", "message/rfc822"); + mimeTypes.put("mhtml", "message/rfc822"); + mimeTypes.put("mid", "audio/mid"); + mimeTypes.put("midi", "audio/mid"); + mimeTypes.put("mix", "application/octet-stream"); + mimeTypes.put("mk", "text/plain"); + mimeTypes.put("mkv", "video/x-matroska"); + mimeTypes.put("mmf", "application/x-smaf"); + mimeTypes.put("mno", "text/xml"); + mimeTypes.put("mny", "application/x-msmoney"); + mimeTypes.put("mod", "video/mpeg"); + mimeTypes.put("mov", "video/quicktime"); + mimeTypes.put("movie", "video/x-sgi-movie"); + mimeTypes.put("mp2", "video/mpeg"); + mimeTypes.put("mp2v", "video/mpeg"); + mimeTypes.put("mp3", "audio/mpeg"); + mimeTypes.put("mp4", "video/mp4"); + mimeTypes.put("mp4v", "video/mp4"); + mimeTypes.put("mpa", "video/mpeg"); + mimeTypes.put("mpe", "video/mpeg"); + mimeTypes.put("mpeg", "video/mpeg"); + mimeTypes.put("mpf", "application/vnd.ms-mediapackage"); + mimeTypes.put("mpg", "video/mpeg"); + mimeTypes.put("mpp", "application/vnd.ms-project"); + mimeTypes.put("mpv2", "video/mpeg"); + mimeTypes.put("mqv", "video/quicktime"); + mimeTypes.put("ms", "application/x-troff-ms"); + mimeTypes.put("msi", "application/octet-stream"); + mimeTypes.put("mso", "application/octet-stream"); + mimeTypes.put("mts", "video/vnd.dlna.mpeg-tts"); + mimeTypes.put("mtx", "application/xml"); + mimeTypes.put("mvb", "application/x-msmediaview"); + mimeTypes.put("mvc", "application/x-miva-compiled"); + mimeTypes.put("mxp", "application/x-mmxp"); + mimeTypes.put("nc", "application/x-netcdf"); + mimeTypes.put("nsc", "video/x-ms-asf"); + mimeTypes.put("nws", "message/rfc822"); + mimeTypes.put("ocx", "application/octet-stream"); + mimeTypes.put("oda", "application/oda"); + mimeTypes.put("odb", "application/vnd.oasis.opendocument.database"); + mimeTypes.put("odc", "application/vnd.oasis.opendocument.chart"); + mimeTypes.put("odf", "application/vnd.oasis.opendocument.formula"); + mimeTypes.put("odg", "application/vnd.oasis.opendocument.graphics"); + mimeTypes.put("odh", "text/plain"); + mimeTypes.put("odi", "application/vnd.oasis.opendocument.image"); + mimeTypes.put("odl", "text/plain"); + mimeTypes.put("odm", "application/vnd.oasis.opendocument.text-master"); + mimeTypes.put("odp", "application/vnd.oasis.opendocument.presentation"); + mimeTypes.put("ods", "application/vnd.oasis.opendocument.spreadsheet"); + mimeTypes.put("odt", "application/vnd.oasis.opendocument.text"); + mimeTypes.put("oga", "audio/ogg"); + mimeTypes.put("ogg", "audio/ogg"); + mimeTypes.put("ogv", "video/ogg"); + mimeTypes.put("ogx", "application/ogg"); + mimeTypes.put("one", "application/onenote"); + mimeTypes.put("onea", "application/onenote"); + mimeTypes.put("onepkg", "application/onenote"); + mimeTypes.put("onetmp", "application/onenote"); + mimeTypes.put("onetoc", "application/onenote"); + mimeTypes.put("onetoc2", "application/onenote"); + mimeTypes.put("opus", "audio/ogg"); + mimeTypes.put("orderedtest", "application/xml"); + mimeTypes.put("osdx", "application/opensearchdescription+xml"); + mimeTypes.put("otf", "application/font-sfnt"); + mimeTypes.put("otg", "application/vnd.oasis.opendocument.graphics-template"); + mimeTypes.put("oth", "application/vnd.oasis.opendocument.text-web"); + mimeTypes.put("otp", "application/vnd.oasis.opendocument.presentation-template"); + mimeTypes.put("ots", "application/vnd.oasis.opendocument.spreadsheet-template"); + mimeTypes.put("ott", "application/vnd.oasis.opendocument.text-template"); + mimeTypes.put("oxt", "application/vnd.openofficeorg.extension"); + mimeTypes.put("p10", "application/pkcs10"); + mimeTypes.put("p12", "application/x-pkcs12"); + mimeTypes.put("p7b", "application/x-pkcs7-certificates"); + mimeTypes.put("p7c", "application/pkcs7-mime"); + mimeTypes.put("p7m", "application/pkcs7-mime"); + mimeTypes.put("p7r", "application/x-pkcs7-certreqresp"); + mimeTypes.put("p7s", "application/pkcs7-signature"); + mimeTypes.put("pbm", "image/x-portable-bitmap"); + mimeTypes.put("pcast", "application/x-podcast"); + mimeTypes.put("pct", "image/pict"); + mimeTypes.put("pcx", "application/octet-stream"); + mimeTypes.put("pcz", "application/octet-stream"); + mimeTypes.put("pdf", "application/pdf"); + mimeTypes.put("pfb", "application/octet-stream"); + mimeTypes.put("pfm", "application/octet-stream"); + mimeTypes.put("pfx", "application/x-pkcs12"); + mimeTypes.put("pgm", "image/x-portable-graymap"); + mimeTypes.put("php", "text/plain"); + mimeTypes.put("pic", "image/pict"); + mimeTypes.put("pict", "image/pict"); + mimeTypes.put("pkgdef", "text/plain"); + mimeTypes.put("pkgundef", "text/plain"); + mimeTypes.put("pko", "application/vnd.ms-pki.pko"); + mimeTypes.put("pls", "audio/scpls"); + mimeTypes.put("pma", "application/x-perfmon"); + mimeTypes.put("pmc", "application/x-perfmon"); + mimeTypes.put("pml", "application/x-perfmon"); + mimeTypes.put("pmr", "application/x-perfmon"); + mimeTypes.put("pmw", "application/x-perfmon"); + mimeTypes.put("png", "image/png"); + mimeTypes.put("pnm", "image/x-portable-anymap"); + mimeTypes.put("pnt", "image/x-macpaint"); + mimeTypes.put("pntg", "image/x-macpaint"); + mimeTypes.put("pnz", "image/png"); + mimeTypes.put("pot", "application/vnd.ms-powerpoint"); + mimeTypes.put("potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"); + mimeTypes.put("potx", "application/vnd.openxmlformats-officedocument.presentationml.template"); + mimeTypes.put("ppa", "application/vnd.ms-powerpoint"); + mimeTypes.put("ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"); + mimeTypes.put("ppm", "image/x-portable-pixmap"); + mimeTypes.put("pps", "application/vnd.ms-powerpoint"); + mimeTypes.put("ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"); + mimeTypes.put("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"); + mimeTypes.put("ppt", "application/vnd.ms-powerpoint"); + mimeTypes.put("pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"); + mimeTypes.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"); + mimeTypes.put("prf", "application/pics-rules"); + mimeTypes.put("prm", "application/octet-stream"); + mimeTypes.put("prx", "application/octet-stream"); + mimeTypes.put("ps", "application/postscript"); + mimeTypes.put("psc1", "application/PowerShell"); + mimeTypes.put("psd", "application/octet-stream"); + mimeTypes.put("psess", "application/xml"); + mimeTypes.put("psm", "application/octet-stream"); + mimeTypes.put("psp", "application/octet-stream"); + mimeTypes.put("pub", "application/x-mspublisher"); + mimeTypes.put("pwz", "application/vnd.ms-powerpoint"); + mimeTypes.put("py", "text/plain"); + mimeTypes.put("qht", "text/x-html-insertion"); + mimeTypes.put("qhtm", "text/x-html-insertion"); + mimeTypes.put("qt", "video/quicktime"); + mimeTypes.put("qti", "image/x-quicktime"); + mimeTypes.put("qtif", "image/x-quicktime"); + mimeTypes.put("qtl", "application/x-quicktimeplayer"); + mimeTypes.put("qxd", "application/octet-stream"); + mimeTypes.put("ra", "audio/x-pn-realaudio"); + mimeTypes.put("ram", "audio/x-pn-realaudio"); + mimeTypes.put("rar", "application/x-rar-compressed"); + mimeTypes.put("ras", "image/x-cmu-raster"); + mimeTypes.put("rat", "application/rat-file"); + mimeTypes.put("rb", "text/plain"); + mimeTypes.put("rc", "text/plain"); + mimeTypes.put("rc2", "text/plain"); + mimeTypes.put("rct", "text/plain"); + mimeTypes.put("rdlc", "application/xml"); + mimeTypes.put("reg", "text/plain"); + mimeTypes.put("resx", "application/xml"); + mimeTypes.put("rf", "image/vnd.rn-realflash"); + mimeTypes.put("rgb", "image/x-rgb"); + mimeTypes.put("rgs", "text/plain"); + mimeTypes.put("rm", "application/vnd.rn-realmedia"); + mimeTypes.put("rmi", "audio/mid"); + mimeTypes.put("rmp", "application/vnd.rn-rn_music_package"); + mimeTypes.put("roff", "application/x-troff"); + mimeTypes.put("rpm", "audio/x-pn-realaudio-plugin"); + mimeTypes.put("rqy", "text/x-ms-rqy"); + mimeTypes.put("rtf", "application/rtf"); + mimeTypes.put("rtx", "text/richtext"); + mimeTypes.put("ruleset", "application/xml"); + mimeTypes.put("s", "text/plain"); + mimeTypes.put("safariextz", "application/x-safari-safariextz"); + mimeTypes.put("scd", "application/x-msschedule"); + mimeTypes.put("scr", "text/plain"); + mimeTypes.put("sct", "text/scriptlet"); + mimeTypes.put("sd2", "audio/x-sd2"); + mimeTypes.put("sdp", "application/sdp"); + mimeTypes.put("sea", "application/octet-stream"); + mimeTypes.put("searchConnector-ms", "application/windows-search-connector+xml"); + mimeTypes.put("setpay", "application/set-payment-initiation"); + mimeTypes.put("setreg", "application/set-registration-initiation"); + mimeTypes.put("settings", "application/xml"); + mimeTypes.put("sgimb", "application/x-sgimb"); + mimeTypes.put("sgml", "text/sgml"); + mimeTypes.put("sh", "application/x-sh"); + mimeTypes.put("shar", "application/x-shar"); + mimeTypes.put("shtml", "text/html"); + mimeTypes.put("sit", "application/x-stuffit"); + mimeTypes.put("sitemap", "application/xml"); + mimeTypes.put("skin", "application/xml"); + mimeTypes.put("sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"); + mimeTypes.put("sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"); + mimeTypes.put("slk", "application/vnd.ms-excel"); + mimeTypes.put("sln", "text/plain"); + mimeTypes.put("slupkg-ms", "application/x-ms-license"); + mimeTypes.put("smd", "audio/x-smd"); + mimeTypes.put("smi", "application/octet-stream"); + mimeTypes.put("smx", "audio/x-smd"); + mimeTypes.put("smz", "audio/x-smd"); + mimeTypes.put("snd", "audio/basic"); + mimeTypes.put("snippet", "application/xml"); + mimeTypes.put("snp", "application/octet-stream"); + mimeTypes.put("sol", "text/plain"); + mimeTypes.put("sor", "text/plain"); + mimeTypes.put("spc", "application/x-pkcs7-certificates"); + mimeTypes.put("spl", "application/futuresplash"); + mimeTypes.put("spx", "audio/ogg"); + mimeTypes.put("src", "application/x-wais-source"); + mimeTypes.put("srf", "text/plain"); + mimeTypes.put("ssisdeploymentmanifest", "text/xml"); + mimeTypes.put("ssm", "application/streamingmedia"); + mimeTypes.put("sst", "application/vnd.ms-pki.certstore"); + mimeTypes.put("stl", "application/vnd.ms-pki.stl"); + mimeTypes.put("sv4cpio", "application/x-sv4cpio"); + mimeTypes.put("sv4crc", "application/x-sv4crc"); + mimeTypes.put("svc", "application/xml"); + mimeTypes.put("svg", "image/svg+xml"); + mimeTypes.put("swf", "application/x-shockwave-flash"); + mimeTypes.put("t", "application/x-troff"); + mimeTypes.put("tar", "application/x-tar"); + mimeTypes.put("tcl", "application/x-tcl"); + mimeTypes.put("testrunconfig", "application/xml"); + mimeTypes.put("testsettings", "application/xml"); + mimeTypes.put("tex", "application/x-tex"); + mimeTypes.put("texi", "application/x-texinfo"); + mimeTypes.put("texinfo", "application/x-texinfo"); + mimeTypes.put("tgz", "application/x-compressed"); + mimeTypes.put("thmx", "application/vnd.ms-officetheme"); + mimeTypes.put("thn", "application/octet-stream"); + mimeTypes.put("tif", "image/tiff"); + mimeTypes.put("tiff", "image/tiff"); + mimeTypes.put("tlh", "text/plain"); + mimeTypes.put("tli", "text/plain"); + mimeTypes.put("toc", "application/octet-stream"); + mimeTypes.put("tr", "application/x-troff"); + mimeTypes.put("trm", "application/x-msterminal"); + mimeTypes.put("trx", "application/xml"); + mimeTypes.put("ts", "video/vnd.dlna.mpeg-tts"); + mimeTypes.put("tsv", "text/tab-separated-values"); + mimeTypes.put("ttf", "application/font-sfnt"); + mimeTypes.put("tts", "video/vnd.dlna.mpeg-tts"); + mimeTypes.put("txt", "text/plain"); + mimeTypes.put("u32", "application/octet-stream"); + mimeTypes.put("uls", "text/iuls"); + mimeTypes.put("user", "text/plain"); + mimeTypes.put("ustar", "application/x-ustar"); + mimeTypes.put("vb", "text/plain"); + mimeTypes.put("vbdproj", "text/plain"); + mimeTypes.put("vbk", "video/mpeg"); + mimeTypes.put("vbproj", "text/plain"); + mimeTypes.put("vbs", "text/vbscript"); + mimeTypes.put("vcf", "text/x-vcard"); + mimeTypes.put("vcproj", "application/xml"); + mimeTypes.put("vcs", "text/plain"); + mimeTypes.put("vcxproj", "application/xml"); + mimeTypes.put("vddproj", "text/plain"); + mimeTypes.put("vdp", "text/plain"); + mimeTypes.put("vdproj", "text/plain"); + mimeTypes.put("vdx", "application/vnd.ms-visio.viewer"); + mimeTypes.put("vml", "text/xml"); + mimeTypes.put("vscontent", "application/xml"); + mimeTypes.put("vsct", "text/xml"); + mimeTypes.put("vsd", "application/vnd.visio"); + mimeTypes.put("vsi", "application/ms-vsi"); + mimeTypes.put("vsix", "application/vsix"); + mimeTypes.put("vsixlangpack", "text/xml"); + mimeTypes.put("vsixmanifest", "text/xml"); + mimeTypes.put("vsmdi", "application/xml"); + mimeTypes.put("vspscc", "text/plain"); + mimeTypes.put("vss", "application/vnd.visio"); + mimeTypes.put("vsscc", "text/plain"); + mimeTypes.put("vssettings", "text/xml"); + mimeTypes.put("vssscc", "text/plain"); + mimeTypes.put("vst", "application/vnd.visio"); + mimeTypes.put("vstemplate", "text/xml"); + mimeTypes.put("vsto", "application/x-ms-vsto"); + mimeTypes.put("vsw", "application/vnd.visio"); + mimeTypes.put("vsx", "application/vnd.visio"); + mimeTypes.put("vtx", "application/vnd.visio"); + mimeTypes.put("wav", "audio/wav"); + mimeTypes.put("wave", "audio/wav"); + mimeTypes.put("wax", "audio/x-ms-wax"); + mimeTypes.put("wbk", "application/msword"); + mimeTypes.put("wbmp", "image/vnd.wap.wbmp"); + mimeTypes.put("wcm", "application/vnd.ms-works"); + mimeTypes.put("wdb", "application/vnd.ms-works"); + mimeTypes.put("wdp", "image/vnd.ms-photo"); + mimeTypes.put("webarchive", "application/x-safari-webarchive"); + mimeTypes.put("webm", "video/webm"); + mimeTypes.put("webp", "image/webp"); + mimeTypes.put("webtest", "application/xml"); + mimeTypes.put("wiq", "application/xml"); + mimeTypes.put("wiz", "application/msword"); + mimeTypes.put("wks", "application/vnd.ms-works"); + mimeTypes.put("wlmp", "application/wlmoviemaker"); + mimeTypes.put("wlpginstall", "application/x-wlpg-detect"); + mimeTypes.put("wlpginstall3", "application/x-wlpg3-detect"); + mimeTypes.put("wm", "video/x-ms-wm"); + mimeTypes.put("wma", "audio/x-ms-wma"); + mimeTypes.put("wmd", "application/x-ms-wmd"); + mimeTypes.put("wmf", "application/x-msmetafile"); + mimeTypes.put("wml", "text/vnd.wap.wml"); + mimeTypes.put("wmlc", "application/vnd.wap.wmlc"); + mimeTypes.put("wmls", "text/vnd.wap.wmlscript"); + mimeTypes.put("wmlsc", "application/vnd.wap.wmlscriptc"); + mimeTypes.put("wmp", "video/x-ms-wmp"); + mimeTypes.put("wmv", "video/x-ms-wmv"); + mimeTypes.put("wmx", "video/x-ms-wmx"); + mimeTypes.put("wmz", "application/x-ms-wmz"); + mimeTypes.put("woff", "application/font-woff"); + mimeTypes.put("wpl", "application/vnd.ms-wpl"); + mimeTypes.put("wps", "application/vnd.ms-works"); + mimeTypes.put("wri", "application/x-mswrite"); + mimeTypes.put("wrl", "x-world/x-vrml"); + mimeTypes.put("wrz", "x-world/x-vrml"); + mimeTypes.put("wsc", "text/scriptlet"); + mimeTypes.put("wsdl", "text/xml"); + mimeTypes.put("wvx", "video/x-ms-wvx"); + mimeTypes.put("x", "application/directx"); + mimeTypes.put("xaf", "x-world/x-vrml"); + mimeTypes.put("xaml", "application/xaml+xml"); + mimeTypes.put("xap", "application/x-silverlight-app"); + mimeTypes.put("xbap", "application/x-ms-xbap"); + mimeTypes.put("xbm", "image/x-xbitmap"); + mimeTypes.put("xdr", "text/plain"); + mimeTypes.put("xht", "application/xhtml+xml"); + mimeTypes.put("xhtml", "application/xhtml+xml"); + mimeTypes.put("xla", "application/vnd.ms-excel"); + mimeTypes.put("xlam", "application/vnd.ms-excel.addin.macroEnabled.12"); + mimeTypes.put("xlc", "application/vnd.ms-excel"); + mimeTypes.put("xld", "application/vnd.ms-excel"); + mimeTypes.put("xlk", "application/vnd.ms-excel"); + mimeTypes.put("xll", "application/vnd.ms-excel"); + mimeTypes.put("xlm", "application/vnd.ms-excel"); + mimeTypes.put("xls", "application/vnd.ms-excel"); + mimeTypes.put("xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"); + mimeTypes.put("xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"); + mimeTypes.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + mimeTypes.put("xlt", "application/vnd.ms-excel"); + mimeTypes.put("xltm", "application/vnd.ms-excel.template.macroEnabled.12"); + mimeTypes.put("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"); + mimeTypes.put("xlw", "application/vnd.ms-excel"); + mimeTypes.put("xml", "text/xml"); + mimeTypes.put("xmta", "application/xml"); + mimeTypes.put("xof", "x-world/x-vrml"); + mimeTypes.put("xoml", "text/plain"); + mimeTypes.put("xpm", "image/x-xpixmap"); + mimeTypes.put("xps", "application/vnd.ms-xpsdocument"); + mimeTypes.put("xrm-ms", "text/xml"); + mimeTypes.put("xsc", "application/xml"); + mimeTypes.put("xsd", "text/xml"); + mimeTypes.put("xsf", "text/xml"); + mimeTypes.put("xsl", "text/xml"); + mimeTypes.put("xslt", "text/xml"); + mimeTypes.put("xsn", "application/octet-stream"); + mimeTypes.put("xss", "application/xml"); + mimeTypes.put("xspf", "application/xspf+xml"); + mimeTypes.put("xtp", "application/octet-stream"); + mimeTypes.put("xwd", "image/x-xwindowdump"); + mimeTypes.put("z", "application/x-compress"); + mimeTypes.put("zip", "application/zip"); + String fileMimeType = mimeTypes.get(fileExtension.toLowerCase(Locale.ROOT)); + return (fileMimeType == null) ? "" : fileMimeType; + } + + /** + * Open file in compatible app. + */ + public static void openFile(Context context, String fullPathAndFilename) { + Uri fileUri = Uri.parse(fullPathAndFilename); + String fileExtension = MimeTypeMap.getFileExtensionFromUrl(fileUri.toString()); + String mimeType = FileUtils.getMimeTypeFromFileExtension(fileExtension); + Log.v(TAG, "openFile: Detected mime type \'" + mimeType + "\' for file \'" + fullPathAndFilename + "\'"); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(fileUri, mimeType); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException anfe) { + Log.w(TAG, "openFile: ActivityNotFoundException. Falling back to app chooser..."); + intent.setDataAndType(Uri.parse(fullPathAndFilename), "application/*"); + Intent chooserIntent = Intent.createChooser(intent, context.getString(R.string.open_file_with)); + try { + context.startActivity(chooserIntent); + } catch (Exception ex) { + Log.e(TAG, "openFile:", ex); + Toast.makeText(context, R.string.open_file_no_compatible_app, Toast.LENGTH_SHORT).show(); + } + } + } + + /** + * Open folder in compatible file manager app. + */ + public static void openFolder(Context context, String folderPath) { + PackageManager pm = context.getPackageManager(); + + // Try to find a compatible file manager app supporting the "resource/folder" Uri type. + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(new File(folderPath)), "resource/folder"); + intent.putExtra("org.openintents.extra.ABSOLUTE_PATH", folderPath); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK); + if (intent.resolveActivity(pm) != null) { + // Launch file manager. + context.startActivity(intent); + return; + } + Log.w(TAG, "openFolder: No compatible file manager app not found (stage #1)"); + + // Try to open the folder with "Root Explorer" if it is installed. + intent = pm.getLaunchIntentForPackage("com.speedsoftware.rootexplorer"); + if (intent != null) { + intent.setAction(Intent.ACTION_VIEW); + intent.setData(Uri.parse(folderPath)); + try { + context.startActivity(intent); + return; + } catch (android.content.ActivityNotFoundException anfe) { + Log.w(TAG, "openFolder: Failed to launch Root Explorer (stage #2)"); + } + } + Log.w(TAG, "openFolder: Root Explorer file manager app not found (stage #2)"); + + // No compatible file manager app found. + suggestFileManagerApp(context); + + /** + * Fallback: Let the user choose from all Uri handling apps. + * This allows the use of third-party file manager apps which + * provide non-standardized Uri handlers. + */ + /* + Log.v(TAG, "Fallback to application chooser to open folder."); + intent.setDataAndType(Uri.parse(folder.path), "application/*"); + Intent chooserIntent = Intent.createChooser(intent, mContext.getString(R.string.open_file_manager)); + if (chooserIntent != null) { + // Launch potential file manager app. + mContext.startActivity(chooserIntent); + return; + } + */ + } + + private static void suggestFileManagerApp(Context context) { + AlertDialog mSuggestFileManagerAppDialog = new AlertDialog.Builder(context) + .setTitle(R.string.suggest_file_manager_app_dialog_title) + .setMessage(R.string.suggest_file_manager_app_dialog_text) + .setPositiveButton(R.string.yes, (d, i) -> { + final String appPackageName = "com.simplemobiletools.filemanager"; + try { + context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName))); + } catch (android.content.ActivityNotFoundException anfe) { + context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName))); + } + }) + .setNegativeButton(R.string.no, (d, i) -> {}) + .show(); + } } diff --git a/app/src/main/java/com/nutomic/syncthingandroid/views/ChangeListAdapter.java b/app/src/main/java/com/nutomic/syncthingandroid/views/ChangeListAdapter.java index 239e5444..bc7d275a 100644 --- a/app/src/main/java/com/nutomic/syncthingandroid/views/ChangeListAdapter.java +++ b/app/src/main/java/com/nutomic/syncthingandroid/views/ChangeListAdapter.java @@ -140,7 +140,7 @@ public class ChangeListAdapter extends RecyclerView.Adapter { intent.setAction(SyncthingService.ACTION_OVERRIDE_CHANGES); mContext.startService(intent); }); - binding.openFolder.setOnClickListener(view -> { onOpenFolderClick(view, folder); } ); + binding.openFolder.setOnClickListener(view -> { FileUtils.openFolder(mContext, folder.path); } ); updateFolderStatusView(binding, folder); return binding.getRoot(); } @@ -174,68 +170,5 @@ public class FoldersAdapter extends ArrayAdapter { } } - private void onOpenFolderClick(View view, Folder folder) { - PackageManager pm = mContext.getPackageManager(); - // Try to find a compatible file manager app supporting the "resource/folder" Uri type. - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(new File(folder.path)), "resource/folder"); - intent.putExtra("org.openintents.extra.ABSOLUTE_PATH", folder.path); - intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK); - if (intent.resolveActivity(pm) != null) { - // Launch file manager. - mContext.startActivity(intent); - return; - } - Log.w(TAG, "No compatible file manager app not found (stage #1)"); - - // Try to open the folder with "Root Explorer" if it is installed. - intent = pm.getLaunchIntentForPackage("com.speedsoftware.rootexplorer"); - if (intent != null) { - intent.setAction(Intent.ACTION_VIEW); - intent.setData(Uri.parse(folder.path)); - try { - mContext.startActivity(intent); - return; - } catch (android.content.ActivityNotFoundException anfe) { - Log.w(TAG, "Failed to launch Root Explorer (stage #2)"); - } - } - Log.w(TAG, "Root Explorer file manager app not found (stage #2)"); - - // No compatible file manager app found. - suggestFileManagerApp(); - - /** - * Fallback: Let the user choose from all Uri handling apps. - * This allows the use of third-party file manager apps which - * provide non-standardized Uri handlers. - */ - /* - Log.v(TAG, "Fallback to application chooser to open folder."); - intent.setDataAndType(Uri.parse(folder.path), "application/*"); - Intent chooserIntent = Intent.createChooser(intent, mContext.getString(R.string.open_file_manager)); - if (chooserIntent != null) { - // Launch potential file manager app. - mContext.startActivity(chooserIntent); - return; - } - */ - } - - private void suggestFileManagerApp() { - AlertDialog mSuggestFileManagerAppDialog = new AlertDialog.Builder(mContext) - .setTitle(R.string.suggest_file_manager_app_dialog_title) - .setMessage(R.string.suggest_file_manager_app_dialog_text) - .setPositiveButton(R.string.yes, (d, i) -> { - final String appPackageName = "com.simplemobiletools.filemanager"; - try { - mContext.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName))); - } catch (android.content.ActivityNotFoundException anfe) { - mContext.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName))); - } - }) - .setNegativeButton(R.string.no, (d, i) -> {}) - .show(); - } } diff --git a/app/src/main/play/en-GB/whatsnew b/app/src/main/play/en-GB/whatsnew index e402b2ba..77b02f32 100644 --- a/app/src/main/play/en-GB/whatsnew +++ b/app/src/main/play/en-GB/whatsnew @@ -1,9 +1,8 @@ Enhancements -* Added "Recent changes" UI [NEW] +* Added "Recent changes" UI, click files to open [NEW] * Specify sync conditions differently for each folder, device [NEW] * Added offline 'tips & tricks' content [NEW] * UI explains why syncthing is running (or not) -* Support in-app editing of folder's ignore list items Fixes * Fixed the "battery eater" * Android 8 and 9 support diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml index 1b4feeb6..c87a7010 100644 --- a/app/src/main/res/values-ca-rES/strings.xml +++ b/app/src/main/res/values-ca-rES/strings.xml @@ -65,7 +65,6 @@ Ens podeu informar dels problemes que trobeu a través de Github. Obre l\'administrador d\'arxius - diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 04db8b41..075f597b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -109,6 +109,8 @@ Bitte melden Sie auftretende Probleme via GitHub. Änderungen überschreiben Dateimanager öffnen + Öffne Datei mit + Keine App zum Öffnen der Datei gefunden. Kein kompatibler Dateimanager gefunden diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 35c30e6b..2e463129 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -65,7 +65,6 @@ Ilmoitathan ystävällisesti kaikista havaitsemistasi ongelmista Githubin kautta Avaa tiedostonhallinta - diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4c3f353e..1aef0b3d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -67,7 +67,6 @@ S\'il vous plaît, soumettez les problèmes que vous rencontrez via Github. Ouvrir le gestionnaire de fichiers - diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index c25e9e08..dcf1f841 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -71,7 +71,6 @@ Néhány eszközön extra alkalmazás-leállító alkalmazást telepített fel a Fájlkezelő megnyitása - diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 1afbefd5..0f1091a9 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -67,7 +67,6 @@ Si prega di segnalare eventuali problemi che si incontrano via Github. Apri il file manager - diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 5fa687d7..d553a621 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -67,7 +67,6 @@ Als je problemen tegenkomt, meld ze dan via GitHub. Bestandsbeheerder openen - diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 41a1c0d4..c8f22d3b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -67,7 +67,6 @@ Por favor, nos avise sobre quaisquer problemas que você encontrar via Github. Abrir gerenciador de arquivos - diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 1bf3b6d9..e3332423 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -76,7 +76,6 @@ Vă rugăm să raportați orice problemă întâlniți, prin intermediul GitHub. Suprascrie schimbări Deschide managerul de fișiere - diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 82165bf2..ca0b7e84 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -72,7 +72,6 @@ Открыть файловый менеджер - diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index a50a2e17..9f7ef822 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -75,7 +75,6 @@ Vänligen rapportera eventuella problem du stöter på via Github. Åsidosätt ändringar Öppna filhanteraren - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 221be274..b986fe50 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -109,6 +109,8 @@ Please report any problems you encounter via Github. Override changes Open file manager + Open file with + No app found to open the file. No compatible file manager app found