From 12a1769047043568b6fe6b8b73fdb97c81c203d0 Mon Sep 17 00:00:00 2001 From: DF1E Date: Sun, 28 May 2017 14:00:50 +0200 Subject: [PATCH 1/6] add DownloadsPage --- .../lightning/activity/BrowserActivity.java | 20 ++ .../lightning/activity/TabsManager.java | 12 + .../browser/lightning/app/AppComponent.java | 3 + .../acr/browser/lightning/app/AppModule.java | 9 + .../lightning/constant/DownloadsPage.java | 131 +++++++++ .../database/downloads/DownloadItem.java | 96 +++++++ .../database/downloads/DownloadsDatabase.java | 261 ++++++++++++++++++ .../database/downloads/DownloadsModel.java | 97 +++++++ .../download/LightningDownloadListener.java | 16 ++ .../acr/browser/lightning/utils/UrlUtils.java | 12 + app/src/main/res/menu-large/main.xml | 3 + app/src/main/res/menu-xlarge/main.xml | 3 + app/src/main/res/menu/main.xml | 3 + app/src/main/res/values/strings.xml | 1 + 14 files changed, 667 insertions(+) create mode 100644 app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java create mode 100644 app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java create mode 100644 app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java create mode 100644 app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java index 960eb65..8e1a416 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java @@ -97,6 +97,7 @@ import acr.browser.lightning.browser.BrowserView; import acr.browser.lightning.browser.TabsView; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.database.HistoryItem; @@ -800,6 +801,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements case R.id.action_history: openHistory(); return true; + case R.id.action_downloads: + openDownloads(); + return true; case R.id.action_add_bookmark: if (currentUrl != null && !UrlUtils.isSpecialUrl(currentUrl)) { addBookmark(currentView.getTitle(), currentUrl); @@ -1598,6 +1602,22 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements }); } + private void openDownloads() { + new DownloadsPage().getDownloadsPage() + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable String item) { + Preconditions.checkNonNull(item); + LightningView view = mTabsManager.getCurrentTab(); + if (view != null) { + view.loadUrl(item); + } + } + }); + } + private View getBookmarkDrawer() { return mSwapBookmarksAndTabs ? mDrawerLeft : mDrawerRight; } diff --git a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java index c8b4945..11ce58c 100644 --- a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java +++ b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java @@ -33,6 +33,7 @@ import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.dialog.BrowserDialog; @@ -171,6 +172,17 @@ public class TabsManager { tab.loadUrl(item); } }); + } else if (UrlUtils.isDownloadsUrl(url)) { + new DownloadsPage().getDownloadsPage() + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable String item) { + Preconditions.checkNonNull(item); + tab.loadUrl(item); + } + }); } else if (UrlUtils.isStartPageUrl(url)) { new StartPage().getHomepage() .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/acr/browser/lightning/app/AppComponent.java b/app/src/main/java/acr/browser/lightning/app/AppComponent.java index 6605da1..8ca9d5c 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppComponent.java +++ b/app/src/main/java/acr/browser/lightning/app/AppComponent.java @@ -9,6 +9,7 @@ import acr.browser.lightning.activity.ThemableBrowserActivity; import acr.browser.lightning.activity.ThemableSettingsActivity; import acr.browser.lightning.browser.BrowserPresenter; import acr.browser.lightning.constant.BookmarkPage; +import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.database.history.HistoryDatabase; @@ -67,6 +68,8 @@ public interface AppComponent { void inject(BookmarkPage bookmarkPage); + void inject(DownloadsPage downloadsPage); + void inject(BrowserPresenter presenter); void inject(TabsManager manager); diff --git a/app/src/main/java/acr/browser/lightning/app/AppModule.java b/app/src/main/java/acr/browser/lightning/app/AppModule.java index d4c68f8..400d783 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppModule.java +++ b/app/src/main/java/acr/browser/lightning/app/AppModule.java @@ -10,6 +10,8 @@ import javax.inject.Singleton; import acr.browser.lightning.database.bookmark.BookmarkDatabase; import acr.browser.lightning.database.bookmark.BookmarkModel; +import acr.browser.lightning.database.downloads.DownloadsDatabase; +import acr.browser.lightning.database.downloads.DownloadsModel; import dagger.Module; import dagger.Provides; @@ -38,6 +40,13 @@ public class AppModule { return new BookmarkDatabase(mApp); } + @NonNull + @Provides + @Singleton + public DownloadsModel provideDownloadsMode() { + return new DownloadsDatabase(mApp); + } + @NonNull @Provides @Singleton diff --git a/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java new file mode 100644 index 0000000..8b701d8 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java @@ -0,0 +1,131 @@ +/* + * Copyright 2014 A.C.R. Development + */ +package acr.browser.lightning.constant; + +import android.app.Application; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.anthonycr.bonsai.Single; +import com.anthonycr.bonsai.SingleAction; +import com.anthonycr.bonsai.SingleOnSubscribe; +import com.anthonycr.bonsai.SingleSubscriber; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.database.downloads.DownloadItem; +import acr.browser.lightning.database.downloads.DownloadsModel; +import acr.browser.lightning.utils.Preconditions; +import acr.browser.lightning.utils.Utils; + +public final class DownloadsPage { + + /** + * The download page standard suffix + */ + public static final String FILENAME = "downloads.html"; + + private static final String HEADING_1 = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + + private static final String HEADING_2 = "\n" + + "\n" + + "\n" + + "
"; + + private static final String PART1 = "
\n" + + "
\n" + + "
\n" + + "

"; + + private static final String PART3 = "

\n

"; + + private static final String PART4 = "

"; + + private static final String END = "
"; + + private File mFilesDir; + + @Inject Application mApp; + @Inject DownloadsModel mManager; + + @NonNull private final String mTitle; + + public DownloadsPage() { + BrowserApp.getAppComponent().inject(this); + mTitle = mApp.getString(R.string.action_downloads); + } + + @NonNull + public Single getDownloadsPage() { + return Single.create(new SingleAction() { + @Override + public void onSubscribe(@NonNull SingleSubscriber subscriber) { + mFilesDir = mApp.getFilesDir(); + + buildDownloadsPage(null); + + File downloadsWebPage = new File(mFilesDir, FILENAME); + + subscriber.onItem(Constants.FILE + downloadsWebPage); + subscriber.onComplete(); + } + }); + } + + private void buildDownloadsPage(@Nullable final String folder) { + mManager.getAllDownloads() + .subscribe(new SingleOnSubscribe>() { + @Override + public void onItem(@Nullable List list) { + Preconditions.checkNonNull(list); + + final File downloadsWebPage; + if (folder == null || folder.isEmpty()) { + downloadsWebPage = new File(mFilesDir, FILENAME); + } else { + downloadsWebPage = new File(mFilesDir, folder + '-' + FILENAME); + } + final StringBuilder downloadsBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2); + + for (int n = 0, size = list.size(); n < size; n++) { + final DownloadItem item = list.get(n); + downloadsBuilder.append(PART1); + downloadsBuilder.append(item.getUrl()); + downloadsBuilder.append(PART2); + downloadsBuilder.append(item.getTitle()); + downloadsBuilder.append(PART3); + downloadsBuilder.append(item.getContentSize()); + downloadsBuilder.append(PART4); + } + downloadsBuilder.append(END); + FileWriter bookWriter = null; + try { + //noinspection IOResourceOpenedButNotSafelyClosed + bookWriter = new FileWriter(downloadsWebPage, false); + bookWriter.write(downloadsBuilder.toString()); + } catch (IOException e) { + e.printStackTrace(); + } finally { + Utils.close(bookWriter); + } + } + }); + } + +} diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java new file mode 100644 index 0000000..884e052 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java @@ -0,0 +1,96 @@ +/* + * Copyright 2014 A.C.R. Development + */ +package acr.browser.lightning.database.downloads; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import acr.browser.lightning.utils.Preconditions; + +public class DownloadItem implements Comparable { + + // private variables + @NonNull + private String mUrl = ""; + + @NonNull + private String mTitle = ""; + + @NonNull + private String mContentSize = ""; + + public DownloadItem() {} + + public DownloadItem(@NonNull String url, @NonNull String title, @NonNull String size) { + Preconditions.checkNonNull(url); + Preconditions.checkNonNull(title); + this.mUrl = url; + this.mTitle = title; + this.mContentSize = size; + } + + @NonNull + public String getUrl() { + return this.mUrl; + } + + public void setUrl(@Nullable String url) { + this.mUrl = (url == null) ? "" : url; + } + + @NonNull + public String getTitle() { + return this.mTitle; + } + + public void setTitle(@Nullable String title) { + this.mTitle = (title == null) ? "" : title; + } + + @NonNull + public String getContentSize() { + return this.mContentSize; + } + + public void setContentSize(@Nullable String size) { + this.mContentSize = (size == null) ? "" : size; + } + + @NonNull + @Override + public String toString() { + return mTitle; + } + + @Override + public int compareTo(@NonNull DownloadItem another) { + int compare = this.mTitle.compareTo(another.mTitle); + if (compare == 0) { + return this.mUrl.compareTo(another.mUrl); + } + return compare; + } + + @Override + public boolean equals(@Nullable Object object) { + + if (this == object) return true; + if (object == null) return false; + if (!(object instanceof DownloadItem)) return false; + + DownloadItem that = (DownloadItem) object; + + return this.mTitle.equals(that.mTitle) && this.mUrl.equals(that.mUrl) + && this.mContentSize.equals(that.mContentSize); + } + + @Override + public int hashCode() { + + int result = mUrl.hashCode(); + result = 31 * result + mTitle.hashCode(); + + return result; + } +} diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java new file mode 100644 index 0000000..db2f83c --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java @@ -0,0 +1,261 @@ +package acr.browser.lightning.database.downloads; + +import android.app.Application; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; + +import com.anthonycr.bonsai.Completable; +import com.anthonycr.bonsai.CompletableAction; +import com.anthonycr.bonsai.CompletableSubscriber; +import com.anthonycr.bonsai.Single; +import com.anthonycr.bonsai.SingleAction; +import com.anthonycr.bonsai.SingleSubscriber; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import acr.browser.lightning.R; + +/** + * The disk backed download database. + * See {@link DownloadsModel} for method + * documentation. + */ +@Singleton +public class DownloadsDatabase extends SQLiteOpenHelper implements DownloadsModel { + + private static final String TAG = "DownloadsDatabase"; + + // Database Version + private static final int DATABASE_VERSION = 1; + + // Database Name + private static final String DATABASE_NAME = "downloadManager"; + + // HistoryItems table name + private static final String TABLE_DOWNLOADS = "download"; + + // HistoryItems Table Columns names + private static final String KEY_ID = "id"; + private static final String KEY_URL = "url"; + private static final String KEY_TITLE = "title"; + private static final String KEY_SIZE = "size"; + + @NonNull private final String DEFAULT_DOWNLOADS_TITLE; + + @Nullable private SQLiteDatabase mDatabase; + + @Inject + public DownloadsDatabase(@NonNull Application application) { + super(application, DATABASE_NAME, null, DATABASE_VERSION); + DEFAULT_DOWNLOADS_TITLE = application.getString(R.string.untitled); + } + + /** + * Lazily initializes the database + * field when called. + * + * @return a non null writable database. + */ + @WorkerThread + @NonNull + private SQLiteDatabase lazyDatabase() { + if (mDatabase == null || !mDatabase.isOpen()) { + mDatabase = getWritableDatabase(); + } + + return mDatabase; + } + + // Creating Tables + @Override + public void onCreate(@NonNull SQLiteDatabase db) { + String CREATE_BOOKMARK_TABLE = "CREATE TABLE " + + DatabaseUtils.sqlEscapeString(TABLE_DOWNLOADS) + '(' + + DatabaseUtils.sqlEscapeString(KEY_ID) + " INTEGER PRIMARY KEY," + + DatabaseUtils.sqlEscapeString(KEY_URL) + " TEXT," + + DatabaseUtils.sqlEscapeString(KEY_TITLE) + " TEXT," + + DatabaseUtils.sqlEscapeString(KEY_SIZE) + " TEXT" + ')'; + db.execSQL(CREATE_BOOKMARK_TABLE); + } + + // Upgrading database + @Override + public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) { + // Drop older table if it exists + db.execSQL("DROP TABLE IF EXISTS " + DatabaseUtils.sqlEscapeString(TABLE_DOWNLOADS)); + // Create tables again + onCreate(db); + } + + @NonNull + private static ContentValues bindBookmarkToContentValues(@NonNull DownloadItem downloadItem) { + ContentValues contentValues = new ContentValues(3); + contentValues.put(KEY_TITLE, downloadItem.getTitle()); + contentValues.put(KEY_URL, downloadItem.getUrl()); + contentValues.put(KEY_SIZE, downloadItem.getContentSize()); + + return contentValues; + } + + @NonNull + private static DownloadItem bindCursorToDownloadItem(@NonNull Cursor cursor) { + DownloadItem download = new DownloadItem(); + + download.setUrl(cursor.getString(cursor.getColumnIndex(KEY_URL))); + download.setTitle(cursor.getString(cursor.getColumnIndex(KEY_TITLE))); + download.setContentSize(cursor.getString(cursor.getColumnIndex(KEY_SIZE))); + + return download; + } + + @NonNull + private static List bindCursorToDownloadItemList(@NonNull Cursor cursor) { + List downloads = new ArrayList<>(); + + while (cursor.moveToNext()) { + downloads.add(bindCursorToDownloadItem(cursor)); + } + + cursor.close(); + + return downloads; + } + + @NonNull + @Override + public Single findDownloadForUrl(@NonNull final String url) { + return Single.create(new SingleAction() { + @Override + public void onSubscribe(@NonNull SingleSubscriber subscriber) { + Cursor cursor = lazyDatabase().query(TABLE_DOWNLOADS, null, KEY_URL + "=?", new String[]{url}, null, null, "1"); + + if (cursor.moveToFirst()) { + subscriber.onItem(bindCursorToDownloadItem(cursor)); + } else { + subscriber.onItem(null); + } + + cursor.close(); + subscriber.onComplete(); + } + }); + } + + @NonNull + @Override + public Single isDownload(@NonNull final String url) { + return Single.create(new SingleAction() { + @Override + public void onSubscribe(@NonNull SingleSubscriber subscriber) { + Cursor cursor = lazyDatabase().query(TABLE_DOWNLOADS, null, KEY_URL + "=?", new String[]{url}, null, null, null, "1"); + + subscriber.onItem(cursor.moveToFirst()); + + cursor.close(); + subscriber.onComplete(); + } + }); + } + + @NonNull + @Override + public Single addDownloadIfNotExists(@NonNull final DownloadItem item) { + return Single.create(new SingleAction() { + @Override + public void onSubscribe(@NonNull SingleSubscriber subscriber) { + Cursor cursor = lazyDatabase().query(TABLE_DOWNLOADS, null, KEY_URL + "=?", new String[]{item.getUrl()}, null, null, "1"); + + if (cursor.moveToFirst()) { + cursor.close(); + subscriber.onItem(false); + subscriber.onComplete(); + return; + } + + cursor.close(); + + long id = lazyDatabase().insert(TABLE_DOWNLOADS, null, bindBookmarkToContentValues(item)); + + subscriber.onItem(id != -1); + subscriber.onComplete(); + } + }); + } + + @NonNull + @Override + public Completable addDownloadsList(@NonNull final List bookmarkItems) { + return Completable.create(new CompletableAction() { + @Override + public void onSubscribe(@NonNull CompletableSubscriber subscriber) { + lazyDatabase().beginTransaction(); + + for (DownloadItem item : bookmarkItems) { + addDownloadIfNotExists(item).subscribe(); + } + + lazyDatabase().setTransactionSuccessful(); + lazyDatabase().endTransaction(); + + subscriber.onComplete(); + } + }); + } + + @NonNull + @Override + public Single deleteDownload(@NonNull final DownloadItem bookmark) { + return Single.create(new SingleAction() { + @Override + public void onSubscribe(@NonNull SingleSubscriber subscriber) { + int rows = lazyDatabase().delete(TABLE_DOWNLOADS, KEY_URL + "=?", new String[]{bookmark.getUrl()}); + + subscriber.onItem(rows > 0); + subscriber.onComplete(); + } + }); + } + + @NonNull + @Override + public Completable deleteAllDownloads() { + return Completable.create(new CompletableAction() { + @Override + public void onSubscribe(@NonNull CompletableSubscriber subscriber) { + lazyDatabase().delete(TABLE_DOWNLOADS, null, null); + + subscriber.onComplete(); + } + }); + } + + @NonNull + @Override + public Single> getAllDownloads() { + return Single.create(new SingleAction>() { + @Override + public void onSubscribe(@NonNull SingleSubscriber> subscriber) { + Cursor cursor = lazyDatabase().query(TABLE_DOWNLOADS, null, null, null, null, null, null); + + subscriber.onItem(bindCursorToDownloadItemList(cursor)); + subscriber.onComplete(); + } + }); + } + + @Override + public long count() { + return DatabaseUtils.queryNumEntries(lazyDatabase(), TABLE_DOWNLOADS); + } + +} diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java new file mode 100644 index 0000000..7ce5f3e --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java @@ -0,0 +1,97 @@ +package acr.browser.lightning.database.downloads; + +import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; + +import com.anthonycr.bonsai.Completable; +import com.anthonycr.bonsai.Single; + +import java.util.List; + +/** + * The interface that should be used to + * communicate with the download database. + *

+ * Created by anthonycr on 5/6/17. + */ +public interface DownloadsModel { + + /** + * Determines if a URL is associated with a download. + * + * @param url the URL to check. + * @return an observable that will emit true if + * the URL is a download, false otherwise. + */ + @NonNull + Single isDownload(@NonNull String url); + + /** + * Gets the download associated with the URL. + * + * @param url the URL to look for. + * @return an observable that will emit either + * the download associated with the URL or null. + */ + @NonNull + Single findDownloadForUrl(@NonNull String url); + + /** + * Adds a download if one does not already exist with + * the same URL. + * + * @param item the download to add. + * @return an observable that emits true if the download + * was added, false otherwise. + */ + @NonNull + Single addDownloadIfNotExists(@NonNull DownloadItem item); + + /** + * Adds a list of downloads to the database. + * + * @param downloadItems the downloads to add. + * @return an observable that emits a complete event + * when all the downloads have been added. + */ + @NonNull + Completable addDownloadsList(@NonNull List downloadItems); + + /** + * Deletes a download from the database. + * + * @param download the download to delete. + * @return an observable that emits true when + * the download is deleted, false otherwise. + */ + @NonNull + Single deleteDownload(@NonNull DownloadItem download); + + /** + * Deletes all downloads in the database. + * + * @return an observable that emits a completion + * event when all downloads have been deleted. + */ + @NonNull + Completable deleteAllDownloads(); + + /** + * Emits a list of all downloads + * + * @return an observable that emits a list + * of all downloads. + */ + @NonNull + Single> getAllDownloads(); + + /** + * A synchronous call to the model + * that returns the number of downloads. + * Should be called from a background thread. + * + * @return the number of downloads in the database. + */ + @WorkerThread + long count(); +} diff --git a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java index 31ed7cb..fba4cba 100644 --- a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java +++ b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java @@ -7,6 +7,7 @@ import android.Manifest; import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; +import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.text.format.Formatter; import android.util.Log; @@ -15,9 +16,12 @@ import android.webkit.URLUtil; import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.database.downloads.DownloadItem; +import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.preference.PreferenceManager; +import com.anthonycr.bonsai.SingleOnSubscribe; import com.anthonycr.grant.PermissionsManager; import com.anthonycr.grant.PermissionsResultAction; @@ -31,6 +35,8 @@ public class LightningDownloadListener implements DownloadListener { @Inject PreferenceManager mPreferenceManager; + @Inject DownloadsModel downloadsModel; + public LightningDownloadListener(Activity context) { BrowserApp.getAppComponent().inject(this); mActivity = context; @@ -75,6 +81,16 @@ public class LightningDownloadListener implements DownloadListener { dialogClickListener).show(); BrowserDialog.setDialogSize(mActivity, dialog); Log.i(TAG, "Downloading: " + fileName); + + downloadsModel.addDownloadIfNotExists(new DownloadItem(url, fileName, downloadSize)).subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable Boolean item) { + super.onItem(item); + + if (item != null && !item) + Log.i(TAG, "error saving download to database"); + } + }); } @Override diff --git a/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java b/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java index 63ba798..654e417 100644 --- a/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/UrlUtils.java @@ -25,6 +25,7 @@ import java.util.regex.Pattern; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; @@ -91,6 +92,7 @@ public class UrlUtils { public static boolean isSpecialUrl(@Nullable String url) { return url != null && url.startsWith(Constants.FILE) && (url.endsWith(BookmarkPage.FILENAME) || + url.endsWith(DownloadsPage.FILENAME) || url.endsWith(HistoryPage.FILENAME) || url.endsWith(StartPage.FILENAME)); } @@ -105,6 +107,16 @@ public class UrlUtils { return url != null && url.startsWith(Constants.FILE) && url.endsWith(BookmarkPage.FILENAME); } + /** + * Determines if the url is a url for the bookmark page. + * + * @param url the url to check, may be null. + * @return true if the url is a bookmark url, false otherwise. + */ + public static boolean isDownloadsUrl(@Nullable String url) { + return url != null && url.startsWith(Constants.FILE) && url.endsWith(DownloadsPage.FILENAME); + } + /** * Determines if the url is a url for the history page. * diff --git a/app/src/main/res/menu-large/main.xml b/app/src/main/res/menu-large/main.xml index 92b63e1..958acfa 100644 --- a/app/src/main/res/menu-large/main.xml +++ b/app/src/main/res/menu-large/main.xml @@ -47,6 +47,9 @@ + diff --git a/app/src/main/res/menu-xlarge/main.xml b/app/src/main/res/menu-xlarge/main.xml index 92b63e1..958acfa 100644 --- a/app/src/main/res/menu-xlarge/main.xml +++ b/app/src/main/res/menu-xlarge/main.xml @@ -47,6 +47,9 @@ + diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml index 9bfa01b..f620979 100644 --- a/app/src/main/res/menu/main.xml +++ b/app/src/main/res/menu/main.xml @@ -29,6 +29,9 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee47f61..7479d79 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ Share History Bookmarks + Downloads Add bookmark Copy link Forward From c3df77677196710d5f6a3a6643e4401ee47f0e37 Mon Sep 17 00:00:00 2001 From: DF1E Date: Sun, 28 May 2017 14:28:48 +0200 Subject: [PATCH 2/6] add delete downloads dialog --- .../lightning/activity/BrowserActivity.java | 12 ++++++ .../lightning/controller/UIController.java | 2 + .../database/downloads/DownloadsDatabase.java | 4 +- .../database/downloads/DownloadsModel.java | 4 +- .../dialog/LightningDialogBuilder.java | 41 +++++++++++++++++++ .../browser/lightning/view/LightningView.java | 26 ++++++++++++ app/src/main/res/values/strings.xml | 2 + 7 files changed, 87 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java index 8e1a416..16eed9e 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java @@ -2130,6 +2130,18 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } } + @Override + public void handleDownloadDeleted() { + final LightningView currentTab = mTabsManager.getCurrentTab(); + if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) + && currentTab.getUrl().endsWith(DownloadsPage.FILENAME)) { + currentTab.loadDownloadspage(); + } + if (currentTab != null) { + mBookmarksView.handleUpdatedUrl(currentTab.getUrl()); + } + } + @Override public void handleBookmarkDeleted(@NonNull HistoryItem item) { mBookmarksView.handleBookmarkDeleted(item); diff --git a/app/src/main/java/acr/browser/lightning/controller/UIController.java b/app/src/main/java/acr/browser/lightning/controller/UIController.java index a39a1bd..a9021a7 100644 --- a/app/src/main/java/acr/browser/lightning/controller/UIController.java +++ b/app/src/main/java/acr/browser/lightning/controller/UIController.java @@ -86,6 +86,8 @@ public interface UIController { void handleBookmarksChange(); + void handleDownloadDeleted(); + void handleBookmarkDeleted(@NonNull HistoryItem item); void handleNewTab(@NonNull LightningDialogBuilder.NewTab newTabType, @NonNull String url); diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java index db2f83c..a14faff 100644 --- a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsDatabase.java @@ -214,11 +214,11 @@ public class DownloadsDatabase extends SQLiteOpenHelper implements DownloadsMode @NonNull @Override - public Single deleteDownload(@NonNull final DownloadItem bookmark) { + public Single deleteDownload(@NonNull final String url) { return Single.create(new SingleAction() { @Override public void onSubscribe(@NonNull SingleSubscriber subscriber) { - int rows = lazyDatabase().delete(TABLE_DOWNLOADS, KEY_URL + "=?", new String[]{bookmark.getUrl()}); + int rows = lazyDatabase().delete(TABLE_DOWNLOADS, KEY_URL + "=?", new String[]{url}); subscriber.onItem(rows > 0); subscriber.onComplete(); diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java index 7ce5f3e..d8dc67e 100644 --- a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java @@ -60,12 +60,12 @@ public interface DownloadsModel { /** * Deletes a download from the database. * - * @param download the download to delete. + * @param url the download url to delete. * @return an observable that emits true when * the download is deleted, false otherwise. */ @NonNull - Single deleteDownload(@NonNull DownloadItem download); + Single deleteDownload(@NonNull String url); /** * Deletes all downloads in the database. diff --git a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index 490a9f8..46f23d8 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -8,6 +8,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.text.TextUtils; +import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; @@ -26,15 +27,20 @@ import acr.browser.lightning.activity.MainActivity; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.database.HistoryItem; 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.history.HistoryModel; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.Utils; +import static android.content.ContentValues.TAG; + /** * TODO Rename this class it doesn't build dialogs only for bookmarks *

@@ -49,6 +55,7 @@ public class LightningDialogBuilder { } @Inject BookmarkModel mBookmarkManager; + @Inject DownloadsModel mDownloadsModel; @Inject PreferenceManager mPreferenceManager; @Inject @@ -152,6 +159,40 @@ public class LightningDialogBuilder { }); } + /** + * Show the appropriated dialog for the long pressed link. + * + * @param activity used to show the dialog + * @param url the long pressed url + */ + public void showLongPressedDialogForDownloadUrl(@NonNull final Activity activity, + @NonNull final UIController uiController, + @NonNull final String url) { + + BrowserDialog.show(activity, R.string.action_bookmarks, + new BrowserDialog.Item(R.string.dialog_delete_download) { + @Override + public void onClick() { + mDownloadsModel.deleteDownload(url).subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable Boolean item) { + if (item != null && !item) + Log.i(TAG, "error deleting download from database"); + else + uiController.handleDownloadDeleted(); + } + }); + } + }, + new BrowserDialog.Item(R.string.dialog_delete_all_downloads) { + @Override + public void onClick() { + // TODO: fix refreshing downloads page + mDownloadsModel.deleteAllDownloads().subscribeOn(Schedulers.io()).subscribe(); + } + }); + } + private void showEditBookmarkDialog(@NonNull final Activity activity, @NonNull final UIController uiController, @NonNull final HistoryItem item) { diff --git a/app/src/main/java/acr/browser/lightning/view/LightningView.java b/app/src/main/java/acr/browser/lightning/view/LightningView.java index 1f137f0..8532dea 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningView.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningView.java @@ -47,6 +47,7 @@ import javax.inject.Inject; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.controller.UIController; @@ -242,6 +243,24 @@ public class LightningView { }); } + /** + * This method gets the bookmark page URL from the {@link BookmarkPage} + * class asynchronously and loads the URL in the WebView on the + * UI thread. It also caches the default folder icon locally. + */ + public void loadDownloadspage() { + new DownloadsPage().getDownloadsPage() + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable String item) { + Preconditions.checkNonNull(item); + loadUrl(item); + } + }); + } + /** * Initialize the preference driven settings of the WebView. This method * must be called whenever the preferences are changed within SharedPreferences. @@ -984,6 +1003,13 @@ public class LightningView { final String newUrl = result.getExtra(); mBookmarksDialogBuilder.showLongPressedDialogForBookmarkUrl(mActivity, mUIController, newUrl); } + } else if (currentUrl.endsWith(DownloadsPage.FILENAME)) { + if (url != null) { + mBookmarksDialogBuilder.showLongPressedDialogForDownloadUrl(mActivity, mUIController, url); + } else if (result != null && result.getExtra() != null) { + final String newUrl = result.getExtra(); + mBookmarksDialogBuilder.showLongPressedDialogForDownloadUrl(mActivity, mUIController, newUrl); + } } } else { if (url != null) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7479d79..71a594c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -246,4 +246,6 @@ Rename folder Remove folder Close browser + Delete download + Delete all downloads From fd3336b27047016552189759d91cb87868b52775 Mon Sep 17 00:00:00 2001 From: DF1E Date: Mon, 29 May 2017 09:14:22 +0200 Subject: [PATCH 3/6] slightly improved history and downloads html --- .../lightning/constant/DownloadsPage.java | 18 +++++++---------- .../lightning/constant/HistoryPage.java | 20 +++++++++++++------ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java index 8b701d8..9bc0a4e 100644 --- a/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java @@ -40,22 +40,18 @@ public final class DownloadsPage { "\n" + ""; - private static final String HEADING_2 = "\n" + - "\n" + - "\n" + - "

"; + private static final String HEADING_2 = "" + + "" + + "" + + "
"; private static final String PART1 = "
\n" + - "
\n" + - "
\n" + - "

"; + private static final String PART2 = "'>

"; - private static final String PART3 = "

\n

"; + private static final String PART3 = "

"; - private static final String PART4 = "

"; + private static final String PART4 = "

"; private static final String END = "
"; diff --git a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java index b175082..94a36c8 100644 --- a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java @@ -37,17 +37,25 @@ public class HistoryPage { public static final String FILENAME = "history.html"; - private static final String HEADING_1 = ""; + private static final String HEADING_1 = "<!DOCTYPE html><html xmlns=http://www.w3.org/1999/xhtml>\n" + + "<head>\n" + + "<meta content=en-us http-equiv=Content-Language />\n" + + "<meta content='text/html; charset=utf-8' http-equiv=Content-Type />\n" + + "<meta name=viewport content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'>\n" + + "<title>"; - private static final String HEADING_2 = "
"; + private static final String HEADING_2 = "" + + "" + + "" + + "
"; - private static final String PART1 = "

"; + private static final String PART2 = "'>

"; - private static final String PART3 = "

"; + private static final String PART3 = "

"; - private static final String PART4 = "

"; + private static final String PART4 = "

"; private static final String END = ""; From c07c925ada16f44a7529f237036bc31e287e9aa6 Mon Sep 17 00:00:00 2001 From: DF1E Date: Mon, 29 May 2017 09:30:29 +0200 Subject: [PATCH 4/6] fix refreshing page after deleting all bookmarks simple forgot to add handleDownloadDeleted() --- .../acr/browser/lightning/dialog/LightningDialogBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index 46f23d8..506debd 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -187,8 +187,8 @@ public class LightningDialogBuilder { new BrowserDialog.Item(R.string.dialog_delete_all_downloads) { @Override public void onClick() { - // TODO: fix refreshing downloads page mDownloadsModel.deleteAllDownloads().subscribeOn(Schedulers.io()).subscribe(); + uiController.handleDownloadDeleted(); } }); } From eddba7e480c1d02ba90c0d57638a0a0e8eb4c018 Mon Sep 17 00:00:00 2001 From: DF1E Date: Wed, 31 May 2017 12:20:02 +0200 Subject: [PATCH 5/6] fixes for downloads page - fixes open downloads - fix if download dialog canceled - save image downloads --- app/src/main/AndroidManifest.xml | 10 ++++++ .../lightning/constant/DownloadsPage.java | 29 +++++++++------ .../dialog/LightningDialogBuilder.java | 27 ++++++-------- .../lightning/download/DownloadHandler.java | 11 +----- .../lightning/download/FetchUrlMimeType.java | 2 +- .../download/LightningDownloadListener.java | 35 ++++++++++--------- .../acr/browser/lightning/utils/Utils.java | 9 +++++ .../lightning/view/LightningWebClient.java | 30 +++++++++++++--- app/src/main/res/xml/filepaths.xml | 5 +++ 9 files changed, 99 insertions(+), 59 deletions(-) create mode 100644 app/src/main/res/xml/filepaths.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 15d8177..f955ce5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -157,6 +157,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java index 9bc0a4e..5e5eb8a 100644 --- a/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/DownloadsPage.java @@ -23,6 +23,7 @@ import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.database.downloads.DownloadItem; import acr.browser.lightning.database.downloads.DownloadsModel; +import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.Utils; @@ -58,6 +59,7 @@ public final class DownloadsPage { private File mFilesDir; @Inject Application mApp; + @Inject PreferenceManager mPreferenceManager; @Inject DownloadsModel mManager; @NonNull private final String mTitle; @@ -74,7 +76,7 @@ public final class DownloadsPage { public void onSubscribe(@NonNull SingleSubscriber subscriber) { mFilesDir = mApp.getFilesDir(); - buildDownloadsPage(null); + buildDownloadsPage(); File downloadsWebPage = new File(mFilesDir, FILENAME); @@ -84,36 +86,41 @@ public final class DownloadsPage { }); } - private void buildDownloadsPage(@Nullable final String folder) { + private void buildDownloadsPage() { mManager.getAllDownloads() .subscribe(new SingleOnSubscribe>() { @Override public void onItem(@Nullable List list) { Preconditions.checkNonNull(list); + String directory = mPreferenceManager.getDownloadDirectory(); - final File downloadsWebPage; - if (folder == null || folder.isEmpty()) { - downloadsWebPage = new File(mFilesDir, FILENAME); - } else { - downloadsWebPage = new File(mFilesDir, folder + '-' + FILENAME); - } final StringBuilder downloadsBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2); for (int n = 0, size = list.size(); n < size; n++) { final DownloadItem item = list.get(n); downloadsBuilder.append(PART1); - downloadsBuilder.append(item.getUrl()); + downloadsBuilder.append("file://"); + downloadsBuilder.append(directory); + downloadsBuilder.append("/"); + downloadsBuilder.append(item.getTitle()); downloadsBuilder.append(PART2); downloadsBuilder.append(item.getTitle()); + + if (!item.getContentSize().isEmpty()) { + downloadsBuilder.append(" ["); + downloadsBuilder.append(item.getContentSize()); + downloadsBuilder.append("]"); + } + downloadsBuilder.append(PART3); - downloadsBuilder.append(item.getContentSize()); + downloadsBuilder.append(item.getUrl()); downloadsBuilder.append(PART4); } downloadsBuilder.append(END); FileWriter bookWriter = null; try { //noinspection IOResourceOpenedButNotSafelyClosed - bookWriter = new FileWriter(downloadsWebPage, false); + bookWriter = new FileWriter(new File(mFilesDir, FILENAME), false); bookWriter.write(downloadsBuilder.toString()); } catch (IOException e) { e.printStackTrace(); diff --git a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index 506debd..706e7f4 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -10,6 +10,7 @@ import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.Log; import android.view.View; +import android.webkit.URLUtil; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.EditText; @@ -27,7 +28,6 @@ import acr.browser.lightning.activity.MainActivity; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.constant.DownloadsPage; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkModel; @@ -169,21 +169,7 @@ public class LightningDialogBuilder { @NonNull final UIController uiController, @NonNull final String url) { - BrowserDialog.show(activity, R.string.action_bookmarks, - new BrowserDialog.Item(R.string.dialog_delete_download) { - @Override - public void onClick() { - mDownloadsModel.deleteDownload(url).subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable Boolean item) { - if (item != null && !item) - Log.i(TAG, "error deleting download from database"); - else - uiController.handleDownloadDeleted(); - } - }); - } - }, + BrowserDialog.show(activity, R.string.action_downloads, new BrowserDialog.Item(R.string.dialog_delete_all_downloads) { @Override public void onClick() { @@ -393,6 +379,15 @@ public class LightningDialogBuilder { @Override public void onClick() { Utils.downloadFile(activity, mPreferenceManager, url, userAgent, "attachment"); + + mDownloadsModel.addDownloadIfNotExists(new DownloadItem(url, URLUtil.guessFileName(url, null, null), "")) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable Boolean item) { + if (item != null && !item) + Log.i(TAG, "error saving download to database"); + } + }); } }); } diff --git a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java index 7fdab9b..6f02b92 100644 --- a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java +++ b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java @@ -47,15 +47,6 @@ public class DownloadHandler { Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) .getPath(); - @Nullable - static String guessFileExtension(@NonNull String filename) { - int lastIndex = filename.lastIndexOf('.') + 1; - if (lastIndex > 0 && filename.length() > lastIndex) { - return filename.substring(lastIndex, filename.length()); - } - return null; - } - /** * Notify the host application a download should be done, or that the data * should be streamed if a streaming viewer is available. @@ -222,7 +213,7 @@ public class DownloadHandler { Utils.showSnackbar(context, R.string.problem_location_download); return; } - String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(guessFileExtension(filename)); + String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(Utils.guessFileExtension(filename)); Log.d(TAG, "New mimetype: " + newMimeType); request.setMimeType(newMimeType); request.setDestinationUri(Uri.parse(Constants.FILE + location + filename)); diff --git a/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java b/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java index fcef5ce..96b7078 100644 --- a/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java +++ b/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java @@ -93,7 +93,7 @@ class FetchUrlMimeType extends Thread { if (mimeType.equalsIgnoreCase("text/plain") || mimeType.equalsIgnoreCase("application/octet-stream")) { String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( - DownloadHandler.guessFileExtension(mUri)); + Utils.guessFileExtension(mUri)); if (newMimeType != null) { mRequest.setMimeType(newMimeType); } diff --git a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java index fba4cba..a38bb93 100644 --- a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java +++ b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java @@ -50,7 +50,15 @@ public class LightningDownloadListener implements DownloadListener { new PermissionsResultAction() { @Override public void onGranted() { - String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype); + final String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype); + final String downloadSize; + + if (contentLength > 0) { + downloadSize = Formatter.formatFileSize(mActivity, contentLength); + } else { + downloadSize = mActivity.getString(R.string.unknown_size); + } + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -58,6 +66,15 @@ public class LightningDownloadListener implements DownloadListener { case DialogInterface.BUTTON_POSITIVE: DownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, contentDisposition, mimetype); + + downloadsModel.addDownloadIfNotExists(new DownloadItem(url, fileName, downloadSize)) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable Boolean item) { + if (item != null && !item) + Log.i(TAG, "error saving download to database"); + } + }); break; case DialogInterface.BUTTON_NEGATIVE: break; @@ -66,12 +83,6 @@ public class LightningDownloadListener implements DownloadListener { }; AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog - String downloadSize; - if (contentLength > 0) { - downloadSize = Formatter.formatFileSize(mActivity, contentLength); - } else { - downloadSize = mActivity.getString(R.string.unknown_size); - } String message = mActivity.getString(R.string.dialog_download, downloadSize); Dialog dialog = builder.setTitle(fileName) .setMessage(message) @@ -81,16 +92,6 @@ public class LightningDownloadListener implements DownloadListener { dialogClickListener).show(); BrowserDialog.setDialogSize(mActivity, dialog); Log.i(TAG, "Downloading: " + fileName); - - downloadsModel.addDownloadIfNotExists(new DownloadItem(url, fileName, downloadSize)).subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable Boolean item) { - super.onItem(item); - - if (item != null && !item) - Log.i(TAG, "error saving download to database"); - } - }); } @Override diff --git a/app/src/main/java/acr/browser/lightning/utils/Utils.java b/app/src/main/java/acr/browser/lightning/utils/Utils.java index 62a2669..0bb7682 100644 --- a/app/src/main/java/acr/browser/lightning/utils/Utils.java +++ b/app/src/main/java/acr/browser/lightning/utils/Utils.java @@ -456,4 +456,13 @@ public final class Utils { return inSampleSize; } + @Nullable + public static String guessFileExtension(@NonNull String filename) { + int lastIndex = filename.lastIndexOf('.') + 1; + if (lastIndex > 0 && filename.length() > lastIndex) { + return filename.substring(lastIndex, filename.length()); + } + return null; + } + } diff --git a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java index 0d1fd33..62cdbda 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java @@ -8,30 +8,31 @@ import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.net.MailTo; +import android.net.Uri; import android.net.http.SslError; import android.os.Build; import android.os.Message; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.view.LayoutInflaterCompat; +import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; -import android.text.InputType; -import android.text.method.PasswordTransformationMethod; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.webkit.HttpAuthHandler; +import android.webkit.MimeTypeMap; import android.webkit.SslErrorHandler; +import android.webkit.URLUtil; import android.webkit.ValueCallback; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.EditText; -import android.widget.LinearLayout; import android.widget.TextView; import java.io.ByteArrayInputStream; +import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; @@ -39,6 +40,7 @@ import java.util.Map; import javax.inject.Inject; +import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; @@ -358,6 +360,26 @@ public class LightningWebClient extends WebViewClient { } return true; } + } else if (url.startsWith("file://")) { + File file = new File(url.replace("file://", "")); + + if (file.exists()) { + String newMimeType = MimeTypeMap.getSingleton() + .getMimeTypeFromExtension(Utils.guessFileExtension(file.toString())); + + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + Uri contentUri = FileProvider.getUriForFile(mActivity, BuildConfig.APPLICATION_ID + ".fileprovider", file); + intent.setDataAndType(contentUri, newMimeType); + + try { + mActivity.startActivity(intent); + } catch (Exception e) { + System.out.println("LightningWebClient: cannot open downloaded file"); + } + return true; + } } return false; } diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml new file mode 100644 index 0000000..065dee8 --- /dev/null +++ b/app/src/main/res/xml/filepaths.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From 20ca7047f3b479531f8c2abad99e5f9c2c4ca6d1 Mon Sep 17 00:00:00 2001 From: DF1E Date: Sat, 3 Jun 2017 10:54:16 +0200 Subject: [PATCH 6/6] minor changes --- .../acr/browser/lightning/constant/Constants.java | 2 -- .../database/downloads/DownloadItem.java | 1 + .../database/downloads/DownloadsModel.java | 2 +- .../lightning/dialog/LightningDialogBuilder.java | 15 +++++++++++---- .../lightning/view/LightningWebClient.java | 5 ++--- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/acr/browser/lightning/constant/Constants.java b/app/src/main/java/acr/browser/lightning/constant/Constants.java index 2e76e78..731d4a5 100644 --- a/app/src/main/java/acr/browser/lightning/constant/Constants.java +++ b/app/src/main/java/acr/browser/lightning/constant/Constants.java @@ -8,8 +8,6 @@ import android.support.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import acr.browser.lightning.BuildConfig; - public final class Constants { private Constants() { diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java index 884e052..90025ba 100644 --- a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadItem.java @@ -25,6 +25,7 @@ public class DownloadItem implements Comparable { public DownloadItem(@NonNull String url, @NonNull String title, @NonNull String size) { Preconditions.checkNonNull(url); Preconditions.checkNonNull(title); + Preconditions.checkNonNull(size); this.mUrl = url; this.mTitle = title; this.mContentSize = size; diff --git a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java index d8dc67e..a5545ef 100644 --- a/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java +++ b/app/src/main/java/acr/browser/lightning/database/downloads/DownloadsModel.java @@ -12,7 +12,7 @@ import java.util.List; * The interface that should be used to * communicate with the download database. *

- * Created by anthonycr on 5/6/17. + * Created by df1e on 29/5/17. */ public interface DownloadsModel { diff --git a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index 706e7f4..9763383 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -16,6 +16,7 @@ import android.widget.AutoCompleteTextView; import android.widget.EditText; import com.anthonycr.bonsai.CompletableOnSubscribe; +import com.anthonycr.bonsai.CompletableSubscriber; import com.anthonycr.bonsai.Schedulers; import com.anthonycr.bonsai.SingleOnSubscribe; @@ -39,14 +40,13 @@ import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.Preconditions; import acr.browser.lightning.utils.Utils; -import static android.content.ContentValues.TAG; - /** * TODO Rename this class it doesn't build dialogs only for bookmarks *

* Created by Stefano Pacifici on 02/09/15, based on Anthony C. Restaino's code. */ public class LightningDialogBuilder { + public static final String TAG = "LightningDialogBuilder"; public enum NewTab { FOREGROUND, @@ -173,8 +173,15 @@ public class LightningDialogBuilder { new BrowserDialog.Item(R.string.dialog_delete_all_downloads) { @Override public void onClick() { - mDownloadsModel.deleteAllDownloads().subscribeOn(Schedulers.io()).subscribe(); - uiController.handleDownloadDeleted(); + mDownloadsModel.deleteAllDownloads() + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.main()) + .subscribe(new CompletableOnSubscribe() { + @Override + public void onComplete() { + uiController.handleDownloadDeleted(); + } + }); } }); } diff --git a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java index 62cdbda..8e3f4bb 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java @@ -22,7 +22,6 @@ import android.view.View; import android.webkit.HttpAuthHandler; import android.webkit.MimeTypeMap; import android.webkit.SslErrorHandler; -import android.webkit.URLUtil; import android.webkit.ValueCallback; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; @@ -360,8 +359,8 @@ public class LightningWebClient extends WebViewClient { } return true; } - } else if (url.startsWith("file://")) { - File file = new File(url.replace("file://", "")); + } else if (url.startsWith(Constants.FILE)) { + File file = new File(url.replace(Constants.FILE, "")); if (file.exists()) { String newMimeType = MimeTypeMap.getSingleton()