Browse Source

Merge latest changes from Anthony's dev branch

master
Stefano Pacifici 9 years ago
parent
commit
3cb576d358
  1. 15
      app/build.gradle
  2. 174
      app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java
  3. 1
      app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java
  4. 19
      app/src/main/AndroidManifest.xml
  5. 344
      app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java
  6. 2
      app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java
  7. 2
      app/src/main/java/acr/browser/lightning/app/BrowserApp.java
  8. 52
      app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java
  9. 57
      app/src/main/java/acr/browser/lightning/async/ImageDownloadTask.java
  10. 14
      app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
  11. 4
      app/src/main/java/acr/browser/lightning/constant/Constants.java
  12. 8
      app/src/main/java/acr/browser/lightning/constant/StartPage.java
  13. 3
      app/src/main/java/acr/browser/lightning/controller/BrowserController.java
  14. 145
      app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java
  15. 46
      app/src/main/java/acr/browser/lightning/database/BookmarkManager.java
  16. 18
      app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java
  17. 74
      app/src/main/java/acr/browser/lightning/database/HistoryItem.java
  18. 4
      app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java
  19. 140
      app/src/main/java/acr/browser/lightning/download/DownloadHandler.java
  20. 13
      app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java
  21. 26
      app/src/main/java/acr/browser/lightning/download/WebAddress.java
  22. 60
      app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java
  23. 62
      app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java
  24. 93
      app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java
  25. 76
      app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java
  26. 227
      app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java
  27. 2
      app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java
  28. 44
      app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java
  29. 69
      app/src/main/java/acr/browser/lightning/object/SearchAdapter.java
  30. 10
      app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java
  31. 30
      app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java
  32. 22
      app/src/main/java/acr/browser/lightning/reading/Converter.java
  33. 33
      app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java
  34. 14
      app/src/main/java/acr/browser/lightning/reading/ImageResult.java
  35. 4
      app/src/main/java/acr/browser/lightning/reading/JResult.java
  36. 26
      app/src/main/java/acr/browser/lightning/reading/OutputFormatter.java
  37. 12
      app/src/main/java/acr/browser/lightning/reading/SHelper.java
  38. 5
      app/src/main/java/acr/browser/lightning/utils/AdBlock.java
  39. 6
      app/src/main/java/acr/browser/lightning/utils/IntentUtils.java
  40. 42
      app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java
  41. 2
      app/src/main/java/acr/browser/lightning/utils/UrlUtils.java
  42. 13
      app/src/main/java/acr/browser/lightning/utils/Utils.java
  43. 4
      app/src/main/java/acr/browser/lightning/utils/WebUtils.java
  44. 44
      app/src/main/java/acr/browser/lightning/view/IconCacheTask.java
  45. 42
      app/src/main/java/acr/browser/lightning/view/LightningView.java
  46. 3
      app/src/main/res/layout/two_line_autocomplete.xml
  47. 2
      app/src/main/res/values/colors.xml
  48. 1
      app/src/main/res/values/dimens.xml
  49. 2
      app/src/main/res/values/strings.xml
  50. 1
      app/src/main/res/xml/preference_about.xml
  51. 3
      app/src/main/res/xml/preference_bookmarks.xml

15
app/build.gradle

@ -3,11 +3,11 @@ apply plugin: 'com.neenbedankt.android-apt'
android { android {
compileSdkVersion 23 compileSdkVersion 23
buildToolsVersion "23.0.0" buildToolsVersion "23.0.1"
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 23 targetSdkVersion 23
versionName "4.1.1a" versionName "4.2.0a"
} }
sourceSets { sourceSets {
lightningPlus.setRoot('src/LightningPlus') lightningPlus.setRoot('src/LightningPlus')
@ -30,16 +30,16 @@ android {
lightningPlus { lightningPlus {
buildConfigField "boolean", "FULL_VERSION", "true" buildConfigField "boolean", "FULL_VERSION", "true"
applicationId "acr.browser.lightning" applicationId "acr.browser.lightning"
versionCode 80 versionCode 81
} }
lightningLite { lightningLite {
buildConfigField "boolean", "FULL_VERSION", "false" buildConfigField "boolean", "FULL_VERSION", "false"
applicationId "acr.browser.barebones" applicationId "acr.browser.barebones"
versionCode 81 versionCode 82
} }
} }
lintOptions { lintOptions {
abortOnError false abortOnError true
} }
} }
@ -52,14 +52,15 @@ dependencies {
compile 'com.squareup:otto:1.3.8' compile 'com.squareup:otto:1.3.8'
compile 'com.google.dagger:dagger:2.0.1' compile 'com.google.dagger:dagger:2.0.1'
apt 'com.google.dagger:dagger-compiler:2.0.1' apt 'com.google.dagger:dagger-compiler:2.0.1'
compile 'com.jakewharton:butterknife:7.0.1'
// Only Lightning Plus needs the proxy libraries // Only Lightning Plus needs the proxy libraries
lightningPlusCompile 'net.i2p.android:client:0.7' compile 'net.i2p.android:client:0.7'
// Use the following code to update the libnetcipher submodule // Use the following code to update the libnetcipher submodule
// git submodule foreach git reset --hard // git submodule foreach git reset --hard
// git submodule update --remote // git submodule update --remote
lightningPlusCompile(project(':libnetcipher')) compile(project(':libnetcipher'))
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'

174
app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java

@ -2,18 +2,39 @@ package acr.browser.lightning.utils;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import com.squareup.otto.Bus;
import net.i2p.android.ui.I2PAndroidHelper;
import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.bus.BrowserEvents;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.preference.PreferenceManager;
import info.guardianproject.netcipher.proxy.OrbotHelper;
import info.guardianproject.netcipher.web.WebkitProxy;
/** /**
* 6/4/2015 Anthony Restaino * 6/4/2015 Anthony Restaino
*/ */
public class ProxyUtils { public class ProxyUtils {
// Helper
private final I2PAndroidHelper mI2PHelper;
private static boolean mI2PHelperBound;
private static boolean mI2PProxyInitialized;
private final PreferenceManager mPreferences;
private static ProxyUtils mInstance; private static ProxyUtils mInstance;
private ProxyUtils(Context context) { private final Bus mEventBus;
private ProxyUtils(Context context) {
mPreferences = BrowserApp.getAppComponent().getPreferenceManager();
mEventBus = BrowserApp.getAppComponent().getBus();
mI2PHelper = new I2PAndroidHelper(context.getApplicationContext());
} }
public static ProxyUtils getInstance() { public static ProxyUtils getInstance() {
@ -28,33 +49,172 @@ public class ProxyUtils {
* proxying for this session * proxying for this session
*/ */
public void checkForProxy(final Activity activity) { public void checkForProxy(final Activity activity) {
boolean useProxy = mPreferences.getUseProxy();
final boolean orbotInstalled = OrbotHelper.isOrbotInstalled(activity);
boolean orbotChecked = mPreferences.getCheckedForTor();
boolean orbot = orbotInstalled && !orbotChecked;
boolean i2pInstalled = mI2PHelper.isI2PAndroidInstalled();
boolean i2pChecked = mPreferences.getCheckedForI2P();
boolean i2p = i2pInstalled && !i2pChecked;
// TODO Is the idea to show this per-session, or only once?
if (!useProxy && (orbot || i2p)) {
if (orbot) mPreferences.setCheckedForTor(true);
if (i2p) mPreferences.setCheckedForI2P(true);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
if (orbotInstalled && i2pInstalled) {
String[] proxyChoices = activity.getResources().getStringArray(R.array.proxy_choices_array);
builder.setTitle(activity.getResources().getString(R.string.http_proxy))
.setSingleChoiceItems(proxyChoices, mPreferences.getProxyChoice(),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mPreferences.setProxyChoice(which);
}
})
.setNeutralButton(activity.getResources().getString(R.string.action_ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (mPreferences.getUseProxy())
initializeProxy(activity);
}
});
} else {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
mPreferences.setProxyChoice(orbotInstalled ?
Constants.PROXY_ORBOT : Constants.PROXY_I2P);
initializeProxy(activity);
break;
case DialogInterface.BUTTON_NEGATIVE:
mPreferences.setProxyChoice(Constants.NO_PROXY);
break;
}
}
};
builder.setMessage(orbotInstalled ? R.string.use_tor_prompt : R.string.use_i2p_prompt)
.setPositiveButton(R.string.yes, dialogClickListener)
.setNegativeButton(R.string.no, dialogClickListener);
}
builder.show();
}
} }
/* /*
* Initialize WebKit Proxying * Initialize WebKit Proxying
*/ */
private void initializeProxy(Activity activity) { private void initializeProxy(Activity activity) {
String host;
int port;
switch (mPreferences.getProxyChoice()) {
case Constants.NO_PROXY:
// We shouldn't be here
return;
case Constants.PROXY_ORBOT:
if (!OrbotHelper.isOrbotRunning(activity))
OrbotHelper.requestStartTor(activity);
host = "localhost";
port = 8118;
break;
case Constants.PROXY_I2P:
mI2PProxyInitialized = true;
if (mI2PHelperBound && !mI2PHelper.isI2PAndroidRunning()) {
mI2PHelper.requestI2PAndroidStart(activity);
}
host = "localhost";
port = 4444;
break;
default:
host = mPreferences.getProxyHost();
port = mPreferences.getProxyPort();
}
try {
WebkitProxy.setProxy(BrowserApp.class.getName(), activity.getApplicationContext(), null, host, port);
} catch (Exception e) {
Log.d(Constants.TAG, "error enabling web proxying", e);
}
} }
public boolean isProxyReady(Context context) { public boolean isProxyReady() {
if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) {
if (!mI2PHelper.isI2PAndroidRunning()) {
mEventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_not_running));
return false;
} else if (!mI2PHelper.areTunnelsActive()) {
mEventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_tunnels_not_ready));
return false;
}
}
return true; return true;
} }
public void updateProxySettings(Activity activity) { public void updateProxySettings(Activity activity) {
if (mPreferences.getUseProxy()) {
initializeProxy(activity);
} else {
try {
WebkitProxy.resetProxy(BrowserApp.class.getName(), activity.getApplicationContext());
} catch (Exception e) {
e.printStackTrace();
}
mI2PProxyInitialized = false;
}
} }
public void onStop() { public void onStop() {
mI2PHelper.unbind();
mI2PHelperBound = false;
} }
public void onStart(final Activity activity) { public void onStart(final Activity activity) {
if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) {
// Try to bind to I2P Android
mI2PHelper.bind(new I2PAndroidHelper.Callback() {
@Override
public void onI2PAndroidBound() {
mI2PHelperBound = true;
if (mI2PProxyInitialized && !mI2PHelper.isI2PAndroidRunning())
mI2PHelper.requestI2PAndroidStart(activity);
}
});
}
} }
public static int setProxyChoice(int choice, Activity activity) { public static int setProxyChoice(int choice, Activity activity) {
switch (choice) {
case Constants.PROXY_ORBOT:
if (!OrbotHelper.isOrbotInstalled(activity)) {
choice = Constants.NO_PROXY;
Utils.showSnackbar(activity, R.string.install_orbot);
}
break;
case Constants.PROXY_I2P:
I2PAndroidHelper ih = new I2PAndroidHelper(activity.getApplicationContext());
if (!ih.isI2PAndroidInstalled()) {
choice = Constants.NO_PROXY;
ih.promptToInstall(activity);
}
break;
case Constants.PROXY_MANUAL:
break;
}
return choice; return choice;
} }
} }

1
app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java

@ -3,7 +3,6 @@ package acr.browser.lightning.utils;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.util.Log; import android.util.Log;

19
app/src/main/AndroidManifest.xml

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!-- Copyright 2014 A.C.R. Development -->
<!-- Copyright 2014 A.C.R. Development -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="acr.browser.lightning" > package="acr.browser.lightning">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
<uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" />
<uses-feature <uses-feature
android:name="android.hardware.location.gps" android:name="android.hardware.location.gps"
@ -24,14 +25,14 @@
android:allowBackup="true" android:allowBackup="true"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" > android:label="@string/app_name">
<activity <activity
android:name=".activity.MainActivity" android:name=".activity.MainActivity"
android:alwaysRetainTaskState="true" android:alwaysRetainTaskState="true"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/Theme.LightTheme" > android:theme="@style/Theme.LightTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -96,7 +97,7 @@
android:name=".activity.SettingsActivity" android:name=".activity.SettingsActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/settings" android:label="@string/settings"
android:theme="@style/Theme.SettingsTheme" > android:theme="@style/Theme.SettingsTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SETTINGS" /> <action android:name="android.intent.action.SETTINGS" />
@ -109,9 +110,9 @@
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:process=":incognito"
android:theme="@style/Theme.DarkTheme" android:theme="@style/Theme.DarkTheme"
android:windowSoftInputMode="stateHidden" android:windowSoftInputMode="stateHidden">
android:process=":incognito">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.INCOGNITO" /> <action android:name="android.intent.action.INCOGNITO" />
@ -122,7 +123,7 @@
android:name=".activity.ReadingActivity" android:name=".activity.ReadingActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/reading_mode" android:label="@string/reading_mode"
android:theme="@style/Theme.SettingsTheme" > android:theme="@style/Theme.SettingsTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.READING" /> <action android:name="android.intent.action.READING" />

344
app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java

@ -32,6 +32,7 @@ import android.provider.MediaStore;
import android.support.annotation.IdRes; import android.support.annotation.IdRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.GravityCompat; import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener; import android.support.v4.widget.DrawerLayout.DrawerListener;
@ -85,6 +86,8 @@ import com.squareup.otto.Subscribe;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
@ -113,20 +116,43 @@ import acr.browser.lightning.utils.Utils;
import acr.browser.lightning.utils.WebUtils; import acr.browser.lightning.utils.WebUtils;
import acr.browser.lightning.view.AnimatedProgressBar; import acr.browser.lightning.view.AnimatedProgressBar;
import acr.browser.lightning.view.LightningView; import acr.browser.lightning.view.LightningView;
import butterknife.Bind;
import butterknife.ButterKnife;
public abstract class BrowserActivity extends ThemableBrowserActivity implements BrowserController, OnClickListener, OnLongClickListener { public abstract class BrowserActivity extends ThemableBrowserActivity implements BrowserController, OnClickListener, OnLongClickListener {
// Layout // Static Layout
private DrawerLayout mDrawerLayout; @Bind(R.id.drawer_layout)
private FrameLayout mBrowserFrame; DrawerLayout mDrawerLayout;
private FullscreenHolder mFullscreenContainer;
private ViewGroup mDrawerLeft, mDrawerRight, mUiLayout, mToolbarLayout;
private RelativeLayout mSearchBar;
// Views @Bind(R.id.content_frame)
private AnimatedProgressBar mProgressBar; FrameLayout mBrowserFrame;
@Bind(R.id.left_drawer)
ViewGroup mDrawerLeft;
@Bind(R.id.right_drawer)
ViewGroup mDrawerRight;
@Bind(R.id.ui_layout)
ViewGroup mUiLayout;
@Bind(R.id.toolbar_layout)
ViewGroup mToolbarLayout;
@Bind(R.id.progress_view)
AnimatedProgressBar mProgressBar;
@Bind(R.id.search_bar)
RelativeLayout mSearchBar;
// Toolbar Views
private AutoCompleteTextView mSearch; private AutoCompleteTextView mSearch;
private ImageView mArrowImage; private ImageView mArrowImage;
// Full Screen Video Views
private FrameLayout mFullscreenContainer;
private VideoView mVideoView; private VideoView mVideoView;
private View mCustomView; private View mCustomView;
@ -138,9 +164,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
private ValueCallback<Uri> mUploadMessage; private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mFilePathCallback; private ValueCallback<Uri[]> mFilePathCallback;
// Context
private Activity mActivity;
// Primatives // Primatives
private boolean mFullScreen, mColorMode, mDarkTheme, private boolean mFullScreen, mColorMode, mDarkTheme,
mIsNewIntent = false, mIsNewIntent = false,
@ -153,14 +176,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
// The singleton BookmarkManager // The singleton BookmarkManager
@Inject @Inject
BookmarkManager bookmarkManager; BookmarkManager mBookmarkManager;
// Event bus // Event bus
@Inject @Inject
Bus eventBus; Bus mEventBus;
@Inject @Inject
BookmarkPage bookmarkPage; BookmarkPage mBookmarkPage;
@Inject @Inject
LightningDialogBuilder bookmarksDialogBuilder; LightningDialogBuilder bookmarksDialogBuilder;
@ -206,11 +229,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
BrowserApp.getAppComponent().inject(this); BrowserApp.getAppComponent().inject(this);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initialize(); initialize();
} }
private synchronized void initialize() { private synchronized void initialize() {
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
@ -220,20 +244,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mIconColor = mDarkTheme ? ThemeUtils.getIconDarkThemeColor(this) : ThemeUtils.getIconLightThemeColor(this); mIconColor = mDarkTheme ? ThemeUtils.getIconDarkThemeColor(this) : ThemeUtils.getIconLightThemeColor(this);
mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet()); mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet());
mActivity = this;
mBrowserFrame = (FrameLayout) findViewById(R.id.content_frame);
mToolbarLayout = (LinearLayout) findViewById(R.id.toolbar_layout);
// initialize background ColorDrawable // initialize background ColorDrawable
mBackground.setColor(((ColorDrawable) mToolbarLayout.getBackground()).getColor()); mBackground.setColor(((ColorDrawable) mToolbarLayout.getBackground()).getColor());
mUiLayout = (LinearLayout) findViewById(R.id.ui_layout);
mProgressBar = (AnimatedProgressBar) findViewById(R.id.progress_view);
mDrawerLeft = (FrameLayout) findViewById(R.id.left_drawer);
// Drawer stutters otherwise // Drawer stutters otherwise
mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null); mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerRight = (ViewGroup) findViewById(R.id.right_drawer);
mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null); mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null);
// TODO Please review this // TODO Please review this
// ImageView tabTitleImage = (ImageView) findViewById(R.id.plusIcon); // ImageView tabTitleImage = (ImageView) findViewById(R.id.plusIcon);
@ -273,15 +289,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
actionBar.setDisplayShowCustomEnabled(true); actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(R.layout.toolbar_content); actionBar.setCustomView(R.layout.toolbar_content);
View v = actionBar.getCustomView(); View customView = actionBar.getCustomView();
LayoutParams lp = v.getLayoutParams(); LayoutParams lp = customView.getLayoutParams();
lp.width = LayoutParams.MATCH_PARENT; lp.width = LayoutParams.MATCH_PARENT;
lp.height = LayoutParams.MATCH_PARENT; lp.height = LayoutParams.MATCH_PARENT;
v.setLayoutParams(lp); customView.setLayoutParams(lp);
mArrowImage = (ImageView) actionBar.getCustomView().findViewById(R.id.arrow); mArrowImage = (ImageView) customView.findViewById(R.id.arrow);
FrameLayout arrowButton = (FrameLayout) actionBar.getCustomView().findViewById( FrameLayout arrowButton = (FrameLayout) customView.findViewById(R.id.arrow_button);
R.id.arrow_button);
if (mShowTabsInDrawer) { if (mShowTabsInDrawer) {
// Use hardware acceleration for the animation // Use hardware acceleration for the animation
mArrowDrawable = new DrawerArrowDrawable(this); mArrowDrawable = new DrawerArrowDrawable(this);
@ -295,17 +310,10 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mProxyUtils = ProxyUtils.getInstance(); mProxyUtils = ProxyUtils.getInstance();
setupFrameLayoutButton(R.id.action_reading, R.id.icon_reading);
setupFrameLayoutButton(R.id.action_toggle_desktop, R.id.icon_desktop);
// create the search EditText in the ToolBar // create the search EditText in the ToolBar
mSearch = (AutoCompleteTextView) actionBar.getCustomView().findViewById(R.id.search); mSearch = (AutoCompleteTextView) customView.findViewById(R.id.search);
mUntitledTitle = getString(R.string.untitled); mUntitledTitle = getString(R.string.untitled);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mBackgroundColor = ContextCompat.getColor(this, R.color.primary_color);
mBackgroundColor = getColor(R.color.primary_color);
} else {
mBackgroundColor = getResources().getColor(R.color.primary_color);
}
mDeleteIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete); mDeleteIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete);
mRefreshIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_refresh); mRefreshIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_refresh);
mClearIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete); mClearIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete);
@ -335,6 +343,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mDrawerLayout.setDrawerShadow(R.drawable.drawer_left_shadow, GravityCompat.START); mDrawerLayout.setDrawerShadow(R.drawable.drawer_left_shadow, GravityCompat.START);
if (API <= Build.VERSION_CODES.JELLY_BEAN_MR2) { if (API <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//noinspection deprecation
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
} }
@ -391,11 +400,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
public void onFocusChange(View v, final boolean hasFocus) { public void onFocusChange(View v, final boolean hasFocus) {
final LightningView currentView = tabsManager.getCurrentTab(); final LightningView currentView = tabsManager.getCurrentTab();
if (!hasFocus && currentView != null) { if (!hasFocus && currentView != null) {
if (currentView.getProgress() < 100) { setIsLoading(currentView.getProgress() < 100);
setIsLoading();
} else {
setIsFinishedLoading();
}
updateUrl(currentView.getUrl(), true); updateUrl(currentView.getUrl(), true);
} else if (hasFocus) { } else if (hasFocus) {
String url = currentView.getUrl(); String url = currentView.getUrl();
@ -704,7 +709,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", currentView.getUrl()); ClipData clip = ClipData.newPlainText("label", currentView.getUrl());
clipboard.setPrimaryClip(clip); clipboard.setPrimaryClip(clip);
Utils.showSnackbar(mActivity, R.string.message_link_copied); Utils.showSnackbar(this, R.string.message_link_copied);
} }
return true; return true;
case R.id.action_settings: case R.id.action_settings:
@ -715,7 +720,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
return true; return true;
case R.id.action_add_bookmark: case R.id.action_add_bookmark:
if (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) { if (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) {
eventBus.post(new BrowserEvents.AddBookmark(currentView.getTitle(), mEventBus.post(new BrowserEvents.AddBookmark(currentView.getTitle(),
currentView.getUrl())); currentView.getUrl()));
} }
return true; return true;
@ -737,7 +742,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* for. It highlights the text entered. * for. It highlights the text entered.
*/ */
private void findInPage() { private void findInPage() {
final AlertDialog.Builder finder = new AlertDialog.Builder(mActivity); final AlertDialog.Builder finder = new AlertDialog.Builder(this);
finder.setTitle(getResources().getString(R.string.action_find)); finder.setTitle(getResources().getString(R.string.action_find));
final EditText getHome = new EditText(this); final EditText getHome = new EditText(this);
getHome.setHint(getResources().getString(R.string.search_hint)); getHome.setHint(getResources().getString(R.string.search_hint));
@ -760,7 +765,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (currentView != null) { if (currentView != null) {
currentView.find(text); currentView.find(text);
} }
mSearchBar = (RelativeLayout) findViewById(R.id.search_bar);
mSearchBar.setVisibility(View.VISIBLE); mSearchBar.setVisibility(View.VISIBLE);
TextView tw = (TextView) findViewById(R.id.search_query); TextView tw = (TextView) findViewById(R.id.search_query);
@ -780,11 +784,11 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (position < 0) { if (position < 0) {
return; return;
} }
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); AlertDialog.Builder builder = new AlertDialog.Builder(this);
ArrayAdapter<String> adapter = new ArrayAdapter<>(mActivity, ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_dropdown_item_1line); android.R.layout.simple_dropdown_item_1line);
adapter.add(mActivity.getString(R.string.close_tab)); adapter.add(this.getString(R.string.close_tab));
adapter.add(mActivity.getString(R.string.close_all_tabs)); adapter.add(this.getString(R.string.close_all_tabs));
builder.setAdapter(adapter, new DialogInterface.OnClickListener() { builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
@Override @Override
@ -818,7 +822,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
// Set the background color so the color mode color doesn't show through // Set the background color so the color mode color doesn't show through
mBrowserFrame.setBackgroundColor(mBackgroundColor); mBrowserFrame.setBackgroundColor(mBackgroundColor);
if (newView == null || currentView == newView) { if (newView == currentView && !currentView.isShown()) {
return; return;
} }
mIsNewIntent = false; mIsNewIntent = false;
@ -871,7 +875,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}, 200); }, 200);
// Should update the bookmark status in BookmarksFragment // Should update the bookmark status in BookmarksFragment
eventBus.post(new BrowserEvents.CurrentPageUrl(newView.getUrl())); mEventBus.post(new BrowserEvents.CurrentPageUrl(newView.getUrl()));
// new Handler().postDelayed(new Runnable() { // new Handler().postDelayed(new Runnable() {
// @Override // @Override
@ -916,7 +920,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
currentTab.loadUrl(url); currentTab.loadUrl(url);
eventBus.post(new BrowserEvents.CurrentPageUrl(url)); mEventBus.post(new BrowserEvents.CurrentPageUrl(url));
} }
@Override @Override
@ -936,8 +940,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (level > TRIM_MEMORY_MODERATE && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { if (level > TRIM_MEMORY_MODERATE && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Log.d(Constants.TAG, "Low Memory, Free Memory"); Log.d(Constants.TAG, "Low Memory, Free Memory");
tabsManager.freeMemory(); tabsManager.freeMemory();
}
} }
}
synchronized boolean newTab(String url, boolean show) { synchronized boolean newTab(String url, boolean show) {
// Limit number of tabs for limited version of app // Limit number of tabs for limited version of app
@ -956,7 +960,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
showTab(tabsManager.size() - 1); showTab(tabsManager.size() - 1);
} }
// TODO Check is this is callable directly from LightningView // TODO Check is this is callable directly from LightningView
eventBus.post(new BrowserEvents.TabsChanged()); mEventBus.post(new BrowserEvents.TabsChanged());
// TODO Restore this // TODO Restore this
// new Handler().postDelayed(new Runnable() { // new Handler().postDelayed(new Runnable() {
@ -992,14 +996,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (current > position) { if (current > position) {
tabsManager.deleteTab(position); tabsManager.deleteTab(position);
showTab(current - 1); showTab(current - 1);
eventBus.post(new BrowserEvents.TabsChanged()); mEventBus.post(new BrowserEvents.TabsChanged());
tabToDelete.onDestroy(); tabToDelete.onDestroy();
} else if (tabsManager.size() > position + 1) { } else if (tabsManager.size() > position + 1) {
if (current == position) { if (current == position) {
showTab(position + 1); showTab(position + 1);
tabsManager.deleteTab(position); tabsManager.deleteTab(position);
showTab(position); showTab(position);
eventBus.post(new BrowserEvents.TabsChanged()); mEventBus.post(new BrowserEvents.TabsChanged());
} else { } else {
tabsManager.deleteTab(position); tabsManager.deleteTab(position);
} }
@ -1010,9 +1014,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
showTab(position - 1); showTab(position - 1);
tabsManager.deleteTab(position); tabsManager.deleteTab(position);
showTab(position - 1); showTab(position - 1);
eventBus.post(new BrowserEvents.TabsChanged()); mEventBus.post(new BrowserEvents.TabsChanged());
} else { } else {
tabsManager.deleteTab(position);
} }
tabToDelete.onDestroy(); tabToDelete.onDestroy();
@ -1024,11 +1027,11 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
performExitCleanUp(); performExitCleanUp();
tabToDelete.pauseTimers(); tabToDelete.pauseTimers();
tabToDelete.onDestroy(); tabToDelete.onDestroy();
eventBus.post(new BrowserEvents.TabsChanged()); mEventBus.post(new BrowserEvents.TabsChanged());
finish(); finish();
} }
} }
eventBus.post(new BrowserEvents.TabsChanged()); mEventBus.post(new BrowserEvents.TabsChanged());
if (mIsNewIntent && isShown) { if (mIsNewIntent && isShown) {
mIsNewIntent = false; mIsNewIntent = false;
@ -1043,17 +1046,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (mPreferences.getClearCacheExit() && currentTab != null && !isIncognito()) { if (mPreferences.getClearCacheExit() && currentTab != null && !isIncognito()) {
WebUtils.clearCache(currentTab.getWebView()); WebUtils.clearCache(currentTab.getWebView());
Log.d(Constants.TAG, "Cache Cleared"); Log.d(Constants.TAG, "Cache Cleared");
} }
if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) {
WebUtils.clearHistory(this); WebUtils.clearHistory(this);
Log.d(Constants.TAG, "History Cleared"); Log.d(Constants.TAG, "History Cleared");
} }
if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) {
WebUtils.clearCookies(this); WebUtils.clearCookies(this);
Log.d(Constants.TAG, "Cookies Cleared"); Log.d(Constants.TAG, "Cookies Cleared");
} }
if (mPreferences.getClearWebStorageExitEnabled() && !isIncognito()) { if (mPreferences.getClearWebStorageExitEnabled() && !isIncognito()) {
WebUtils.clearWebStorage(); WebUtils.clearWebStorage();
@ -1076,7 +1076,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mBrowserFrame.setBackgroundColor(mBackgroundColor); mBrowserFrame.setBackgroundColor(mBackgroundColor);
performExitCleanUp(); performExitCleanUp();
tabsManager.shutdown(); tabsManager.shutdown();
eventBus.post(new BrowserEvents.TabsChanged()); mEventBus.post(new BrowserEvents.TabsChanged());
finish(); finish();
} }
@ -1086,7 +1086,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) {
mDrawerLayout.closeDrawer(mDrawerLeft); mDrawerLayout.closeDrawer(mDrawerLeft);
} else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { } else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) {
eventBus mEventBus
.post(new BrowserEvents.UserPressedBack()); .post(new BrowserEvents.UserPressedBack());
} else { } else {
if (currentTab != null) { if (currentTab != null) {
@ -1127,13 +1127,13 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
overridePendingTransition(R.anim.fade_in_scale, R.anim.slide_down_out); overridePendingTransition(R.anim.fade_in_scale, R.anim.slide_down_out);
} }
eventBus.unregister(busEventListener); mEventBus.unregister(mBusEventListener);
} }
void saveOpenTabs() { void saveOpenTabs() {
if (mPreferences.getRestoreLostTabsEnabled()) { if (mPreferences.getRestoreLostTabsEnabled()) {
final String s = tabsManager.tabsString(); final String s = tabsManager.tabsString();
mPreferences.setMemoryUrl(s); mPreferences.setMemoryUrl(s.toString());
} }
} }
@ -1181,7 +1181,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
filter.addAction(NETWORK_BROADCAST_ACTION); filter.addAction(NETWORK_BROADCAST_ACTION);
registerReceiver(mNetworkReceiver, filter); registerReceiver(mNetworkReceiver, filter);
eventBus.register(busEventListener); mEventBus.register(mBusEventListener);
} }
/** /**
@ -1210,12 +1210,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* @param tabBackground the optional LinearLayout to color * @param tabBackground the optional LinearLayout to color
*/ */
private void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable final Drawable tabBackground) { private void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable final Drawable tabBackground) {
final int defaultColor; final int defaultColor = ContextCompat.getColor(this, R.color.primary_color);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
defaultColor = getResources().getColor(R.color.primary_color);
} else {
defaultColor = getColor(R.color.primary_color);
}
if (mCurrentUiColor == Color.BLACK) { if (mCurrentUiColor == Color.BLACK) {
mCurrentUiColor = defaultColor; mCurrentUiColor = defaultColor;
} }
@ -1268,7 +1263,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (url == null || mSearch == null || mSearch.hasFocus()) { if (url == null || mSearch == null || mSearch.hasFocus()) {
return; return;
} }
eventBus.post(new BrowserEvents.CurrentPageUrl(url)); mEventBus.post(new BrowserEvents.CurrentPageUrl(url));
if (shortUrl && !url.startsWith(Constants.FILE)) { if (shortUrl && !url.startsWith(Constants.FILE)) {
switch (mPreferences.getUrlBoxContentChoice()) { switch (mPreferences.getUrlBoxContentChoice()) {
case 0: // Default, show only the domain case 0: // Default, show only the domain
@ -1297,11 +1292,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
@Override @Override
public void updateProgress(int n) { public void updateProgress(int n) {
if (n >= 100) { setIsLoading(n < 100);
setIsFinishedLoading();
} else {
setIsLoading();
}
mProgressBar.setProgress(n); mProgressBar.setProgress(n);
} }
@ -1341,7 +1332,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
try { try {
String url; String url;
url = ((TextView) arg1.findViewById(R.id.url)).getText().toString(); url = ((TextView) arg1.findViewById(R.id.url)).getText().toString();
if (url.startsWith(mActivity.getString(R.string.suggestion))) { if (url.startsWith(BrowserActivity.this.getString(R.string.suggestion))) {
url = ((TextView) arg1.findViewById(R.id.title)).getText().toString(); url = ((TextView) arg1.findViewById(R.id.title)).getText().toString();
} else { } else {
getUrl.setText(url); getUrl.setText(url);
@ -1360,7 +1351,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}); });
getUrl.setSelectAllOnFocus(true); getUrl.setSelectAllOnFocus(true);
mSearchAdapter = new SearchAdapter(mActivity, mDarkTheme, isIncognito()); mSearchAdapter = new SearchAdapter(this, mDarkTheme, isIncognito());
getUrl.setAdapter(mSearchAdapter); getUrl.setAdapter(mSearchAdapter);
} }
@ -1373,7 +1364,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
@Override @Override
public void run() { public void run() {
loadUrlInCurrentView(HistoryPage.getHistoryPage(mActivity)); loadUrlInCurrentView(HistoryPage.getHistoryPage(BrowserActivity.this));
mSearch.setText(""); mSearch.setText("");
} }
@ -1471,7 +1462,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mFilePathCallback = filePathCallback; mFilePathCallback = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) { if (takePictureIntent.resolveActivity(this.getPackageManager()) != null) {
// Create the File where the photo should go // Create the File where the photo should go
File photoFile = null; File photoFile = null;
try { try {
@ -1493,7 +1484,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*"); contentSelectionIntent.setType("*/*");
Intent[] intentArray; Intent[] intentArray;
if (takePictureIntent != null) { if (takePictureIntent != null) {
@ -1507,7 +1498,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser"); chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
mActivity.startActivityForResult(chooserIntent, 1); this.startActivityForResult(chooserIntent, 1);
} }
@Override @Override
@ -1527,7 +1518,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
mOriginalOrientation = getRequestedOrientation(); mOriginalOrientation = getRequestedOrientation();
FrameLayout decor = (FrameLayout) getWindow().getDecorView(); FrameLayout decor = (FrameLayout) getWindow().getDecorView();
mFullscreenContainer = new FullscreenHolder(this); mFullscreenContainer = new FrameLayout(this);
mFullscreenContainer.setBackgroundColor(ContextCompat.getColor(this, android.R.color.black));
mCustomView = view; mCustomView = view;
mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS);
decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS);
@ -1603,9 +1595,15 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
/** /**
* turns on fullscreen mode in the app * This method sets whether or not the activity will display
* in full-screen mode (i.e. the ActionBar will be hidden) and
* whether or not immersive mode should be set. This is used to
* set both parameters correctly as during a full-screen video,
* both need to be set, but other-wise we leave it up to user
* preference.
* *
* @param enabled whether to enable fullscreen or not * @param enabled true to enable full-screen, false otherwise
* @param immersive true to enable immersive mode, false otherwise
*/ */
private void setFullscreen(boolean enabled, boolean immersive) { private void setFullscreen(boolean enabled, boolean immersive) {
mIsFullScreen = enabled; mIsFullScreen = enabled;
@ -1630,31 +1628,25 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
/** /**
* a class extending FramLayout used to display fullscreen videos * This interface method is used by the LightningView to obtain an
* image that is displayed as a placeholder on a video until the video
* has initialized and can begin loading.
*
* @return a Bitmap that can be used as a place holder for videos.
*/ */
private class FullscreenHolder extends FrameLayout {
public FullscreenHolder(Context ctx) {
super(ctx);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setBackgroundColor(ctx.getColor(android.R.color.black));
} else {
setBackgroundColor(ctx.getResources().getColor(android.R.color.black));
}
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent evt) {
return true;
}
}
@Override @Override
public Bitmap getDefaultVideoPoster() { public Bitmap getDefaultVideoPoster() {
return BitmapFactory.decodeResource(getResources(), android.R.drawable.spinner_background); return BitmapFactory.decodeResource(getResources(), android.R.drawable.spinner_background);
} }
/**
* An interface method so that we can inflate a view to send to
* a LightningView when it needs to display a video and has to
* show a loading dialog. Inflates a progress view and returns it.
*
* @return A view that should be used to display the state
* of a video's loading progress.
*/
@Override @Override
public View getVideoLoadingProgressView() { public View getVideoLoadingProgressView() {
LayoutInflater inflater = LayoutInflater.from(this); LayoutInflater inflater = LayoutInflater.from(this);
@ -1662,7 +1654,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
/** /**
* handles javascript requests to create a new window in the browser * This method handles the JavaScript callback to create a new tab.
* Basically this handles the event that JavaScript needs to create
* a popup.
*
* @param resultMsg the transport message used to send the URL to
* the newly created WebView.
*/ */
@Override @Override
public void onCreateWindow(Message resultMsg) { public void onCreateWindow(Message resultMsg) {
@ -1679,9 +1676,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
/** /**
* Closes the specified view, implementing the JavaScript callback to close a window * Closes the specified {@link LightningView}. This implements
* the JavaScript callback that asks the tab to close itself and
* is especially helpful when a page creates a redirect and does
* not need the tab to stay open any longer.
* *
* @param view the LightningView to close * @param view the LightningView to close, delete it.
*/ */
@Override @Override
public void onCloseWindow(LightningView view) { public void onCloseWindow(LightningView view) {
@ -1689,16 +1689,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
/** /**
* returns the Activity instance for this activity, * Hide the ActionBar using an animation if we are in full-screen
* very helpful when creating things in other classes... I think * mode. This method also re-parents the ActionBar if its parent is
*/ * incorrect so that the animation can happen correctly.
@Override
public Activity getActivity() {
return mActivity;
}
/**
* it hides the action bar, seriously what else were you expecting
*/ */
@Override @Override
public void hideActionBar() { public void hideActionBar() {
@ -1730,14 +1723,15 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}; };
show.setDuration(250); show.setDuration(250);
show.setInterpolator(new DecelerateInterpolator()); show.setInterpolator(new DecelerateInterpolator());
// show.setFillAfter(true);
currentWebView.startAnimation(show); currentWebView.startAnimation(show);
} }
} }
} }
/** /**
* obviously it shows the action bar if it's hidden * Display the ActionBar using an animation if we are in full-screen
* mode. This method also re-parents the ActionBar if its parent is
* incorrect so that the animation can happen correctly.
*/ */
@Override @Override
public void showActionBar() { public void showActionBar() {
@ -1761,7 +1755,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mToolbarLayout.setTranslationY(0); mToolbarLayout.setTranslationY(0);
if (view != null) { if (view != null) {
view.setTranslationY(height); view.setTranslationY(height);
} }
} }
final LightningView currentTab = tabsManager.getCurrentTab(); final LightningView currentTab = tabsManager.getCurrentTab();
if (currentTab == null) if (currentTab == null)
@ -1781,7 +1775,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
}; };
show.setDuration(250); show.setDuration(250);
show.setInterpolator(new DecelerateInterpolator()); show.setInterpolator(new DecelerateInterpolator());
// show.setFillAfter(true);
if (view != null) { if (view != null) {
view.startAnimation(show); view.startAnimation(show);
} }
@ -1794,28 +1787,16 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* and that it should display the stop icon to indicate to the user that * and that it should display the stop icon to indicate to the user that
* pressing it stops the page from loading * pressing it stops the page from loading
*/ */
private void setIsLoading() { private void setIsLoading(boolean isLoading) {
if (!mSearch.hasFocus()) {
mIcon = mDeleteIcon;
mSearch.setCompoundDrawables(null, null, mDeleteIcon, null);
}
}
/**
* This tells the search bar that the page is finished loading and it should
* display the refresh icon
*/
private void setIsFinishedLoading() {
if (!mSearch.hasFocus()) { if (!mSearch.hasFocus()) {
mIcon = mRefreshIcon; mIcon = isLoading ? mDeleteIcon : mRefreshIcon;
mSearch.setCompoundDrawables(null, null, mRefreshIcon, null); mSearch.setCompoundDrawables(null, null, mIcon, null);
} }
} }
/** /**
* handle presses on the refresh icon in the search bar, if the page is * handle presses on the refresh icon in the search bar, if the page is
* loading, stop the page, if it is done loading refresh the page. * loading, stop the page, if it is done loading refresh the page.
* <p/>
* See setIsFinishedLoading and setIsLoading for displaying the correct icon * See setIsFinishedLoading and setIsLoading for displaying the correct icon
*/ */
private void refreshOrStop() { private void refreshOrStop() {
@ -1829,6 +1810,13 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
} }
/**
* Handle the click event for the views that are using
* this class as a click listener. This method should
* distinguish between the various views using their IDs.
*
* @param v the view that the user has clicked
*/
@Override @Override
public void onClick(View v) { public void onClick(View v) {
final LightningView currentTab = tabsManager.getCurrentTab(); final LightningView currentTab = tabsManager.getCurrentTab();
@ -1866,12 +1854,33 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
} }
/**
* Handle long presses on views that use this class
* as their OnLongClickListener. This method should
* distinguish between the IDs of the views that are
* getting clicked.
*
* @param view the view that has been long pressed
* @return returns true since the method handles the long press
* event
*/
@Override @Override
public boolean onLongClick(View view) { public boolean onLongClick(View view) {
return true; return true;
} }
// TODO Check if all the calls are relative to TabsFragement // TODO Check if all the calls are relative to TabsFragement
/**
* A utility method that creates a FrameLayout button with the given ID and
* sets the image of the button to the given image ID. The OnClick and OnLongClick
* listeners are set to this class, so BrowserActivity should handle those events
* there. Additionally, it tints the images according to the current theme.
* This method only is a convenience so that this code does not have to be repeated
* for the several "Buttons" that use this.
*
* @param buttonId the view id of the button
* @param imageId the image to set as the button image
*/
private void setupFrameLayoutButton(@IdRes int buttonId, @IdRes int imageId) { private void setupFrameLayoutButton(@IdRes int buttonId, @IdRes int imageId) {
final View frameButton = findViewById(buttonId); final View frameButton = findViewById(buttonId);
final ImageView buttonImage = (ImageView) findViewById(imageId); final ImageView buttonImage = (ImageView) findViewById(imageId);
@ -1880,6 +1889,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
} }
/**
* This NetworkReceiver notifies each of the WebViews in the browser whether
* the network is currently connected or not. This is important because some
* JavaScript properties rely on the WebView knowing the current network state.
* It is used to help the browser be compliant with the HTML5 spec, sec. 5.7.7
*/
private final NetworkReceiver mNetworkReceiver = new NetworkReceiver() { private final NetworkReceiver mNetworkReceiver = new NetworkReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@ -1887,21 +1902,32 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
boolean isConnected = isConnected(context); boolean isConnected = isConnected(context);
Log.d(Constants.TAG, "Network Connected: " + String.valueOf(isConnected)); Log.d(Constants.TAG, "Network Connected: " + String.valueOf(isConnected));
tabsManager.notifyConnectioneStatus(isConnected); tabsManager.notifyConnectioneStatus(isConnected);
} }
}; };
/**
* Handle the callback that permissions requested have been granted or not.
* This method should act upon the results of the permissions request.
*
* @param requestCode the request code sent when initially making the request
* @param permissions the array of the permissions that was requested
* @param grantResults the results of the permissions requests that provides
* information on whether the request was granted or not
*/
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionsManager.getInstance().notifyPermissionsChange(permissions); PermissionsManager.getInstance().notifyPermissionsChange(permissions);
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
} }
private final Object busEventListener = new Object() { private final Object mBusEventListener = new Object() {
/** /**
* Load the given url in the current tab, used by the the * Load the given url in the current tab, used by the the
* {@link acr.browser.lightning.fragment.BookmarksFragment} and by the * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the
* {@link LightningDialogBuilder} * {@link LightningDialogBuilder}
* @param event The event as it comes from the bus *
* @param event Bus event indicating that the user has clicked a bookmark
*/ */
@Subscribe @Subscribe
public void loadUrlInCurrentTab(final BrowserEvents.OpenUrlInCurrentTab event) { public void loadUrlInCurrentTab(final BrowserEvents.OpenUrlInCurrentTab event) {
@ -1921,7 +1947,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* Load the given url in a new tab, used by the the * Load the given url in a new tab, used by the the
* {@link acr.browser.lightning.fragment.BookmarksFragment} and by the * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the
* {@link LightningDialogBuilder} * {@link LightningDialogBuilder}
* @param event The event as it comes from the bus *
* @param event Bus event indicating that the user wishes
* to open a bookmark in a new tab
*/ */
@Subscribe @Subscribe
public void loadUrlInNewTab(final BrowserEvents.OpenUrlInNewTab event) { public void loadUrlInNewTab(final BrowserEvents.OpenUrlInNewTab event) {
@ -1934,13 +1962,13 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* message this receiver answer firing the * message this receiver answer firing the
* {@link acr.browser.lightning.bus.BrowserEvents.AddBookmark} message * {@link acr.browser.lightning.bus.BrowserEvents.AddBookmark} message
* *
* @param event basically a marker * @param event an event that the user wishes to bookmark the current page
*/ */
@Subscribe @Subscribe
public void bookmarkCurrentPage(final BookmarkEvents.WantToBookmarkCurrentPage event) { public void bookmarkCurrentPage(final BookmarkEvents.WantToBookmarkCurrentPage event) {
final LightningView currentTab = tabsManager.getCurrentTab(); final LightningView currentTab = tabsManager.getCurrentTab();
if (currentTab != null) { if (currentTab != null) {
eventBus.post(new BrowserEvents.AddBookmark(currentTab.getTitle(), currentTab.getUrl())); mEventBus.post(new BrowserEvents.AddBookmark(currentTab.getTitle(), currentTab.getUrl()));
} }
} }
@ -1948,7 +1976,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* This message is received when a bookmark was added by the * This message is received when a bookmark was added by the
* {@link acr.browser.lightning.fragment.BookmarksFragment} * {@link acr.browser.lightning.fragment.BookmarksFragment}
* *
* @param event a marker * @param event the event that a bookmark has been added
*/ */
@Subscribe @Subscribe
public void bookmarkAdded(final BookmarkEvents.Added event) { public void bookmarkAdded(final BookmarkEvents.Added event) {
@ -1956,9 +1984,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
/** /**
* This is received when the user edit a bookmark * This method is called when the user edits a bookmark.
* *
* @param event the event that the bookmark has changed * @param event the event that the bookmark has changed.
*/ */
@Subscribe @Subscribe
public void bookmarkChanged(final BookmarkEvents.BookmarkChanged event) { public void bookmarkChanged(final BookmarkEvents.BookmarkChanged event) {
@ -1969,12 +1997,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
currentTab.loadBookmarkpage(); currentTab.loadBookmarkpage();
} }
if (currentTab != null) { if (currentTab != null) {
eventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl()));
} }
} }
/** /**
* Notify the browser that a bookmark was deleted * Notify the browser that a bookmark was deleted.
* *
* @param event the event that the bookmark has been deleted * @param event the event that the bookmark has been deleted
*/ */
@ -1987,7 +2015,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
currentTab.loadBookmarkpage(); currentTab.loadBookmarkpage();
} }
if (currentTab != null) { if (currentTab != null) {
eventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl()));
} }
} }
@ -1996,7 +2024,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
* to {@link acr.browser.lightning.bus.BrowserEvents.UserPressedBack} message if the * to {@link acr.browser.lightning.bus.BrowserEvents.UserPressedBack} message if the
* fragement is showing the boomarks root folder. * fragement is showing the boomarks root folder.
* *
* @param event a marker * @param event an event notifying the browser that the bookmark drawer
* should be closed.
*/ */
@Subscribe @Subscribe
public void closeBookmarks(final BookmarkEvents.CloseBookmarks event) { public void closeBookmarks(final BookmarkEvents.CloseBookmarks event) {
@ -2086,7 +2115,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (url != null) { if (url != null) {
BrowserActivity.this.newTab(url, true); BrowserActivity.this.newTab(url, true);
Utils.showSnackbar(mActivity, R.string.deleted_tab); Utils.showSnackbar(BrowserActivity.this, R.string.deleted_tab);
} }
mPreferences.setSavedUrl(null); mPreferences.setSavedUrl(null);
} }
@ -2096,7 +2125,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
if (event.message != null) { if (event.message != null) {
Utils.showSnackbar(BrowserActivity.this, event.message); Utils.showSnackbar(BrowserActivity.this, event.message);
} else { } else {
Utils.showSnackbar(mActivity, event.stringRes); } Utils.showSnackbar(BrowserActivity.this, event.stringRes);
}
} }
}; };
} }

2
app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java

@ -37,7 +37,7 @@ public abstract class ThemableBrowserActivity extends AppCompatActivity {
} }
} }
public boolean isTablet() { boolean isTablet() {
return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
} }

2
app/src/main/java/acr/browser/lightning/app/BrowserApp.java

@ -26,7 +26,7 @@ public class BrowserApp extends Application {
return appComponent; return appComponent;
} }
public void buildDepencyGraph() { private void buildDepencyGraph() {
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
} }

52
app/src/main/java/acr/browser/lightning/async/AsyncExecutor.java

@ -0,0 +1,52 @@
package acr.browser.lightning.async;
import android.support.annotation.NonNull;
import android.util.Log;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
/**
* Created 9/27/2015 Anthony Restaino
*/
public class AsyncExecutor implements Executor {
private static final String TAG = AsyncExecutor.class.getSimpleName();
private static final AsyncExecutor INSTANCE = new AsyncExecutor();
private final Queue<Runnable> mQueue = new ArrayDeque<>(1);
private final ExecutorService mExecutor = Executors.newFixedThreadPool(4);
private AsyncExecutor() {}
public static AsyncExecutor getInstance() {
return INSTANCE;
}
public synchronized void notifyThreadFinish() {
if (mQueue.isEmpty()) {
return;
}
Runnable runnable = mQueue.remove();
execute(runnable);
}
@Override
protected void finalize() throws Throwable {
mExecutor.shutdownNow();
super.finalize();
}
@Override
public void execute(@NonNull Runnable command) {
try {
mExecutor.execute(command);
} catch (RejectedExecutionException ignored) {
mQueue.add(command);
Log.d(TAG, "Thread was enqueued");
}
}
}

57
app/src/main/java/acr/browser/lightning/utils/DownloadImageTask.java → app/src/main/java/acr/browser/lightning/async/ImageDownloadTask.java

@ -1,4 +1,4 @@
package acr.browser.lightning.utils; package acr.browser.lightning.async;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@ -11,33 +11,35 @@ import android.widget.ImageView;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.Utils;
/** public class ImageDownloadTask extends AsyncTask<Void, Void, Bitmap> {
* Created by Stefano Pacifici on 25/08/15.
*/
public class DownloadImageTask extends AsyncTask<Void, Void, Bitmap> {
final ImageView bmImage; private static final String TAG = ImageDownloadTask.class.getSimpleName();
final HistoryItem mWeb; private static final File mCacheDir = BrowserApp.getAppContext().getCacheDir();
final File mCacheDir; private final WeakReference<ImageView> bmImage;
final String mUrl; private final HistoryItem mWeb;
final Bitmap mDefaultBitmap; private final String mUrl;
private final Bitmap mDefaultBitmap;
public DownloadImageTask(@NonNull ImageView bmImage, @NonNull HistoryItem web, public ImageDownloadTask(@NonNull ImageView bmImage, @NonNull HistoryItem web, @NonNull Bitmap defaultBitmap) {
@NonNull Bitmap defaultBitmap) { // Set a tag on the ImageView so we know if the view
this.bmImage = bmImage; // has gone out of scope and should not be used
bmImage.setTag(web.getUrl().hashCode());
this.bmImage = new WeakReference<>(bmImage);
this.mWeb = web; this.mWeb = web;
this.mCacheDir = BrowserApp.getAppContext().getCacheDir();
this.mUrl = web.getUrl(); this.mUrl = web.getUrl();
this.mDefaultBitmap = defaultBitmap; this.mDefaultBitmap = defaultBitmap;
} }
@Override
protected Bitmap doInBackground(Void... params) { protected Bitmap doInBackground(Void... params) {
Bitmap mIcon = null; Bitmap mIcon = null;
// unique path for each url that is bookmarked. // unique path for each url that is bookmarked.
@ -60,6 +62,8 @@ public class DownloadImageTask extends AsyncTask<Void, Void, Bitmap> {
final URL urlDownload = new URL(urlDisplay); final URL urlDownload = new URL(urlDisplay);
final HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection(); final HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection();
connection.setDoInput(true); connection.setDoInput(true);
connection.setConnectTimeout(1000);
connection.setReadTimeout(1000);
connection.connect(); connection.connect();
in = connection.getInputStream(); in = connection.getInputStream();
@ -74,8 +78,8 @@ public class DownloadImageTask extends AsyncTask<Void, Void, Bitmap> {
Log.d(Constants.TAG, "Downloaded: " + urlDisplay); Log.d(Constants.TAG, "Downloaded: " + urlDisplay);
} }
} catch (Exception e) { } catch (Exception ignored) {
e.printStackTrace(); Log.d(TAG, "Could not download: " + urlDisplay);
} finally { } finally {
Utils.close(in); Utils.close(in);
Utils.close(fos); Utils.close(fos);
@ -89,10 +93,11 @@ public class DownloadImageTask extends AsyncTask<Void, Void, Bitmap> {
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
// if not, download it... // if not, download it...
final URL urlDownload = new URL("https://www.google.com/s2/favicons?domain_url=" final URL urlDownload = new URL("https://www.google.com/s2/favicons?domain_url=" + uri.toString());
+ uri.toString());
final HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection(); final HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection();
connection.setDoInput(true); connection.setDoInput(true);
connection.setConnectTimeout(1000);
connection.setReadTimeout(1000);
connection.connect(); connection.connect();
in = connection.getInputStream(); in = connection.getInputStream();
@ -107,7 +112,7 @@ public class DownloadImageTask extends AsyncTask<Void, Void, Bitmap> {
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); Log.d(TAG, "Could not download Google favicon");
} finally { } finally {
Utils.close(in); Utils.close(in);
Utils.close(fos); Utils.close(fos);
@ -120,10 +125,16 @@ public class DownloadImageTask extends AsyncTask<Void, Void, Bitmap> {
} }
} }
protected void onPostExecute(Bitmap result) { @Override
final Bitmap fav = Utils.padFavicon(result); protected void onPostExecute(Bitmap bitmap) {
bmImage.setImageBitmap(fav); super.onPostExecute(bitmap);
AsyncExecutor.getInstance().notifyThreadFinish();
final Bitmap fav = Utils.padFavicon(bitmap);
ImageView view = bmImage.get();
if (view != null && view.getTag().equals(mWeb.getUrl().hashCode())) {
view.setImageBitmap(fav);
}
mWeb.setBitmap(fav); mWeb.setBitmap(fav);
// notifyBookmarkDataSetChanged();
} }
} }

14
app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java

@ -20,7 +20,7 @@ import acr.browser.lightning.utils.Utils;
public final class BookmarkPage { public final class BookmarkPage {
public static final String HEADING = "<!DOCTYPE html><html xmlns=http://www.w3.org/1999/xhtml>\n" + private static final String HEADING = "<!DOCTYPE html><html xmlns=http://www.w3.org/1999/xhtml>\n" +
"<head>\n" + "<head>\n" +
"<meta content=en-us http-equiv=Content-Language />\n" + "<meta content=en-us http-equiv=Content-Language />\n" +
"<meta content='text/html; charset=utf-8' http-equiv=Content-Type />\n" + "<meta content='text/html; charset=utf-8' http-equiv=Content-Type />\n" +
@ -33,21 +33,21 @@ public final class BookmarkPage {
"width:130px;font-size: small;font-family: Arial, Helvetica, 'sans-serif';white-space:nowrap;overflow:hidden;text-align:left;vertical-align:middle;margin:auto;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.box a{width:100%;height:100%;position:absolute;left:0;top:0}img{vertical-align:middle;margin-right:10px;width:20px;height:20px;}.margin{margin:10px}</style>\n" + "width:130px;font-size: small;font-family: Arial, Helvetica, 'sans-serif';white-space:nowrap;overflow:hidden;text-align:left;vertical-align:middle;margin:auto;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.box a{width:100%;height:100%;position:absolute;left:0;top:0}img{vertical-align:middle;margin-right:10px;width:20px;height:20px;}.margin{margin:10px}</style>\n" +
"<body><div id=content>"; "<body><div id=content>";
public static final String PART1 = "<div class=box><a href='"; private static final String PART1 = "<div class=box><a href='";
public static final String PART2 = "'></a>\n" + private static final String PART2 = "'></a>\n" +
"<div class=margin>\n" + "<div class=margin>\n" +
"<div class=box-content>\n" + "<div class=box-content>\n" +
"<p class=ellipses>\n" + "<p class=ellipses>\n" +
"<img src='"; "<img src='";
public static final String PART3 = "http://www.google.com/s2/favicons?domain="; private static final String PART3 = "http://www.google.com/s2/favicons?domain=";
public static final String PART4 = "' />"; private static final String PART4 = "' />";
public static final String PART5 = "</p></div></div></div>"; private static final String PART5 = "</p></div></div></div>";
public static final String END = "</div></body></html>"; private static final String END = "</div></body></html>";
@Inject @Inject
BookmarkManager manager; BookmarkManager manager;

4
app/src/main/java/acr/browser/lightning/constant/Constants.java

@ -3,8 +3,6 @@
*/ */
package acr.browser.lightning.constant; package acr.browser.lightning.constant;
import android.os.Environment;
import acr.browser.lightning.BuildConfig; import acr.browser.lightning.BuildConfig;
public final class Constants { public final class Constants {
@ -27,8 +25,6 @@ public final class Constants {
public static final String HOMEPAGE = "about:home"; public static final String HOMEPAGE = "about:home";
public static final String BAIDU_SEARCH = "https://www.baidu.com/s?wd="; public static final String BAIDU_SEARCH = "https://www.baidu.com/s?wd=";
public static final String YANDEX_SEARCH = "https://yandex.ru/yandsearch?lr=21411&text="; public static final String YANDEX_SEARCH = "https://yandex.ru/yandsearch?lr=21411&text=";
public static final String EXTERNAL_STORAGE = Environment.getExternalStorageDirectory()
.toString();
public static final String JAVASCRIPT_INVERT_PAGE = "javascript:(function(){var e='img {-webkit-filter: invert(100%);'+'-moz-filter: invert(100%);'+'-o-filter: invert(100%);'+'-ms-filter: invert(100%); }',t=document.getElementsByTagName('head')[0],n=document.createElement('style');if(!window.counter){window.counter=1}else{window.counter++;if(window.counter%2==0){var e='html {-webkit-filter: invert(0%); -moz-filter: invert(0%); -o-filter: invert(0%); -ms-filter: invert(0%); }'}}n.type='text/css';if(n.styleSheet){n.styleSheet.cssText=e}else{n.appendChild(document.createTextNode(e))}t.appendChild(n)})();"; public static final String JAVASCRIPT_INVERT_PAGE = "javascript:(function(){var e='img {-webkit-filter: invert(100%);'+'-moz-filter: invert(100%);'+'-o-filter: invert(100%);'+'-ms-filter: invert(100%); }',t=document.getElementsByTagName('head')[0],n=document.createElement('style');if(!window.counter){window.counter=1}else{window.counter++;if(window.counter%2==0){var e='html {-webkit-filter: invert(0%); -moz-filter: invert(0%); -o-filter: invert(0%); -ms-filter: invert(0%); }'}}n.type='text/css';if(n.styleSheet){n.styleSheet.cssText=e}else{n.appendChild(document.createTextNode(e))}t.appendChild(n)})();";
public static final String JAVASCRIPT_TEXT_REFLOW = "javascript:document.getElementsByTagName('body')[0].style.width=window.innerWidth+'px';"; public static final String JAVASCRIPT_TEXT_REFLOW = "javascript:document.getElementsByTagName('body')[0].style.width=window.innerWidth+'px';";

8
app/src/main/java/acr/browser/lightning/constant/StartPage.java

@ -16,9 +16,9 @@ import acr.browser.lightning.utils.Utils;
public class StartPage { public class StartPage {
public static final String FILENAME = "homepage.html"; private static final String FILENAME = "homepage.html";
public static final String HEAD = "<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\">" private static final String HEAD = "<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ "<head>" + "<head>"
+ "<meta content=\"en-us\" http-equiv=\"Content-Language\" />" + "<meta content=\"en-us\" http-equiv=\"Content-Language\" />"
+ "<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" />" + "<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" />"
@ -41,11 +41,11 @@ public class StartPage {
+ "font-size: 12px;-moz-border-radius: 2px;-webkit-border-radius: 2px;" + "font-size: 12px;-moz-border-radius: 2px;-webkit-border-radius: 2px;"
+ "border-radius: 2px;}</style><body> <div class=\"outer\"><div class=\"middle\"><div class=\"inner\"><img class=\"smaller\" src=\""; + "border-radius: 2px;}</style><body> <div class=\"outer\"><div class=\"middle\"><div class=\"inner\"><img class=\"smaller\" src=\"";
public static final String MIDDLE = "\" ></br></br><form onsubmit=\"return search()\" class=\"search_bar\">" private static final String MIDDLE = "\" ></br></br><form onsubmit=\"return search()\" class=\"search_bar\">"
+ "<input type=\"submit\" id=\"search_submit\" value=\"Search\" ><span><input class=\"search\" type=\"text\" value=\"\" id=\"search_input\" >" + "<input type=\"submit\" id=\"search_submit\" value=\"Search\" ><span><input class=\"search\" type=\"text\" value=\"\" id=\"search_input\" >"
+ "</span></form></br></br></div></div></div><script type=\"text/javascript\">function search(){if(document.getElementById(\"search_input\").value != \"\"){window.location.href = \""; + "</span></form></br></br></div></div></div><script type=\"text/javascript\">function search(){if(document.getElementById(\"search_input\").value != \"\"){window.location.href = \"";
public static final String END = "\" + document.getElementById(\"search_input\").value;document.getElementById(\"search_input\").value = \"\";}return false;}</script></body></html>"; private static final String END = "\" + document.getElementById(\"search_input\").value;document.getElementById(\"search_input\").value = \"\";}return false;}</script></body></html>";
/** /**
* This method builds the homepage and returns the local URL to be loaded * This method builds the homepage and returns the local URL to be loaded

3
app/src/main/java/acr/browser/lightning/controller/BrowserController.java

@ -3,7 +3,6 @@
*/ */
package acr.browser.lightning.controller; package acr.browser.lightning.controller;
import android.app.Activity;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Message; import android.os.Message;
@ -38,8 +37,6 @@ public interface BrowserController {
void onCloseWindow(LightningView view); void onCloseWindow(LightningView view);
Activity getActivity();
void hideActionBar(); void hideActionBar();
void showActionBar(); void showActionBar();

145
app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java

@ -0,0 +1,145 @@
package acr.browser.lightning.database;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import acr.browser.lightning.utils.Utils;
public class BookmarkLocalSync {
private static final String TAG = BookmarkLocalSync.class.getSimpleName();
private static final String STOCK_BOOKMARKS_CONTENT = "content://browser/bookmarks";
private static final String CHROME_BOOKMARKS_CONTENT = "content://com.android.chrome.browser/bookmarks";
private static final String COLUMN_TITLE = "title";
private static final String COLUMN_URL = "url";
private static final String COLUMN_BOOKMARK = "bookmark";
private final Context mContext;
public BookmarkLocalSync(Context context) {
mContext = context;
}
@NonNull
@WorkerThread
public List<HistoryItem> getBookmarksFromStockBrowser() {
List<HistoryItem> list = new ArrayList<>();
if (!isStockSupported()) {
return list;
}
Cursor cursor = getStockCursor();
try {
if (cursor != null) {
for (int n = 0; n < cursor.getColumnCount(); n++) {
Log.d(TAG, cursor.getColumnName(n));
}
while (cursor.moveToNext()) {
if (cursor.getInt(2) == 1) {
String url = cursor.getString(0);
String title = cursor.getString(1);
if (url.isEmpty()) {
continue;
}
if (title == null || title.isEmpty()) {
title = Utils.getDomainName(url);
}
list.add(new HistoryItem(url, title));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
Utils.close(cursor);
return list;
}
@NonNull
@WorkerThread
public List<HistoryItem> getBookmarksFromChrome() {
List<HistoryItem> list = new ArrayList<>();
if (!isChromeSupported()) {
return list;
}
Cursor cursor = getStockCursor();
try {
if (cursor != null) {
for (int n = 0; n < cursor.getColumnCount(); n++) {
Log.d(TAG, cursor.getColumnName(n));
}
while (cursor.moveToNext()) {
if (cursor.getInt(2) == 1) {
String url = cursor.getString(0);
String title = cursor.getString(1);
if (url.isEmpty()) {
continue;
}
if (title == null || title.isEmpty()) {
title = Utils.getDomainName(url);
}
list.add(new HistoryItem(url, title));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
Utils.close(cursor);
return list;
}
@WorkerThread
public boolean isStockSupported() {
Cursor cursor = getStockCursor();
Utils.close(cursor);
return cursor != null;
}
@WorkerThread
public boolean isChromeSupported() {
Cursor cursor = getChromeCursor();
Utils.close(cursor);
return cursor != null;
}
@Nullable
@WorkerThread
private Cursor getChromeCursor() {
Cursor cursor;
Uri uri = Uri.parse(CHROME_BOOKMARKS_CONTENT);
try {
cursor = mContext.getContentResolver().query(uri,
new String[]{COLUMN_URL, COLUMN_TITLE, COLUMN_BOOKMARK}, null, null, null);
} catch (IllegalArgumentException e) {
return null;
}
return cursor;
}
@Nullable
@WorkerThread
private Cursor getStockCursor() {
Cursor cursor;
Uri uri = Uri.parse(STOCK_BOOKMARKS_CONTENT);
try {
cursor = mContext.getContentResolver().query(uri,
new String[]{COLUMN_URL, COLUMN_TITLE, COLUMN_BOOKMARK}, null, null, null);
} catch (IllegalArgumentException e) {
return null;
}
return cursor;
}
}

46
app/src/main/java/acr/browser/lightning/database/BookmarkManager.java

@ -66,7 +66,6 @@ public class BookmarkManager {
} }
/** /**
*
* @return true if the BookmarkManager was initialized, false otherwise * @return true if the BookmarkManager was initialized, false otherwise
*/ */
public boolean isReady() { public boolean isReady() {
@ -75,8 +74,9 @@ public class BookmarkManager {
/** /**
* Look for bookmark using the url * Look for bookmark using the url
* @param url the lookup url *
* @return the bookmark as an {@link HistoryItem} or null * @param url the lookup url
* @return the bookmark as an {@link HistoryItem} or null
*/ */
@Nullable @Nullable
public HistoryItem findBookmarkForUrl(final String url) { public HistoryItem findBookmarkForUrl(final String url) {
@ -87,7 +87,7 @@ public class BookmarkManager {
* Initialize the BookmarkManager, it's a one-time operation and will be executed asynchronously. * Initialize the BookmarkManager, it's a one-time operation and will be executed asynchronously.
* When done, mReady flag will been set to true. * When done, mReady flag will been set to true.
*/ */
private class BookmarkInitializer implements Runnable{ private class BookmarkInitializer implements Runnable {
private final Context mContext; private final Context mContext;
public BookmarkInitializer(Context context) { public BookmarkInitializer(Context context) {
@ -101,15 +101,14 @@ public class BookmarkManager {
final File bookmarksFile = new File(mFilesDir, FILE_BOOKMARKS); final File bookmarksFile = new File(mFilesDir, FILE_BOOKMARKS);
BufferedReader bookmarksReader = null; BufferedReader bookmarksReader = null;
InputStream inputStream = null;
try { try {
final InputStream inputStream;
if (bookmarksFile.exists() && bookmarksFile.isFile()) { if (bookmarksFile.exists() && bookmarksFile.isFile()) {
inputStream = new FileInputStream(bookmarksFile); inputStream = new FileInputStream(bookmarksFile);
} else { } else {
inputStream = mContext.getResources().openRawResource(R.raw.default_bookmarks); inputStream = mContext.getResources().openRawResource(R.raw.default_bookmarks);
} }
bookmarksReader = bookmarksReader = new BufferedReader(new InputStreamReader(inputStream));
new BufferedReader(new InputStreamReader(inputStream));
String line; String line;
while ((line = bookmarksReader.readLine()) != null) { while ((line = bookmarksReader.readLine()) != null) {
try { try {
@ -130,6 +129,7 @@ public class BookmarkManager {
Log.e(TAG, "Error reading the bookmarks file", e); Log.e(TAG, "Error reading the bookmarks file", e);
} finally { } finally {
Utils.close(bookmarksReader); Utils.close(bookmarksReader);
Utils.close(inputStream);
} }
mBookmarksMap = bookmarks; mBookmarksMap = bookmarks;
mReady = true; mReady = true;
@ -195,8 +195,8 @@ public class BookmarkManager {
* This method adds the the HistoryItem item to permanent bookmark storage.<br> * This method adds the the HistoryItem item to permanent bookmark storage.<br>
* This operation is blocking if the manager is still not ready. * This operation is blocking if the manager is still not ready.
* *
* @param item the item to add * @param item the item to add
* @return It returns true if the operation was successful. * @return It returns true if the operation was successful.
*/ */
public synchronized boolean addBookmark(@NonNull HistoryItem item) { public synchronized boolean addBookmark(@NonNull HistoryItem item) {
final String url = item.getUrl(); final String url = item.getUrl();
@ -213,7 +213,7 @@ public class BookmarkManager {
* *
* @param list the list of HistoryItems to add to bookmarks * @param list the list of HistoryItems to add to bookmarks
*/ */
private synchronized void addBookmarkList(List<HistoryItem> list) { public synchronized void addBookmarkList(List<HistoryItem> list) {
if (list == null || list.isEmpty()) { if (list == null || list.isEmpty()) {
return; return;
} }
@ -251,7 +251,7 @@ public class BookmarkManager {
if (newName.isEmpty()) { if (newName.isEmpty()) {
return; return;
} }
for (HistoryItem item: mBookmarksMap.values()) { for (HistoryItem item : mBookmarksMap.values()) {
if (item.getFolder().equals(oldName)) { if (item.getFolder().equals(oldName)) {
item.setFolder(newName); item.setFolder(newName);
} else if (item.isFolder() && item.getTitle().equals(oldName)) { } else if (item.isFolder() && item.getTitle().equals(oldName)) {
@ -269,7 +269,7 @@ public class BookmarkManager {
*/ */
public synchronized void deleteFolder(@NonNull String name) { public synchronized void deleteFolder(@NonNull String name) {
final Map<String, HistoryItem> bookmarks = new HashMap<>(); final Map<String, HistoryItem> bookmarks = new HashMap<>();
for (HistoryItem item: mBookmarksMap.values()) { for (HistoryItem item : mBookmarksMap.values()) {
final String url = item.getUrl(); final String url = item.getUrl();
if (item.isFolder()) { if (item.isFolder()) {
if (!item.getTitle().equals(name)) { if (!item.getTitle().equals(name)) {
@ -357,8 +357,7 @@ public class BookmarkManager {
* This is a disk-bound operation and should not be * This is a disk-bound operation and should not be
* done very frequently. * done very frequently.
* *
* @param sort force to sort the returned bookmarkList * @param sort force to sort the returned bookmarkList
*
* @return returns a list of bookmarks that can be sorted * @return returns a list of bookmarks that can be sorted
*/ */
public synchronized List<HistoryItem> getAllBookmarks(boolean sort) { public synchronized List<HistoryItem> getAllBookmarks(boolean sort) {
@ -385,7 +384,7 @@ public class BookmarkManager {
folder = ""; folder = "";
} }
mCurrentFolder = folder; mCurrentFolder = folder;
for (HistoryItem item: mBookmarksMap.values()) { for (HistoryItem item : mBookmarksMap.values()) {
if (item.getFolder().equals(folder)) if (item.getFolder().equals(folder))
bookmarks.add(item); bookmarks.add(item);
} }
@ -404,6 +403,15 @@ public class BookmarkManager {
return mCurrentFolder.isEmpty(); return mCurrentFolder.isEmpty();
} }
/**
* Returns the current folder
*
* @return the current folder
*/
public String getCurrentFolder() {
return mCurrentFolder;
}
/** /**
* Method is used internally for searching the bookmarks * Method is used internally for searching the bookmarks
* *
@ -411,7 +419,7 @@ public class BookmarkManager {
*/ */
private Set<String> getBookmarkUrls(List<HistoryItem> list) { private Set<String> getBookmarkUrls(List<HistoryItem> list) {
Set<String> set = new HashSet<>(); Set<String> set = new HashSet<>();
for (HistoryItem item: mBookmarksMap.values()) { for (HistoryItem item : mBookmarksMap.values()) {
if (!item.isFolder()) if (!item.isFolder())
set.add(item.getUrl()); set.add(item.getUrl());
} }
@ -425,9 +433,9 @@ public class BookmarkManager {
* *
* @return a list of all folders * @return a list of all folders
*/ */
public synchronized List<HistoryItem> getFolders(boolean sort) { private synchronized List<HistoryItem> getFolders(boolean sort) {
final HashMap<String, HistoryItem> folders = new HashMap<>(); final HashMap<String, HistoryItem> folders = new HashMap<>();
for (HistoryItem item: mBookmarksMap.values()) { for (HistoryItem item : mBookmarksMap.values()) {
final String folderName = item.getFolder(); final String folderName = item.getFolder();
if (folderName != null && !folderName.isEmpty() && !folders.containsKey(folderName)) { if (folderName != null && !folderName.isEmpty() && !folders.containsKey(folderName)) {
final HistoryItem folder = new HistoryItem(); final HistoryItem folder = new HistoryItem();
@ -453,7 +461,7 @@ public class BookmarkManager {
*/ */
public synchronized List<String> getFolderTitles() { public synchronized List<String> getFolderTitles() {
final Set<String> folders = new HashSet<>(); final Set<String> folders = new HashSet<>();
for (HistoryItem item: mBookmarksMap.values()) { for (HistoryItem item : mBookmarksMap.values()) {
final String folderName = item.getFolder(); final String folderName = item.getFolder();
if (folderName != null && !folderName.isEmpty()) { if (folderName != null && !folderName.isEmpty()) {
folders.add(folderName); folders.add(folderName);

18
app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java

@ -26,7 +26,7 @@ public class HistoryDatabase extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2; private static final int DATABASE_VERSION = 2;
// Database Name // Database Name
public static final String DATABASE_NAME = "historyManager"; private static final String DATABASE_NAME = "historyManager";
// HistoryItems table name // HistoryItems table name
private static final String TABLE_HISTORY = "history"; private static final String TABLE_HISTORY = "history";
@ -158,7 +158,6 @@ public class HistoryDatabase extends SQLiteOpenHelper {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
HistoryItem item = new HistoryItem(); HistoryItem item = new HistoryItem();
item.setID(Integer.parseInt(cursor.getString(0)));
item.setUrl(cursor.getString(1)); item.setUrl(cursor.getString(1));
item.setTitle(cursor.getString(2)); item.setTitle(cursor.getString(2));
item.setImageId(R.drawable.ic_history); item.setImageId(R.drawable.ic_history);
@ -183,7 +182,6 @@ public class HistoryDatabase extends SQLiteOpenHelper {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
HistoryItem item = new HistoryItem(); HistoryItem item = new HistoryItem();
item.setID(Integer.parseInt(cursor.getString(0)));
item.setUrl(cursor.getString(1)); item.setUrl(cursor.getString(1));
item.setTitle(cursor.getString(2)); item.setTitle(cursor.getString(2));
item.setImageId(R.drawable.ic_history); item.setImageId(R.drawable.ic_history);
@ -208,7 +206,6 @@ public class HistoryDatabase extends SQLiteOpenHelper {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
HistoryItem item = new HistoryItem(); HistoryItem item = new HistoryItem();
item.setID(Integer.parseInt(cursor.getString(0)));
item.setUrl(cursor.getString(1)); item.setUrl(cursor.getString(1));
item.setTitle(cursor.getString(2)); item.setTitle(cursor.getString(2));
item.setImageId(R.drawable.ic_history); item.setImageId(R.drawable.ic_history);
@ -220,19 +217,6 @@ public class HistoryDatabase extends SQLiteOpenHelper {
return itemList; return itemList;
} }
public synchronized int updateHistoryItem(HistoryItem item) {
mLock = true;
openIfNecessary();
ContentValues values = new ContentValues();
values.put(KEY_URL, item.getUrl());
values.put(KEY_TITLE, item.getTitle());
values.put(KEY_TIME_VISITED, System.currentTimeMillis());
int update = mDatabase.update(TABLE_HISTORY, values, KEY_ID + " = ?",
new String[]{String.valueOf(item.getId())});
mLock = false;
return update;
}
public int getHistoryItemsCount() { public int getHistoryItemsCount() {
mLock = true; mLock = true;
openIfNecessary(); openIfNecessary();

74
app/src/main/java/acr/browser/lightning/database/HistoryItem.java

@ -5,23 +5,29 @@ package acr.browser.lightning.database;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public class HistoryItem implements Comparable<HistoryItem> { public class HistoryItem implements Comparable<HistoryItem> {
// private variables // private variables
private int mId = 0; @NonNull
private String mUrl = ""; private String mUrl = "";
@NonNull
private String mTitle = ""; private String mTitle = "";
@NonNull
private String mFolder = ""; private String mFolder = "";
@Nullable
private Bitmap mBitmap = null; private Bitmap mBitmap = null;
private int mImageId = 0; private int mImageId = 0;
private int mOrder = 0; private int mOrder = 0;
private boolean mIsFolder = false; private boolean mIsFolder = false;
// Empty constructor // Empty constructor
public HistoryItem() { public HistoryItem() {}
}
public HistoryItem(HistoryItem item) { public HistoryItem(HistoryItem item) {
this.mUrl = item.mUrl; this.mUrl = item.mUrl;
@ -32,42 +38,24 @@ public class HistoryItem implements Comparable<HistoryItem> {
} }
// constructor // constructor
public HistoryItem(int id, String url, String title) { public HistoryItem(@NonNull String url, @NonNull String title) {
this.mId = id;
this.mUrl = url;
this.mTitle = title;
this.mBitmap = null;
}
// constructor
public HistoryItem(String url, String title) {
this.mUrl = url; this.mUrl = url;
this.mTitle = title; this.mTitle = title;
this.mBitmap = null; this.mBitmap = null;
} }
// constructor // constructor
public HistoryItem(String url, String title, int imageId) { public HistoryItem(@NonNull String url, @NonNull String title, int imageId) {
this.mUrl = url; this.mUrl = url;
this.mTitle = title; this.mTitle = title;
this.mBitmap = null; this.mBitmap = null;
this.mImageId = imageId; this.mImageId = imageId;
} }
// getting ID
public int getId() {
return this.mId;
}
public int getImageId() { public int getImageId() {
return this.mImageId; return this.mImageId;
} }
// setting id
public void setID(int id) {
this.mId = id;
}
public void setImageId(int id) { public void setImageId(int id) {
this.mImageId = id; this.mImageId = id;
} }
@ -131,40 +119,34 @@ public class HistoryItem implements Comparable<HistoryItem> {
@Override @Override
public int compareTo(@NonNull HistoryItem another) { public int compareTo(@NonNull HistoryItem another) {
return mTitle.compareTo(another.mTitle); int compare = this.mTitle.compareTo(another.mTitle);
if (compare == 0) {
return this.mUrl.compareTo(another.mUrl);
}
return compare;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object object) {
if (this == o) { if (this == object) return true;
return true; if (object == null) return false;
} if (!(object instanceof HistoryItem)) return false;
if (o == null || ((Object) this).getClass() != o.getClass()) {
return false;
}
HistoryItem that = (HistoryItem) o; HistoryItem that = (HistoryItem) object;
if (mId != that.mId) { return mImageId == that.mImageId &&
return false; this.mTitle.equals(that.mTitle) && this.mUrl.equals(that.mUrl) &&
} this.mFolder.equals(that.mFolder);
if (mImageId != that.mImageId) {
return false;
}
if (mBitmap != null ? !mBitmap.equals(that.mBitmap) : that.mBitmap != null) {
return false;
}
return mTitle.equals(that.mTitle) && mUrl.equals(that.mUrl);
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = mId; int result = mUrl.hashCode();
result = 31 * result + mUrl.hashCode(); result = 31 * result + mImageId;
result = 31 * result + mTitle.hashCode(); result = 31 * result + mTitle.hashCode();
result = 31 * result + (mBitmap != null ? mBitmap.hashCode() : 0); result = 32 * result + mFolder.hashCode();
result = 31 * result + mImageId; result = 31 * result + mImageId;
return result; return result;

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

@ -114,7 +114,7 @@ public class LightningDialogBuilder {
.show(); .show();
} }
public void showEditBookmarkDialog(final Context context, final HistoryItem item) { private void showEditBookmarkDialog(final Context context, final HistoryItem item) {
final AlertDialog.Builder editBookmarkDialog = new AlertDialog.Builder(context); final AlertDialog.Builder editBookmarkDialog = new AlertDialog.Builder(context);
editBookmarkDialog.setTitle(R.string.title_edit_bookmark); editBookmarkDialog.setTitle(R.string.title_edit_bookmark);
final View dialogLayout = View.inflate(context, R.layout.dialog_edit_bookmark, null); final View dialogLayout = View.inflate(context, R.layout.dialog_edit_bookmark, null);
@ -178,7 +178,7 @@ public class LightningDialogBuilder {
.show(); .show();
} }
public void showRenameFolderDialog(final Context context, final HistoryItem item) { private void showRenameFolderDialog(final Context context, final HistoryItem item) {
// assert item.isFolder(); // assert item.isFolder();
final AlertDialog.Builder editFolderDialog = new AlertDialog.Builder(context); final AlertDialog.Builder editFolderDialog = new AlertDialog.Builder(context);
editFolderDialog.setTitle(R.string.title_rename_folder); editFolderDialog.setTitle(R.string.title_rename_folder);

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

@ -19,8 +19,12 @@ import android.util.Log;
import android.webkit.CookieManager; import android.webkit.CookieManager;
import android.webkit.URLUtil; import android.webkit.URLUtil;
import java.io.File;
import java.io.IOException;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
@ -29,7 +33,12 @@ import acr.browser.lightning.utils.Utils;
*/ */
public class DownloadHandler { public class DownloadHandler {
private static final String LOGTAG = "DLHandler"; private static final String TAG = DownloadHandler.class.getSimpleName();
private static final String COOKIE_REQUEST_HEADER = "Cookie";
public static final String DEFAULT_DOWNLOAD_PATH =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.getPath();
/** /**
@ -153,7 +162,8 @@ public class DownloadHandler {
} catch (Exception e) { } catch (Exception e) {
// This only happens for very bad urls, we want to catch the // This only happens for very bad urls, we want to catch the
// exception here // exception here
Log.e(LOGTAG, "Exception while trying to parse url '" + url + '\'', e); Log.e(TAG, "Exception while trying to parse url '" + url + '\'', e);
Utils.showSnackbar(activity, R.string.problem_download);
return; return;
} }
@ -172,15 +182,37 @@ public class DownloadHandler {
// depending on mimetype? // depending on mimetype?
String location = BrowserApp.getAppComponent().getPreferenceManager().getDownloadDirectory(); String location = BrowserApp.getAppComponent().getPreferenceManager().getDownloadDirectory();
request.setDestinationInExternalPublicDir(location, filename); Uri downloadFolder;
if (location != null) {
location = addNecessarySlashes(location);
downloadFolder = Uri.parse(location);
} else {
location = addNecessarySlashes(DEFAULT_DOWNLOAD_PATH);
downloadFolder = Uri.parse(location);
BrowserApp.getAppComponent().getPreferenceManager().setDownloadDirectory(location);
}
File dir = new File(downloadFolder.getPath());
if (!dir.isDirectory() && !dir.mkdirs()) {
// Cannot make the directory
Utils.showSnackbar(activity, R.string.problem_location_download);
return;
}
if (!isWriteAccessAvailable(downloadFolder)) {
Utils.showSnackbar(activity, R.string.problem_location_download);
return;
}
request.setDestinationUri(Uri.parse(Constants.FILE + location + filename));
// let this downloaded file be scanned by MediaScanner - so that it can // let this downloaded file be scanned by MediaScanner - so that it can
// show up in Gallery app, for example. // show up in Gallery app, for example.
request.setVisibleInDownloadsUi(true);
request.allowScanningByMediaScanner(); request.allowScanningByMediaScanner();
request.setDescription(webAddress.getHost()); request.setDescription(webAddress.getHost());
// XXX: Have to use the old url since the cookies were stored using the // XXX: Have to use the old url since the cookies were stored using the
// old percent-encoded url. // old percent-encoded url.
String cookies = CookieManager.getInstance().getCookie(url); String cookies = CookieManager.getInstance().getCookie(url);
request.addRequestHeader("cookie", cookies); request.addRequestHeader(COOKIE_REQUEST_HEADER, cookies);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (mimetype == null) { if (mimetype == null) {
if (TextUtils.isEmpty(addressString)) { if (TextUtils.isEmpty(addressString)) {
@ -192,7 +224,7 @@ public class DownloadHandler {
} else { } else {
final DownloadManager manager = (DownloadManager) activity final DownloadManager manager = (DownloadManager) activity
.getSystemService(Context.DOWNLOAD_SERVICE); .getSystemService(Context.DOWNLOAD_SERVICE);
new Thread("Browser download") { new Thread() {
@Override @Override
public void run() { public void run() {
try { try {
@ -201,11 +233,107 @@ public class DownloadHandler {
// Probably got a bad URL or something // Probably got a bad URL or something
e.printStackTrace(); e.printStackTrace();
Utils.showSnackbar(activity, R.string.cannot_download); Utils.showSnackbar(activity, R.string.cannot_download);
} catch (SecurityException e) {
// TODO write a download utility that downloads files rather than rely on the system
// because the system can only handle Environment.getExternal... as a path
Utils.showSnackbar(activity, R.string.problem_location_download);
} }
} }
}.start(); }.start();
Utils.showSnackbar(activity, R.string.download_pending); Utils.showSnackbar(activity, activity.getString(R.string.download_pending) + ' ' + filename);
}
}
private static final String sFileName = "test";
private static final String sFileExtension = ".txt";
/**
* Determine whether there is write access in the given directory. Returns false if a
* file cannot be created in the directory or if the directory does not exist.
*
* @param directory the directory to check for write access
* @return returns true if the directory can be written to or is in a directory that can
* be written to. false if there is no write access.
*/
public static boolean isWriteAccessAvailable(String directory) {
if (directory == null || directory.isEmpty()) {
return false;
}
String dir = addNecessarySlashes(directory);
dir = getFirstRealParentDirectory(dir);
File file = new File(dir + sFileName + sFileExtension);
for (int n = 0; n < 100; n++) {
if (!file.exists()) {
try {
if (file.createNewFile()) {
file.delete();
}
return true;
} catch (IOException ignored) {
return false;
}
} else {
file = new File(dir + sFileName + '-' + n + sFileExtension);
}
}
return file.canWrite();
}
/**
* Returns the first parent directory of a directory that exists. This is useful
* for subdirectories that do not exist but their parents do.
*
* @param directory the directory to find the first existent parent
* @return the first existent parent
*/
private static String getFirstRealParentDirectory(String directory) {
if (directory == null || directory.isEmpty()) {
return "/";
}
directory = addNecessarySlashes(directory);
File file = new File(directory);
if (!file.isDirectory()) {
int indexSlash = directory.lastIndexOf('/');
if (indexSlash > 0) {
String parent = directory.substring(0, indexSlash);
int previousIndex = parent.lastIndexOf('/');
if (previousIndex > 0) {
return getFirstRealParentDirectory(parent.substring(0, previousIndex));
} else {
return "/";
}
} else {
return "/";
}
} else {
return directory;
} }
}
private static boolean isWriteAccessAvailable(Uri fileUri) {
File file = new File(fileUri.getPath());
try {
if (file.createNewFile()) {
file.delete();
}
return true;
} catch (IOException ignored) {
return false;
}
}
public static String addNecessarySlashes(String originalPath) {
if (originalPath == null || originalPath.length() == 0) {
return "/";
} }
if (originalPath.charAt(originalPath.length() - 1) != '/') {
originalPath = originalPath + '/';
}
if (originalPath.charAt(0) != '/') {
originalPath = '/' + originalPath;
}
return originalPath;
}
} }

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

@ -25,9 +25,9 @@ import acr.browser.lightning.utils.Utils;
* just clicks on the link, we will do the same steps of correcting the mimetype * just clicks on the link, we will do the same steps of correcting the mimetype
* down in android.os.webkit.LoadListener rather than handling it here. * down in android.os.webkit.LoadListener rather than handling it here.
*/ */
public class FetchUrlMimeType extends Thread { class FetchUrlMimeType extends Thread {
private final Context mContext; private final Activity mActivity;
private final DownloadManager.Request mRequest; private final DownloadManager.Request mRequest;
@ -39,12 +39,11 @@ public class FetchUrlMimeType extends Thread {
public FetchUrlMimeType(Activity activity, DownloadManager.Request request, String uri, public FetchUrlMimeType(Activity activity, DownloadManager.Request request, String uri,
String cookies, String userAgent) { String cookies, String userAgent) {
mContext = activity.getApplicationContext(); mActivity = activity;
mRequest = request; mRequest = request;
mUri = uri; mUri = uri;
mCookies = cookies; mCookies = cookies;
mUserAgent = userAgent; mUserAgent = userAgent;
Utils.showSnackbar(activity, R.string.download_pending);
} }
@Override @Override
@ -87,6 +86,7 @@ public class FetchUrlMimeType extends Thread {
connection.disconnect(); connection.disconnect();
} }
String filename = "";
if (mimeType != null) { if (mimeType != null) {
if (mimeType.equalsIgnoreCase("text/plain") if (mimeType.equalsIgnoreCase("text/plain")
|| mimeType.equalsIgnoreCase("application/octet-stream")) { || mimeType.equalsIgnoreCase("application/octet-stream")) {
@ -96,13 +96,14 @@ public class FetchUrlMimeType extends Thread {
mRequest.setMimeType(newMimeType); mRequest.setMimeType(newMimeType);
} }
} }
String filename = URLUtil.guessFileName(mUri, contentDisposition, mimeType); filename = URLUtil.guessFileName(mUri, contentDisposition, mimeType);
mRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename); mRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
} }
// Start the download // Start the download
DownloadManager manager = (DownloadManager) mContext DownloadManager manager = (DownloadManager) mActivity
.getSystemService(Context.DOWNLOAD_SERVICE); .getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(mRequest); manager.enqueue(mRequest);
Utils.showSnackbar(mActivity, mActivity.getString(R.string.download_pending) + ' ' + filename);
} }
} }

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

@ -3,6 +3,8 @@
*/ */
package acr.browser.lightning.download; package acr.browser.lightning.download;
import android.support.annotation.NonNull;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -11,28 +13,28 @@ import static android.util.Patterns.GOOD_IRI_CHAR;
/** /**
* Web Address Parser * Web Address Parser
* * <p/>
* This is called WebAddress, rather than URL or URI, because it attempts to * This is called WebAddress, rather than URL or URI, because it attempts to
* parse the stuff that a user will actually type into a browser address widget. * parse the stuff that a user will actually type into a browser address widget.
* * <p/>
* Unlike java.net.uri, this parser will not choke on URIs missing schemes. It * Unlike java.net.uri, this parser will not choke on URIs missing schemes. It
* will only throw a ParseException if the input is really hosed. * will only throw a ParseException if the input is really hosed.
* * <p/>
* If given an https scheme but no port, fills in port * If given an https scheme but no port, fills in port
*/ */
public class WebAddress { class WebAddress {
private String mScheme; private String mScheme;
private String mHost; private String mHost;
private int mPort; private int mPort;
private String mPath; private String mPath;
private String mAuthInfo; private String mAuthInfo;
static final int MATCH_GROUP_SCHEME = 1; private static final int MATCH_GROUP_SCHEME = 1;
static final int MATCH_GROUP_AUTHORITY = 2; private static final int MATCH_GROUP_AUTHORITY = 2;
static final int MATCH_GROUP_HOST = 3; private static final int MATCH_GROUP_HOST = 3;
static final int MATCH_GROUP_PORT = 4; private static final int MATCH_GROUP_PORT = 4;
static final int MATCH_GROUP_PATH = 5; private static final int MATCH_GROUP_PATH = 5;
static final Pattern sAddressPattern = Pattern.compile( private static final Pattern sAddressPattern = Pattern.compile(
/* scheme */"(?:(http|https|file)\\:\\/\\/)?" + /* scheme */"(?:(http|https|file)\\:\\/\\/)?" +
/* authority */"(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" + /* authority */"(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
/* host */"([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" + /* host */"([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" +
@ -43,7 +45,7 @@ public class WebAddress {
/** /**
* Parses given URI-like string. * Parses given URI-like string.
*/ */
public WebAddress(String address) { public WebAddress(String address) throws IllegalArgumentException {
if (address == null) { if (address == null) {
throw new IllegalArgumentException("address can't be null"); throw new IllegalArgumentException("address can't be null");
@ -134,7 +136,7 @@ public class WebAddress {
return mScheme; return mScheme;
} }
public void setHost(String host) { public void setHost(@NonNull String host) {
mHost = host; mHost = host;
} }

60
app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java

@ -8,7 +8,6 @@ import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import java.util.Arrays; import java.util.Arrays;
@ -16,7 +15,6 @@ import java.util.List;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.preference.PreferenceManager;
public class AdvancedSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { public class AdvancedSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
@ -62,7 +60,7 @@ public class AdvancedSettingsFragment extends LightningPreferenceFragment implem
cbcookiesInkognito.setOnPreferenceChangeListener(this); cbcookiesInkognito.setOnPreferenceChangeListener(this);
cbrestoreTabs.setOnPreferenceChangeListener(this); cbrestoreTabs.setOnPreferenceChangeListener(this);
switch (preferenceManager.getRenderingMode()) { switch (mPreferenceManager.getRenderingMode()) {
case 0: case 0:
renderingmode.setSummary(getString(R.string.name_normal)); renderingmode.setSummary(getString(R.string.name_normal));
break; break;
@ -77,16 +75,16 @@ public class AdvancedSettingsFragment extends LightningPreferenceFragment implem
break; break;
} }
textEncoding.setSummary(preferenceManager.getTextEncoding()); textEncoding.setSummary(mPreferenceManager.getTextEncoding());
mUrlOptions = getResources().getStringArray(R.array.url_content_array); mUrlOptions = getResources().getStringArray(R.array.url_content_array);
int option = preferenceManager.getUrlBoxContentChoice(); int option = mPreferenceManager.getUrlBoxContentChoice();
urlcontent.setSummary(mUrlOptions[option]); urlcontent.setSummary(mUrlOptions[option]);
cbAllowPopups.setChecked(preferenceManager.getPopupsEnabled()); cbAllowPopups.setChecked(mPreferenceManager.getPopupsEnabled());
cbenablecookies.setChecked(preferenceManager.getCookiesEnabled()); cbenablecookies.setChecked(mPreferenceManager.getCookiesEnabled());
cbcookiesInkognito.setChecked(preferenceManager.getIncognitoCookiesEnabled()); cbcookiesInkognito.setChecked(mPreferenceManager.getIncognitoCookiesEnabled());
cbrestoreTabs.setChecked(preferenceManager.getRestoreLostTabsEnabled()); cbrestoreTabs.setChecked(mPreferenceManager.getRestoreLostTabsEnabled());
} }
@Override @Override
@ -111,19 +109,19 @@ public class AdvancedSettingsFragment extends LightningPreferenceFragment implem
// switch preferences // switch preferences
switch (preference.getKey()) { switch (preference.getKey()) {
case SETTINGS_NEWWINDOW: case SETTINGS_NEWWINDOW:
preferenceManager.setPopupsEnabled((Boolean) newValue); mPreferenceManager.setPopupsEnabled((Boolean) newValue);
cbAllowPopups.setChecked((Boolean) newValue); cbAllowPopups.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_ENABLECOOKIES: case SETTINGS_ENABLECOOKIES:
preferenceManager.setCookiesEnabled((Boolean) newValue); mPreferenceManager.setCookiesEnabled((Boolean) newValue);
cbenablecookies.setChecked((Boolean) newValue); cbenablecookies.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_COOKIESINKOGNITO: case SETTINGS_COOKIESINKOGNITO:
preferenceManager.setIncognitoCookiesEnabled((Boolean) newValue); mPreferenceManager.setIncognitoCookiesEnabled((Boolean) newValue);
cbcookiesInkognito.setChecked((Boolean) newValue); cbcookiesInkognito.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_RESTORETABS: case SETTINGS_RESTORETABS:
preferenceManager.setRestoreLostTabsEnabled((Boolean) newValue); mPreferenceManager.setRestoreLostTabsEnabled((Boolean) newValue);
cbrestoreTabs.setChecked((Boolean) newValue); cbrestoreTabs.setChecked((Boolean) newValue);
return true; return true;
default: default:
@ -139,12 +137,12 @@ public class AdvancedSettingsFragment extends LightningPreferenceFragment implem
mActivity.getString(R.string.name_grayscale), mActivity.getString(R.string.name_grayscale),
mActivity.getString(R.string.name_inverted_grayscale)}; mActivity.getString(R.string.name_inverted_grayscale)};
int n = preferenceManager.getRenderingMode(); int n = mPreferenceManager.getRenderingMode();
picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() { picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
preferenceManager.setRenderingMode(which); mPreferenceManager.setRenderingMode(which);
switch (which) { switch (which) {
case 0: case 0:
renderingmode.setSummary(getString(R.string.name_normal)); renderingmode.setSummary(getString(R.string.name_normal));
@ -161,13 +159,7 @@ public class AdvancedSettingsFragment extends LightningPreferenceFragment implem
} }
} }
}); });
picker.setNeutralButton(getResources().getString(R.string.action_ok), picker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show(); picker.show();
} }
@ -175,22 +167,16 @@ public class AdvancedSettingsFragment extends LightningPreferenceFragment implem
AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); AlertDialog.Builder picker = new AlertDialog.Builder(mActivity);
picker.setTitle(getResources().getString(R.string.text_encoding)); picker.setTitle(getResources().getString(R.string.text_encoding));
final List<String> textEncodingList = Arrays.asList(Constants.TEXT_ENCODINGS); final List<String> textEncodingList = Arrays.asList(Constants.TEXT_ENCODINGS);
int n = textEncodingList.indexOf(preferenceManager.getTextEncoding()); int n = textEncodingList.indexOf(mPreferenceManager.getTextEncoding());
picker.setSingleChoiceItems(Constants.TEXT_ENCODINGS, n, new DialogInterface.OnClickListener() { picker.setSingleChoiceItems(Constants.TEXT_ENCODINGS, n, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
preferenceManager.setTextEncoding(Constants.TEXT_ENCODINGS[which]); mPreferenceManager.setTextEncoding(Constants.TEXT_ENCODINGS[which]);
textEncoding.setSummary(Constants.TEXT_ENCODINGS[which]); textEncoding.setSummary(Constants.TEXT_ENCODINGS[which]);
} }
}); });
picker.setNeutralButton(getResources().getString(R.string.action_ok), picker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show(); picker.show();
} }
@ -198,24 +184,18 @@ public class AdvancedSettingsFragment extends LightningPreferenceFragment implem
AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); AlertDialog.Builder picker = new AlertDialog.Builder(mActivity);
picker.setTitle(getResources().getString(R.string.url_contents)); picker.setTitle(getResources().getString(R.string.url_contents));
int n = preferenceManager.getUrlBoxContentChoice(); int n = mPreferenceManager.getUrlBoxContentChoice();
picker.setSingleChoiceItems(mUrlOptions, n, new DialogInterface.OnClickListener() { picker.setSingleChoiceItems(mUrlOptions, n, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
preferenceManager.setUrlBoxContentChoice(which); mPreferenceManager.setUrlBoxContentChoice(which);
if (which < mUrlOptions.length) { if (which < mUrlOptions.length) {
urlcontent.setSummary(mUrlOptions[which]); urlcontent.setSummary(mUrlOptions[which]);
} }
} }
}); });
picker.setNeutralButton(getResources().getString(R.string.action_ok), picker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show(); picker.show();
} }
} }

62
app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java

@ -6,6 +6,7 @@ package acr.browser.lightning.fragment;
import android.Manifest; import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
@ -16,29 +17,64 @@ import android.support.v7.app.AlertDialog;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.database.BookmarkLocalSync;
import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.BookmarkManager;
import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.PermissionsManager; import acr.browser.lightning.utils.PermissionsManager;
import acr.browser.lightning.utils.Utils;
public class BookmarkSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener { public class BookmarkSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener {
private static final String SETTINGS_EXPORT = "export_bookmark"; private static final String SETTINGS_EXPORT = "export_bookmark";
private static final String SETTINGS_IMPORT = "import_bookmark"; private static final String SETTINGS_IMPORT = "import_bookmark";
private static final String SETTINGS_IMPORT_BROWSER = "import_browser";
private Activity mActivity; private Activity mActivity;
@Inject BookmarkManager mBookmarkManager; @Inject
BookmarkManager mBookmarkManager;
private File[] mFileList; private File[] mFileList;
private String[] mFileNameList; private String[] mFileNameList;
private BookmarkLocalSync mSync;
private static final String[] REQUIRED_PERMISSIONS = new String[]{ private static final String[] REQUIRED_PERMISSIONS = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE Manifest.permission.WRITE_EXTERNAL_STORAGE
}; };
private static final File mPath = new File(Environment.getExternalStorageDirectory().toString()); private static final File mPath = new File(Environment.getExternalStorageDirectory().toString());
private class ImportBookmarksTask extends AsyncTask<Void, Void, Integer> {
@Override
protected Integer doInBackground(Void... params) {
List<HistoryItem> list = null;
if (mSync.isStockSupported()) {
list = mSync.getBookmarksFromStockBrowser();
} else if (mSync.isChromeSupported()) {
list = mSync.getBookmarksFromChrome();
}
int count = 0;
if (list != null && !list.isEmpty()) {
mBookmarkManager.addBookmarkList(list);
count = list.size();
}
return count;
}
@Override
protected void onPostExecute(Integer num) {
super.onPostExecute(num);
if (mActivity != null) {
int number = num;
final String message = mActivity.getResources().getString(R.string.message_import);
Utils.showSnackbar(mActivity, number + " " + message);
}
}
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -56,15 +92,34 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
} }
} }
@Override
public void onDestroy() {
super.onDestroy();
mActivity = null;
}
private void initPrefs() { private void initPrefs() {
Preference exportpref = findPreference(SETTINGS_EXPORT); Preference exportpref = findPreference(SETTINGS_EXPORT);
Preference importpref = findPreference(SETTINGS_IMPORT); Preference importpref = findPreference(SETTINGS_IMPORT);
mSync = new BookmarkLocalSync(mActivity);
exportpref.setOnPreferenceClickListener(this); exportpref.setOnPreferenceClickListener(this);
importpref.setOnPreferenceClickListener(this); importpref.setOnPreferenceClickListener(this);
new Thread(mInitializeImportPreference).start();
} }
private final Runnable mInitializeImportPreference = new Runnable() {
@Override
public void run() {
Preference importStock = findPreference(SETTINGS_IMPORT_BROWSER);
importStock.setEnabled(mSync.isStockSupported() || mSync.isChromeSupported());
importStock.setOnPreferenceClickListener(BookmarkSettingsFragment.this);
}
};
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
switch (preference.getKey()) { switch (preference.getKey()) {
@ -79,6 +134,9 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
createDialog(); createDialog();
} }
return true; return true;
case SETTINGS_IMPORT_BROWSER:
new ImportBookmarksTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return true;
default: default:
return false; return false;
} }
@ -114,7 +172,7 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
} }
} }
private class SortName implements Comparator<File> { private static class SortName implements Comparator<File> {
@Override @Override
public int compare(File a, File b) { public int compare(File a, File b) {

93
app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java

@ -3,7 +3,6 @@ package acr.browser.lightning.fragment;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.IdRes; import android.support.annotation.IdRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -38,13 +37,14 @@ import javax.inject.Inject;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.activity.BrowserActivity; import acr.browser.lightning.activity.BrowserActivity;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.async.AsyncExecutor;
import acr.browser.lightning.bus.BookmarkEvents; import acr.browser.lightning.bus.BookmarkEvents;
import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.bus.BrowserEvents;
import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.BookmarkManager;
import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.dialog.LightningDialogBuilder; import acr.browser.lightning.dialog.LightningDialogBuilder;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.DownloadImageTask; import acr.browser.lightning.async.ImageDownloadTask;
import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.ThemeUtils;
/** /**
@ -58,14 +58,14 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
// Event bus // Event bus
@Inject @Inject
Bus eventBus; Bus mEventBus;
// Dialog builder // Dialog builder
@Inject @Inject
LightningDialogBuilder bookmarksDialogBuilder; LightningDialogBuilder mBookmarksDialogBuilder;
@Inject @Inject
PreferenceManager preferenceManager; PreferenceManager mPreferenceManager;
// Adapter // Adapter
private BookmarkViewAdapter mBookmarkAdapter; private BookmarkViewAdapter mBookmarkAdapter;
@ -81,10 +81,10 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
private ImageView mBookmarkTitleImage, mBookmarkImage; private ImageView mBookmarkTitleImage, mBookmarkImage;
// Colors // Colors
private int mIconColor; private int mIconColor, mScrollIndex;
// Init asynchronously the bookmark manager // Init asynchronously the bookmark manager
private final Runnable initBookmarkManager = new Runnable() { private final Runnable mInitBookmarkManager = new Runnable() {
@Override @Override
public void run() { public void run() {
final Context context = getContext(); final Context context = getContext();
@ -101,20 +101,20 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
} }
// Handle bookmark click // Handle bookmark click
private final OnItemClickListener itemClickListener = new OnItemClickListener() { private final OnItemClickListener mItemClickListener = new OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final HistoryItem item = mBookmarks.get(position); final HistoryItem item = mBookmarks.get(position);
if (item.isFolder()) { if (item.isFolder()) {
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(item.getTitle(), true), mScrollIndex = mBookmarksListView.getFirstVisiblePosition();
true); setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(item.getTitle(), true), true);
} else { } else {
eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(item.getUrl())); mEventBus.post(new BrowserEvents.OpenUrlInCurrentTab(item.getUrl()));
} }
} }
}; };
private final OnItemLongClickListener itemLongClickListener = new OnItemLongClickListener() { private final OnItemLongClickListener mItemLongClickListener = new OnItemLongClickListener() {
@Override @Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
final HistoryItem item = mBookmarks.get(position); final HistoryItem item = mBookmarks.get(position);
@ -123,56 +123,62 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
} }
}; };
@Override
public void onResume() {
super.onResume();
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false);
}
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.bookmark_drawer, container, false); final View view = inflater.inflate(R.layout.bookmark_drawer, container, false);
mBookmarksListView = (ListView) view.findViewById(R.id.right_drawer_list); mBookmarksListView = (ListView) view.findViewById(R.id.right_drawer_list);
mBookmarksListView.setOnItemClickListener(itemClickListener); mBookmarksListView.setOnItemClickListener(mItemClickListener);
mBookmarksListView.setOnItemLongClickListener(itemLongClickListener); mBookmarksListView.setOnItemLongClickListener(mItemLongClickListener);
mBookmarkTitleImage = (ImageView) view.findViewById(R.id.starIcon); mBookmarkTitleImage = (ImageView) view.findViewById(R.id.starIcon);
mBookmarkImage = (ImageView) view.findViewById(R.id.icon_star); mBookmarkImage = (ImageView) view.findViewById(R.id.icon_star);
final View backView = view.findViewById(R.id.bookmark_back_button); final View backView = view.findViewById(R.id.bookmark_back_button);
backView.setOnClickListener(new View.OnClickListener() { backView.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (mBookmarkManager == null) if (mBookmarkManager == null) return;
return;
if (!mBookmarkManager.isRootFolder()) { if (!mBookmarkManager.isRootFolder()) {
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), true); setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), true);
mBookmarksListView.setSelection(mScrollIndex);
} }
} }
}); });
setupNavigationButton(view, R.id.action_add_bookmark, R.id.icon_star);
// Must be called here, only here we have a reference to the ListView // Must be called here, only here we have a reference to the ListView
new Thread(initBookmarkManager).run(); new Thread(mInitBookmarkManager).run();
return view; return view;
} }
@Override @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) { public void onActivityCreated(@Nullable Bundle savedInstanceState) {
// TODO this code depend way too much on BrowserActivity // TODO remove dependency on BrowserActivity
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
final BrowserActivity activity = (BrowserActivity) getActivity(); final BrowserActivity activity = (BrowserActivity) getActivity();
boolean darkTheme = preferenceManager.getUseTheme() != 0 || activity.isIncognito(); boolean darkTheme = mPreferenceManager.getUseTheme() != 0 || ((BrowserActivity) activity).isIncognito();
mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme); mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme);
mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme); mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme);
mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) : mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) :
ThemeUtils.getIconLightThemeColor(activity); ThemeUtils.getIconLightThemeColor(activity);
setupFrameLayoutButton(getView(), R.id.action_add_bookmark, R.id.icon_star);
mBookmarkTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); mBookmarkTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
eventBus.register(this); mEventBus.register(this);
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
eventBus.unregister(this); mEventBus.unregister(this);
} }
@Subscribe @Subscribe
@ -182,8 +188,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
mBookmarks.add(item); mBookmarks.add(item);
Collections.sort(mBookmarks, new BookmarkManager.SortIgnoreCase()); Collections.sort(mBookmarks, new BookmarkManager.SortIgnoreCase());
mBookmarkAdapter.notifyDataSetChanged(); mBookmarkAdapter.notifyDataSetChanged();
eventBus mEventBus.post(new BookmarkEvents.Added(item));
.post(new BookmarkEvents.Added(item));
updateBookmarkIndicator(event.url); updateBookmarkIndicator(event.url);
} }
} }
@ -195,12 +200,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
@Subscribe @Subscribe
public void bookmarkChanged(BookmarkEvents.BookmarkChanged event) { public void bookmarkChanged(BookmarkEvents.BookmarkChanged event) {
// final int size = mBookmarks.size(); String folder = mBookmarkManager.getCurrentFolder();
mBookmarks.remove(event.oldBookmark); setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(folder, true), false);
// assert mBookmarks.size() < size;
mBookmarks.add(event.newBookmark);
mBookmarkAdapter.notifyDataSetChanged();
Collections.sort(mBookmarks, new BookmarkManager.SortIgnoreCase());
} }
private void updateBookmarkIndicator(final String url) { private void updateBookmarkIndicator(final String url) {
@ -216,30 +217,33 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
@Subscribe @Subscribe
public void userPressedBack(final BrowserEvents.UserPressedBack event) { public void userPressedBack(final BrowserEvents.UserPressedBack event) {
if (mBookmarkManager.isRootFolder()) { if (mBookmarkManager.isRootFolder()) {
eventBus mEventBus.post(new BookmarkEvents.CloseBookmarks());
.post(new BookmarkEvents.CloseBookmarks());
} else { } else {
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), true); setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), true);
mBookmarksListView.setSelection(mScrollIndex);
} }
} }
@Subscribe @Subscribe
public void bookmarkDeleted(final BookmarkEvents.Deleted event) { public void bookmarkDeleted(final BookmarkEvents.Deleted event) {
// final int size = mBookmarks.size();
mBookmarks.remove(event.item); mBookmarks.remove(event.item);
// assert mBookmarks.size() < size; if (event.item.isFolder()) {
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false);
} else {
mBookmarkAdapter.notifyDataSetChanged(); mBookmarkAdapter.notifyDataSetChanged();
} }
}
private void setBookmarkDataSet(List<HistoryItem> items, boolean animate) { private void setBookmarkDataSet(List<HistoryItem> items, boolean animate) {
mBookmarks.clear(); mBookmarks.clear();
mBookmarks.addAll(items); mBookmarks.addAll(items);
mBookmarkAdapter.notifyDataSetChanged(); mBookmarkAdapter.notifyDataSetChanged();
final int resource; final int resource;
if (mBookmarkManager.isRootFolder()) if (mBookmarkManager.isRootFolder()) {
resource = R.drawable.ic_action_star; resource = R.drawable.ic_action_star;
else } else {
resource = R.drawable.ic_action_back; resource = R.drawable.ic_action_back;
}
final Animation startRotation = new Animation() { final Animation startRotation = new Animation() {
@Override @Override
@ -280,8 +284,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
} }
} }
// TODO this is basically a copy/paste from BrowserActivity, should be changed private void setupNavigationButton(@NonNull View view, @IdRes int buttonId, @IdRes int imageId) {
private void setupFrameLayoutButton(@NonNull View view, @IdRes int buttonId, @IdRes int imageId) {
FrameLayout frameButton = (FrameLayout) view.findViewById(buttonId); FrameLayout frameButton = (FrameLayout) view.findViewById(buttonId);
frameButton.setOnClickListener(this); frameButton.setOnClickListener(this);
frameButton.setOnLongClickListener(this); frameButton.setOnLongClickListener(this);
@ -291,9 +294,9 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
private void handleLongPress(final HistoryItem item, final int position) { private void handleLongPress(final HistoryItem item, final int position) {
if (item.isFolder()) { if (item.isFolder()) {
bookmarksDialogBuilder.showBookmarkFolderLongPressedDialog(getContext(), item); mBookmarksDialogBuilder.showBookmarkFolderLongPressedDialog(getContext(), item);
} else { } else {
bookmarksDialogBuilder.showLongPressLinkDialog(getContext(), item.getUrl()); mBookmarksDialogBuilder.showLongPressLinkDialog(getContext(), item.getUrl());
} }
} }
@ -301,7 +304,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
public void onClick(View v) { public void onClick(View v) {
switch (v.getId()) { switch (v.getId()) {
case R.id.action_add_bookmark: case R.id.action_add_bookmark:
eventBus.post(new BookmarkEvents.WantToBookmarkCurrentPage()); mEventBus.post(new BookmarkEvents.WantToBookmarkCurrentPage());
break; break;
default: default:
break; break;
@ -343,19 +346,19 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
HistoryItem web = mBookmarks.get(position); HistoryItem web = mBookmarks.get(position);
holder.txtTitle.setText(web.getTitle()); holder.txtTitle.setText(web.getTitle());
holder.favicon.setImageBitmap(mWebpageBitmap);
if (web.isFolder()) { if (web.isFolder()) {
holder.favicon.setImageBitmap(mFolderBitmap); holder.favicon.setImageBitmap(mFolderBitmap);
} else if (web.getBitmap() == null) { } else if (web.getBitmap() == null) {
new DownloadImageTask(holder.favicon, web, mWebpageBitmap) holder.favicon.setImageBitmap(mWebpageBitmap);
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new ImageDownloadTask(holder.favicon, web, mWebpageBitmap)
.executeOnExecutor(AsyncExecutor.getInstance());
} else { } else {
holder.favicon.setImageBitmap(web.getBitmap()); holder.favicon.setImageBitmap(web.getBitmap());
} }
return row; return row;
} }
class BookmarkViewHolder { private class BookmarkViewHolder {
TextView txtTitle; TextView txtTitle;
ImageView favicon; ImageView favicon;
} }

76
app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java

@ -8,7 +8,6 @@ import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -19,7 +18,6 @@ import android.widget.SeekBar;
import android.widget.TextView; import android.widget.TextView;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.preference.PreferenceManager;
public class DisplaySettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { public class DisplaySettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
@ -57,7 +55,7 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
private void initPrefs() { private void initPrefs() {
// mPreferences storage // mPreferences storage
mThemeOptions = this.getResources().getStringArray(R.array.themes); mThemeOptions = this.getResources().getStringArray(R.array.themes);
mCurrentTheme = preferenceManager.getUseTheme(); mCurrentTheme = mPreferenceManager.getUseTheme();
theme = findPreference(SETTINGS_THEME); theme = findPreference(SETTINGS_THEME);
Preference textsize = findPreference(SETTINGS_TEXTSIZE); Preference textsize = findPreference(SETTINGS_TEXTSIZE);
@ -75,13 +73,13 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
cboverview.setOnPreferenceChangeListener(this); cboverview.setOnPreferenceChangeListener(this);
cbreflow.setOnPreferenceChangeListener(this); cbreflow.setOnPreferenceChangeListener(this);
cbstatus.setChecked(preferenceManager.getHideStatusBarEnabled()); cbstatus.setChecked(mPreferenceManager.getHideStatusBarEnabled());
cbfullscreen.setChecked(preferenceManager.getFullScreenEnabled()); cbfullscreen.setChecked(mPreferenceManager.getFullScreenEnabled());
cbviewport.setChecked(preferenceManager.getUseWideViewportEnabled()); cbviewport.setChecked(mPreferenceManager.getUseWideViewportEnabled());
cboverview.setChecked(preferenceManager.getOverviewModeEnabled()); cboverview.setChecked(mPreferenceManager.getOverviewModeEnabled());
cbreflow.setChecked(preferenceManager.getTextReflowEnabled()); cbreflow.setChecked(mPreferenceManager.getTextReflowEnabled());
theme.setSummary(mThemeOptions[preferenceManager.getUseTheme()]); theme.setSummary(mThemeOptions[mPreferenceManager.getUseTheme()]);
} }
@Override @Override
@ -103,23 +101,23 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
// switch preferences // switch preferences
switch (preference.getKey()) { switch (preference.getKey()) {
case SETTINGS_HIDESTATUSBAR: case SETTINGS_HIDESTATUSBAR:
preferenceManager.setHideStatusBarEnabled((Boolean) newValue); mPreferenceManager.setHideStatusBarEnabled((Boolean) newValue);
cbstatus.setChecked((Boolean) newValue); cbstatus.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_FULLSCREEN: case SETTINGS_FULLSCREEN:
preferenceManager.setFullScreenEnabled((Boolean) newValue); mPreferenceManager.setFullScreenEnabled((Boolean) newValue);
cbfullscreen.setChecked((Boolean) newValue); cbfullscreen.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_VIEWPORT: case SETTINGS_VIEWPORT:
preferenceManager.setUseWideViewportEnabled((Boolean) newValue); mPreferenceManager.setUseWideViewportEnabled((Boolean) newValue);
cbviewport.setChecked((Boolean) newValue); cbviewport.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_OVERVIEWMODE: case SETTINGS_OVERVIEWMODE:
preferenceManager.setOverviewModeEnabled((Boolean) newValue); mPreferenceManager.setOverviewModeEnabled((Boolean) newValue);
cboverview.setChecked((Boolean) newValue); cboverview.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_REFLOW: case SETTINGS_REFLOW:
preferenceManager.setTextReflowEnabled((Boolean) newValue); mPreferenceManager.setTextReflowEnabled((Boolean) newValue);
cbreflow.setChecked((Boolean) newValue); cbreflow.setChecked((Boolean) newValue);
return true; return true;
default: default:
@ -137,32 +135,17 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
sample.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT)); sample.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT));
sample.setGravity(Gravity.CENTER_HORIZONTAL); sample.setGravity(Gravity.CENTER_HORIZONTAL);
view.addView(sample); view.addView(sample);
bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { bar.setOnSeekBarChangeListener(new TextSeekBarListener(sample));
@Override
public void onProgressChanged(SeekBar view, int size, boolean user) {
sample.setTextSize(getTextSize(size));
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onStopTrackingTouch(SeekBar arg0) {
}
});
final int MAX = 5; final int MAX = 5;
bar.setMax(MAX); bar.setMax(MAX);
bar.setProgress(MAX - preferenceManager.getTextSize()); bar.setProgress(MAX - mPreferenceManager.getTextSize());
builder.setView(view); builder.setView(view);
builder.setTitle(R.string.title_text_size); builder.setTitle(R.string.title_text_size);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface arg0, int arg1) { public void onClick(DialogInterface arg0, int arg1) {
preferenceManager.setTextSize(MAX - bar.getProgress()); mPreferenceManager.setTextSize(MAX - bar.getProgress());
} }
}); });
@ -192,12 +175,12 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); AlertDialog.Builder picker = new AlertDialog.Builder(mActivity);
picker.setTitle(getResources().getString(R.string.theme)); picker.setTitle(getResources().getString(R.string.theme));
int n = preferenceManager.getUseTheme(); int n = mPreferenceManager.getUseTheme();
picker.setSingleChoiceItems(mThemeOptions, n, new DialogInterface.OnClickListener() { picker.setSingleChoiceItems(mThemeOptions, n, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
preferenceManager.setUseTheme(which); mPreferenceManager.setUseTheme(which);
if (which < mThemeOptions.length) { if (which < mThemeOptions.length) {
theme.setSummary(mThemeOptions[which]); theme.setSummary(mThemeOptions[which]);
} }
@ -208,7 +191,7 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
if (mCurrentTheme != preferenceManager.getUseTheme()) { if (mCurrentTheme != mPreferenceManager.getUseTheme()) {
getActivity().onBackPressed(); getActivity().onBackPressed();
} }
} }
@ -216,11 +199,32 @@ public class DisplaySettingsFragment extends LightningPreferenceFragment impleme
picker.setOnCancelListener(new DialogInterface.OnCancelListener() { picker.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override @Override
public void onCancel(DialogInterface dialog) { public void onCancel(DialogInterface dialog) {
if (mCurrentTheme != preferenceManager.getUseTheme()) { if (mCurrentTheme != mPreferenceManager.getUseTheme()) {
getActivity().onBackPressed(); getActivity().onBackPressed();
} }
} }
}); });
picker.show(); picker.show();
} }
private static class TextSeekBarListener implements SeekBar.OnSeekBarChangeListener {
private final TextView sample;
public TextSeekBarListener(TextView sample) {this.sample = sample;}
@Override
public void onProgressChanged(SeekBar view, int size, boolean user) {
this.sample.setTextSize(getTextSize(size));
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onStopTrackingTouch(SeekBar arg0) {
}
}
} }

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

@ -5,27 +5,26 @@ package acr.browser.lightning.fragment;
import android.app.Activity; import android.app.Activity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceFragment; import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.InputFilter; import android.text.InputFilter;
import android.util.Log; import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.download.DownloadHandler;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ProxyUtils;
import acr.browser.lightning.utils.ThemeUtils;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
public class GeneralSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { public class GeneralSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
@ -91,25 +90,25 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
cbgooglesuggest.setOnPreferenceChangeListener(this); cbgooglesuggest.setOnPreferenceChangeListener(this);
cbDrawerTabs.setOnPreferenceChangeListener(this); cbDrawerTabs.setOnPreferenceChangeListener(this);
mAgentChoice = preferenceManager.getUserAgentChoice(); mAgentChoice = mPreferenceManager.getUserAgentChoice();
mHomepage = preferenceManager.getHomepage(); mHomepage = mPreferenceManager.getHomepage();
mDownloadLocation = preferenceManager.getDownloadDirectory(); mDownloadLocation = mPreferenceManager.getDownloadDirectory();
mProxyChoices = getResources().getStringArray(R.array.proxy_choices_array); mProxyChoices = getResources().getStringArray(R.array.proxy_choices_array);
int choice = preferenceManager.getProxyChoice(); int choice = mPreferenceManager.getProxyChoice();
if (choice == Constants.PROXY_MANUAL) { if (choice == Constants.PROXY_MANUAL) {
proxy.setSummary(preferenceManager.getProxyHost() + ':' + preferenceManager.getProxyPort()); proxy.setSummary(mPreferenceManager.getProxyHost() + ':' + mPreferenceManager.getProxyPort());
} else { } else {
proxy.setSummary(mProxyChoices[choice]); proxy.setSummary(mProxyChoices[choice]);
} }
if (API >= 19) { if (API >= 19) {
preferenceManager.setFlashSupport(0); mPreferenceManager.setFlashSupport(0);
} }
setSearchEngineSummary(preferenceManager.getSearchChoice()); setSearchEngineSummary(mPreferenceManager.getSearchChoice());
downloadloc.setSummary(Constants.EXTERNAL_STORAGE + '/' + mDownloadLocation); downloadloc.setSummary(mDownloadLocation);
if (mHomepage.contains("about:home")) { if (mHomepage.contains("about:home")) {
home.setSummary(getResources().getString(R.string.action_homepage)); home.setSummary(getResources().getString(R.string.action_homepage));
@ -135,28 +134,28 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
useragent.setSummary(getResources().getString(R.string.agent_custom)); useragent.setSummary(getResources().getString(R.string.agent_custom));
} }
int flashNum = preferenceManager.getFlashSupport(); int flashNum = mPreferenceManager.getFlashSupport();
boolean imagesBool = preferenceManager.getBlockImagesEnabled(); boolean imagesBool = mPreferenceManager.getBlockImagesEnabled();
boolean enableJSBool = preferenceManager.getJavaScriptEnabled(); boolean enableJSBool = mPreferenceManager.getJavaScriptEnabled();
proxy.setEnabled(Constants.FULL_VERSION); // proxy.setEnabled(Constants.FULL_VERSION);
cbAds.setEnabled(Constants.FULL_VERSION); cbAds.setEnabled(Constants.FULL_VERSION);
cbFlash.setEnabled(API < 19); cbFlash.setEnabled(API < 19);
cbImages.setChecked(imagesBool); cbImages.setChecked(imagesBool);
cbJsScript.setChecked(enableJSBool); cbJsScript.setChecked(enableJSBool);
cbFlash.setChecked(flashNum > 0); cbFlash.setChecked(flashNum > 0);
cbAds.setChecked(Constants.FULL_VERSION && preferenceManager.getAdBlockEnabled()); cbAds.setChecked(Constants.FULL_VERSION && mPreferenceManager.getAdBlockEnabled());
cbColorMode.setChecked(preferenceManager.getColorModeEnabled()); cbColorMode.setChecked(mPreferenceManager.getColorModeEnabled());
cbgooglesuggest.setChecked(preferenceManager.getGoogleSearchSuggestionsEnabled()); cbgooglesuggest.setChecked(mPreferenceManager.getGoogleSearchSuggestionsEnabled());
cbDrawerTabs.setChecked(preferenceManager.getShowTabsInDrawer(true)); cbDrawerTabs.setChecked(mPreferenceManager.getShowTabsInDrawer(true));
} }
private void searchUrlPicker() { private void searchUrlPicker() {
final AlertDialog.Builder urlPicker = new AlertDialog.Builder(mActivity); final AlertDialog.Builder urlPicker = new AlertDialog.Builder(mActivity);
urlPicker.setTitle(getResources().getString(R.string.custom_url)); urlPicker.setTitle(getResources().getString(R.string.custom_url));
final EditText getSearchUrl = new EditText(mActivity); final EditText getSearchUrl = new EditText(mActivity);
String mSearchUrl = preferenceManager.getSearchUrl(); String mSearchUrl = mPreferenceManager.getSearchUrl();
getSearchUrl.setText(mSearchUrl); getSearchUrl.setText(mSearchUrl);
urlPicker.setView(getSearchUrl); urlPicker.setView(getSearchUrl);
urlPicker.setPositiveButton(getResources().getString(R.string.action_ok), urlPicker.setPositiveButton(getResources().getString(R.string.action_ok),
@ -164,7 +163,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String text = getSearchUrl.getText().toString(); String text = getSearchUrl.getText().toString();
preferenceManager.setSearchUrl(text); mPreferenceManager.setSearchUrl(text);
searchengine.setSummary(getResources().getString(R.string.custom_url) + ": " searchengine.setSummary(getResources().getString(R.string.custom_url) + ": "
+ text); + text);
} }
@ -181,7 +180,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
preferenceManager.setFlashSupport(1); mPreferenceManager.setFlashSupport(1);
} }
}) })
.setNegativeButton(getResources().getString(R.string.action_auto), .setNegativeButton(getResources().getString(R.string.action_auto),
@ -189,13 +188,13 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
preferenceManager.setFlashSupport(2); mPreferenceManager.setFlashSupport(2);
} }
}).setOnCancelListener(new DialogInterface.OnCancelListener() { }).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override @Override
public void onCancel(DialogInterface dialog) { public void onCancel(DialogInterface dialog) {
preferenceManager.setFlashSupport(0); mPreferenceManager.setFlashSupport(0);
} }
}); });
@ -206,7 +205,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
private void proxyChoicePicker() { private void proxyChoicePicker() {
AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); AlertDialog.Builder picker = new AlertDialog.Builder(mActivity);
picker.setTitle(getResources().getString(R.string.http_proxy)); picker.setTitle(getResources().getString(R.string.http_proxy));
picker.setSingleChoiceItems(mProxyChoices, preferenceManager.getProxyChoice(), picker.setSingleChoiceItems(mProxyChoices, mPreferenceManager.getProxyChoice(),
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
@ -214,12 +213,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
setProxyChoice(which); setProxyChoice(which);
} }
}); });
picker.setNeutralButton(getResources().getString(R.string.action_ok), picker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show(); picker.show();
} }
@ -236,7 +230,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
break; break;
} }
preferenceManager.setProxyChoice(choice); mPreferenceManager.setProxyChoice(choice);
if (choice < mProxyChoices.length) if (choice < mProxyChoices.length)
proxy.setSummary(mProxyChoices[choice]); proxy.setSummary(mProxyChoices[choice]);
} }
@ -254,8 +248,8 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
filterArray[0] = new InputFilter.LengthFilter(maxCharacters - 1); filterArray[0] = new InputFilter.LengthFilter(maxCharacters - 1);
eProxyPort.setFilters(filterArray); eProxyPort.setFilters(filterArray);
eProxyHost.setText(preferenceManager.getProxyHost()); eProxyHost.setText(mPreferenceManager.getProxyHost());
eProxyPort.setText(Integer.toString(preferenceManager.getProxyPort())); eProxyPort.setText(Integer.toString(mPreferenceManager.getProxyPort()));
new AlertDialog.Builder(mActivity) new AlertDialog.Builder(mActivity)
.setTitle(R.string.manual_proxy) .setTitle(R.string.manual_proxy)
@ -270,10 +264,10 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
// larger than max integer // larger than max integer
proxyPort = Integer.parseInt(eProxyPort.getText().toString()); proxyPort = Integer.parseInt(eProxyPort.getText().toString());
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
proxyPort = preferenceManager.getProxyPort(); proxyPort = mPreferenceManager.getProxyPort();
} }
preferenceManager.setProxyHost(proxyHost); mPreferenceManager.setProxyHost(proxyHost);
preferenceManager.setProxyPort(proxyPort); mPreferenceManager.setProxyPort(proxyPort);
proxy.setSummary(proxyHost + ':' + proxyPort); proxy.setSummary(proxyHost + ':' + proxyPort);
} }
}).show(); }).show();
@ -287,29 +281,24 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
"DuckDuckGo (Privacy)", "DuckDuckGo Lite (Privacy)", "Baidu (Chinese)", "DuckDuckGo (Privacy)", "DuckDuckGo Lite (Privacy)", "Baidu (Chinese)",
"Yandex (Russian)"}; "Yandex (Russian)"};
int n = preferenceManager.getSearchChoice(); int n = mPreferenceManager.getSearchChoice();
picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() { picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
preferenceManager.setSearchChoice(which); mPreferenceManager.setSearchChoice(which);
setSearchEngineSummary(which); setSearchEngineSummary(which);
} }
}); });
picker.setNeutralButton(getResources().getString(R.string.action_ok), picker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show(); picker.show();
} }
private void homepageDialog() { private void homepageDialog() {
AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); AlertDialog.Builder picker = new AlertDialog.Builder(mActivity);
picker.setTitle(getResources().getString(R.string.home)); picker.setTitle(getResources().getString(R.string.home));
mHomepage = preferenceManager.getHomepage(); mHomepage = mPreferenceManager.getHomepage();
int n; int n;
if (mHomepage.contains("about:home")) { if (mHomepage.contains("about:home")) {
n = 1; n = 1;
@ -327,15 +316,15 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which + 1) { switch (which + 1) {
case 1: case 1:
preferenceManager.setHomepage("about:home"); mPreferenceManager.setHomepage("about:home");
home.setSummary(getResources().getString(R.string.action_homepage)); home.setSummary(getResources().getString(R.string.action_homepage));
break; break;
case 2: case 2:
preferenceManager.setHomepage("about:blank"); mPreferenceManager.setHomepage("about:blank");
home.setSummary(getResources().getString(R.string.action_blank)); home.setSummary(getResources().getString(R.string.action_blank));
break; break;
case 3: case 3:
preferenceManager.setHomepage("about:bookmarks"); mPreferenceManager.setHomepage("about:bookmarks");
home.setSummary(getResources().getString(R.string.action_bookmarks)); home.setSummary(getResources().getString(R.string.action_bookmarks));
break; break;
case 4: case 4:
@ -344,12 +333,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
} }
} }
}); });
picker.setNeutralButton(getResources().getString(R.string.action_ok), picker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show(); picker.show();
} }
@ -357,7 +341,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
final AlertDialog.Builder homePicker = new AlertDialog.Builder(mActivity); final AlertDialog.Builder homePicker = new AlertDialog.Builder(mActivity);
homePicker.setTitle(getResources().getString(R.string.title_custom_homepage)); homePicker.setTitle(getResources().getString(R.string.title_custom_homepage));
final EditText getHome = new EditText(mActivity); final EditText getHome = new EditText(mActivity);
mHomepage = preferenceManager.getHomepage(); mHomepage = mPreferenceManager.getHomepage();
if (!mHomepage.startsWith("about:")) { if (!mHomepage.startsWith("about:")) {
getHome.setText(mHomepage); getHome.setText(mHomepage);
} else { } else {
@ -369,7 +353,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String text = getHome.getText().toString(); String text = getHome.getText().toString();
preferenceManager.setHomepage(text); mPreferenceManager.setHomepage(text);
home.setSummary(text); home.setSummary(text);
} }
}); });
@ -379,48 +363,42 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
private void downloadLocDialog() { private void downloadLocDialog() {
AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); AlertDialog.Builder picker = new AlertDialog.Builder(mActivity);
picker.setTitle(getResources().getString(R.string.title_download_location)); picker.setTitle(getResources().getString(R.string.title_download_location));
mDownloadLocation = preferenceManager.getDownloadDirectory(); mDownloadLocation = mPreferenceManager.getDownloadDirectory();
int n; int n;
if (mDownloadLocation.contains(Environment.DIRECTORY_DOWNLOADS)) { if (mDownloadLocation.contains(Environment.DIRECTORY_DOWNLOADS)) {
n = 1; n = 0;
} else { } else {
n = 2; n = 1;
} }
picker.setSingleChoiceItems(R.array.download_folder, n - 1, picker.setSingleChoiceItems(R.array.download_folder, n,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which + 1) { switch (which) {
case 1: case 0:
preferenceManager.setDownloadDirectory(Environment.DIRECTORY_DOWNLOADS); mPreferenceManager.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH);
downloadloc.setSummary(Constants.EXTERNAL_STORAGE + '/' downloadloc.setSummary(DownloadHandler.DEFAULT_DOWNLOAD_PATH);
+ Environment.DIRECTORY_DOWNLOADS);
break; break;
case 2: case 1:
downPicker(); downPicker();
break; break;
} }
} }
}); });
picker.setNeutralButton(getResources().getString(R.string.action_ok), picker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show(); picker.show();
} }
private void agentDialog() { private void agentDialog() {
AlertDialog.Builder agentPicker = new AlertDialog.Builder(mActivity); AlertDialog.Builder agentPicker = new AlertDialog.Builder(mActivity);
agentPicker.setTitle(getResources().getString(R.string.title_user_agent)); agentPicker.setTitle(getResources().getString(R.string.title_user_agent));
mAgentChoice = preferenceManager.getUserAgentChoice(); mAgentChoice = mPreferenceManager.getUserAgentChoice();
agentPicker.setSingleChoiceItems(R.array.user_agent, mAgentChoice - 1, agentPicker.setSingleChoiceItems(R.array.user_agent, mAgentChoice - 1,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
preferenceManager.setUserAgentChoice(which + 1); mPreferenceManager.setUserAgentChoice(which + 1);
switch (which + 1) { switch (which + 1) {
case 1: case 1:
useragent.setSummary(getResources().getString(R.string.agent_default)); useragent.setSummary(getResources().getString(R.string.agent_default));
@ -438,18 +416,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
} }
} }
}); });
agentPicker.setNeutralButton(getResources().getString(R.string.action_ok), agentPicker.setNeutralButton(getResources().getString(R.string.action_ok), null);
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
agentPicker.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Log.i("Cancelled", "");
}
});
agentPicker.show(); agentPicker.show();
} }
@ -463,7 +430,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String text = getAgent.getText().toString(); String text = getAgent.getText().toString();
preferenceManager.setUserAgentString(text); mPreferenceManager.setUserAgentString(text);
useragent.setSummary(getResources().getString(R.string.agent_custom)); useragent.setSummary(getResources().getString(R.string.agent_custom));
} }
}); });
@ -475,36 +442,25 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
LinearLayout layout = new LinearLayout(mActivity); LinearLayout layout = new LinearLayout(mActivity);
downLocationPicker.setTitle(getResources().getString(R.string.title_download_location)); downLocationPicker.setTitle(getResources().getString(R.string.title_download_location));
final EditText getDownload = new EditText(mActivity); final EditText getDownload = new EditText(mActivity);
getDownload.setText(preferenceManager.getDownloadDirectory()); getDownload.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
getDownload.setText(mPreferenceManager.getDownloadDirectory());
final int errorColor = ContextCompat.getColor(getActivity(), R.color.error_red);
final int regularColor = ThemeUtils.getTextColor(getActivity());
getDownload.setTextColor(regularColor);
getDownload.addTextChangedListener(new DownloadLocationTextWatcher(getDownload, errorColor, regularColor));
getDownload.setText(mPreferenceManager.getDownloadDirectory());
int padding = Utils.dpToPx(10);
TextView v = new TextView(mActivity);
v.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
v.setTextColor(Color.DKGRAY);
v.setText(Constants.EXTERNAL_STORAGE + '/');
v.setPadding(padding, padding, 0, padding);
layout.addView(v);
layout.addView(getDownload); layout.addView(getDownload);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
Drawable drawable;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
drawable = getResources().getDrawable(android.R.drawable.edit_text, getActivity().getTheme());
} else {
drawable = getResources().getDrawable(android.R.drawable.edit_text);
}
layout.setBackground(drawable);
} else {
layout.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.edit_text));
}
downLocationPicker.setView(layout); downLocationPicker.setView(layout);
downLocationPicker.setPositiveButton(getResources().getString(R.string.action_ok), downLocationPicker.setPositiveButton(getResources().getString(R.string.action_ok),
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String text = getDownload.getText().toString(); String text = getDownload.getText().toString();
preferenceManager.setDownloadDirectory(text); text = DownloadHandler.addNecessarySlashes(text);
downloadloc.setSummary(Constants.EXTERNAL_STORAGE + '/' + text); mPreferenceManager.setDownloadDirectory(text);
downloadloc.setSummary(text);
} }
}); });
downLocationPicker.show(); downLocationPicker.show();
@ -578,40 +534,67 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme
if (cbFlash.isChecked()) { if (cbFlash.isChecked()) {
getFlashChoice(); getFlashChoice();
} else { } else {
preferenceManager.setFlashSupport(0); mPreferenceManager.setFlashSupport(0);
} }
if (!Utils.isFlashInstalled(mActivity) && cbFlash.isChecked()) { if (!Utils.isFlashInstalled(mActivity) && cbFlash.isChecked()) {
Utils.createInformativeDialog(mActivity, R.string.title_warning, R.string.dialog_adobe_not_installed); Utils.createInformativeDialog(mActivity, R.string.title_warning, R.string.dialog_adobe_not_installed);
cbFlash.setEnabled(false); cbFlash.setEnabled(false);
preferenceManager.setFlashSupport(0); mPreferenceManager.setFlashSupport(0);
} }
cbFlash.setChecked((Boolean) newValue); cbFlash.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_ADS: case SETTINGS_ADS:
preferenceManager.setAdBlockEnabled((Boolean) newValue); mPreferenceManager.setAdBlockEnabled((Boolean) newValue);
cbAds.setChecked((Boolean) newValue); cbAds.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_IMAGES: case SETTINGS_IMAGES:
preferenceManager.setBlockImagesEnabled((Boolean) newValue); mPreferenceManager.setBlockImagesEnabled((Boolean) newValue);
cbImages.setChecked((Boolean) newValue); cbImages.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_JAVASCRIPT: case SETTINGS_JAVASCRIPT:
preferenceManager.setJavaScriptEnabled((Boolean) newValue); mPreferenceManager.setJavaScriptEnabled((Boolean) newValue);
cbJsScript.setChecked((Boolean) newValue); cbJsScript.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_COLORMODE: case SETTINGS_COLORMODE:
preferenceManager.setColorModeEnabled((Boolean) newValue); mPreferenceManager.setColorModeEnabled((Boolean) newValue);
cbColorMode.setChecked((Boolean) newValue); cbColorMode.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_GOOGLESUGGESTIONS: case SETTINGS_GOOGLESUGGESTIONS:
preferenceManager.setGoogleSearchSuggestionsEnabled((Boolean) newValue); mPreferenceManager.setGoogleSearchSuggestionsEnabled((Boolean) newValue);
cbgooglesuggest.setChecked((Boolean) newValue); cbgooglesuggest.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_DRAWERTABS: case SETTINGS_DRAWERTABS:
preferenceManager.setShowTabsInDrawer((Boolean) newValue); mPreferenceManager.setShowTabsInDrawer((Boolean) newValue);
cbDrawerTabs.setChecked((Boolean) newValue); cbDrawerTabs.setChecked((Boolean) newValue);
default: default:
return false; return false;
} }
} }
private static class DownloadLocationTextWatcher implements TextWatcher {
private final EditText getDownload;
private final int errorColor;
private final int regularColor;
public DownloadLocationTextWatcher(EditText getDownload, int errorColor, int regularColor) {
this.getDownload = getDownload;
this.errorColor = errorColor;
this.regularColor = regularColor;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
if (!DownloadHandler.isWriteAccessAvailable(s.toString())) {
this.getDownload.setTextColor(this.errorColor);
} else {
this.getDownload.setTextColor(this.regularColor);
}
}
}
} }

2
app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java

@ -17,7 +17,7 @@ import acr.browser.lightning.preference.PreferenceManager;
public class LightningPreferenceFragment extends PreferenceFragment { public class LightningPreferenceFragment extends PreferenceFragment {
@Inject @Inject
PreferenceManager preferenceManager; PreferenceManager mPreferenceManager;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {

44
app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java

@ -11,12 +11,10 @@ import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.webkit.WebView; import android.webkit.WebView;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
import acr.browser.lightning.utils.WebUtils; import acr.browser.lightning.utils.WebUtils;
@ -77,13 +75,13 @@ public class PrivacySettingsFragment extends LightningPreferenceFragment impleme
cbcookiesexit.setOnPreferenceChangeListener(this); cbcookiesexit.setOnPreferenceChangeListener(this);
cbwebstorageexit.setOnPreferenceChangeListener(this); cbwebstorageexit.setOnPreferenceChangeListener(this);
cblocation.setChecked(preferenceManager.getLocationEnabled()); cblocation.setChecked(mPreferenceManager.getLocationEnabled());
cbsavepasswords.setChecked(preferenceManager.getSavePasswordsEnabled()); cbsavepasswords.setChecked(mPreferenceManager.getSavePasswordsEnabled());
cbcacheexit.setChecked(preferenceManager.getClearCacheExit()); cbcacheexit.setChecked(mPreferenceManager.getClearCacheExit());
cbhistoryexit.setChecked(preferenceManager.getClearHistoryExitEnabled()); cbhistoryexit.setChecked(mPreferenceManager.getClearHistoryExitEnabled());
cbcookiesexit.setChecked(preferenceManager.getClearCookiesExitEnabled()); cbcookiesexit.setChecked(mPreferenceManager.getClearCookiesExitEnabled());
cb3cookies.setChecked(preferenceManager.getBlockThirdPartyCookiesEnabled()); cb3cookies.setChecked(mPreferenceManager.getBlockThirdPartyCookiesEnabled());
cbwebstorageexit.setChecked(preferenceManager.getClearWebStorageExitEnabled()); cbwebstorageexit.setChecked(mPreferenceManager.getClearWebStorageExitEnabled());
cb3cookies.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); cb3cookies.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
@ -149,12 +147,7 @@ public class PrivacySettingsFragment extends LightningPreferenceFragment impleme
clear.start(); clear.start();
} }
}) })
.setNegativeButton(getResources().getString(R.string.action_no), .setNegativeButton(getResources().getString(R.string.action_no), null).show();
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
}
}).show();
} }
private void clearCookiesDialog() { private void clearCookiesDialog() {
@ -174,12 +167,7 @@ public class PrivacySettingsFragment extends LightningPreferenceFragment impleme
clear.start(); clear.start();
} }
}) })
.setNegativeButton(getResources().getString(R.string.action_no), .setNegativeButton(getResources().getString(R.string.action_no), null).show();
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
}
}).show();
} }
private void clearCache() { private void clearCache() {
@ -209,31 +197,31 @@ public class PrivacySettingsFragment extends LightningPreferenceFragment impleme
// switch preferences // switch preferences
switch (preference.getKey()) { switch (preference.getKey()) {
case SETTINGS_LOCATION: case SETTINGS_LOCATION:
preferenceManager.setLocationEnabled((Boolean) newValue); mPreferenceManager.setLocationEnabled((Boolean) newValue);
cblocation.setChecked((Boolean) newValue); cblocation.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_THIRDPCOOKIES: case SETTINGS_THIRDPCOOKIES:
preferenceManager.setBlockThirdPartyCookiesEnabled((Boolean) newValue); mPreferenceManager.setBlockThirdPartyCookiesEnabled((Boolean) newValue);
cb3cookies.setChecked((Boolean) newValue); cb3cookies.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_SAVEPASSWORD: case SETTINGS_SAVEPASSWORD:
preferenceManager.setSavePasswordsEnabled((Boolean) newValue); mPreferenceManager.setSavePasswordsEnabled((Boolean) newValue);
cbsavepasswords.setChecked((Boolean) newValue); cbsavepasswords.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_CACHEEXIT: case SETTINGS_CACHEEXIT:
preferenceManager.setClearCacheExit((Boolean) newValue); mPreferenceManager.setClearCacheExit((Boolean) newValue);
cbcacheexit.setChecked((Boolean) newValue); cbcacheexit.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_HISTORYEXIT: case SETTINGS_HISTORYEXIT:
preferenceManager.setClearHistoryExitEnabled((Boolean) newValue); mPreferenceManager.setClearHistoryExitEnabled((Boolean) newValue);
cbhistoryexit.setChecked((Boolean) newValue); cbhistoryexit.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_COOKIEEXIT: case SETTINGS_COOKIEEXIT:
preferenceManager.setClearCookiesExitEnabled((Boolean) newValue); mPreferenceManager.setClearCookiesExitEnabled((Boolean) newValue);
cbcookiesexit.setChecked((Boolean) newValue); cbcookiesexit.setChecked((Boolean) newValue);
return true; return true;
case SETTINGS_WEBSTORAGEEXIT: case SETTINGS_WEBSTORAGEEXIT:
preferenceManager.setClearWebStorageExitEnabled((Boolean) newValue); mPreferenceManager.setClearWebStorageExitEnabled((Boolean) newValue);
cbwebstorageexit.setChecked((Boolean) newValue); cbwebstorageexit.setChecked((Boolean) newValue);
return true; return true;
default: default:

69
app/src/main/java/acr/browser/lightning/object/SearchAdapter.java

@ -34,6 +34,7 @@ import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Pattern;
import javax.inject.Inject; import javax.inject.Inject;
@ -48,11 +49,12 @@ import acr.browser.lightning.utils.Utils;
public class SearchAdapter extends BaseAdapter implements Filterable { public class SearchAdapter extends BaseAdapter implements Filterable {
private final List<HistoryItem> mHistory = new ArrayList<>(); private static final Pattern SPACE_PATTERN = Pattern.compile(" ", Pattern.LITERAL);
private final List<HistoryItem> mBookmarks = new ArrayList<>(); private final List<HistoryItem> mHistory = new ArrayList<>(5);
private final List<HistoryItem> mSuggestions = new ArrayList<>(); private final List<HistoryItem> mBookmarks = new ArrayList<>(5);
private final List<HistoryItem> mFilteredList = new ArrayList<>(); private final List<HistoryItem> mSuggestions = new ArrayList<>(5);
private final List<HistoryItem> mAllBookmarks = new ArrayList<>(); private final List<HistoryItem> mFilteredList = new ArrayList<>(5);
private final List<HistoryItem> mAllBookmarks = new ArrayList<>(5);
private final Object mLock = new Object(); private final Object mLock = new Object();
private final Context mContext; private final Context mContext;
private boolean mUseGoogle = true; private boolean mUseGoogle = true;
@ -87,14 +89,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
mSearchSubtitle = mContext.getString(R.string.suggestion); mSearchSubtitle = mContext.getString(R.string.suggestion);
mDarkTheme = dark || incognito; mDarkTheme = dark || incognito;
mIncognito = incognito; mIncognito = incognito;
Thread delete = new Thread(new Runnable() { Thread delete = new Thread(new ClearCacheRunnable());
@Override
public void run() {
deleteOldCacheFiles();
}
});
mSearchDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_search, mDarkTheme); mSearchDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_search, mDarkTheme);
mBookmarkDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_bookmark, mDarkTheme); mBookmarkDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_bookmark, mDarkTheme);
mHistoryDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_history, mDarkTheme); mHistoryDrawable = ThemeUtils.getThemedDrawable(context, R.drawable.ic_history, mDarkTheme);
@ -102,7 +97,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
delete.start(); delete.start();
} }
private void deleteOldCacheFiles() { private static void deleteOldCacheFiles() {
File dir = new File(BrowserApp.getAppContext().getCacheDir().toString()); File dir = new File(BrowserApp.getAppContext().getCacheDir().toString());
String[] fileList = dir.list(new NameFilter()); String[] fileList = dir.list(new NameFilter());
long earliestTimeAllowed = System.currentTimeMillis() - INTERVAL_DAY; long earliestTimeAllowed = System.currentTimeMillis() - INTERVAL_DAY;
@ -114,7 +109,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
} }
} }
private class NameFilter implements FilenameFilter { private static class NameFilter implements FilenameFilter {
@Override @Override
public boolean accept(File dir, String filename) { public boolean accept(File dir, String filename) {
@ -215,6 +210,15 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
return mFilter; return mFilter;
} }
private static class ClearCacheRunnable implements Runnable {
@Override
public void run() {
deleteOldCacheFiles();
}
}
private class SearchFilter extends Filter { private class SearchFilter extends Filter {
@Override @Override
@ -225,7 +229,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
} }
String query = constraint.toString().toLowerCase(Locale.getDefault()); String query = constraint.toString().toLowerCase(Locale.getDefault());
if (mUseGoogle && !mIncognito && !mIsExecuting) { if (mUseGoogle && !mIncognito && !mIsExecuting) {
new RetrieveSearchSuggestions().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, query); new RetrieveSearchSuggestions().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, query);
} }
int counter = 0; int counter = 0;
@ -275,7 +279,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
} }
private class SuggestionHolder { private static class SuggestionHolder {
ImageView mImage; ImageView mImage;
TextView mTitle; TextView mTitle;
TextView mUrl; TextView mUrl;
@ -293,7 +297,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
List<HistoryItem> filter = new ArrayList<>(); List<HistoryItem> filter = new ArrayList<>();
String query = arg0[0]; String query = arg0[0];
try { try {
query = query.replace(" ", "+"); query = SPACE_PATTERN.matcher(query).replaceAll("+");
URLEncoder.encode(query, ENCODING); URLEncoder.encode(query, ENCODING);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
e.printStackTrace(); e.printStackTrace();
@ -337,6 +341,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
@Override @Override
protected void onPostExecute(List<HistoryItem> result) { protected void onPostExecute(List<HistoryItem> result) {
mIsExecuting = false;
synchronized (mSuggestions) { synchronized (mSuggestions) {
mSuggestions.clear(); mSuggestions.clear();
mSuggestions.addAll(result); mSuggestions.addAll(result);
@ -348,7 +353,6 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
mFilteredList.addAll(filtered); mFilteredList.addAll(filtered);
notifyDataSetChanged(); notifyDataSetChanged();
} }
mIsExecuting = false;
} }
} }
@ -356,6 +360,7 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
/** /**
* This method downloads the search suggestions for the specific query. * This method downloads the search suggestions for the specific query.
* NOTE: This is a blocking operation, do not run on the UI thread. * NOTE: This is a blocking operation, do not run on the UI thread.
*
* @param query the query to get suggestions for * @param query the query to get suggestions for
* @return the cache file containing the suggestions * @return the cache file containing the suggestions
*/ */
@ -409,32 +414,6 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
return connectivity.getActiveNetworkInfo(); return connectivity.getActiveNetworkInfo();
} }
// The old suggestions algorithm, leaving here just for reference
// private List<HistoryItem> getSuggestions() {
// List<HistoryItem> filteredList = new ArrayList<>();
//
// int suggestionsSize = mSuggestions.size();
// int historySize = mHistory.size();
// int bookmarkSize = mBookmarks.size();
//
// int maxSuggestions = (bookmarkSize + historySize < 3) ? (5 - bookmarkSize - historySize) : (bookmarkSize < 2) ? (4 - bookmarkSize) : (historySize < 1) ? 3 : 2;
// int maxHistory = (suggestionsSize + bookmarkSize < 4) ? (5 - suggestionsSize - bookmarkSize) : 1;
// int maxBookmarks = (suggestionsSize + historySize < 3) ? (5 - suggestionsSize - historySize) : 2;
//
// for (int n = 0; n < bookmarkSize && n < maxBookmarks; n++) {
// filteredList.add(mBookmarks.get(n));
// }
//
// for (int n = 0; n < historySize && n < maxHistory; n++) {
// filteredList.add(mHistory.get(n));
// }
//
// for (int n = 0; n < suggestionsSize && n < maxSuggestions; n++) {
// filteredList.add(mSuggestions.get(n));
// }
// return filteredList;
// }
private List<HistoryItem> getFilteredList() { private List<HistoryItem> getFilteredList() {
List<HistoryItem> list = new ArrayList<>(5); List<HistoryItem> list = new ArrayList<>(5);
synchronized (mBookmarks) { synchronized (mBookmarks) {

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

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

30
app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java

@ -385,16 +385,16 @@ public class ArticleTextExtractor {
// System.out.println("date modified element " + elem.toString()); // System.out.println("date modified element " + elem.toString());
} }
if (dateStr != null && dateStr.isEmpty()) { if (dateStr.isEmpty()) {
dateStr = SHelper.innerTrim(doc.select("meta[name=utime]").attr("content")); dateStr = SHelper.innerTrim(doc.select("meta[name=utime]").attr("content"));
} }
if (dateStr != null && dateStr.isEmpty()) { if (dateStr.isEmpty()) {
dateStr = SHelper.innerTrim(doc.select("meta[name=pdate]").attr("content")); dateStr = SHelper.innerTrim(doc.select("meta[name=pdate]").attr("content"));
} }
if (dateStr != null && dateStr.isEmpty()) { if (dateStr.isEmpty()) {
dateStr = SHelper.innerTrim(doc.select("meta[property=article:published]").attr("content")); dateStr = SHelper.innerTrim(doc.select("meta[property=article:published]").attr("content"));
} }
if (dateStr != null && dateStr.isEmpty()) { if (dateStr.isEmpty()) {
return parseDate(dateStr); return parseDate(dateStr);
} }
@ -492,9 +492,7 @@ public class ArticleTextExtractor {
if (el.hasAttr("content")) { if (el.hasAttr("content")) {
dateStr = el.attr("content"); dateStr = el.attr("content");
Date parsedDate = parseDate(dateStr); Date parsedDate = parseDate(dateStr);
if (parsedDate != null) { return parsedDate;
return parsedDate;
}
} }
} }
@ -686,14 +684,12 @@ public class ArticleTextExtractor {
private static Collection<String> extractKeywords(Document doc) { private static Collection<String> extractKeywords(Document doc) {
String content = SHelper.innerTrim(doc.select("head meta[name=keywords]").attr("content")); String content = SHelper.innerTrim(doc.select("head meta[name=keywords]").attr("content"));
if (content != null) { if (content.startsWith("[") && content.endsWith("]"))
if (content.startsWith("[") && content.endsWith("]")) content = content.substring(1, content.length() - 1);
content = content.substring(1, content.length() - 1);
String[] split = content.split("\\s*,\\s*"); String[] split = content.split("\\s*,\\s*");
if (split.length > 1 || (split.length > 0 && split[0] != null && !split[0].isEmpty())) if (split.length > 1 || (split.length > 0 && split[0] != null && !split[0].isEmpty()))
return Arrays.asList(split); return Arrays.asList(split);
}
return Collections.emptyList(); return Collections.emptyList();
} }
@ -968,7 +964,7 @@ public class ArticleTextExtractor {
return weight; return weight;
} }
private Element determineImageSource(Element el, List<ImageResult> images) { private static Element determineImageSource(Element el, List<ImageResult> images) {
int maxWeight = 0; int maxWeight = 0;
Element maxNode = null; Element maxNode = null;
Elements els = el.select("img"); Elements els = el.select("img");
@ -1190,7 +1186,7 @@ public class ArticleTextExtractor {
charlen = 4; charlen = 4;
} else if (c <= 0xdfff) { } else if (c <= 0xdfff) {
charlen = 0; charlen = 0;
} else if (c <= 0xffff) { } else {
charlen = 3; charlen = 3;
} }
if (resultlen + charlen > length) { if (resultlen + charlen > length) {
@ -1208,7 +1204,7 @@ public class ArticleTextExtractor {
* *
* @author Chris Alexander, chris@chris-alexander.co.uk * @author Chris Alexander, chris@chris-alexander.co.uk
*/ */
private class ImageComparator implements Comparator<ImageResult> { private static class ImageComparator implements Comparator<ImageResult> {
@Override @Override
public int compare(ImageResult o1, ImageResult o2) { public int compare(ImageResult o1, ImageResult o2) {

22
app/src/main/java/acr/browser/lightning/reading/Converter.java

@ -35,9 +35,9 @@ import acr.browser.lightning.constant.Constants;
*/ */
public class Converter { public class Converter {
public final static String UTF8 = "UTF-8"; private final static String UTF8 = "UTF-8";
public final static String ISO = "ISO-8859-1"; private final static String ISO = "ISO-8859-1";
public final static int K2 = 2048; private final static int K2 = 2048;
private int maxBytes = 1000000 / 2; private int maxBytes = 1000000 / 2;
private String encoding; private String encoding;
private String url; private String url;
@ -99,7 +99,7 @@ public class Converter {
* The max bytes that we want to read from the input stream * The max bytes that we want to read from the input stream
* @return String * @return String
*/ */
public String streamToString(InputStream is, int maxBytes, String enc) { private String streamToString(InputStream is, int maxBytes, String enc) {
encoding = enc; encoding = enc;
// Http 1.1. standard is iso-8859-1 not utf8 :( // Http 1.1. standard is iso-8859-1 not utf8 :(
// but we force utf-8 as youtube assumes it ;) // but we force utf-8 as youtube assumes it ;)
@ -181,8 +181,8 @@ public class Converter {
* *
* @throws IOException * @throws IOException
*/ */
protected static String detectCharset(String key, ByteArrayOutputStream bos, BufferedInputStream in, private static String detectCharset(String key, ByteArrayOutputStream bos, BufferedInputStream in,
String enc) throws IOException { String enc) throws IOException {
// Grab better encoding from stream // Grab better encoding from stream
byte[] arr = new byte[K2]; byte[] arr = new byte[K2];
@ -204,24 +204,24 @@ public class Converter {
int lastEncIndex; int lastEncIndex;
if (startChar == '\'') if (startChar == '\'')
// if we have charset='something' // if we have charset='something'
lastEncIndex = str.indexOf("'", ++encIndex + clength); lastEncIndex = str.indexOf('\'', ++encIndex + clength);
else if (startChar == '\"') else if (startChar == '\"')
// if we have charset="something" // if we have charset="something"
lastEncIndex = str.indexOf("\"", ++encIndex + clength); lastEncIndex = str.indexOf('\"', ++encIndex + clength);
else { else {
// if we have "text/html; charset=utf-8" // if we have "text/html; charset=utf-8"
int first = str.indexOf("\"", encIndex + clength); int first = str.indexOf('\"', encIndex + clength);
if (first < 0) if (first < 0)
first = Integer.MAX_VALUE; first = Integer.MAX_VALUE;
// or "text/html; charset=utf-8 " // or "text/html; charset=utf-8 "
int sec = str.indexOf(" ", encIndex + clength); int sec = str.indexOf(' ', encIndex + clength);
if (sec < 0) if (sec < 0)
sec = Integer.MAX_VALUE; sec = Integer.MAX_VALUE;
lastEncIndex = Math.min(first, sec); lastEncIndex = Math.min(first, sec);
// or "text/html; charset=utf-8 ' // or "text/html; charset=utf-8 '
int third = str.indexOf("'", encIndex + clength); int third = str.indexOf('\'', encIndex + clength);
if (third > 0) if (third > 0)
lastEncIndex = Math.min(lastEncIndex, third); lastEncIndex = Math.min(lastEncIndex, third);
} }

33
app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java

@ -28,6 +28,7 @@ import java.net.URL;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
@ -39,6 +40,8 @@ import java.util.zip.InflaterInputStream;
*/ */
public class HtmlFetcher { public class HtmlFetcher {
private static final Pattern SPACE = Pattern.compile(" ");
static { static {
SHelper.enableCookieMgmt(); SHelper.enableCookieMgmt();
SHelper.enableUserAgentOverwrite(); SHelper.enableUserAgentOverwrite();
@ -50,8 +53,8 @@ public class HtmlFetcher {
String line; String line;
Set<String> existing = new LinkedHashSet<>(); Set<String> existing = new LinkedHashSet<>();
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
int index1 = line.indexOf("\""); int index1 = line.indexOf('\"');
int index2 = line.indexOf("\"", index1 + 1); int index2 = line.indexOf('\"', index1 + 1);
String url = line.substring(index1 + 1, index2); String url = line.substring(index1 + 1, index2);
String domainStr = SHelper.extractDomain(url, true); String domainStr = SHelper.extractDomain(url, true);
String counterStr = ""; String counterStr = "";
@ -204,8 +207,8 @@ public class HtmlFetcher {
// main workhorse to call externally // main workhorse to call externally
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
public JResult fetchAndExtract(String url, int timeout, boolean resolve, private JResult fetchAndExtract(String url, int timeout, boolean resolve,
int maxContentSize, boolean forceReload) throws Exception { int maxContentSize, boolean forceReload) throws Exception {
String originalUrl = url; String originalUrl = url;
url = SHelper.removeHashbang(url); url = SHelper.removeHashbang(url);
String gUrl = SHelper.getUrlFromUglyGoogleRedirect(url); String gUrl = SHelper.getUrlFromUglyGoogleRedirect(url);
@ -298,7 +301,7 @@ public class HtmlFetcher {
} }
// Ugly hack to break free from any cached versions, a few URLs required this. // Ugly hack to break free from any cached versions, a few URLs required this.
public static String getURLtoBreakCache(String url) { private static String getURLtoBreakCache(String url) {
try { try {
URL aURL = new URL(url); URL aURL = new URL(url);
if (aURL.getQuery() != null && aURL.getQuery().isEmpty()) { if (aURL.getQuery() != null && aURL.getQuery().isEmpty()) {
@ -311,7 +314,7 @@ public class HtmlFetcher {
} }
} }
public String lessText(String text) { private String lessText(String text) {
if (text == null) if (text == null)
return ""; return "";
@ -325,13 +328,13 @@ public class HtmlFetcher {
return SHelper.useDomainOfFirstArg4Second(url, urlOrPath); return SHelper.useDomainOfFirstArg4Second(url, urlOrPath);
} }
public String fetchAsString(String urlAsString, int timeout) private String fetchAsString(String urlAsString, int timeout)
throws IOException { throws IOException {
return fetchAsString(urlAsString, timeout, true); return fetchAsString(urlAsString, timeout, true);
} }
// main routine to get raw webpage content // main routine to get raw webpage content
public String fetchAsString(String urlAsString, int timeout, boolean includeSomeGooseOptions) private String fetchAsString(String urlAsString, int timeout, boolean includeSomeGooseOptions)
throws IOException { throws IOException {
HttpURLConnection hConn = createUrlConnection(urlAsString, timeout, includeSomeGooseOptions); HttpURLConnection hConn = createUrlConnection(urlAsString, timeout, includeSomeGooseOptions);
hConn.setInstanceFollowRedirects(true); hConn.setInstanceFollowRedirects(true);
@ -349,7 +352,7 @@ public class HtmlFetcher {
return createConverter(urlAsString).streamToString(is, enc); return createConverter(urlAsString).streamToString(is, enc);
} }
public static Converter createConverter(String url) { private static Converter createConverter(String url) {
return new Converter(url); return new Converter(url);
} }
@ -361,8 +364,8 @@ public class HtmlFetcher {
* @return the resolved url if any. Or null if it couldn't resolve the url * @return the resolved url if any. Or null if it couldn't resolve the url
* (within the specified time) or the same url if response code is OK * (within the specified time) or the same url if response code is OK
*/ */
public String getResolvedUrl(String urlAsString, int timeout, private String getResolvedUrl(String urlAsString, int timeout,
int num_redirects) { int num_redirects) {
String newUrl = null; String newUrl = null;
int responseCode = -1; int responseCode = -1;
try { try {
@ -381,7 +384,7 @@ public class HtmlFetcher {
newUrl = hConn.getHeaderField("Location"); newUrl = hConn.getHeaderField("Location");
// Note that the max recursion level is 5. // Note that the max recursion level is 5.
if (responseCode / 100 == 3 && newUrl != null && num_redirects < 5) { if (responseCode / 100 == 3 && newUrl != null && num_redirects < 5) {
newUrl = newUrl.replaceAll(" ", "+"); newUrl = SPACE.matcher(newUrl).replaceAll("+");
// some services use (none-standard) utf8 in their location header // some services use (none-standard) utf8 in their location header
if (urlAsString.startsWith("http://bit.ly") if (urlAsString.startsWith("http://bit.ly")
|| urlAsString.startsWith("http://is.gd")) || urlAsString.startsWith("http://is.gd"))
@ -413,7 +416,7 @@ public class HtmlFetcher {
* to non-ASCII characters. Workaround for broken origin servers that send * to non-ASCII characters. Workaround for broken origin servers that send
* UTF-8 in the Location: header. * UTF-8 in the Location: header.
*/ */
static String encodeUriFromHeader(String badLocation) { private static String encodeUriFromHeader(String badLocation) {
StringBuilder sb = new StringBuilder(badLocation.length()); StringBuilder sb = new StringBuilder(badLocation.length());
for (char ch : badLocation.toCharArray()) { for (char ch : badLocation.toCharArray()) {
@ -428,8 +431,8 @@ public class HtmlFetcher {
return sb.toString(); return sb.toString();
} }
protected HttpURLConnection createUrlConnection(String urlAsStr, int timeout, private HttpURLConnection createUrlConnection(String urlAsStr, int timeout,
boolean includeSomeGooseOptions) throws IOException { boolean includeSomeGooseOptions) throws IOException {
URL url = new URL(urlAsStr); URL url = new URL(urlAsStr);
//using proxy may increase latency //using proxy may increase latency
HttpURLConnection hConn = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); HttpURLConnection hConn = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);

14
app/src/main/java/acr/browser/lightning/reading/ImageResult.java

@ -7,15 +7,15 @@ import org.jsoup.nodes.Element;
* *
* @author Chris Alexander, chris@chris-alexander.co.uk * @author Chris Alexander, chris@chris-alexander.co.uk
*/ */
public class ImageResult { class ImageResult {
public final String src; private final String src;
public final Integer weight; public final Integer weight;
public final String title; private final String title;
public final int height; private final int height;
public final int width; private final int width;
public final String alt; private final String alt;
public final boolean noFollow; private final boolean noFollow;
public Element element; public Element element;
public ImageResult(String src, Integer weight, String title, int height, int width, String alt, public ImageResult(String src, Integer weight, String title, int height, int width, String alt,

4
app/src/main/java/acr/browser/lightning/reading/JResult.java

@ -47,7 +47,7 @@ public class JResult implements Serializable {
private Date date; private Date date;
private Collection<String> keywords; private Collection<String> keywords;
private List<ImageResult> images = null; private List<ImageResult> images = null;
private List<Map<String, String>> links = new ArrayList<>(); private final List<Map<String, String>> links = new ArrayList<>();
private String type; private String type;
private String sitename; private String sitename;
private String language; private String language;
@ -230,7 +230,7 @@ public class JResult implements Serializable {
} }
public void addLink(String url, String text, Integer pos) { public void addLink(String url, String text, Integer pos) {
Map link = new HashMap(); Map<String, String> link = new HashMap<>();
link.put("url", url); link.put("url", url);
link.put("text", text); link.put("text", text);
link.put("offset", String.valueOf(pos)); link.put("offset", String.valueOf(pos));

26
app/src/main/java/acr/browser/lightning/reading/OutputFormatter.java

@ -41,8 +41,8 @@ public class OutputFormatter {
this(minFirstParagraphText, minParagraphText, NODES_TO_REPLACE); this(minFirstParagraphText, minParagraphText, NODES_TO_REPLACE);
} }
public OutputFormatter(int minFirstParagraphText, int minParagraphText, private OutputFormatter(int minFirstParagraphText, int minParagraphText,
List<String> nodesToReplace) { List<String> nodesToReplace) {
this.minFirstParagraphText = minFirstParagraphText; this.minFirstParagraphText = minFirstParagraphText;
this.minParagraphText = minParagraphText; this.minParagraphText = minParagraphText;
this.nodesToReplace = nodesToReplace; this.nodesToReplace = nodesToReplace;
@ -91,7 +91,7 @@ public class OutputFormatter {
* If there are elements inside our top node that have a negative gravity * If there are elements inside our top node that have a negative gravity
* score remove them * score remove them
*/ */
protected void removeNodesWithNegativeScores(Element topNode) { private void removeNodesWithNegativeScores(Element topNode) {
Elements gravityItems = topNode.select("*[gravityScore]"); Elements gravityItems = topNode.select("*[gravityScore]");
for (Element item : gravityItems) { for (Element item : gravityItems) {
int score = getScore(item); int score = getScore(item);
@ -102,7 +102,7 @@ public class OutputFormatter {
} }
} }
protected int append(Element node, StringBuilder sb, String tagName) { private int append(Element node, StringBuilder sb, String tagName) {
int countOfP = 0; // Number of P elements in the article int countOfP = 0; // Number of P elements in the article
int paragraphWithTextIndex = 0; int paragraphWithTextIndex = 0;
// is select more costly then getElementsByTag? // is select more costly then getElementsByTag?
@ -134,14 +134,14 @@ public class OutputFormatter {
return countOfP; return countOfP;
} }
protected static void setParagraphIndex(Element node, String tagName) { private static void setParagraphIndex(Element node, String tagName) {
int paragraphIndex = 0; int paragraphIndex = 0;
for (Element e : node.select(tagName)) { for (Element e : node.select(tagName)) {
e.attr("paragraphIndex", Integer.toString(paragraphIndex++)); e.attr("paragraphIndex", Integer.toString(paragraphIndex++));
} }
} }
protected int getMinParagraph(int paragraphIndex) { private int getMinParagraph(int paragraphIndex) {
if (paragraphIndex < 1) { if (paragraphIndex < 1) {
return minFirstParagraphText; return minFirstParagraphText;
} else { } else {
@ -149,7 +149,7 @@ public class OutputFormatter {
} }
} }
protected static int getParagraphIndex(Element el) { private static int getParagraphIndex(Element el) {
try { try {
return Integer.parseInt(el.attr("paragraphIndex")); return Integer.parseInt(el.attr("paragraphIndex"));
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
@ -157,7 +157,7 @@ public class OutputFormatter {
} }
} }
protected static int getScore(Element el) { private static int getScore(Element el) {
try { try {
return Integer.parseInt(el.attr("gravityScore")); return Integer.parseInt(el.attr("gravityScore"));
} catch (Exception ex) { } catch (Exception ex) {
@ -165,7 +165,7 @@ public class OutputFormatter {
} }
} }
boolean unlikely(Node e) { private boolean unlikely(Node e) {
if (e.attr("class") != null && e.attr("class").toLowerCase().contains("caption")) if (e.attr("class") != null && e.attr("class").toLowerCase().contains("caption"))
return true; return true;
@ -174,7 +174,7 @@ public class OutputFormatter {
return unlikelyPattern.matcher(style).find() || unlikelyPattern.matcher(clazz).find(); return unlikelyPattern.matcher(style).find() || unlikelyPattern.matcher(clazz).find();
} }
void appendTextSkipHidden(Element e, StringBuilder accum, int indent) { private void appendTextSkipHidden(Element e, StringBuilder accum, int indent) {
for (Node child : e.childNodes()) { for (Node child : e.childNodes()) {
if (unlikely(child)) { if (unlikely(child)) {
continue; continue;
@ -195,17 +195,17 @@ public class OutputFormatter {
} }
} }
static boolean lastCharIsWhitespace(StringBuilder accum) { private static boolean lastCharIsWhitespace(StringBuilder accum) {
return accum.length() != 0 && Character.isWhitespace(accum.charAt(accum.length() - 1)); return accum.length() != 0 && Character.isWhitespace(accum.charAt(accum.length() - 1));
} }
protected String node2Text(Element el) { private String node2Text(Element el) {
StringBuilder sb = new StringBuilder(200); StringBuilder sb = new StringBuilder(200);
appendTextSkipHidden(el, sb, 0); appendTextSkipHidden(el, sb, 0);
return sb.toString(); return sb.toString();
} }
public OutputFormatter setUnlikelyPattern(String unlikelyPattern) { private OutputFormatter setUnlikelyPattern(String unlikelyPattern) {
this.unlikelyPattern = Pattern.compile(unlikelyPattern); this.unlikelyPattern = Pattern.compile(unlikelyPattern);
return this; return this;
} }

12
app/src/main/java/acr/browser/lightning/reading/SHelper.java

@ -39,9 +39,9 @@ import javax.net.ssl.X509TrustManager;
/** /**
* @author Peter Karich * @author Peter Karich
*/ */
public class SHelper { class SHelper {
public static final String UTF8 = "UTF-8"; private static final String UTF8 = "UTF-8";
private static final Pattern SPACE = Pattern.compile(" "); private static final Pattern SPACE = Pattern.compile(" ");
public static String replaceSpaces(String url) { public static String replaceSpaces(String url) {
@ -122,7 +122,7 @@ public class SHelper {
return str1.substring(res[0], res[1]); return str1.substring(res[0], res[1]);
} }
public static int[] longestSubstring(String str1, String str2) { private static int[] longestSubstring(String str1, String str2) {
if (str1 == null || str1.isEmpty() || str2 == null || str2.isEmpty()) if (str1 == null || str1.isEmpty() || str2 == null || str2.isEmpty())
return null; return null;
@ -193,7 +193,7 @@ public class SHelper {
url = url.substring("m.".length()); url = url.substring("m.".length());
} }
int slashIndex = url.indexOf("/"); int slashIndex = url.indexOf('/');
if (slashIndex > 0) if (slashIndex > 0)
url = url.substring(0, slashIndex); url = url.substring(0, slashIndex);
@ -280,7 +280,7 @@ public class SHelper {
} }
} }
public static String urlDecode(String str) { private static String urlDecode(String str) {
try { try {
return URLDecoder.decode(str, UTF8); return URLDecoder.decode(str, UTF8);
} catch (UnsupportedEncodingException ex) { } catch (UnsupportedEncodingException ex) {
@ -300,7 +300,7 @@ public class SHelper {
return printNode(root, 0); return printNode(root, 0);
} }
public static String printNode(Element root, int indentation) { private static String printNode(Element root, int indentation) {
StringBuilder sb = new StringBuilder(indentation); StringBuilder sb = new StringBuilder(indentation);
for (int i = 0; i < indentation; i++) { for (int i = 0; i < indentation; i++) {
sb.append(' '); sb.append(' ');

5
app/src/main/java/acr/browser/lightning/utils/AdBlock.java

@ -58,8 +58,9 @@ public class AdBlock {
@Override @Override
public void run() { public void run() {
AssetManager asset = context.getAssets(); AssetManager asset = context.getAssets();
BufferedReader reader = null;
try { try {
BufferedReader reader = new BufferedReader(new InputStreamReader( reader = new BufferedReader(new InputStreamReader(
asset.open(BLOCKED_DOMAINS_LIST_FILE_NAME))); asset.open(BLOCKED_DOMAINS_LIST_FILE_NAME)));
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
@ -68,6 +69,8 @@ public class AdBlock {
} catch (IOException e) { } catch (IOException e) {
Log.wtf(TAG, "Reading blocked domains list from file '" Log.wtf(TAG, "Reading blocked domains list from file '"
+ BLOCKED_DOMAINS_LIST_FILE_NAME + "' failed.", e); + BLOCKED_DOMAINS_LIST_FILE_NAME + "' failed.", e);
} finally {
Utils.close(reader);
} }
} }
}); });

6
app/src/main/java/acr/browser/lightning/utils/IntentUtils.java

@ -16,8 +16,6 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import acr.browser.lightning.controller.BrowserController;
public class IntentUtils { public class IntentUtils {
private final Context mActivity; private final Context mActivity;
@ -29,8 +27,8 @@ public class IntentUtils {
"(?:http|https|file):\\/\\/" + "|(?:inline|data|about|javascript):" + "|(?:.*:.*@)" "(?:http|https|file):\\/\\/" + "|(?:inline|data|about|javascript):" + "|(?:.*:.*@)"
+ ')' + "(.*)"); + ')' + "(.*)");
public IntentUtils(Context context) { public IntentUtils(Activity activity) {
mActivity = context.getApplicationContext(); mActivity = activity;
} }
public boolean startActivityForUrl(WebView tab, String url) { public boolean startActivityForUrl(WebView tab, String url) {

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

@ -1,7 +1,6 @@
package acr.browser.lightning.utils; package acr.browser.lightning.utils;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@ -12,11 +11,11 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.AttrRes; import android.support.annotation.AttrRes;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.TypedValue; import android.util.TypedValue;
import android.widget.ImageView; import android.widget.ImageView;
@ -46,17 +45,11 @@ public class ThemeUtils {
} }
public static int getIconLightThemeColor(@NonNull Context context) { public static int getIconLightThemeColor(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return ContextCompat.getColor(context, R.color.icon_light_theme);
return context.getResources().getColor(R.color.icon_light_theme, context.getTheme());
}
return context.getResources().getColor(R.color.icon_light_theme);
} }
public static int getIconDarkThemeColor(@NonNull Context context) { public static int getIconDarkThemeColor(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return ContextCompat.getColor(context, R.color.icon_dark_theme);
return context.getResources().getColor(R.color.icon_dark_theme, context.getTheme());
}
return context.getResources().getColor(R.color.icon_dark_theme);
} }
public static void themeImageView(ImageView icon, Context context, boolean dark) { public static void themeImageView(ImageView icon, Context context, boolean dark) {
@ -80,12 +73,7 @@ public class ThemeUtils {
@Nullable @Nullable
public static Drawable getThemedDrawable(@NonNull Context context, @DrawableRes int res, boolean dark) { public static Drawable getThemedDrawable(@NonNull Context context, @DrawableRes int res, boolean dark) {
int color = dark ? getIconDarkThemeColor(context) : getIconLightThemeColor(context); int color = dark ? getIconDarkThemeColor(context) : getIconLightThemeColor(context);
final Drawable drawable; final Drawable drawable = ContextCompat.getDrawable(context, res);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = context.getResources().getDrawable(res);
} else {
drawable = context.getDrawable(res);
}
if (drawable == null) if (drawable == null)
return null; return null;
drawable.mutate(); drawable.mutate();
@ -95,12 +83,7 @@ public class ThemeUtils {
@Nullable @Nullable
public static Drawable getLightThemedDrawable(@NonNull Context context, @DrawableRes int res) { public static Drawable getLightThemedDrawable(@NonNull Context context, @DrawableRes int res) {
final Drawable drawable; final Drawable drawable = ContextCompat.getDrawable(context, res);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = context.getResources().getDrawable(res);
} else {
drawable = context.getDrawable(res);
}
if (drawable == null) if (drawable == null)
return null; return null;
drawable.mutate(); drawable.mutate();
@ -109,15 +92,12 @@ public class ThemeUtils {
} }
public static ColorDrawable getSelectedBackground(@NonNull Context context, boolean dark) { public static ColorDrawable getSelectedBackground(@NonNull Context context, boolean dark) {
Resources res = context.getResources(); final int color = (dark) ? ContextCompat.getColor(context, R.color.selected_dark) :
int color; ContextCompat.getColor(context, R.color.selected_light);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
color = (dark) ? res.getColor(R.color.selected_dark, context.getTheme()) :
res.getColor(R.color.selected_light, context.getTheme());
} else {
color = (dark) ? res.getColor(R.color.selected_dark) :
res.getColor(R.color.selected_light);
}
return new ColorDrawable(color); return new ColorDrawable(color);
} }
public static int getTextColor(Context context) {
return getColor(context, android.R.attr.editTextColor);
}
} }

2
app/src/main/java/acr/browser/lightning/utils/UrlUtils.java

@ -25,7 +25,7 @@ import java.util.regex.Pattern;
* Utility methods for Url manipulation * Utility methods for Url manipulation
*/ */
public class UrlUtils { public class UrlUtils {
static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile( private static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
"(?i)" + // switch on case insensitive matching "(?i)" + // switch on case insensitive matching
'(' + // begin group for schema '(' + // begin group for schema
"(?:http|https|file):\\/\\/" + "(?:http|https|file):\\/\\/" +

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

@ -18,10 +18,7 @@ import android.graphics.LinearGradient;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.Shader; import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@ -151,7 +148,7 @@ public final class Utils {
} }
} }
public static boolean deleteDir(File dir) { private static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) { if (dir != null && dir.isDirectory()) {
String[] children = dir.list(); String[] children = dir.list();
for (String aChildren : children) { for (String aChildren : children) {
@ -262,14 +259,6 @@ public final class Utils {
} }
} }
public static Drawable getDrawable(Context context, @DrawableRes int res) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return context.getDrawable(res);
} else {
return context.getResources().getDrawable(res);
}
}
/** /**
* Draws the trapezoid background for the horizontal tabs on a canvas object using * Draws the trapezoid background for the horizontal tabs on a canvas object using
* the specified color. * the specified color.

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

@ -23,7 +23,9 @@ public class WebUtils {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
c.removeAllCookies(null); c.removeAllCookies(null);
} else { } else {
//noinspection deprecation
CookieSyncManager.createInstance(context); CookieSyncManager.createInstance(context);
//noinspection deprecation
c.removeAllCookie(); c.removeAllCookie();
} }
} }
@ -38,7 +40,9 @@ public class WebUtils {
m.clearFormData(); m.clearFormData();
m.clearHttpAuthUsernamePassword(); m.clearHttpAuthUsernamePassword();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
//noinspection deprecation
m.clearUsernamePassword(); m.clearUsernamePassword();
//noinspection deprecation
WebIconDatabase.getInstance().removeAllIcons(); WebIconDatabase.getInstance().removeAllIcons();
} }
Utils.trimCache(context); Utils.trimCache(context);

44
app/src/main/java/acr/browser/lightning/view/IconCacheTask.java

@ -0,0 +1,44 @@
package acr.browser.lightning.view;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.utils.Utils;
/**
* @author Anthony C. Restaino
* @date 2015/09/29
*/
class IconCacheTask implements Runnable{
private final Uri uri;
private final Bitmap icon;
public IconCacheTask(Uri uri, Bitmap icon) {
this.uri = uri;
this.icon = icon;
}
@Override
public void run() {
String hash = String.valueOf(uri.getHost().hashCode());
Log.d(Constants.TAG, "Caching icon for " + uri.getHost());
FileOutputStream fos = null;
try {
File image = new File(BrowserApp.getAppContext().getCacheDir(), hash + ".png");
fos = new FileOutputStream(image);
icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
Utils.close(fos);
}
}
}

42
app/src/main/java/acr/browser/lightning/view/LightningView.java

@ -108,7 +108,9 @@ public class LightningView {
mWebView.setDrawingCacheEnabled(false); mWebView.setDrawingCacheEnabled(false);
mWebView.setWillNotCacheDrawing(true); mWebView.setWillNotCacheDrawing(true);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
//noinspection deprecation
mWebView.setAnimationCacheEnabled(false); mWebView.setAnimationCacheEnabled(false);
//noinspection deprecation
mWebView.setAlwaysDrawnWithCacheEnabled(false); mWebView.setAlwaysDrawnWithCacheEnabled(false);
} }
mWebView.setBackgroundColor(Color.WHITE); mWebView.setBackgroundColor(Color.WHITE);
@ -206,12 +208,15 @@ public class LightningView {
if (API < Build.VERSION_CODES.KITKAT) { if (API < Build.VERSION_CODES.KITKAT) {
switch (mPreferences.getFlashSupport()) { switch (mPreferences.getFlashSupport()) {
case 0: case 0:
//noinspection deprecation
settings.setPluginState(PluginState.OFF); settings.setPluginState(PluginState.OFF);
break; break;
case 1: case 1:
//noinspection deprecation
settings.setPluginState(PluginState.ON_DEMAND); settings.setPluginState(PluginState.ON_DEMAND);
break; break;
case 2: case 2:
//noinspection deprecation
settings.setPluginState(PluginState.ON); settings.setPluginState(PluginState.ON);
break; break;
default: default:
@ -222,12 +227,14 @@ public class LightningView {
setUserAgent(context, mPreferences.getUserAgentChoice()); setUserAgent(context, mPreferences.getUserAgentChoice());
if (mPreferences.getSavePasswordsEnabled() && !mIsIncognitoTab) { if (mPreferences.getSavePasswordsEnabled() && !mIsIncognitoTab) {
if (API < 18) { if (API < Build.VERSION_CODES.JELLY_BEAN_MR2) {
//noinspection deprecation
settings.setSavePassword(true); settings.setSavePassword(true);
} }
settings.setSaveFormData(true); settings.setSaveFormData(true);
} else { } else {
if (API < 18) { if (API < Build.VERSION_CODES.JELLY_BEAN_MR2) {
//noinspection deprecation
settings.setSavePassword(false); settings.setSavePassword(false);
} }
settings.setSaveFormData(false); settings.setSaveFormData(false);
@ -300,9 +307,11 @@ public class LightningView {
@SuppressLint("NewApi") @SuppressLint("NewApi")
private void initializeSettings(WebSettings settings, Context context) { private void initializeSettings(WebSettings settings, Context context) {
if (API < Build.VERSION_CODES.JELLY_BEAN_MR2) { if (API < Build.VERSION_CODES.JELLY_BEAN_MR2) {
//noinspection deprecation
settings.setAppCacheMaxSize(Long.MAX_VALUE); settings.setAppCacheMaxSize(Long.MAX_VALUE);
} }
if (API < Build.VERSION_CODES.JELLY_BEAN_MR1) { if (API < Build.VERSION_CODES.JELLY_BEAN_MR1) {
//noinspection deprecation
settings.setEnableSmoothTransition(true); settings.setEnableSmoothTransition(true);
} }
if (API > Build.VERSION_CODES.JELLY_BEAN) { if (API > Build.VERSION_CODES.JELLY_BEAN) {
@ -338,6 +347,7 @@ public class LightningView {
settings.setAppCachePath(context.getDir("appcache", 0).getPath()); settings.setAppCachePath(context.getDir("appcache", 0).getPath());
settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath()); settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath());
if (API < Build.VERSION_CODES.KITKAT) { if (API < Build.VERSION_CODES.KITKAT) {
//noinspection deprecation
settings.setDatabasePath(context.getDir("databases", 0).getPath()); settings.setDatabasePath(context.getDir("databases", 0).getPath());
} }
} }
@ -353,7 +363,7 @@ public class LightningView {
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
public void setUserAgent(Context context, int choice) { private void setUserAgent(Context context, int choice) {
if (mWebView == null) return; if (mWebView == null) return;
WebSettings settings = mWebView.getSettings(); WebSettings settings = mWebView.getSettings();
switch (choice) { switch (choice) {
@ -395,9 +405,11 @@ public class LightningView {
} }
public synchronized void freeMemory() { public synchronized void freeMemory() {
if (mWebView != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) if (mWebView != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
//noinspection deprecation
mWebView.freeMemory(); mWebView.freeMemory();
} }
}
public void setForegroundTab(boolean isForeground) { public void setForegroundTab(boolean isForeground) {
isForegroundTab = isForeground; isForegroundTab = isForeground;
@ -514,12 +526,26 @@ public class LightningView {
} }
} }
/**
* Naive caching of the favicon according to the domain name of the URL
* @param icon the icon to cache
*/
private void cacheFavicon(final Bitmap icon) {
if (icon == null) return;
final Uri uri = Uri.parse(getUrl());
if (uri.getHost() == null) {
return;
}
new Thread(new IconCacheTask(uri, icon)).start();
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
public synchronized void find(String text) { public synchronized void find(String text) {
if (mWebView != null) { if (mWebView != null) {
if (API >= Build.VERSION_CODES.JELLY_BEAN_MR1) { if (API >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mWebView.findAllAsync(text); mWebView.findAllAsync(text);
} else { } else {
//noinspection deprecation
mWebView.findAll(text); mWebView.findAll(text);
} }
} }
@ -640,12 +666,6 @@ public class LightningView {
} }
} }
public synchronized void invalidate() {
if (mWebView != null) {
mWebView.invalidate();
}
}
public String getTitle() { public String getTitle() {
return mTitle.getTitle(); return mTitle.getTitle();
} }
@ -722,7 +742,7 @@ public class LightningView {
msg.setTarget(webViewHandler); msg.setTarget(webViewHandler);
mWebView.requestFocusNodeHref(msg); mWebView.requestFocusNodeHref(msg);
} }
} }
} }
/** /**

3
app/src/main/res/layout/two_line_autocomplete.xml

@ -21,7 +21,8 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="5dp"> android:paddingLeft="5dp"
android:paddingRight="5dp">
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"

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

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

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

@ -3,6 +3,5 @@
<!-- Default screen margins, per the Android Design guidelines. --> <!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="navigation_width">260dp</dimen> <dimen name="navigation_width">260dp</dimen>
<dimen name="search_bar_height">48dp</dimen> <dimen name="search_bar_height">48dp</dimen>
<dimen name="abc_action_button_min_width_overflow_material">48dp</dimen>
</resources> </resources>

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

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

1
app/src/main/res/xml/preference_about.xml

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--suppress AndroidElementNotAllowed -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/settings_about"> <PreferenceCategory android:title="@string/settings_about">

3
app/src/main/res/xml/preference_bookmarks.xml

@ -8,5 +8,8 @@
<Preference <Preference
android:key="import_bookmark" android:key="import_bookmark"
android:title="@string/import_backup" /> android:title="@string/import_backup" />
<Preference
android:key="import_browser"
android:title="@string/importbookmarks" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>
Loading…
Cancel
Save