Fixed StrictMode problems, created a reactive implementation class, fixed potential NPEs, fixed memory leak
* Fixed places where IO was done on main thread * Created reactive class Observable so that work could easily be done on other threads * Fixed potential NPEs in LightningView * Fixed memory leak where ConnectivityManager was leaking activity
This commit is contained in:
parent
ba3edc00e8
commit
e2d46bdae2
@ -27,6 +27,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.StrictMode;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.IdRes;
|
||||
@ -89,6 +90,7 @@ import java.io.IOException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import acr.browser.lightning.BuildConfig;
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.browser.BrowserPresenter;
|
||||
@ -108,10 +110,13 @@ import acr.browser.lightning.dialog.LightningDialogBuilder;
|
||||
import acr.browser.lightning.fragment.BookmarksFragment;
|
||||
import acr.browser.lightning.fragment.TabsFragment;
|
||||
import acr.browser.lightning.object.SearchAdapter;
|
||||
import acr.browser.lightning.react.Schedulers;
|
||||
import acr.browser.lightning.react.Subscription;
|
||||
import acr.browser.lightning.receiver.NetworkReceiver;
|
||||
|
||||
import com.anthonycr.grant.PermissionsManager;
|
||||
|
||||
import acr.browser.lightning.react.Observable;
|
||||
import acr.browser.lightning.utils.ProxyUtils;
|
||||
import acr.browser.lightning.utils.ThemeUtils;
|
||||
import acr.browser.lightning.utils.UrlUtils;
|
||||
@ -217,11 +222,26 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
|
||||
public abstract void updateHistory(@Nullable final String title, @NonNull final String url);
|
||||
|
||||
abstract void updateCookiePreference();
|
||||
abstract Observable<Void> updateCookiePreference();
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||
.detectDiskReads()
|
||||
.detectDiskWrites()
|
||||
.detectNetwork()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||
.detectLeakedClosableObjects()
|
||||
.detectLeakedSqlLiteObjects()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
BrowserApp.getAppComponent().inject(this);
|
||||
setContentView(R.layout.activity_main);
|
||||
@ -339,11 +359,21 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
|
||||
}
|
||||
|
||||
mTabsManager.restoreTabsAndHandleIntent(this, getIntent(), isIncognito());
|
||||
// At this point we always have at least a tab in the tab manager
|
||||
showTab(0);
|
||||
mTabsManager.restoreTabsAndHandleIntent(this, getIntent(), isIncognito())
|
||||
.subscribe(new Subscription<Void>() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
// At this point we always have at least a tab in the tab manager
|
||||
showTab(0);
|
||||
|
||||
mProxyUtils.checkForProxy(this);
|
||||
mProxyUtils.checkForProxy(BrowserActivity.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Void item) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class SearchListenerClass implements OnKeyListener, OnEditorActionListener, OnFocusChangeListener, OnTouchListener {
|
||||
@ -624,7 +654,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
break;
|
||||
}
|
||||
|
||||
updateCookiePreference();
|
||||
updateCookiePreference().subscribeOn(Schedulers.worker()).subscribe();
|
||||
mProxyUtils.updateProxySettings(this);
|
||||
}
|
||||
|
||||
@ -1080,7 +1110,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
currentTab.onPause();
|
||||
}
|
||||
try {
|
||||
unregisterReceiver(mNetworkReceiver);
|
||||
BrowserApp.get(this).unregisterReceiver(mNetworkReceiver);
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -1142,7 +1172,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(NETWORK_BROADCAST_ACTION);
|
||||
registerReceiver(mNetworkReceiver, filter);
|
||||
BrowserApp.get(this).registerReceiver(mNetworkReceiver, filter);
|
||||
|
||||
mEventBus.register(mBusEventListener);
|
||||
}
|
||||
|
@ -9,17 +9,26 @@ import android.webkit.CookieManager;
|
||||
import android.webkit.CookieSyncManager;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.react.Action;
|
||||
import acr.browser.lightning.react.Observable;
|
||||
import acr.browser.lightning.react.Subscriber;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IncognitoActivity extends BrowserActivity {
|
||||
|
||||
@Override
|
||||
public void updateCookiePreference() {
|
||||
CookieManager cookieManager = CookieManager.getInstance();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
CookieSyncManager.createInstance(this);
|
||||
}
|
||||
cookieManager.setAcceptCookie(mPreferences.getIncognitoCookiesEnabled());
|
||||
public Observable<Void> updateCookiePreference() {
|
||||
return Observable.create(new Action<Void>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscriber<Void> subscriber) {
|
||||
CookieManager cookieManager = CookieManager.getInstance();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
CookieSyncManager.createInstance(IncognitoActivity.this);
|
||||
}
|
||||
cookieManager.setAcceptCookie(mPreferences.getIncognitoCookiesEnabled());
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,17 +9,26 @@ import android.webkit.CookieManager;
|
||||
import android.webkit.CookieSyncManager;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.react.Action;
|
||||
import acr.browser.lightning.react.Observable;
|
||||
import acr.browser.lightning.react.Subscriber;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MainActivity extends BrowserActivity {
|
||||
|
||||
@Override
|
||||
public void updateCookiePreference() {
|
||||
CookieManager cookieManager = CookieManager.getInstance();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
CookieSyncManager.createInstance(this);
|
||||
}
|
||||
cookieManager.setAcceptCookie(mPreferences.getCookiesEnabled());
|
||||
public Observable<Void> updateCookiePreference() {
|
||||
return Observable.create(new Action<Void>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscriber<Void> subscriber) {
|
||||
CookieManager cookieManager = CookieManager.getInstance();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
CookieSyncManager.createInstance(MainActivity.this);
|
||||
}
|
||||
cookieManager.setAcceptCookie(mPreferences.getCookiesEnabled());
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,7 +23,12 @@ import javax.inject.Singleton;
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
import acr.browser.lightning.react.Action;
|
||||
import acr.browser.lightning.react.Schedulers;
|
||||
import acr.browser.lightning.react.Subscriber;
|
||||
import acr.browser.lightning.react.Subscription;
|
||||
import acr.browser.lightning.utils.FileUtils;
|
||||
import acr.browser.lightning.react.Observable;
|
||||
import acr.browser.lightning.view.LightningView;
|
||||
|
||||
/**
|
||||
@ -56,47 +61,72 @@ public class TabsManager {
|
||||
* @param intent the intent that started the browser activity.
|
||||
* @param incognito whether or not we are in incognito mode.
|
||||
*/
|
||||
public synchronized void restoreTabsAndHandleIntent(@NonNull final Activity activity,
|
||||
@Nullable final Intent intent,
|
||||
final boolean incognito) {
|
||||
// If incognito, only create one tab, do not handle intent
|
||||
// in order to protect user privacy
|
||||
if (incognito && mTabList.isEmpty()) {
|
||||
newTab(activity, null, true);
|
||||
return;
|
||||
}
|
||||
public synchronized Observable<Void> restoreTabsAndHandleIntent(@NonNull final Activity activity,
|
||||
@Nullable final Intent intent,
|
||||
final boolean incognito) {
|
||||
return Observable.create(new Action<Void>() {
|
||||
@Override
|
||||
public void onSubscribe(final Subscriber<Void> subscriber) {
|
||||
|
||||
|
||||
// If incognito, only create one tab, do not handle intent
|
||||
// in order to protect user privacy
|
||||
if (incognito && mTabList.isEmpty()) {
|
||||
newTab(activity, null, true);
|
||||
subscriber.onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
String dataString = null;
|
||||
if (intent != null) {
|
||||
dataString = intent.getDataString();
|
||||
}
|
||||
final String url = dataString;
|
||||
mTabList.clear();
|
||||
mCurrentTab = null;
|
||||
if (mPreferenceManager.getRestoreLostTabsEnabled()) {
|
||||
restoreState()
|
||||
.subscribeOn(Schedulers.worker())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new Subscription<Bundle>() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
if (url != null) {
|
||||
if (url.startsWith(Constants.FILE)) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setCancelable(true)
|
||||
.setTitle(R.string.title_warning)
|
||||
.setMessage(R.string.message_blocked_local)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.action_open, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
newTab(activity, url, false);
|
||||
}
|
||||
}).show();
|
||||
} else {
|
||||
newTab(activity, url, false);
|
||||
}
|
||||
}
|
||||
if (mTabList.size() == 0) {
|
||||
newTab(activity, null, false);
|
||||
}
|
||||
subscriber.onComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(Bundle item) {
|
||||
LightningView tab = newTab(activity, "", false);
|
||||
if (tab.getWebView() != null) {
|
||||
tab.getWebView().restoreState(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String url = null;
|
||||
if (intent != null) {
|
||||
url = intent.getDataString();
|
||||
}
|
||||
mTabList.clear();
|
||||
mCurrentTab = null;
|
||||
if (mPreferenceManager.getRestoreLostTabsEnabled()) {
|
||||
restoreState(activity);
|
||||
}
|
||||
if (url != null) {
|
||||
if (url.startsWith(Constants.FILE)) {
|
||||
final String urlToLoad = url;
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setCancelable(true)
|
||||
.setTitle(R.string.title_warning)
|
||||
.setMessage(R.string.message_blocked_local)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.action_open, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
newTab(activity, urlToLoad, false);
|
||||
}
|
||||
})
|
||||
.show();
|
||||
} else {
|
||||
newTab(activity, url, false);
|
||||
}
|
||||
}
|
||||
if (mTabList.size() == 0) {
|
||||
newTab(activity, null, false);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -285,22 +315,23 @@ public class TabsManager {
|
||||
* It will create new tabs for each tab saved
|
||||
* and will delete the saved instance file when
|
||||
* restoration is complete.
|
||||
*
|
||||
* @param activity the Activity needed to create tabs.
|
||||
*/
|
||||
private void restoreState(@NonNull Activity activity) {
|
||||
Bundle savedState = FileUtils.readBundleFromStorage(mApp, BUNDLE_STORAGE);
|
||||
if (savedState != null) {
|
||||
Log.d(Constants.TAG, "Restoring previous WebView state now");
|
||||
for (String key : savedState.keySet()) {
|
||||
if (key.startsWith(BUNDLE_KEY)) {
|
||||
LightningView tab = newTab(activity, "", false);
|
||||
if (tab.getWebView() != null) {
|
||||
tab.getWebView().restoreState(savedState.getBundle(key));
|
||||
private Observable<Bundle> restoreState() {
|
||||
return Observable.create(new Action<Bundle>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscriber<Bundle> subscriber) {
|
||||
Bundle savedState = FileUtils.readBundleFromStorage(mApp, BUNDLE_STORAGE);
|
||||
if (savedState != null) {
|
||||
Log.d(Constants.TAG, "Restoring previous WebView state now");
|
||||
for (String key : savedState.keySet()) {
|
||||
if (key.startsWith(BUNDLE_KEY)) {
|
||||
subscriber.onNext(savedState.getBundle(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
subscriber.onComplete();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,8 +25,8 @@ import acr.browser.lightning.utils.Utils;
|
||||
public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
||||
|
||||
private static final String TAG = ImageDownloadTask.class.getSimpleName();
|
||||
private final File mCacheDir;
|
||||
@NonNull private final WeakReference<ImageView> mFaviconImage;
|
||||
@NonNull private final WeakReference<Context> mContextReference;
|
||||
@NonNull private final HistoryItem mWeb;
|
||||
private final String mUrl;
|
||||
@NonNull private final Bitmap mDefaultBitmap;
|
||||
@ -42,7 +42,7 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
||||
this.mWeb = web;
|
||||
this.mUrl = web.getUrl();
|
||||
this.mDefaultBitmap = defaultBitmap;
|
||||
this.mCacheDir = BrowserApp.get(context).getCacheDir();
|
||||
this.mContextReference = new WeakReference<>(context.getApplicationContext());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -53,12 +53,17 @@ public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
|
||||
if (mUrl == null) {
|
||||
return mDefaultBitmap;
|
||||
}
|
||||
Context context = mContextReference.get();
|
||||
if (context == null) {
|
||||
return mDefaultBitmap;
|
||||
}
|
||||
File cache = context.getCacheDir();
|
||||
final Uri uri = Uri.parse(mUrl);
|
||||
if (uri.getHost() == null || uri.getScheme() == null) {
|
||||
return mDefaultBitmap;
|
||||
}
|
||||
final String hash = String.valueOf(uri.getHost().hashCode());
|
||||
final File image = new File(mCacheDir, hash + ".png");
|
||||
final File image = new File(cache, hash + ".png");
|
||||
final String urlDisplay = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico";
|
||||
// checks to see if the image exists
|
||||
if (!image.exists()) {
|
||||
|
@ -60,17 +60,17 @@ public final class BookmarkPage extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
private static final String END = "</div></body></html>";
|
||||
|
||||
private final File mFilesDir;
|
||||
private final File mCacheDir;
|
||||
private File mFilesDir;
|
||||
private File mCacheDir;
|
||||
|
||||
private Application mApp;
|
||||
private final BookmarkManager mManager;
|
||||
@NonNull private final WeakReference<LightningView> mTabReference;
|
||||
private final Bitmap mFolderIcon;
|
||||
@NonNull private final String mTitle;
|
||||
|
||||
public BookmarkPage(LightningView tab, @NonNull Application app, BookmarkManager manager, Bitmap folderIcon) {
|
||||
mFilesDir = app.getFilesDir();
|
||||
mCacheDir = app.getCacheDir();
|
||||
mApp = app;
|
||||
mTitle = app.getString(R.string.action_bookmarks);
|
||||
mManager = manager;
|
||||
mTabReference = new WeakReference<>(tab);
|
||||
@ -79,6 +79,8 @@ public final class BookmarkPage extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
mCacheDir = mApp.getCacheDir();
|
||||
mFilesDir = mApp.getFilesDir();
|
||||
cacheDefaultFolderIcon();
|
||||
buildBookmarkPage(null, mManager);
|
||||
return null;
|
||||
|
@ -41,7 +41,7 @@ public class HistoryPage extends AsyncTask<Void, Void, Void> {
|
||||
private static final String END = "</div></body></html>";
|
||||
|
||||
@NonNull private final WeakReference<LightningView> mTabReference;
|
||||
private final File mFilesDir;
|
||||
@NonNull private final Application mApp;
|
||||
@NonNull private final String mTitle;
|
||||
private final HistoryDatabase mHistoryDatabase;
|
||||
|
||||
@ -49,7 +49,7 @@ public class HistoryPage extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
public HistoryPage(LightningView tab, @NonNull Application app, HistoryDatabase database) {
|
||||
mTabReference = new WeakReference<>(tab);
|
||||
mFilesDir = app.getFilesDir();
|
||||
mApp = app;
|
||||
mTitle = app.getString(R.string.action_history);
|
||||
mHistoryDatabase = database;
|
||||
}
|
||||
@ -88,7 +88,7 @@ public class HistoryPage extends AsyncTask<Void, Void, Void> {
|
||||
}
|
||||
|
||||
historyBuilder.append(END);
|
||||
File historyWebPage = new File(mFilesDir, FILENAME);
|
||||
File historyWebPage = new File(mApp.getFilesDir(), FILENAME);
|
||||
FileWriter historyWriter = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
|
@ -55,7 +55,7 @@ public class StartPage extends AsyncTask<Void, Void, Void> {
|
||||
private static final String END = "\" + document.getElementById(\"search_input\").value;document.getElementById(\"search_input\").value = \"\";}return false;}</script></body></html>";
|
||||
|
||||
@NonNull private final String mTitle;
|
||||
private final File mFilesDir;
|
||||
@NonNull private final Application mApp;
|
||||
@NonNull private final WeakReference<LightningView> mTabReference;
|
||||
|
||||
@Inject PreferenceManager mPreferenceManager;
|
||||
@ -65,7 +65,7 @@ public class StartPage extends AsyncTask<Void, Void, Void> {
|
||||
public StartPage(LightningView tab, @NonNull Application app) {
|
||||
BrowserApp.getAppComponent().inject(this);
|
||||
mTitle = app.getString(R.string.home);
|
||||
mFilesDir = app.getFilesDir();
|
||||
mApp = app;
|
||||
mTabReference = new WeakReference<>(tab);
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ public class StartPage extends AsyncTask<Void, Void, Void> {
|
||||
homepageBuilder.append(searchUrl);
|
||||
homepageBuilder.append(END);
|
||||
|
||||
File homepage = new File(mFilesDir, FILENAME);
|
||||
File homepage = new File(mApp.getFilesDir(), FILENAME);
|
||||
FileWriter hWriter = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
|
@ -55,12 +55,11 @@ public class BookmarkManager {
|
||||
private Map<String, HistoryItem> mBookmarksMap;
|
||||
@NonNull private String mCurrentFolder = "";
|
||||
@NonNull private final ExecutorService mExecutor;
|
||||
private final File mFilesDir;
|
||||
private File mFilesDir;
|
||||
|
||||
@Inject
|
||||
public BookmarkManager(@NonNull Context context) {
|
||||
mExecutor = Executors.newSingleThreadExecutor();
|
||||
mFilesDir = context.getFilesDir();
|
||||
DEFAULT_BOOKMARK_TITLE = context.getString(R.string.untitled);
|
||||
mExecutor.execute(new BookmarkInitializer(context));
|
||||
}
|
||||
@ -90,6 +89,7 @@ public class BookmarkManager {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (BookmarkManager.this) {
|
||||
mFilesDir = mContext.getFilesDir();
|
||||
final Map<String, HistoryItem> bookmarks = new HashMap<>();
|
||||
final File bookmarksFile = new File(mFilesDir, FILE_BOOKMARKS);
|
||||
|
||||
|
@ -18,6 +18,7 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
|
||||
@Singleton
|
||||
public class HistoryDatabase extends SQLiteOpenHelper {
|
||||
@ -43,7 +44,18 @@ public class HistoryDatabase extends SQLiteOpenHelper {
|
||||
@Inject
|
||||
public HistoryDatabase(@NonNull Context context) {
|
||||
super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
|
||||
mDatabase = this.getWritableDatabase();
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
BrowserApp.getTaskThread().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (HistoryDatabase.this) {
|
||||
mDatabase = HistoryDatabase.this.getWritableDatabase();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Creating Tables
|
||||
|
@ -430,6 +430,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
|
||||
|
||||
private static NetworkInfo getActiveNetworkInfo(@NonNull Context context) {
|
||||
ConnectivityManager connectivity = (ConnectivityManager) context
|
||||
.getApplicationContext()
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivity == null) {
|
||||
return null;
|
||||
|
@ -0,0 +1,5 @@
|
||||
package acr.browser.lightning.react;
|
||||
|
||||
public interface Action<T> {
|
||||
void onSubscribe(Subscriber<T> subscriber);
|
||||
}
|
130
app/src/main/java/acr/browser/lightning/react/Observable.java
Normal file
130
app/src/main/java/acr/browser/lightning/react/Observable.java
Normal file
@ -0,0 +1,130 @@
|
||||
package acr.browser.lightning.react;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
|
||||
/**
|
||||
* An RxJava implementation. This class allows work
|
||||
* to be done on a certain thread and then allows
|
||||
* items to be emitted on a different thread. It is
|
||||
* a replacement for {@link android.os.AsyncTask}.
|
||||
*
|
||||
* @param <T> the type that the Observable will emit.
|
||||
*/
|
||||
public class Observable<T> {
|
||||
|
||||
private Action<T> mAction;
|
||||
@Nullable private Executor mSubscriber;
|
||||
@Nullable private Executor mObserver;
|
||||
private final Executor mDefault;
|
||||
|
||||
public Observable(Action<T> action) {
|
||||
mAction = action;
|
||||
Looper looper = Looper.myLooper();
|
||||
Preconditions.checkNonNull(looper);
|
||||
mDefault = new ThreadExecutor(looper);
|
||||
}
|
||||
|
||||
public static <T> Observable<T> create(@NonNull Action<T> action) {
|
||||
Preconditions.checkNonNull(action);
|
||||
return new Observable<>(action);
|
||||
}
|
||||
|
||||
public Observable<T> subscribeOn(@NonNull Executor subscribeExecutor) {
|
||||
mSubscriber = subscribeExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Observable<T> observeOn(@NonNull Executor observerExecutor) {
|
||||
mObserver = observerExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void subscribe() {
|
||||
executeOnSubscriberThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mAction.onSubscribe(new Subscriber<T>() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(T item) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void subscribe(@NonNull final Subscription<T> subscription) {
|
||||
Preconditions.checkNonNull(subscription);
|
||||
executeOnSubscriberThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mAction.onSubscribe(new Subscriber<T>() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
executeOnObserverThread(new OnCompleteRunnable(subscription));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(final T item) {
|
||||
executeOnObserverThread(new OnNextRunnable(subscription, item));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void executeOnObserverThread(@NonNull Runnable runnable) {
|
||||
if (mObserver != null) {
|
||||
mObserver.execute(runnable);
|
||||
} else {
|
||||
mDefault.execute(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeOnSubscriberThread(@NonNull Runnable runnable) {
|
||||
if (mSubscriber != null) {
|
||||
mSubscriber.execute(runnable);
|
||||
} else {
|
||||
mDefault.execute(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private static class OnCompleteRunnable implements Runnable {
|
||||
private final Subscription subscription;
|
||||
|
||||
public OnCompleteRunnable(Subscription subscription) {this.subscription = subscription;}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
subscription.onComplete();
|
||||
}
|
||||
}
|
||||
|
||||
private class OnNextRunnable implements Runnable {
|
||||
private final Subscription<T> subscription;
|
||||
private final T item;
|
||||
|
||||
public OnNextRunnable(Subscription<T> subscription, T item) {
|
||||
this.subscription = subscription;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
subscription.onNext(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package acr.browser.lightning.react;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class Schedulers {
|
||||
private static final Executor sWorker = Executors.newCachedThreadPool();
|
||||
private static final Executor sMain = new ThreadExecutor(Looper.getMainLooper());
|
||||
|
||||
public static Executor worker() {
|
||||
return sWorker;
|
||||
}
|
||||
|
||||
public static Executor main() {
|
||||
return sMain;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package acr.browser.lightning.react;
|
||||
|
||||
public interface Subscriber<T> {
|
||||
void onComplete();
|
||||
|
||||
void onNext(T item);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package acr.browser.lightning.react;
|
||||
|
||||
public interface Subscription<T> {
|
||||
void onComplete();
|
||||
|
||||
void onNext(T item);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package acr.browser.lightning.react;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
class ThreadExecutor implements Executor {
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
public ThreadExecutor(@NonNull Looper looper) {
|
||||
mHandler = new Handler(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull Runnable command) {
|
||||
mHandler.post(command);
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
|
||||
/**
|
||||
@ -30,22 +31,27 @@ public class FileUtils {
|
||||
* @param bundle the bundle to store in persistent storage.
|
||||
* @param name the name of the file to store the bundle in.
|
||||
*/
|
||||
public static void writeBundleToStorage(@NonNull Application app, Bundle bundle, @NonNull String name) {
|
||||
File outputFile = new File(app.getFilesDir(), name);
|
||||
FileOutputStream outputStream = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
outputStream = new FileOutputStream(outputFile);
|
||||
Parcel parcel = Parcel.obtain();
|
||||
parcel.writeBundle(bundle);
|
||||
outputStream.write(parcel.marshall());
|
||||
outputStream.flush();
|
||||
parcel.recycle();
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Unable to write bundle to storage");
|
||||
} finally {
|
||||
Utils.close(outputStream);
|
||||
}
|
||||
public static void writeBundleToStorage(final @NonNull Application app, final Bundle bundle, final @NonNull String name) {
|
||||
BrowserApp.getIOThread().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
File outputFile = new File(app.getFilesDir(), name);
|
||||
FileOutputStream outputStream = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
outputStream = new FileOutputStream(outputFile);
|
||||
Parcel parcel = Parcel.obtain();
|
||||
parcel.writeBundle(bundle);
|
||||
outputStream.write(parcel.marshall());
|
||||
outputStream.flush();
|
||||
parcel.recycle();
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Unable to write bundle to storage");
|
||||
} finally {
|
||||
Utils.close(outputStream);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,6 +34,7 @@ import android.webkit.WebView;
|
||||
|
||||
import com.squareup.otto.Bus;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
|
||||
@ -51,6 +52,11 @@ import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.dialog.LightningDialogBuilder;
|
||||
import acr.browser.lightning.download.LightningDownloadListener;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
import acr.browser.lightning.react.Action;
|
||||
import acr.browser.lightning.react.Observable;
|
||||
import acr.browser.lightning.react.Schedulers;
|
||||
import acr.browser.lightning.react.Subscriber;
|
||||
import acr.browser.lightning.react.Subscription;
|
||||
import acr.browser.lightning.utils.ProxyUtils;
|
||||
import acr.browser.lightning.utils.ThemeUtils;
|
||||
import acr.browser.lightning.utils.UrlUtils;
|
||||
@ -325,7 +331,10 @@ public class LightningView {
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
private void initializeSettings() {
|
||||
WebSettings settings = mWebView.getSettings();
|
||||
if (mWebView == null) {
|
||||
return;
|
||||
}
|
||||
final WebSettings settings = mWebView.getSettings();
|
||||
if (API < Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
//noinspection deprecation
|
||||
settings.setAppCacheMaxSize(Long.MAX_VALUE);
|
||||
@ -364,12 +373,56 @@ public class LightningView {
|
||||
settings.setAllowUniversalAccessFromFileURLs(false);
|
||||
}
|
||||
|
||||
settings.setAppCachePath(BrowserApp.get(mActivity).getDir("appcache", 0).getPath());
|
||||
settings.setGeolocationDatabasePath(BrowserApp.get(mActivity).getDir("geolocation", 0).getPath());
|
||||
if (API < Build.VERSION_CODES.KITKAT) {
|
||||
//noinspection deprecation
|
||||
settings.setDatabasePath(BrowserApp.get(mActivity).getDir("databases", 0).getPath());
|
||||
}
|
||||
getPathObservable("appcache")
|
||||
.subscribeOn(Schedulers.worker())
|
||||
.subscribe(new Subscription<File>() {
|
||||
@Override
|
||||
public void onComplete() {}
|
||||
|
||||
@Override
|
||||
public void onNext(File item) {
|
||||
settings.setAppCachePath(item.getPath());
|
||||
}
|
||||
});
|
||||
|
||||
getPathObservable("geolocation")
|
||||
.subscribeOn(Schedulers.worker())
|
||||
.subscribe(new Subscription<File>() {
|
||||
@Override
|
||||
public void onComplete() {}
|
||||
|
||||
@Override
|
||||
public void onNext(File item) {
|
||||
settings.setGeolocationDatabasePath(item.getPath());
|
||||
}
|
||||
});
|
||||
|
||||
getPathObservable("databases")
|
||||
.subscribeOn(Schedulers.worker())
|
||||
.subscribe(new Subscription<File>() {
|
||||
@Override
|
||||
public void onComplete() {}
|
||||
|
||||
@Override
|
||||
public void onNext(File item) {
|
||||
if (API < Build.VERSION_CODES.KITKAT) {
|
||||
//noinspection deprecation
|
||||
settings.setDatabasePath(item.getPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private Observable<File> getPathObservable(final String subFolder) {
|
||||
return Observable.create(new Action<File>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscriber<File> subscriber) {
|
||||
File file = BrowserApp.get(mActivity).getDir(subFolder, 0);
|
||||
subscriber.onNext(file);
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -554,6 +607,9 @@ public class LightningView {
|
||||
* of the current LightningView.
|
||||
*/
|
||||
private void setHardwareRendering() {
|
||||
if (mWebView == null) {
|
||||
return;
|
||||
}
|
||||
mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
|
||||
}
|
||||
|
||||
@ -563,6 +619,9 @@ public class LightningView {
|
||||
* the layers when necessary.
|
||||
*/
|
||||
private void setNormalRendering() {
|
||||
if (mWebView == null) {
|
||||
return;
|
||||
}
|
||||
mWebView.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
|
||||
@ -573,6 +632,9 @@ public class LightningView {
|
||||
* the view.
|
||||
*/
|
||||
public void setSoftwareRendering() {
|
||||
if (mWebView == null) {
|
||||
return;
|
||||
}
|
||||
mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
||||
}
|
||||
|
||||
@ -839,6 +901,9 @@ public class LightningView {
|
||||
* a workaround.
|
||||
*/
|
||||
private void longClickPage(@Nullable final String url) {
|
||||
if (mWebView == null) {
|
||||
return;
|
||||
}
|
||||
final WebView.HitTestResult result = mWebView.getHitTestResult();
|
||||
String currentUrl = mWebView.getUrl();
|
||||
if (currentUrl != null && UrlUtils.isSpecialUrl(currentUrl)) {
|
||||
@ -1043,6 +1108,9 @@ public class LightningView {
|
||||
Message msg = mWebViewHandler.obtainMessage();
|
||||
if (msg != null) {
|
||||
msg.setTarget(mWebViewHandler);
|
||||
if (mWebView == null) {
|
||||
return;
|
||||
}
|
||||
mWebView.requestFocusNodeHref(msg);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user