Added support for downloading files to directories not lying in the directory returned by getExternalStorage
Useful for devices with both internal and external storage
This commit is contained in:
parent
b3f991e598
commit
6f36410e87
@ -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 {
|
||||
|
||||
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 {
|
||||
// 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 {
|
||||
// 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 {
|
||||
} 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 {
|
||||
}
|
||||
}
|
||||
}.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* 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 {
|
||||
return mScheme;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
public void setHost(@NonNull String host) {
|
||||
mHost = host;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
LinearLayout layout = new LinearLayout(mActivity);
|
||||
downLocationPicker.setTitle(getResources().getString(R.string.title_download_location));
|
||||
final EditText getDownload = new EditText(mActivity);
|
||||
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) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (!DownloadHandler.isWriteAccessAvailable(s.toString())) {
|
||||
getDownload.setTextColor(errorColor);
|
||||
} else {
|
||||
getDownload.setTextColor(regularColor);
|
||||
}
|
||||
}
|
||||
});
|
||||
getDownload.setText(mPreferences.getDownloadDirectory());
|
||||
|
||||
int padding = Utils.dpToPx(10);
|
||||
|
||||
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();
|
||||
|
@ -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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -120,4 +120,8 @@ public class ThemeUtils {
|
||||
}
|
||||
return new ColorDrawable(color);
|
||||
}
|
||||
|
||||
public static int getTextColor(Context context){
|
||||
return getColor(context, android.R.attr.editTextColor);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
@ -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…
Reference in New Issue
Block a user