Browse Source

Merge pull request #581 from DF1E/dev

download improvements
master
Anthony Restaino 8 years ago committed by GitHub
parent
commit
be1548e9a8
  1. 3
      app/src/main/java/acr/browser/lightning/app/AppComponent.java
  2. 8
      app/src/main/java/acr/browser/lightning/app/AppModule.java
  3. 17
      app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java
  4. 157
      app/src/main/java/acr/browser/lightning/download/DownloadHandler.java
  5. 17
      app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java
  6. 10
      app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java
  7. 4
      app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java
  8. 86
      app/src/main/java/acr/browser/lightning/utils/FileUtils.java
  9. 36
      app/src/main/java/acr/browser/lightning/utils/Utils.java
  10. 7
      app/src/main/java/acr/browser/lightning/view/LightningWebClient.java
  11. 3
      app/src/main/res/values-de/strings.xml
  12. 1
      app/src/main/res/values/strings.xml

3
app/src/main/java/acr/browser/lightning/app/AppComponent.java

@ -14,6 +14,7 @@ import acr.browser.lightning.constant.HistoryPage;
import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.constant.StartPage;
import acr.browser.lightning.database.history.HistoryDatabase; import acr.browser.lightning.database.history.HistoryDatabase;
import acr.browser.lightning.dialog.LightningDialogBuilder; import acr.browser.lightning.dialog.LightningDialogBuilder;
import acr.browser.lightning.download.DownloadHandler;
import acr.browser.lightning.download.LightningDownloadListener; import acr.browser.lightning.download.LightningDownloadListener;
import acr.browser.lightning.fragment.BookmarkSettingsFragment; import acr.browser.lightning.fragment.BookmarkSettingsFragment;
import acr.browser.lightning.fragment.BookmarksFragment; import acr.browser.lightning.fragment.BookmarksFragment;
@ -80,6 +81,8 @@ public interface AppComponent {
void inject(LightningChromeClient chromeClient); void inject(LightningChromeClient chromeClient);
void inject(DownloadHandler downloadHandler);
HistoryDatabase historyDatabase(); HistoryDatabase historyDatabase();
} }

8
app/src/main/java/acr/browser/lightning/app/AppModule.java

@ -14,6 +14,7 @@ import acr.browser.lightning.database.downloads.DownloadsDatabase;
import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.database.downloads.DownloadsModel;
import acr.browser.lightning.database.history.HistoryDatabase; import acr.browser.lightning.database.history.HistoryDatabase;
import acr.browser.lightning.database.history.HistoryModel; import acr.browser.lightning.database.history.HistoryModel;
import acr.browser.lightning.download.DownloadHandler;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@ -56,6 +57,13 @@ public class AppModule {
return new HistoryDatabase(mApp); return new HistoryDatabase(mApp);
} }
@NonNull
@Provides
@Singleton
public DownloadHandler provideDownloadHandler() {
return new DownloadHandler();
}
@NonNull @NonNull
@Provides @Provides
@Singleton @Singleton

17
app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java

@ -8,9 +8,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.view.View; import android.view.View;
import android.webkit.URLUtil;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.EditText; import android.widget.EditText;
@ -31,13 +29,12 @@ import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.controller.UIController; import acr.browser.lightning.controller.UIController;
import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.database.bookmark.BookmarkModel; import acr.browser.lightning.database.bookmark.BookmarkModel;
import acr.browser.lightning.database.downloads.DownloadItem;
import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.database.downloads.DownloadsModel;
import acr.browser.lightning.database.history.HistoryModel; import acr.browser.lightning.database.history.HistoryModel;
import acr.browser.lightning.download.DownloadHandler;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.IntentUtils;
import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.Preconditions;
import acr.browser.lightning.utils.Utils;
/** /**
* TODO Rename this class it doesn't build dialogs only for bookmarks * TODO Rename this class it doesn't build dialogs only for bookmarks
@ -57,6 +54,7 @@ public class LightningDialogBuilder {
@Inject DownloadsModel mDownloadsModel; @Inject DownloadsModel mDownloadsModel;
@Inject HistoryModel mHistoryModel; @Inject HistoryModel mHistoryModel;
@Inject PreferenceManager mPreferenceManager; @Inject PreferenceManager mPreferenceManager;
@Inject DownloadHandler mDownloadHandler;
@Inject @Inject
public LightningDialogBuilder() { public LightningDialogBuilder() {
@ -385,16 +383,7 @@ public class LightningDialogBuilder {
new BrowserDialog.Item(R.string.dialog_download_image) { new BrowserDialog.Item(R.string.dialog_download_image) {
@Override @Override
public void onClick() { public void onClick() {
Utils.downloadFile(activity, mPreferenceManager, url, userAgent, "attachment"); mDownloadHandler.onDownloadStart(activity, mPreferenceManager, url, userAgent, "attachment", null, "");
mDownloadsModel.addDownloadIfNotExists(new DownloadItem(url, URLUtil.guessFileName(url, null, null), ""))
.subscribe(new SingleOnSubscribe<Boolean>() {
@Override
public void onItem(@Nullable Boolean item) {
if (item != null && !item)
Log.i(TAG, "error saving download to database");
}
});
} }
}); });
} }

157
app/src/main/java/acr/browser/lightning/download/DownloadHandler.java

@ -23,16 +23,26 @@ import android.webkit.CookieManager;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.webkit.URLUtil; import android.webkit.URLUtil;
import com.anthonycr.bonsai.SingleOnSubscribe;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import javax.inject.Inject;
import acr.browser.lightning.BuildConfig; import acr.browser.lightning.BuildConfig;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.activity.BrowserActivity;
import acr.browser.lightning.activity.MainActivity; import acr.browser.lightning.activity.MainActivity;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.database.downloads.DownloadItem;
import acr.browser.lightning.database.downloads.DownloadsModel;
import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.dialog.BrowserDialog;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.FileUtils;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
import acr.browser.lightning.view.LightningView;
/** /**
* Handle download requests * Handle download requests
@ -43,9 +53,11 @@ public class DownloadHandler {
private static final String COOKIE_REQUEST_HEADER = "Cookie"; private static final String COOKIE_REQUEST_HEADER = "Cookie";
public static final String DEFAULT_DOWNLOAD_PATH = @Inject DownloadsModel downloadsModel;
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.getPath(); public DownloadHandler() {
BrowserApp.getAppComponent().inject(this);
}
/** /**
* Notify the host application a download should be done, or that the data * Notify the host application a download should be done, or that the data
@ -56,9 +68,10 @@ public class DownloadHandler {
* @param userAgent User agent of the downloading application. * @param userAgent User agent of the downloading application.
* @param contentDisposition Content-disposition http header, if present. * @param contentDisposition Content-disposition http header, if present.
* @param mimetype The mimetype of the content reported by the server * @param mimetype The mimetype of the content reported by the server
* @param contentSize The size of the content
*/ */
public static void onDownloadStart(@NonNull Activity context, @NonNull PreferenceManager manager, String url, String userAgent, public void onDownloadStart(@NonNull Activity context, @NonNull PreferenceManager manager, String url, String userAgent,
@Nullable String contentDisposition, String mimetype) { @Nullable String contentDisposition, String mimetype, String contentSize) {
Log.d(TAG, "DOWNLOAD: Trying to download from URL: " + url); Log.d(TAG, "DOWNLOAD: Trying to download from URL: " + url);
Log.d(TAG, "DOWNLOAD: Content disposition: " + contentDisposition); Log.d(TAG, "DOWNLOAD: Content disposition: " + contentDisposition);
@ -68,7 +81,7 @@ public class DownloadHandler {
// if we're dealing wih A/V content that's not explicitly marked // if we're dealing wih A/V content that's not explicitly marked
// for download, check if it's streamable. // for download, check if it's streamable.
if (contentDisposition == null if (contentDisposition == null
|| !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) { || !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) {
// query the package manager to see if there's a registered handler // query the package manager to see if there's a registered handler
// that matches. // that matches.
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
@ -80,12 +93,12 @@ public class DownloadHandler {
intent.setSelector(null); intent.setSelector(null);
} }
ResolveInfo info = context.getPackageManager().resolveActivity(intent, ResolveInfo info = context.getPackageManager().resolveActivity(intent,
PackageManager.MATCH_DEFAULT_ONLY); PackageManager.MATCH_DEFAULT_ONLY);
if (info != null) { if (info != null) {
// If we resolved to ourselves, we don't want to attempt to // If we resolved to ourselves, we don't want to attempt to
// load the url only to try and download it again. // load the url only to try and download it again.
if (BuildConfig.APPLICATION_ID.equals(info.activityInfo.packageName) if (BuildConfig.APPLICATION_ID.equals(info.activityInfo.packageName)
|| MainActivity.class.getName().equals(info.activityInfo.name)) { || MainActivity.class.getName().equals(info.activityInfo.name)) {
// someone (other than us) knows how to handle this mime // someone (other than us) knows how to handle this mime
// type with this scheme, don't download. // type with this scheme, don't download.
try { try {
@ -98,7 +111,7 @@ public class DownloadHandler {
} }
} }
} }
onDownloadStartNoStream(context, manager, url, userAgent, contentDisposition, mimetype); onDownloadStartNoStream(context, manager, url, userAgent, contentDisposition, mimetype, contentSize);
} }
// This is to work around the fact that java.net.URI throws Exceptions // This is to work around the fact that java.net.URI throws Exceptions
@ -141,11 +154,12 @@ public class DownloadHandler {
* @param userAgent User agent of the downloading application. * @param userAgent User agent of the downloading application.
* @param contentDisposition Content-disposition http header, if present. * @param contentDisposition Content-disposition http header, if present.
* @param mimetype The mimetype of the content reported by the server * @param mimetype The mimetype of the content reported by the server
* @param contentSize The size of the content
*/ */
/* package */ /* package */
private static void onDownloadStartNoStream(@NonNull final Activity context, @NonNull PreferenceManager preferences, private void onDownloadStartNoStream(@NonNull final Activity context, @NonNull PreferenceManager preferences,
String url, String userAgent, String url, String userAgent,
String contentDisposition, @Nullable String mimetype) { String contentDisposition, @Nullable String mimetype, String contentSize) {
final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype);
// Check to see if we have an SDCard // Check to see if we have an SDCard
@ -164,8 +178,8 @@ public class DownloadHandler {
} }
Dialog dialog = new AlertDialog.Builder(context).setTitle(title) Dialog dialog = new AlertDialog.Builder(context).setTitle(title)
.setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg) .setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg)
.setPositiveButton(R.string.action_ok, null).show(); .setPositiveButton(R.string.action_ok, null).show();
BrowserDialog.setDialogSize(context, dialog); BrowserDialog.setDialogSize(context, dialog);
return; return;
} }
@ -198,16 +212,8 @@ public class DownloadHandler {
// or, should it be set to one of several Environment.DIRECTORY* dirs // or, should it be set to one of several Environment.DIRECTORY* dirs
// depending on mimetype? // depending on mimetype?
String location = preferences.getDownloadDirectory(); String location = preferences.getDownloadDirectory();
Uri downloadFolder; location = FileUtils.addNecessarySlashes(location);
location = addNecessarySlashes(location); Uri downloadFolder = Uri.parse(location);
downloadFolder = Uri.parse(location);
File dir = new File(downloadFolder.getPath());
if (!dir.isDirectory() && !dir.mkdirs()) {
// Cannot make the directory
Utils.showSnackbar(context, R.string.problem_location_download);
return;
}
if (!isWriteAccessAvailable(downloadFolder)) { if (!isWriteAccessAvailable(downloadFolder)) {
Utils.showSnackbar(context, R.string.problem_location_download); Utils.showSnackbar(context, R.string.problem_location_download);
@ -240,7 +246,7 @@ public class DownloadHandler {
} else { } else {
Log.d(TAG, "Valid mimetype, attempting to download"); Log.d(TAG, "Valid mimetype, attempting to download");
final DownloadManager manager = (DownloadManager) context final DownloadManager manager = (DownloadManager) context
.getSystemService(Context.DOWNLOAD_SERVICE); .getSystemService(Context.DOWNLOAD_SERVICE);
try { try {
manager.enqueue(request); manager.enqueue(request);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -254,80 +260,30 @@ public class DownloadHandler {
} }
Utils.showSnackbar(context, context.getString(R.string.download_pending) + ' ' + filename); Utils.showSnackbar(context, context.getString(R.string.download_pending) + ' ' + filename);
} }
}
private static final String sFileName = "test";
private static final String sFileExtension = ".txt";
/** // save download in database
* Determine whether there is write access in the given directory. Returns false if a BrowserActivity browserActivity = (BrowserActivity) context;
* file cannot be created in the directory or if the directory does not exist. LightningView view = browserActivity.getTabModel().getCurrentTab();
*
* @param directory the directory to check for write access
* @return returns true if the directory can be written to or is in a directory that can
* be written to. false if there is no write access.
*/
public static boolean isWriteAccessAvailable(@Nullable String directory) {
if (directory == null || directory.isEmpty()) {
return false;
}
String dir = addNecessarySlashes(directory);
dir = getFirstRealParentDirectory(dir);
File file = new File(dir + sFileName + sFileExtension);
for (int n = 0; n < 100; n++) {
if (!file.exists()) {
try {
if (file.createNewFile()) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
return true;
} catch (IOException ignored) {
return false;
}
} else {
file = new File(dir + sFileName + '-' + n + sFileExtension);
}
}
return file.canWrite();
}
/** if (view != null && !view.isIncognito()) {
* Returns the first parent directory of a directory that exists. This is useful downloadsModel.addDownloadIfNotExists(new DownloadItem(url, filename, contentSize))
* for subdirectories that do not exist but their parents do. .subscribe(new SingleOnSubscribe<Boolean>() {
* @Override
* @param directory the directory to find the first existent parent public void onItem(@Nullable Boolean item) {
* @return the first existent parent if (item != null && !item)
*/ Log.i(TAG, "error saving download to database");
@Nullable }
private static String getFirstRealParentDirectory(@Nullable String directory) { });
while (true) {
if (directory == null || directory.isEmpty()) {
return "/";
}
directory = addNecessarySlashes(directory);
File file = new File(directory);
if (!file.isDirectory()) {
int indexSlash = directory.lastIndexOf('/');
if (indexSlash > 0) {
String parent = directory.substring(0, indexSlash);
int previousIndex = parent.lastIndexOf('/');
if (previousIndex > 0) {
directory = parent.substring(0, previousIndex);
} else {
return "/";
}
} else {
return "/";
}
} else {
return directory;
}
} }
} }
private static boolean isWriteAccessAvailable(@NonNull Uri fileUri) { private static boolean isWriteAccessAvailable(@NonNull Uri fileUri) {
File file = new File(fileUri.getPath()); File file = new File(fileUri.getPath());
if (!file.isDirectory() && !file.mkdirs()) {
return false;
}
try { try {
if (file.createNewFile()) { if (file.createNewFile()) {
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
@ -338,19 +294,4 @@ public class DownloadHandler {
return false; return false;
} }
} }
}
@NonNull
public static String addNecessarySlashes(@Nullable String originalPath) {
if (originalPath == null || originalPath.length() == 0) {
return "/";
}
if (originalPath.charAt(originalPath.length() - 1) != '/') {
originalPath = originalPath + '/';
}
if (originalPath.charAt(0) != '/') {
originalPath = '/' + originalPath;
}
return originalPath;
}
}

17
app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java

@ -7,7 +7,6 @@ import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.util.Log; import android.util.Log;
@ -16,12 +15,10 @@ import android.webkit.URLUtil;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.database.downloads.DownloadItem;
import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.database.downloads.DownloadsModel;
import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.dialog.BrowserDialog;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import com.anthonycr.bonsai.SingleOnSubscribe;
import com.anthonycr.grant.PermissionsManager; import com.anthonycr.grant.PermissionsManager;
import com.anthonycr.grant.PermissionsResultAction; import com.anthonycr.grant.PermissionsResultAction;
@ -34,7 +31,7 @@ public class LightningDownloadListener implements DownloadListener {
private final Activity mActivity; private final Activity mActivity;
@Inject PreferenceManager mPreferenceManager; @Inject PreferenceManager mPreferenceManager;
@Inject DownloadHandler mDownloadHandler;
@Inject DownloadsModel downloadsModel; @Inject DownloadsModel downloadsModel;
public LightningDownloadListener(Activity context) { public LightningDownloadListener(Activity context) {
@ -64,17 +61,7 @@ public class LightningDownloadListener implements DownloadListener {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which) { switch (which) {
case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_POSITIVE:
DownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, mDownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, "attachment", null, "");
contentDisposition, mimetype);
downloadsModel.addDownloadIfNotExists(new DownloadItem(url, fileName, downloadSize))
.subscribe(new SingleOnSubscribe<Boolean>() {
@Override
public void onItem(@Nullable Boolean item) {
if (item != null && !item)
Log.i(TAG, "error saving download to database");
}
});
break; break;
case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEGATIVE:
break; break;

10
app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java

@ -25,7 +25,7 @@ import acr.browser.lightning.BuildConfig;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.dialog.BrowserDialog;
import acr.browser.lightning.download.DownloadHandler; import acr.browser.lightning.utils.FileUtils;
import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ProxyUtils;
import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.ThemeUtils;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
@ -459,8 +459,8 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which) { switch (which) {
case 0: case 0:
mPreferenceManager.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH); mPreferenceManager.setDownloadDirectory(FileUtils.DEFAULT_DOWNLOAD_PATH);
downloadloc.setSummary(DownloadHandler.DEFAULT_DOWNLOAD_PATH); downloadloc.setSummary(FileUtils.DEFAULT_DOWNLOAD_PATH);
break; break;
case 1: case 1:
downPicker(); downPicker();
@ -539,7 +539,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String text = getDownload.getText().toString(); String text = getDownload.getText().toString();
text = DownloadHandler.addNecessarySlashes(text); text = FileUtils.addNecessarySlashes(text);
mPreferenceManager.setDownloadDirectory(text); mPreferenceManager.setDownloadDirectory(text);
downloadloc.setSummary(text); downloadloc.setSummary(text);
} }
@ -667,7 +667,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@Override @Override
public void afterTextChanged(@NonNull Editable s) { public void afterTextChanged(@NonNull Editable s) {
if (!DownloadHandler.isWriteAccessAvailable(s.toString())) { if (!FileUtils.isWriteAccessAvailable(s.toString())) {
this.getDownload.setTextColor(this.errorColor); this.getDownload.setTextColor(this.errorColor);
} else { } else {
this.getDownload.setTextColor(this.regularColor); this.getDownload.setTextColor(this.regularColor);

4
app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java

@ -9,7 +9,7 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.download.DownloadHandler; import acr.browser.lightning.utils.FileUtils;
@Singleton @Singleton
public class PreferenceManager { public class PreferenceManager {
@ -150,7 +150,7 @@ public class PreferenceManager {
@NonNull @NonNull
public String getDownloadDirectory() { public String getDownloadDirectory() {
return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, DownloadHandler.DEFAULT_DOWNLOAD_PATH); return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, FileUtils.DEFAULT_DOWNLOAD_PATH);
} }
public int getFlashSupport() { public int getFlashSupport() {

86
app/src/main/java/acr/browser/lightning/utils/FileUtils.java

@ -28,6 +28,9 @@ public class FileUtils {
private static final String TAG = "FileUtils"; private static final String TAG = "FileUtils";
public static final String DEFAULT_DOWNLOAD_PATH =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
/** /**
* Writes a bundle to persistent storage in the files directory * Writes a bundle to persistent storage in the files directory
* using the specified file name. This method is a blocking * using the specified file name. This method is a blocking
@ -160,4 +163,87 @@ public class FileUtils {
return megaBytes * 1024 * 1024; return megaBytes * 1024 * 1024;
} }
/**
* Determine whether there is write access in the given directory. Returns false if a
* file cannot be created in the directory or if the directory does not exist.
*
* @param directory the directory to check for write access
* @return returns true if the directory can be written to or is in a directory that can
* be written to. false if there is no write access.
*/
public static boolean isWriteAccessAvailable(@Nullable String directory) {
if (directory == null || directory.isEmpty()) {
return false;
}
final String sFileName = "test";
final String sFileExtension = ".txt";
String dir = addNecessarySlashes(directory);
dir = getFirstRealParentDirectory(dir);
File file = new File(dir + sFileName + sFileExtension);
for (int n = 0; n < 100; n++) {
if (!file.exists()) {
try {
if (file.createNewFile()) {
//noinspection ResultOfMethodCallIgnored
file.delete();
}
return true;
} catch (IOException ignored) {
return false;
}
} else {
file = new File(dir + sFileName + '-' + n + sFileExtension);
}
}
return file.canWrite();
}
/**
* Returns the first parent directory of a directory that exists. This is useful
* for subdirectories that do not exist but their parents do.
*
* @param directory the directory to find the first existent parent
* @return the first existent parent
*/
@Nullable
private static String getFirstRealParentDirectory(@Nullable String directory) {
while (true) {
if (directory == null || directory.isEmpty()) {
return "/";
}
directory = addNecessarySlashes(directory);
File file = new File(directory);
if (!file.isDirectory()) {
int indexSlash = directory.lastIndexOf('/');
if (indexSlash > 0) {
String parent = directory.substring(0, indexSlash);
int previousIndex = parent.lastIndexOf('/');
if (previousIndex > 0) {
directory = parent.substring(0, previousIndex);
} else {
return "/";
}
} else {
return "/";
}
} else {
return directory;
}
}
}
@NonNull
public static String addNecessarySlashes(@Nullable String originalPath) {
if (originalPath == null || originalPath.length() == 0) {
return "/";
}
if (originalPath.charAt(originalPath.length() - 1) != '/') {
originalPath = originalPath + '/';
}
if (originalPath.charAt(0) != '/') {
originalPath = '/' + originalPath;
}
return originalPath;
}
} }

36
app/src/main/java/acr/browser/lightning/utils/Utils.java

@ -3,7 +3,6 @@
*/ */
package acr.browser.lightning.utils; package acr.browser.lightning.utils;
import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
@ -33,12 +32,8 @@ import android.text.TextUtils;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.webkit.URLUtil;
import android.widget.Toast; import android.widget.Toast;
import com.anthonycr.grant.PermissionsManager;
import com.anthonycr.grant.PermissionsResultAction;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -52,8 +47,6 @@ import acr.browser.lightning.activity.MainActivity;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.dialog.BrowserDialog;
import acr.browser.lightning.download.DownloadHandler;
import acr.browser.lightning.preference.PreferenceManager;
public final class Utils { public final class Utils {
@ -63,35 +56,6 @@ public final class Utils {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
} }
/**
* Downloads a file from the specified URL. Handles permissions
* requests, and creates all the necessary dialogs that must be
* showed to the user.
*
* @param activity activity needed to created dialogs.
* @param url url to download from.
* @param userAgent the user agent of the browser.
* @param contentDisposition the content description of the file.
*/
public static void downloadFile(@NonNull final Activity activity, @NonNull final PreferenceManager manager, final String url,
final String userAgent, final String contentDisposition) {
PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, new PermissionsResultAction() {
@Override
public void onGranted() {
String fileName = URLUtil.guessFileName(url, null, null);
DownloadHandler.onDownloadStart(activity, manager, url, userAgent, contentDisposition, null);
Log.i(TAG, "Downloading: " + fileName);
}
@Override
public void onDenied(String permission) {
// TODO Show Message
}
});
}
/** /**
* Creates a new intent that can launch the email * Creates a new intent that can launch the email
* app with a subject, address, body, and cc. It * app with a subject, address, body, and cc. It

7
app/src/main/java/acr/browser/lightning/view/LightningWebClient.java

@ -366,8 +366,7 @@ public class LightningWebClient extends WebViewClient {
String newMimeType = MimeTypeMap.getSingleton() String newMimeType = MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(Utils.guessFileExtension(file.toString())); .getMimeTypeFromExtension(Utils.guessFileExtension(file.toString()));
Intent intent = new Intent(); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(mActivity, BuildConfig.APPLICATION_ID + ".fileprovider", file); Uri contentUri = FileProvider.getUriForFile(mActivity, BuildConfig.APPLICATION_ID + ".fileprovider", file);
intent.setDataAndType(contentUri, newMimeType); intent.setDataAndType(contentUri, newMimeType);
@ -377,8 +376,10 @@ public class LightningWebClient extends WebViewClient {
} catch (Exception e) { } catch (Exception e) {
System.out.println("LightningWebClient: cannot open downloaded file"); System.out.println("LightningWebClient: cannot open downloaded file");
} }
return true; } else {
Utils.showSnackbar(mActivity, R.string.message_open_download_fail);
} }
return true;
} }
return false; return false;
} }

3
app/src/main/res/values-de/strings.xml

@ -160,7 +160,7 @@
<string name="export_bookmarks">Exportiere Datensicherung</string> <string name="export_bookmarks">Exportiere Datensicherung</string>
<string name="import_backup">Importiere Lesezeichen aus Datensicherung</string> <string name="import_backup">Importiere Lesezeichen aus Datensicherung</string>
<string name="bookmark_export_path">Lesezeichen exportiert nach</string> <string name="bookmark_export_path">Lesezeichen exportiert nach</string>
<string name="bookmark_settings">Lesezeichen Einstellungen</string> <string name="bookmark_settings">Lesezeichen</string>
<string name="import_bookmark_error">Lesezeichen konnten nicht importiert werden</string> <string name="import_bookmark_error">Lesezeichen konnten nicht importiert werden</string>
<string name="title_chooser">Wähle Datei aus</string> <string name="title_chooser">Wähle Datei aus</string>
<string name="settings_general">Allgemein</string> <string name="settings_general">Allgemein</string>
@ -246,4 +246,5 @@
<string name="host">Host:</string> <string name="host">Host:</string>
<string name="hosts_source">Quelle für Werbeblocker</string> <string name="hosts_source">Quelle für Werbeblocker</string>
<string name="settings_display">Anzeige</string> <string name="settings_display">Anzeige</string>
<string name="message_open_download_fail">Diese Datei existiert nicht mehr</string>
</resources> </resources>

1
app/src/main/res/values/strings.xml

@ -229,6 +229,7 @@
<string name="action_delete_all_bookmarks">Delete all bookmarks</string> <string name="action_delete_all_bookmarks">Delete all bookmarks</string>
<string name="flash_not_supported">Flash Player is not supported by your system</string> <string name="flash_not_supported">Flash Player is not supported by your system</string>
<string name="upsell_plus_version">Upgrade to Lightning Plus to block ads</string> <string name="upsell_plus_version">Upgrade to Lightning Plus to block ads</string>
<string name="message_open_download_fail">This file doesn\'t exist anymore</string>
<string name="faq">FAQ</string> <string name="faq">FAQ</string>
<string name="faq_description">Frequently asked questions</string> <string name="faq_description">Frequently asked questions</string>

Loading…
Cancel
Save