Browse Source

Added support for downloading files to directories not lying in the directory returned by getExternalStorage

Useful for devices with both internal and external storage
master
Anthony Restaino 9 years ago
parent
commit
6f36410e87
  1. 128
      app/src/main/java/acr/browser/lightning/download/DownloadHandler.java
  2. 11
      app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java
  3. 12
      app/src/main/java/acr/browser/lightning/download/WebAddress.java
  4. 78
      app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java
  5. 10
      app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java
  6. 4
      app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java
  7. 2
      app/src/main/res/values/colors.xml
  8. 2
      app/src/main/res/values/strings.xml

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

@ -13,12 +13,17 @@ import android.content.pm.PackageManager; @@ -13,12 +13,17 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.URLUtil;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import acr.browser.lightning.R;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.Utils;
@ -30,6 +35,10 @@ public class DownloadHandler { @@ -30,6 +35,10 @@ public class DownloadHandler {
private static final String LOGTAG = "DLHandler";
public static final String DEFAULT_DOWNLOAD_PATH =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.getPath();
/**
* Notify the host application a download should be done, or that the data
@ -153,6 +162,7 @@ public class DownloadHandler { @@ -153,6 +162,7 @@ public class DownloadHandler {
// This only happens for very bad urls, we want to catch the
// exception here
Log.e(LOGTAG, "Exception while trying to parse url '" + url + '\'', e);
Utils.showSnackbar(activity, R.string.problem_download);
return;
}
@ -171,9 +181,29 @@ public class DownloadHandler { @@ -171,9 +181,29 @@ public class DownloadHandler {
// depending on mimetype?
String location = PreferenceManager.getInstance().getDownloadDirectory();
request.setDestinationInExternalPublicDir(location, filename);
Uri downloadLocation;
if (location != null) {
downloadLocation = Uri.parse(addNecessarySlashes(location));
} else {
downloadLocation = Uri.parse(addNecessarySlashes(DEFAULT_DOWNLOAD_PATH));
PreferenceManager.getInstance().setDownloadDirectory(downloadLocation.getPath());
}
File dir = new File(downloadLocation.getPath());
if (!dir.isDirectory() && !dir.mkdirs()) {
// Cannot make the directory
Utils.showSnackbar(activity, R.string.problem_location_download);
return;
}
if (!isWriteAccessAvailable(downloadLocation)) {
Utils.showSnackbar(activity, R.string.problem_location_download);
return;
}
request.setDestinationUri(downloadLocation);
// let this downloaded file be scanned by MediaScanner - so that it can
// show up in Gallery app, for example.
request.setVisibleInDownloadsUi(true);
request.allowScanningByMediaScanner();
request.setDescription(webAddress.getHost());
// XXX: Have to use the old url since the cookies were stored using the
@ -191,7 +221,7 @@ public class DownloadHandler { @@ -191,7 +221,7 @@ public class DownloadHandler {
} else {
final DownloadManager manager = (DownloadManager) activity
.getSystemService(Context.DOWNLOAD_SERVICE);
new Thread("Browser download") {
new Thread() {
@Override
public void run() {
try {
@ -203,8 +233,100 @@ public class DownloadHandler { @@ -203,8 +233,100 @@ public class DownloadHandler {
}
}
}.start();
Utils.showSnackbar(activity, R.string.download_pending);
Utils.showSnackbar(activity, activity.getString(R.string.download_pending) + ' ' + filename);
}
}
private static final String sFileName = "test";
private static final String sFileExtension = ".txt";
/**
* 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(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()) {
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
*/
private static String getFirstRealParentDirectory(String directory) {
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) {
return getFirstRealParentDirectory(parent.substring(0, previousIndex));
} else {
return "/";
}
} else {
return "/";
}
} else {
return directory;
}
}
private static boolean isWriteAccessAvailable(Uri fileUri) {
File file = new File(fileUri.getPath());
try {
if (file.createNewFile()) {
file.delete();
}
return true;
} catch (IOException ignored) {
return false;
}
}
public static String addNecessarySlashes(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;
}
}

11
app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java

@ -27,7 +27,7 @@ import acr.browser.lightning.utils.Utils; @@ -27,7 +27,7 @@ import acr.browser.lightning.utils.Utils;
*/
public class FetchUrlMimeType extends Thread {
private final Context mContext;
private final Activity mActivity;
private final DownloadManager.Request mRequest;
@ -39,12 +39,11 @@ public class FetchUrlMimeType extends Thread { @@ -39,12 +39,11 @@ public class FetchUrlMimeType extends Thread {
public FetchUrlMimeType(Activity activity, DownloadManager.Request request, String uri,
String cookies, String userAgent) {
mContext = activity.getApplicationContext();
mActivity = activity;
mRequest = request;
mUri = uri;
mCookies = cookies;
mUserAgent = userAgent;
Utils.showSnackbar(activity, R.string.download_pending);
}
@Override
@ -87,6 +86,7 @@ public class FetchUrlMimeType extends Thread { @@ -87,6 +86,7 @@ public class FetchUrlMimeType extends Thread {
connection.disconnect();
}
String filename = "";
if (mimeType != null) {
if (mimeType.equalsIgnoreCase("text/plain")
|| mimeType.equalsIgnoreCase("application/octet-stream")) {
@ -96,13 +96,14 @@ public class FetchUrlMimeType extends Thread { @@ -96,13 +96,14 @@ public class FetchUrlMimeType extends Thread {
mRequest.setMimeType(newMimeType);
}
}
String filename = URLUtil.guessFileName(mUri, contentDisposition, mimeType);
filename = URLUtil.guessFileName(mUri, contentDisposition, mimeType);
mRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
}
// Start the download
DownloadManager manager = (DownloadManager) mContext
DownloadManager manager = (DownloadManager) mActivity
.getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(mRequest);
Utils.showSnackbar(mActivity, mActivity.getString(R.string.download_pending) + ' ' + filename);
}
}

12
app/src/main/java/acr/browser/lightning/download/WebAddress.java

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
*/
package acr.browser.lightning.download;
import android.support.annotation.NonNull;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -11,13 +13,13 @@ import static android.util.Patterns.GOOD_IRI_CHAR; @@ -11,13 +13,13 @@ import static android.util.Patterns.GOOD_IRI_CHAR;
/**
* Web Address Parser
*
* <p/>
* This is called WebAddress, rather than URL or URI, because it attempts to
* parse the stuff that a user will actually type into a browser address widget.
*
* <p/>
* Unlike java.net.uri, this parser will not choke on URIs missing schemes. It
* will only throw a ParseException if the input is really hosed.
*
* <p/>
* If given an https scheme but no port, fills in port
*/
public class WebAddress {
@ -43,7 +45,7 @@ public class WebAddress { @@ -43,7 +45,7 @@ public class WebAddress {
/**
* Parses given URI-like string.
*/
public WebAddress(String address) {
public WebAddress(String address) throws IllegalArgumentException {
if (address == null) {
throw new IllegalArgumentException("address can't be null");
@ -134,7 +136,7 @@ public class WebAddress { @@ -134,7 +136,7 @@ public class WebAddress {
return mScheme;
}
public void setHost(String host) {
public void setHost(@NonNull String host) {
mHost = host;
}

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

@ -5,27 +5,28 @@ package acr.browser.lightning.fragment; @@ -5,27 +5,28 @@ package acr.browser.lightning.fragment;
import android.app.Activity;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import acr.browser.lightning.R;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.download.DownloadHandler;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.ProxyUtils;
import acr.browser.lightning.utils.ThemeUtils;
import acr.browser.lightning.utils.Utils;
public class GeneralSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
@ -113,7 +114,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @@ -113,7 +114,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe
setSearchEngineSummary(mPreferences.getSearchChoice());
downloadloc.setSummary(Constants.EXTERNAL_STORAGE + '/' + mDownloadLocation);
downloadloc.setSummary(mDownloadLocation);
if (mHomepage.contains("about:home")) {
home.setSummary(getResources().getString(R.string.action_homepage));
@ -143,7 +144,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @@ -143,7 +144,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe
boolean imagesBool = mPreferences.getBlockImagesEnabled();
boolean enableJSBool = mPreferences.getJavaScriptEnabled();
proxy.setEnabled(Constants.FULL_VERSION);
// proxy.setEnabled(Constants.FULL_VERSION);
cbAds.setEnabled(Constants.FULL_VERSION);
cbFlash.setEnabled(API < 19);
@ -386,22 +387,21 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @@ -386,22 +387,21 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe
mDownloadLocation = mPreferences.getDownloadDirectory();
int n;
if (mDownloadLocation.contains(Environment.DIRECTORY_DOWNLOADS)) {
n = 1;
n = 0;
} else {
n = 2;
n = 1;
}
picker.setSingleChoiceItems(R.array.download_folder, n - 1,
picker.setSingleChoiceItems(R.array.download_folder, n,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which + 1) {
case 1:
mPreferences.setDownloadDirectory(Environment.DIRECTORY_DOWNLOADS);
downloadloc.setSummary(Constants.EXTERNAL_STORAGE + '/'
+ Environment.DIRECTORY_DOWNLOADS);
switch (which) {
case 0:
mPreferences.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH);
downloadloc.setSummary(DownloadHandler.DEFAULT_DOWNLOAD_PATH);
break;
case 2:
case 1:
downPicker();
break;
}
@ -479,36 +479,42 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @@ -479,36 +479,42 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe
LinearLayout layout = new LinearLayout(mActivity);
downLocationPicker.setTitle(getResources().getString(R.string.title_download_location));
final EditText getDownload = new EditText(mActivity);
getDownload.setText(mPreferences.getDownloadDirectory());
getDownload.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
getDownload.setText(PreferenceManager.getInstance().getDownloadDirectory());
final int errorColor = ContextCompat.getColor(getActivity(), R.color.error_red);
final int regularColor = ThemeUtils.getTextColor(getActivity());
getDownload.setTextColor(regularColor);
getDownload.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
int padding = Utils.dpToPx(10);
@Override
public void afterTextChanged(Editable s) {
if (!DownloadHandler.isWriteAccessAvailable(s.toString())) {
getDownload.setTextColor(errorColor);
} else {
getDownload.setTextColor(regularColor);
}
}
});
getDownload.setText(mPreferences.getDownloadDirectory());
TextView v = new TextView(mActivity);
v.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
v.setTextColor(Color.DKGRAY);
v.setText(Constants.EXTERNAL_STORAGE + '/');
v.setPadding(padding, padding, 0, padding);
layout.addView(v);
layout.addView(getDownload);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
Drawable drawable;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
drawable = getResources().getDrawable(android.R.drawable.edit_text, getActivity().getTheme());
} else {
drawable = getResources().getDrawable(android.R.drawable.edit_text);
}
layout.setBackground(drawable);
} else {
layout.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.edit_text));
}
downLocationPicker.setView(layout);
downLocationPicker.setPositiveButton(getResources().getString(R.string.action_ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String text = getDownload.getText().toString();
text = DownloadHandler.addNecessarySlashes(text);
mPreferences.setDownloadDirectory(text);
downloadloc.setSummary(Constants.EXTERNAL_STORAGE + '/' + text);
downloadloc.setSummary(text);
}
});
downLocationPicker.show();

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

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
package acr.browser.lightning.preference;
import android.content.SharedPreferences;
import android.os.Environment;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.download.DownloadHandler;
public class PreferenceManager {
@ -14,7 +14,7 @@ public class PreferenceManager { @@ -14,7 +14,7 @@ public class PreferenceManager {
public static final String BLOCK_IMAGES = "blockimages";
public static final String CLEAR_CACHE_EXIT = "cache";
public static final String COOKIES = "cookies";
public static final String DOWNLOAD_DIRECTORY = "download";
public static final String DOWNLOAD_DIRECTORY = "downloadLocation";
public static final String FULL_SCREEN = "fullscreen";
public static final String HIDE_STATUS_BAR = "hidestatus";
public static final String HOMEPAGE = "home";
@ -117,7 +117,7 @@ public class PreferenceManager { @@ -117,7 +117,7 @@ public class PreferenceManager {
}
public String getDownloadDirectory() {
return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, Environment.DIRECTORY_DOWNLOADS);
return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, DownloadHandler.DEFAULT_DOWNLOAD_PATH);
}
public int getFlashSupport() {
@ -244,7 +244,7 @@ public class PreferenceManager { @@ -244,7 +244,7 @@ public class PreferenceManager {
return mPrefs.getString(Name.TEXT_ENCODING, Constants.DEFAULT_ENCODING);
}
public boolean getShowTabsInDrawer(boolean defaultValue){
public boolean getShowTabsInDrawer(boolean defaultValue) {
return mPrefs.getBoolean(Name.SHOW_TABS_IN_DRAWER, defaultValue);
}
@ -260,7 +260,7 @@ public class PreferenceManager { @@ -260,7 +260,7 @@ public class PreferenceManager {
mPrefs.edit().putString(name, value).apply();
}
public void setShowTabsInDrawer(boolean show){
public void setShowTabsInDrawer(boolean show) {
putBoolean(Name.SHOW_TABS_IN_DRAWER, show);
}

4
app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java

@ -120,4 +120,8 @@ public class ThemeUtils { @@ -120,4 +120,8 @@ public class ThemeUtils {
}
return new ColorDrawable(color);
}
public static int getTextColor(Context context){
return getColor(context, android.R.attr.editTextColor);
}
}

2
app/src/main/res/values/colors.xml

@ -33,4 +33,6 @@ @@ -33,4 +33,6 @@
<color name="icon_light_theme">#8A000000</color>
<color name="icon_dark_theme">#FFFFFFFF</color>
<color name="error_red">#F44336</color>
</resources>

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

@ -101,6 +101,8 @@ @@ -101,6 +101,8 @@
<string name="action_find">Find in Page</string>
<string name="download_pending">Starting download\u2026</string>
<string name="cannot_download">Can only download \"http\" or \"https\" URLs.</string>
<string name="problem_download">Invalid URL encountered, cannot download</string>
<string name="problem_location_download">Cannot download to the specified location</string>
<string name="download_no_sdcard_dlg_title" >No SD card</string>
<string name="download_no_sdcard_dlg_msg" >USB storage is required to download the file.</string>
<string name="download_sdcard_busy_dlg_title">USB storage unavailable</string>

Loading…
Cancel
Save