diff --git a/app/src/main/java/forgottosave/lichesswebapp/MainActivity.java b/app/src/main/java/forgottosave/lichesswebapp/MainActivity.java index 41b8439..65d634d 100644 --- a/app/src/main/java/forgottosave/lichesswebapp/MainActivity.java +++ b/app/src/main/java/forgottosave/lichesswebapp/MainActivity.java @@ -9,19 +9,16 @@ import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ClipboardManager; -import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.content.res.ColorStateList; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteStatement; import android.graphics.Bitmap; import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.http.SslCertificate; import android.net.http.SslError; @@ -32,17 +29,9 @@ import android.os.Message; import android.preference.PreferenceManager; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.Editable; import android.text.Html; -import android.text.TextWatcher; -import android.util.DisplayMetrics; import android.util.Log; import android.util.Patterns; -import android.util.TypedValue; -import android.view.GestureDetector; -import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; @@ -53,23 +42,14 @@ import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebSettings; -import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewClient; -import android.widget.ArrayAdapter; -import android.widget.BaseAdapter; import android.widget.EditText; -import android.widget.Filter; -import android.widget.Filterable; import android.widget.FrameLayout; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import org.json.JSONArray; -import org.json.JSONException; - -import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; @@ -81,13 +61,10 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -95,29 +72,11 @@ public class MainActivity extends Activity { - private static class Tab { - Tab(WebView w) { - this.webview = w; - } - - WebView webview; - boolean isDesktopUA; - } - - private static final String TAG = MainActivity.class.getSimpleName(); - - static final String searchUrl = "https://www.google.com/search?q=%s"; - static final String searchCompleteUrl = "https://www.google.com/complete/search?client=firefox&q=%s"; - static final String desktopUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36"; - + static final String startPageUrl = "https://lichess.org"; static final int FORM_FILE_CHOOSER = 1; - - static final int PERMISSION_REQUEST_EXPORT_BOOKMARKS = 1; - static final int PERMISSION_REQUEST_IMPORT_BOOKMARKS = 2; static final int PERMISSION_REQUEST_DOWNLOAD = 3; - private ArrayList tabs = new ArrayList<>(); - private int currentTabIndex; + private WebView baseWebView; private FrameLayout webviews; private boolean isFullscreen; private SharedPreferences prefs; @@ -125,73 +84,10 @@ private static class Tab { private ArrayList requestsLog; private final View[] fullScreenView = new View[1]; private final WebChromeClient.CustomViewCallback[] fullScreenCallback = new WebChromeClient.CustomViewCallback[1]; - private EditText searchEdit; - private TextView searchCount; - private TextView txtTabCount; - - private SQLiteDatabase placesDb; private ValueCallback fileUploadCallback; private boolean fileUploadCallbackShouldReset; - private static class MenuAction { - - static HashMap actions = new HashMap<>(); - - private MenuAction(String title, int icon, Runnable action) { - this(title, icon, action, null); - } - - private MenuAction(String title, int icon, Runnable action, MyBooleanSupplier getState) { - this.title = title; - this.icon = icon; - this.action = action; - this.getState = getState; - actions.put(title, this); - } - - @Override - public String toString() { - return title; - } - - private String title; - private int icon; - private Runnable action; - private MyBooleanSupplier getState; - } - - final String[] shortMenu = { - "Desktop UA", "Log requests", "Find on page", "Page info", "Share URL", - "Open URL in app", "Full menu" - }; - - MenuAction getAction(String name) { - MenuAction action = MenuAction.actions.get(name); - if (action == null) throw new IllegalArgumentException("name"); - return action; - } - - static class TitleAndUrl { - String title; - String url; - } - - static class TitleAndBundle { - String title; - Bundle bundle; - } - - private ArrayList closedTabs = new ArrayList<>(); - - private Tab getCurrentTab() { - return tabs.get(currentTabIndex); - } - - private WebView getCurrentWebView() { - return getCurrentTab().webview; - } - @SuppressLint({"SetJavaScriptEnabled", "DefaultLocale"}) private WebView createWebView(Bundle bundle) { final ProgressBar progressBar = findViewById(R.id.progressbar); @@ -286,7 +182,7 @@ public void onPageStarted(WebView view, String url, Bitmap favicon) { progressBar.setProgress(0); progressBar.setVisibility(View.VISIBLE); // TODO findViewById(R.id.loadingScreen).setVisibility(View.VISIBLE); - if (view == getCurrentWebView()) { + if (view == baseWebView) { view.requestFocus(); } injectCSS(view); @@ -297,24 +193,6 @@ public void onPageFinished(WebView view, String url) { injectCSS(view); } - @Override - public void onReceivedHttpAuthRequest(WebView view, final HttpAuthHandler handler, String host, String realm) { - new AlertDialog.Builder(MainActivity.this) - .setTitle(host) - .setView(R.layout.login_password) - .setCancelable(false) - .setPositiveButton("OK", (dialog, which) -> { - String username = ((EditText) ((Dialog) dialog).findViewById(R.id.username)).getText().toString(); - String password = ((EditText) ((Dialog) dialog).findViewById(R.id.password)).getText().toString(); - handler.proceed(username, password); - }) - .setNegativeButton("Cancel", (dialog, which) -> handler.cancel()).show(); - } - - final InputStream emptyInputStream = new ByteArrayInputStream(new byte[0]); - - String lastMainPage = ""; - @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // For intent:// URLs, redirect to browser_fallback_url if given @@ -415,16 +293,13 @@ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError e .setNegativeButton("Cancel", (dialog, which) -> {}) .show(); }); - webview.setFindListener((activeMatchOrdinal, numberOfMatches, isDoneCounting) -> - searchCount.setText(numberOfMatches == 0 ? "Not found" : - String.format("%d / %d", activeMatchOrdinal + 1, numberOfMatches))); return webview; } private void showLongPressMenu(String linkUrl, String imageUrl) { String url; String title; - String[] options = new String[]{"Open in new tab", "Copy URL", "Show full URL", "Download"}; + String[] options = new String[]{"Copy URL", "Show full URL", "App Settings"}; if (imageUrl == null) { if (linkUrl == null) { @@ -452,25 +327,19 @@ private void showLongPressMenu(String linkUrl, String imageUrl) { new AlertDialog.Builder(MainActivity.this).setTitle(title).setItems(options, (dialog, which) -> { switch (which) { case 0: - newTab(url); - break; - case 1: ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); assert clipboard != null; ClipData clipData = ClipData.newPlainText("URL", url); clipboard.setPrimaryClip(clipData); break; - case 2: + case 1: new AlertDialog.Builder(MainActivity.this) .setTitle("Full URL") .setMessage(url) .setPositiveButton("OK", (dialog1, which1) -> {}) .show(); break; - case 3: - startDownload(url, null); - break; - case 4: + case 2: showLongPressMenu(null, imageUrl); break; } @@ -533,37 +402,17 @@ private void startDownload(String url, String filename) { dm.enqueue(request); } - private void newTabCommon(WebView webview) { - boolean isDesktopUA = !tabs.isEmpty() && getCurrentTab().isDesktopUA; - webview.getSettings().setUserAgentString(isDesktopUA ? desktopUA : null); - webview.getSettings().setUseWideViewPort(isDesktopUA); + private void newTab(String url) { + WebView webview = createWebView(null); + webview.getSettings().setUserAgentString(null); + webview.getSettings().setUseWideViewPort(false); webview.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); webview.setVisibility(View.GONE); - Tab tab = new Tab(webview); - tab.isDesktopUA = isDesktopUA; - tabs.add(tab); + baseWebView = webview; webviews.addView(webview); - setTabCountText(tabs.size()); - } - - private void newTab(String url) { - WebView webview = createWebView(null); - newTabCommon(webview); loadUrl(url, webview); } - private void newTabFromBundle(Bundle bundle) { - WebView webview = createWebView(bundle); - newTabCommon(webview); - } - - private void switchToTab(int tab) { - getCurrentWebView().setVisibility(View.GONE); - currentTabIndex = tab; - getCurrentWebView().setVisibility(View.VISIBLE); - getCurrentWebView().requestFocus(); - } - private void updateFullScreen() { final int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar @@ -587,12 +436,6 @@ public void uncaughtException(Thread t, Throwable e) { } }); - try { - placesDb = new PlacesDbHelper(this).getWritableDatabase(); - } catch (SQLiteException e) { - Log.e(TAG, "Can't open database", e); - } - prefs = PreferenceManager.getDefaultSharedPreferences(this); setContentView(R.layout.activity_main); getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> updateFullScreen()); @@ -600,24 +443,20 @@ public void uncaughtException(Thread t, Throwable e) { isFullscreen = false; webviews = findViewById(R.id.webviews); - currentTabIndex = 0; // setup edit text findViewById(R.id.searchFindNext).setOnClickListener(v -> { hideKeyboard(); - getCurrentWebView().findNext(true); + baseWebView.findNext(true); }); - newTab("https://lichess.org"); - getCurrentWebView().setVisibility(View.VISIBLE); - getCurrentWebView().requestFocus(); + newTab(startPageUrl); + baseWebView.setVisibility(View.VISIBLE); + baseWebView.requestFocus(); } @Override protected void onDestroy() { - if (placesDb != null) { - placesDb.close(); - } super.onDestroy(); } @@ -637,291 +476,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } - private void setTabCountText(int count) { - if (txtTabCount != null) { - txtTabCount.setText(String.valueOf(count)); - } - } - - private void pageInfo() { - String s = "URL: " + getCurrentWebView().getUrl() + "\n"; - s += "Title: " + getCurrentWebView().getTitle() + "\n\n"; - SslCertificate certificate = getCurrentWebView().getCertificate(); - s += certificate == null ? "Not secure" : "Certificate:\n" + certificateToStr(certificate); - - new AlertDialog.Builder(this) - .setTitle("Page info") - .setMessage(s) - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - } - - private void toggleFullscreen() { - isFullscreen = !isFullscreen; - updateFullScreen(); - } - - private void showBookmarks() { - if (placesDb == null) return; - Cursor cursor = placesDb.rawQuery("SELECT title, url, id as _id FROM bookmarks", null); - AlertDialog dialog = new AlertDialog.Builder(this) - .setTitle("Bookmarks") - .setOnDismissListener(dlg -> cursor.close()) - .setCursor(cursor, (dlg, which) -> { - cursor.moveToPosition(which); - String url = cursor.getString(cursor.getColumnIndex("url")); - loadUrl(url, getCurrentWebView()); - }, "title") - .create(); - dialog.getListView().setOnItemLongClickListener((parent, view, position, id) -> { - cursor.moveToPosition(position); - int rowid = cursor.getInt(cursor.getColumnIndex("_id")); - String title = cursor.getString(cursor.getColumnIndex("title")); - String url = cursor.getString(cursor.getColumnIndex("url")); - dialog.dismiss(); - new AlertDialog.Builder(MainActivity.this) - .setTitle(title) - .setItems(new String[] {"Rename", "Change URL", "Delete"}, (dlg, which) -> { - switch (which) { - case 0: { - EditText editView = new EditText(this); - editView.setText(title); - new AlertDialog.Builder(this) - .setTitle("Rename bookmark") - .setView(editView) - .setPositiveButton("Rename", (renameDlg, which1) -> { - placesDb.execSQL("UPDATE bookmarks SET title=? WHERE id=?", new Object[] {editView.getText(), rowid}); - }) - .setNegativeButton("Cancel", (renameDlg, which1) -> { - }) - .show(); - break; - } - case 1: { - EditText editView = new EditText(this); - editView.setText(url); - new AlertDialog.Builder(this) - .setTitle("Change bookmark URL") - .setView(editView) - .setPositiveButton("Change URL", (renameDlg, which1) -> { - placesDb.execSQL("UPDATE bookmarks SET url=? WHERE id=?", new Object[] {editView.getText(), rowid}); - }) - .setNegativeButton("Cancel", (renameDlg, which1) -> { - }) - .show(); - break; - } - case 2: - placesDb.execSQL("DELETE FROM bookmarks WHERE id = ?", new Object[] {rowid}); - break; - } - }) - .show(); - return true; - }); - dialog.show(); - } - - private void addBookmark() { - if (placesDb == null) return; - ContentValues values = new ContentValues(2); - values.put("title", getCurrentWebView().getTitle()); - values.put("url", getCurrentWebView().getUrl()); - placesDb.insert("bookmarks", null, values); - } - - private void exportBookmarks() { - if (placesDb == null) { - new AlertDialog.Builder(this) - .setTitle("Export bookmarks error") - .setMessage("Can't open bookmarks database") - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - return; - } - if (!hasOrRequestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, - null, - PERMISSION_REQUEST_EXPORT_BOOKMARKS)) { - return; - } - File file = new File(Environment.getExternalStorageDirectory(), "bookmarks.html"); - if (file.exists()) { - new AlertDialog.Builder(this) - .setTitle("Export bookmarks") - .setMessage("The file bookmarks.html already exists on SD card. Overwrite?") - .setNegativeButton("Cancel", (dialog, which) -> {}) - .setPositiveButton("Overwrite", (dialog, which) -> { - //noinspection ResultOfMethodCallIgnored - file.delete(); - exportBookmarks(); - }) - .show(); - return; - } - try { - FileOutputStream fos = new FileOutputStream(file); - BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos)); - bw.write("\n" + - "\n" + - "\n" + - "Bookmarks\n" + - "

Bookmarks Menu

\n" + - "\n" + - "

\n"); - try (Cursor cursor = placesDb.rawQuery("SELECT title, url FROM bookmarks", null)) { - final int titleIdx = cursor.getColumnIndex("title"); - final int urlIdx = cursor.getColumnIndex("url"); - while (cursor.moveToNext()) { - bw.write("

"); - bw.write(Html.escapeHtml(cursor.getString(titleIdx))); - bw.write("\n"); - } - } - bw.write("
\n"); - bw.close(); - Toast.makeText(this, "Bookmarks exported to bookmarks.html on SD card", Toast.LENGTH_LONG).show(); - } catch (IOException e) { - new AlertDialog.Builder(this) - .setTitle("Export bookmarks error") - .setMessage(e.toString()) - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - } - } - - @SuppressLint("DefaultLocale") - private void importBookmarks() { - if (placesDb == null) { - new AlertDialog.Builder(this) - .setTitle("Import bookmarks error") - .setMessage("Can't open bookmarks database") - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - return; - } - if (!hasOrRequestPermission(Manifest.permission.READ_EXTERNAL_STORAGE, - null, - PERMISSION_REQUEST_IMPORT_BOOKMARKS)) { - return; - } - File file = new File(Environment.getExternalStorageDirectory(), "bookmarks.html"); - StringBuilder sb = new StringBuilder(); - try { - char[] buf = new char[16*1024]; - FileInputStream fis = new FileInputStream(file); - BufferedReader br = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8)); - int count; - while ((count = br.read(buf)) != -1) { - sb.append(buf, 0, count); - } - br.close(); - } catch (FileNotFoundException e) { - new AlertDialog.Builder(this) - .setTitle("Import bookmarks error") - .setMessage("Bookmarks should be placed in a bookmarks.html file on the SD Card") - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - return; - } catch (IOException e) { - new AlertDialog.Builder(this) - .setTitle("Import bookmarks error") - .setMessage(e.toString()) - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - return; - } - - ArrayList bookmarks = new ArrayList<>(); - Pattern pattern = Pattern.compile("]*>([^<]*)"); - Matcher matcher = pattern.matcher(sb); - while (matcher.find()) { - TitleAndUrl pair = new TitleAndUrl(); - pair.url = matcher.group(1); - pair.title = matcher.group(2); - if (pair.url == null || pair.title == null) continue; - pair.title = Html.fromHtml(pair.title).toString(); - bookmarks.add(pair); - } - - if (bookmarks.isEmpty()) { - new AlertDialog.Builder(this) - .setTitle("Import bookmarks") - .setMessage("No bookmarks found in bookmarks.html") - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - return; - } - - try { - placesDb.beginTransaction(); - SQLiteStatement stmt = placesDb.compileStatement("INSERT INTO bookmarks (title, url) VALUES (?,?)"); - for (TitleAndUrl pair : bookmarks) { - stmt.bindString(1, pair.title); - stmt.bindString(2, pair.url); - stmt.execute(); - } - placesDb.setTransactionSuccessful(); - Toast.makeText(this, String.format("Imported %d bookmarks", bookmarks.size()), Toast.LENGTH_SHORT).show(); - } finally { - placesDb.endTransaction(); - } - } - - private void deleteAllBookmarks() { - if (placesDb == null) { - new AlertDialog.Builder(this) - .setTitle("Bookmarks error") - .setMessage("Can't open bookmarks database") - .setPositiveButton("OK", (dialog, which) -> {}) - .show(); - return; - } - new AlertDialog.Builder(this) - .setTitle("Delete all bookmarks?") - .setMessage("This action cannot be undone") - .setNegativeButton("Cancel", (dialog, which) -> {}) - .setPositiveButton("Delete All", (dialog, which) -> placesDb.execSQL("DELETE FROM bookmarks")) - .show(); - } - - private void clearHistoryCache() { - WebView v = getCurrentWebView(); - v.clearCache(true); - v.clearFormData(); - v.clearHistory(); - CookieManager.getInstance().removeAllCookies(null); - WebStorage.getInstance().deleteAllData(); - } - - private void closeCurrentTab() { - if (getCurrentWebView().getUrl() != null && !getCurrentWebView().getUrl().equals("about:blank")) { - TitleAndBundle titleAndBundle = new TitleAndBundle(); - titleAndBundle.title = getCurrentWebView().getTitle(); - titleAndBundle.bundle = new Bundle(); - getCurrentWebView().saveState(titleAndBundle.bundle); - closedTabs.add(0, titleAndBundle); - if (closedTabs.size() > 500) { - closedTabs.remove(closedTabs.size() - 1); - } - } - ((FrameLayout) findViewById(R.id.webviews)).removeView(getCurrentWebView()); - getCurrentWebView().destroy(); - tabs.remove(currentTabIndex); - if (currentTabIndex >= tabs.size()) { - currentTabIndex = tabs.size() - 1; - } - if (currentTabIndex == -1) { - // We just closed the last tab - newTab(""); - currentTabIndex = 0; - } - getCurrentWebView().setVisibility(View.VISIBLE); - setTabCountText(tabs.size()); - getCurrentWebView().requestFocus(); - } - private String getUrlFromIntent(Intent intent) { if (Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() != null) { return intent.getDataString(); @@ -939,50 +493,6 @@ protected void onNewIntent(Intent intent) { String url = getUrlFromIntent(intent); if (!url.isEmpty()) { newTab(url); - switchToTab(tabs.size() - 1); - } - } - - private void toggleDesktopUA() { - Tab tab = getCurrentTab(); - tab.isDesktopUA = !tab.isDesktopUA; - getCurrentWebView().getSettings().setUserAgentString(tab.isDesktopUA ? desktopUA : null); - getCurrentWebView().getSettings().setUseWideViewPort(tab.isDesktopUA); - getCurrentWebView().reload(); - } - - private void toggleThirdPartyCookies() { - CookieManager cookieManager = CookieManager.getInstance(); - boolean newValue = !cookieManager.acceptThirdPartyCookies(getCurrentWebView()); - cookieManager.setAcceptThirdPartyCookies(getCurrentWebView(), newValue); - } - - private void findOnPage() { - searchEdit.setText(""); - findViewById(R.id.loadingScreen).setVisibility(View.VISIBLE); - searchEdit.requestFocus(); - showKeyboard(); - } - - private void shareUrl() { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); - intent.putExtra(Intent.EXTRA_TEXT, getCurrentWebView().getUrl()); - intent.setType("text/plain"); - startActivity(Intent.createChooser(intent, "Share URL")); - } - - private void openUrlInApp() { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(getCurrentWebView().getUrl())); - try { - startActivity(i); - } catch (ActivityNotFoundException e) { - new AlertDialog.Builder(MainActivity.this) - .setTitle("Open in app") - .setMessage("No app can open this URL.") - .setPositiveButton("OK", (dialog1, which1) -> {}) - .show(); } } @@ -1002,7 +512,7 @@ private void loadUrl(String url, WebView webview) { url = guess; } } else { - url = URLUtil.composeSearchUrl(url, searchUrl, "%s"); + url = URLUtil.composeSearchUrl(url, startPageUrl, "%s"); } webview.loadUrl(url); @@ -1015,20 +525,12 @@ private void hideKeyboard() { assert imm != null; } - private void showKeyboard() { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - assert imm != null; - imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0); - } - @Override public void onBackPressed() { if (findViewById(R.id.fullScreenVideo).getVisibility() == View.VISIBLE && fullScreenCallback[0] != null) { fullScreenCallback[0].onCustomViewHidden(); - } else if (getCurrentWebView().canGoBack()) { - getCurrentWebView().goBack(); - } else if (tabs.size() > 1) { - closeCurrentTab(); + } else if (baseWebView.canGoBack()) { + baseWebView.goBack(); } else { super.onBackPressed(); } @@ -1043,25 +545,6 @@ private void injectCSS(WebView webview) { "a, a * {text-decoration: none !important; color:#32658b !important}" + "a:visited, a:visited * {color: #783b78 !important}" + "* {max-width: 100vw !important} pre {white-space: pre-wrap !important}"; -/* - String cssDolphin = "*,:before,:after,html *{color:#61615f!important;-webkit-border-image:none!important;border-image:none!important;background:none!important;background-image:none!important;box-shadow:none!important;text-shadow:none!important;border-color:#212a32!important}\n" + - "\n" + - "body{background-color:#000000!important}\n" + - "html a,html a *{text-decoration:none!important;color:#394c65!important}\n" + - "html a:hover,html a:hover *{color:#394c65!important;background:#1b1e23!important}\n" + - "html a:visited,html a:visited *,html a:active,html a:active *{color:#58325b!important}\n" + - "html select,html option,html textarea,html button{color:#aaa!important;border:1px solid #212a32!important;background:#161a1e!important;border-color:#212a32!important;border-style:solid}\n" + - "html select:hover,html option:hover,html button:hover,html textarea:hover,html select:focus,html option:focus,html button:focus,html textarea:focus{color:#bbb!important;background:#161a1e!important;border-color:#777 #999 #999 #777 !important}\n" + - "html input,html input[type=text],html input[type=search],html input[type=password]{color:#4e4e4e!important;background-color:#161a1e!important;box-shadow:1px 0 4px rgba(16,18,23,.75) inset,0 1px 4px rgba(16,18,23,.75) inset!important;border-color:#1a1c27!important;border-style:solid!important}\n" + - "html input:focus,html input[type=text]:focus,html input[type=search]:focus,html input[type=password]:focus{color:#bbb!important;outline:none!important;background:#161a1e!important;border-color:#1a3973}\n" + - "html input:hover,html select:hover,html option:hover,html button:hover,html textarea:hover,html input:focus,html select:focus,html option:focus,html button:focus,html textarea:focus{color:#bbb!important;background:#093681!important;opacity:0.4!important;border-color:#777 #999 #999 #777 !important}\n" + - "html input[type=button],html input[type=submit],html input[type=reset],html input[type=image]{color:#4e4e4e!important;border-color:#888 #666 #666 #888 !important}\n" + - "html input[type=button],html input[type=submit],html input[type=reset]{border:1px solid #212a32!important;background-image:0 color-stop(1,#181a23))!important}\n" + - "html input[type=button]:hover,html input[type=submit]:hover,html input[type=reset]:hover,html input[type=image]:hover{border-color:#777 #999 #999 #777 !important}\n" + - "html input[type=button]:hover,html input[type=submit]:hover,html input[type=reset]:hover{border:1px solid #666!important;background-image:0 color-stop(1,#262939))!important}\n" + - "html img,html svg{opacity:.5!important;border-color:#111!important}\n" + - "html ::-webkit-input-placeholder{color:#4e4e4e!important}\n"; -*/ final String styleElementId = "night_mode_style_4398357"; // should be unique String js; js = "if (document.head && document.getElementById('" + styleElementId + "')) {" + @@ -1075,9 +558,7 @@ private void injectCSS(WebView webview) { " fr.contentDocument.head.removeChild(style);" + "}"; webview.evaluateJavascript("javascript:(function() {" + js + "})()", null); - if (!getCurrentTab().isDesktopUA) { - webview.evaluateJavascript("javascript:document.querySelector('meta[name=viewport]').content='width=device-width, initial-scale=1.0, maximum-scale=3.0, user-scalable=1';", null); - } + webview.evaluateJavascript("javascript:document.querySelector('meta[name=viewport]').content='width=device-width, initial-scale=1.0, maximum-scale=3.0, user-scalable=1';", null); } catch (Exception e) { e.printStackTrace(); } @@ -1102,296 +583,4 @@ boolean hasOrRequestPermission(String permission, String explanation, int reques requestPermissions(new String[] {permission}, requestCode); return false; } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { - return; - } - switch (requestCode) { - case PERMISSION_REQUEST_EXPORT_BOOKMARKS: - exportBookmarks(); - break; - case PERMISSION_REQUEST_IMPORT_BOOKMARKS: - importBookmarks(); - break; - } - } - - // java.util.function.BooleanSupplier requires API 24 - interface MyBooleanSupplier { - boolean getAsBoolean(); - } - - private class MyGestureDetector extends GestureDetector.SimpleOnGestureListener { - private static final double FORBIDDEN_ZONE_MIN = Math.PI / 4 - Math.PI / 12; - private static final double FORBIDDEN_ZONE_MAX = Math.PI / 4 + Math.PI / 12; - private static final int MIN_VELOCITY_DP = 80; // 0.5 inch/sec - private static final int MIN_DISTANCE_DP = 80; // 0.5 inch - private final float MIN_VELOCITY_PX; - private final float MIN_DISTANCE_PX; - - MyGestureDetector(Context context) { - float density = context.getResources().getDisplayMetrics().density; - MIN_VELOCITY_PX = MIN_VELOCITY_DP * density; - MIN_DISTANCE_PX = MIN_DISTANCE_DP * density; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - float velocitySquared = velocityX * velocityX + velocityY * velocityY; - if (velocitySquared < MIN_VELOCITY_PX * MIN_VELOCITY_PX) { - // too slow - return false; - } - - float deltaX = e2.getX() - e1.getX(); - float deltaY = e2.getY() - e1.getY(); - - if (Math.abs(deltaX) < MIN_DISTANCE_PX && Math.abs(deltaY) < MIN_DISTANCE_PX) { - // small movement - return false; - } - - double angle = Math.atan2(Math.abs(deltaY), Math.abs(deltaX)); - if (angle > FORBIDDEN_ZONE_MIN && angle < FORBIDDEN_ZONE_MAX) { - return false; - } - - if (Math.abs(deltaX) > Math.abs(deltaY)) { - if (deltaX > 0) { - return onFlingRight(); - } else { - return onFlingLeft(); - } - } else { - if (deltaY > 0) { - return onFlingDown(); - } else { - return onFlingUp(); - } - } - } - - boolean onFlingRight() { - return true; - } - - boolean onFlingLeft() { - return true; - } - - boolean onFlingUp() { - return true; - } - - boolean onFlingDown() { - return true; - } - } - - static class ArrayAdapterWithCurrentItem extends ArrayAdapter { - int currentIndex; - - ArrayAdapterWithCurrentItem(@NonNull Context context, int resource, @NonNull T[] objects, int currentIndex) { - super(context, resource, objects); - this.currentIndex = currentIndex; - } - - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = super.getView(position, convertView, parent); - TextView textView = view.findViewById(android.R.id.text1); - int icon = position == currentIndex ? android.R.drawable.ic_menu_mylocation : R.drawable.empty; - Drawable d = getContext().getResources().getDrawable(icon, null); - int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getContext().getResources().getDisplayMetrics()); - d.setBounds(0, 0, size, size); - textView.setCompoundDrawablesRelative(d, null, null, null); - textView.setCompoundDrawablePadding( - (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, getContext().getResources().getDisplayMetrics())); - return view; - } - } - - static class MenuActionArrayAdapter extends ArrayAdapter { - - MenuActionArrayAdapter(@NonNull Context context, int resource, @NonNull MenuAction[] objects) { - super(context, resource, objects); - } - - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = super.getView(position, convertView, parent); - TextView textView = view.findViewById(android.R.id.text1); - DisplayMetrics m = getContext().getResources().getDisplayMetrics(); - - MenuAction item = getItem(position); - assert item != null; - - Drawable left = getContext().getResources().getDrawable(item.icon != 0 ? item.icon : R.drawable.empty, null); - int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, m); - left.setBounds(0, 0, size, size); - left.setTint(Color.rgb(0x61, 0x61, 0x5f)); - - Drawable right = null; - if (item.getState != null) { - int icon = item.getState.getAsBoolean() ? android.R.drawable.checkbox_on_background : - android.R.drawable.checkbox_off_background; - right = getContext().getResources().getDrawable(icon, null); - right.setBounds(0, 0, size, size); - } - - textView.setCompoundDrawables(left, null, right, null); - textView.setCompoundDrawablePadding( - (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, m)); - - return view; - } - } - - static class SearchAutocompleteAdapter extends BaseAdapter implements Filterable { - - interface OnSearchCommitListener { - void onSearchCommit(String text); - } - - private final Context mContext; - private final OnSearchCommitListener commitListener; - private List completions = new ArrayList<>(); - - SearchAutocompleteAdapter(Context context, OnSearchCommitListener commitListener) { - mContext = context; - this.commitListener = commitListener; - } - - @Override - public int getCount() { - return completions.size(); - } - - @Override - public Object getItem(int position) { - return completions.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @SuppressLint("ClickableViewAccessibility") - @Override - @SuppressWarnings("ConstantConditions") - public View getView(final int position, View convertView, ViewGroup parent) { - if (convertView == null) { - LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false); - } - TextView v = convertView.findViewById(android.R.id.text1); - v.setText(completions.get(position)); - int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, mContext.getResources().getDisplayMetrics()); - //noinspection AndroidLintClickableViewAccessibility - v.setOnTouchListener((v1, event) -> { - if (event.getAction() != MotionEvent.ACTION_DOWN) { - return false; - } - TextView t = (TextView) v1; - if (event.getX() > t.getWidth() - t.getCompoundPaddingRight()) { - commitListener.onSearchCommit(getItem(position).toString()); - return true; - } - return false; - }); - //noinspection AndroidLintClickableViewAccessibility - parent.setOnTouchListener((dropdown, event) -> { - if (event.getX() > dropdown.getWidth() - size * 2) { - return true; - } - return false; - }); - return convertView; - } - - @Override - public Filter getFilter() { - return new Filter() { - @Override - protected FilterResults performFiltering(CharSequence constraint) { - // Invoked on a worker thread - FilterResults filterResults = new FilterResults(); - if (constraint != null) { - List results = getCompletions(constraint.toString()); - filterResults.values = results; - filterResults.count = results.size(); - } - return filterResults; - } - - @Override - @SuppressWarnings("unchecked") - protected void publishResults(CharSequence constraint, FilterResults results) { - if (results != null && results.count > 0) { - completions = (List) results.values; - notifyDataSetChanged(); - } else { - notifyDataSetInvalidated(); - } - } - }; - } - - // Runs on a worker thread - private List getCompletions(String text) { - int total = 0; - byte[] data = new byte[16384]; - try { - URL url = new URL(URLUtil.composeSearchUrl(text, searchCompleteUrl, "%s")); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - try { - InputStream in = new BufferedInputStream(urlConnection.getInputStream()); - while (total <= data.length) { - int count = in.read(data, total, data.length - total); - if (count == -1) { - break; - } - total += count; - } - if (total == data.length) { - // overflow - return new ArrayList<>(); - } - } finally { - urlConnection.disconnect(); - } - } catch (IOException e) { - // Swallow exception and return empty list - return new ArrayList<>(); - } - - // Result looks like: - // [ "original query", ["completion1", "completion2", ...], ...] - - JSONArray jsonArray; - try { - jsonArray = new JSONArray(new String(data, StandardCharsets.UTF_8)); - } catch (JSONException e) { - return new ArrayList<>(); - } - jsonArray = jsonArray.optJSONArray(1); - if (jsonArray == null) { - return new ArrayList<>(); - } - final int MAX_RESULTS = 10; - List result = new ArrayList<>(Math.min(jsonArray.length(), MAX_RESULTS)); - for (int i = 0; i < jsonArray.length() && result.size() < MAX_RESULTS; i++) { - String s = jsonArray.optString(i); - if (s != null && !s.isEmpty()) { - result.add(s); - } - } - return result; - } - } }