diff --git a/modules/web/src/main/AndroidManifest.xml b/modules/web/src/main/AndroidManifest.xml
index 0625d6e6..09483aef 100644
--- a/modules/web/src/main/AndroidManifest.xml
+++ b/modules/web/src/main/AndroidManifest.xml
@@ -7,6 +7,12 @@
+
+
+
+
+
+
result.confirm())
+ .show();
+ return true;
+ }
+
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
+ Context ctx = view.getContext();
+ ActivityDelegate.get(ctx).createDialogBuilder(ctx)
+ .setMessage(message)
+ .setNegativeButton(android.R.string.cancel, (d, w) -> result.cancel())
+ .setPositiveButton(android.R.string.ok, (d, w) -> result.confirm())
+ .show();
+ return true;
+ }
+
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
+ Context ctx = view.getContext();
+ ActivityDelegate a = ActivityDelegate.get(ctx);
+ EditText text = a.createEditText(ctx);
+ text.setSingleLine();
+ text.setText(defaultValue);
+ a.createDialogBuilder(ctx)
+ .setTitle(message).setView(text)
+ .setNegativeButton(android.R.string.cancel, (d, i) -> result.cancel())
+ .setPositiveButton(android.R.string.ok, (d, i) -> result.confirm(text.getText().toString())).show();
+ return true;
+ }
+
+ @Override
+ public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
+ return onJsConfirm(view, url, message, result);
+ }
+
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
if (view instanceof ViewGroup) {
@@ -197,4 +251,59 @@ public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermiss
});
}
}
+
+ @Override
+ public void onPermissionRequest(PermissionRequest request) {
+ Log.d("Permissions requested: ", Arrays.toString(request.getResources()));
+ Map perms = new HashMap<>();
+
+ for (String p : request.getResources()) {
+ switch (p) {
+ case PermissionRequest.RESOURCE_AUDIO_CAPTURE:
+ perms.put(Manifest.permission.RECORD_AUDIO, p);
+ break;
+ case PermissionRequest.RESOURCE_VIDEO_CAPTURE:
+ perms.put(Manifest.permission.CAMERA, p);
+ break;
+ }
+ }
+
+ if (perms.isEmpty()) {
+ Log.d("No permissions granted");
+ request.deny();
+ return;
+ }
+
+ MainActivityDelegate a = MainActivityDelegate.get(getWebView().getContext());
+
+ if (BuildConfig.AUTO && a.isCarActivity()) {
+ // Activity.checkPermissions() is not supported by AA
+ Log.d("Granted permissions: ", perms.values());
+ request.grant(perms.values().toArray(new String[0]));
+ return;
+ }
+
+ String[] keys = perms.keySet().toArray(new String[0]);
+ FutureSupplier perm = a.getAppActivity().checkPermissions(keys);
+ perm.onCompletion((r, err) -> {
+ if (err != null) {
+ Log.e(err, "Permission request failed");
+ request.deny();
+ } else {
+ List granted = new ArrayList<>(r.length);
+
+ for (int i = 0; i < r.length; i++) {
+ if (r[i] == PERMISSION_GRANTED) granted.add(perms.get(keys[i]));
+ }
+
+ if (granted.isEmpty()) {
+ Log.d("No permissions granted");
+ request.deny();
+ } else {
+ Log.d("Granted permissions: ", granted);
+ request.grant(granted.toArray(new String[0]));
+ }
+ }
+ });
+ }
}
diff --git a/modules/web/src/main/java/me/aap/fermata/addon/web/FermataWebView.java b/modules/web/src/main/java/me/aap/fermata/addon/web/FermataWebView.java
index f6ef3516..fe327b53 100644
--- a/modules/web/src/main/java/me/aap/fermata/addon/web/FermataWebView.java
+++ b/modules/web/src/main/java/me/aap/fermata/addon/web/FermataWebView.java
@@ -18,6 +18,8 @@
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
+import java.util.List;
+
import me.aap.fermata.BuildConfig;
import me.aap.fermata.ui.activity.FermataActivity;
import me.aap.fermata.ui.activity.MainActivityDelegate;
@@ -35,7 +37,7 @@
* @author Andrey Pavlenko
*/
public class FermataWebView extends WebView implements TextChangedListener,
- TextView.OnEditorActionListener {
+ TextView.OnEditorActionListener, PreferenceStore.Listener {
private final boolean isCar;
private WebBrowserAddon addon;
private FermataChromeClient chrome;
@@ -60,22 +62,55 @@ public void init(WebBrowserAddon addon, FermataWebClient webClient, FermataChrom
setWebViewClient(webClient);
setWebChromeClient(chromeClient);
WebSettings s = getSettings();
- s.setJavaScriptEnabled(true);
s.setSupportZoom(true);
+ s.setBuiltInZoomControls(true);
+ s.setDisplayZoomControls(false);
s.setDatabaseEnabled(true);
s.setDomStorageEnabled(true);
+ s.setAllowFileAccess(true);
s.setLoadWithOverviewMode(true);
- s.setAllowUniversalAccessFromFileURLs(true);
+ s.setJavaScriptEnabled(true);
+ s.setJavaScriptCanOpenWindowsAutomatically(true);
+
+ setDesktopMode(addon.isDesktopVersion(), false);
+ addon.getPreferenceStore().addBroadcastListener(this);
addJavascriptInterface(createJsInterface(), FermataJsInterface.NAME);
+ CookieManager.getInstance().setAcceptThirdPartyCookies(this, true);
if (WebViewFeature.isFeatureSupported(FORCE_DARK)) {
- PreferenceStore store = addon.getPreferenceStore();
- if (store.getBooleanPref(addon.getForceDarkPref())) {
+ if (addon.isForceDark()) {
WebSettingsCompat.setForceDark(s, WebSettingsCompat.FORCE_DARK_ON);
}
}
}
+ @Override
+ public void onPreferenceChanged(PreferenceStore store, List> prefs) {
+ WebBrowserAddon addon = getAddon();
+
+ if ((addon != null) && prefs.contains(addon.getDesktopVersionPref())) {
+ setDesktopMode(store.getBooleanPref(addon.getDesktopVersionPref()), true);
+ }
+ }
+
+ private void setDesktopMode(boolean v, boolean reload) {
+ WebSettings s = getSettings();
+ s.setUseWideViewPort(v);
+
+ if (v) {
+ String ua = s.getUserAgentString();
+ int i1 = ua.indexOf('(') + 1;
+ int i2 = ua.indexOf(')', i1);
+ ua = ua.substring(0, i1) + "X11; Linux x86_64" + ua.substring(i2).replace("Mobile ", "");
+ Log.d("Changing UserAgent to " + ua);
+ s.setUserAgentString(ua);
+ } else {
+ s.setUserAgentString(null);
+ }
+
+ if (reload) reload();
+ }
+
protected FermataJsInterface createJsInterface() {
return new FermataJsInterface(this);
}
@@ -112,7 +147,13 @@ protected void pageLoaded(String uri) {
if (f == null) return;
ToolBarView.Mediator m = f.getToolBarMediator();
- if (m instanceof WebToolBarMediator) ((WebToolBarMediator) m).setAddress(a.getToolBar(), uri);
+
+ if (m instanceof WebToolBarMediator) {
+ WebToolBarMediator wm = (WebToolBarMediator) m;
+ ToolBarView tb = a.getToolBar();
+ wm.setAddress(tb, uri);
+ wm.setButtonsVisibility(tb, canGoBack(), canGoForward());
+ }
CookieManager.getInstance().flush();
}
@@ -151,21 +192,33 @@ private void setTextInput(CharSequence text) {
Log.d(text);
loadUrl("javascript:\n" +
"var e = document.activeElement;\n" +
- "if (e.isContentEditable) e.innerText = '" + text + "';\n" +
- "else e.value = '" + text + "';\n" +
- "e.dispatchEvent(new KeyboardEvent('keyup'));"
+ "var text = '" + text + "';\n" +
+ "if (e.isContentEditable) e.innerText = text;\n" +
+ "else e.value = text;\n" +
+ "e.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true }));\n" +
+ "e.dispatchEvent(new KeyboardEvent('keypress', { bubbles: true }));\n" +
+ "e.dispatchEvent(new InputEvent('input', { bubbles: true, data: text, inputType: 'insertText' }));\n" +
+ "e.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));\n" +
+ "e.dispatchEvent(new Event('change', { bubbles: true }));"
);
}
- protected void sendEnterEvent() {
+
+ protected void submitForm() {
if (!BuildConfig.AUTO) return;
loadUrl("javascript:\n" +
- "var e = new KeyboardEvent('keydown',\n" +
- "{ code: 'Enter', key: 'Enter', charKode: 13, keyCode: 13, view: window });\n" +
- "document.activeElement.dispatchEvent(e);\n" +
- "e = new KeyboardEvent('keyup',\n" +
- "{ code: 'Enter', key: 'Enter', charKode: 13, keyCode: 13, view: window });\n" +
- "document.activeElement.dispatchEvent(e);");
+ "var ae = document.activeElement;\n" +
+ "if (ae.form != null) {\n" +
+ " ae.form.submit();\n" +
+ "} else {\n" +
+ " var e = new KeyboardEvent('keydown',\n" +
+ " { code: 'Enter', key: 'Enter', keyCode: 13, view: window, bubbles: true });\n" +
+ " ae.dispatchEvent(e);\n" +
+ " e = new KeyboardEvent('keyup',\n" +
+ " { code: 'Enter', key: 'Enter', keyCode: 13, view: window, bubbles: true });\n" +
+ " ae.dispatchEvent(e);\n" +
+ "}"
+ );
}
public void showKeyboard(String text) {
@@ -213,7 +266,7 @@ public boolean onEditorAction(TextView v, int actionId, @Nullable KeyEvent event
case EditorInfo.IME_ACTION_SEND:
case EditorInfo.IME_ACTION_NEXT:
case EditorInfo.IME_ACTION_DONE:
- sendEnterEvent();
+ submitForm();
hideKeyboard();
}
@@ -223,7 +276,18 @@ public boolean onEditorAction(TextView v, int actionId, @Nullable KeyEvent event
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
FermataChromeClient chrome = getWebChromeClient();
- if ((chrome != null) && chrome.isFullScreen()) chrome.onTouchEvent(this, ev);
+
+ if ((chrome != null) && chrome.isFullScreen()) {
+ chrome.onTouchEvent(this, ev);
+ } else if (BuildConfig.AUTO) {
+ FermataActivity a = MainActivityDelegate.get(getContext()).getAppActivity();
+
+ if (a.isInputActive()) {
+ a.stopInput(this);
+ return true;
+ }
+ }
+
return super.onInterceptTouchEvent(ev);
}
}
diff --git a/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserAddon.java b/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserAddon.java
index 9c35090c..3e1a29e6 100644
--- a/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserAddon.java
+++ b/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserAddon.java
@@ -28,6 +28,7 @@
public class WebBrowserAddon implements FermataAddon {
private static final Pref> LAST_URL = Pref.s("LAST_URL", "http://google.com");
private static final Pref FORCE_DARK = Pref.b("FORCE_DARK", false);
+ private static final Pref DESKTOP_VERSION = Pref.b("DESKTOP_VERSION", false);
private static final Pref> BOOKMARKS = Pref.sa("BOOKMARKS");
private final SharedPreferenceStore preferenceStore;
@@ -64,6 +65,22 @@ public Pref getForceDarkPref() {
return FORCE_DARK;
}
+ public boolean isForceDark() {
+ return getPreferenceStore().getBooleanPref(getForceDarkPref());
+ }
+
+ public Pref getDesktopVersionPref() {
+ return DESKTOP_VERSION;
+ }
+
+ public boolean isDesktopVersion() {
+ return getPreferenceStore().getBooleanPref(getDesktopVersionPref());
+ }
+
+ public void setDesktopVersion(boolean v) {
+ getPreferenceStore().applyBooleanPref(DESKTOP_VERSION, v);
+ }
+
Map getBookmarks() {
String[] p = getPreferenceStore().getStringArrayPref(BOOKMARKS);
if (p.length == 0) return Collections.emptyMap();
diff --git a/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserFragment.java b/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserFragment.java
index 5fb1d76e..5f187755 100644
--- a/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserFragment.java
+++ b/modules/web/src/main/java/me/aap/fermata/addon/web/WebBrowserFragment.java
@@ -152,8 +152,9 @@ public void contributeToNavBarMenu(OverlayMenu.Builder b) {
b.addItem(me.aap.fermata.R.id.refresh, me.aap.fermata.R.drawable.refresh,
me.aap.fermata.R.string.refresh).setHandler(this);
- if (v.canGoForward()) {
- b.addItem(R.id.browser_forward, R.drawable.forward, R.string.go_forward).setHandler(this);
+ if (isDesktopVersionSupported()) {
+ b.addItem(R.id.desktop_version, R.drawable.desktop, R.string.desktop_version)
+ .setChecked(getAddon().isDesktopVersion()).setHandler(this);
}
FermataChromeClient chrome = v.getWebChromeClient();
@@ -171,6 +172,10 @@ public void contributeToNavBarMenu(OverlayMenu.Builder b) {
me.aap.fermata.R.string.bookmarks).setSubmenu(this::bookmarksMenu);
}
+ protected boolean isDesktopVersionSupported() {
+ return true;
+ }
+
@Override
public boolean menuItemSelected(OverlayMenuItem item) {
FermataWebView v = getWebView();
@@ -182,8 +187,9 @@ public boolean menuItemSelected(OverlayMenuItem item) {
case me.aap.fermata.R.id.refresh:
v.reload();
return true;
- case R.id.browser_forward:
- v.goForward();
+ case R.id.desktop_version:
+ WebBrowserAddon addon = getAddon();
+ addon.setDesktopVersion(!addon.isDesktopVersion());
return true;
case R.id.fullscreen:
case R.id.fullscreen_exit:
@@ -197,7 +203,7 @@ public boolean menuItemSelected(OverlayMenuItem item) {
return false;
}
- private void bookmarksMenu(OverlayMenu.Builder b) {
+ public void bookmarksMenu(OverlayMenu.Builder b) {
WebBrowserAddon a = getAddon();
if (a == null) return;
diff --git a/modules/web/src/main/java/me/aap/fermata/addon/web/WebToolBarMediator.java b/modules/web/src/main/java/me/aap/fermata/addon/web/WebToolBarMediator.java
index fca15b67..c8ee55a1 100644
--- a/modules/web/src/main/java/me/aap/fermata/addon/web/WebToolBarMediator.java
+++ b/modules/web/src/main/java/me/aap/fermata/addon/web/WebToolBarMediator.java
@@ -13,7 +13,8 @@
import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.KeyEvent.KEYCODE_NUMPAD_ENTER;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.LEFT;
import static me.aap.utils.ui.UiUtils.toPx;
@@ -31,23 +32,39 @@ public static WebToolBarMediator getInstance() {
@Override
public void enable(ToolBarView tb, ActivityFragment f) {
WebBrowserFragment b = (WebBrowserFragment) f;
+ FermataWebView wv = b.getWebView();
EditText t = createAddress(tb, b);
String url = b.getUrl();
if (url != null) t.setText(url);
addView(tb, t, R.id.browser_addr, LEFT);
+ addButton(tb, R.drawable.forward, v -> wv.goForward(), R.id.browser_forward, LEFT);
+ addButton(tb, me.aap.utils.R.drawable.back, v -> wv.goBack(), me.aap.utils.R.id.tool_bar_back_button, LEFT);
+ addButton(tb, R.drawable.clear, v -> t.setText(""), R.id.browser_addr_clear);
+ addButton(tb, me.aap.fermata.R.drawable.bookmark_filled, v -> onBookmarksButtonClick(b), me.aap.fermata.R.id.bookmarks);
+ setButtonsVisibility(tb, wv.canGoBack(), wv.canGoForward());
ToolBarView.Mediator.super.enable(tb, f);
}
+ private void onBookmarksButtonClick(WebBrowserFragment f) {
+ f.getActivityDelegate().getToolBarMenu().show(b -> f.bookmarksMenu(b));
+ }
+
public void setAddress(ToolBarView tb, String addr) {
EditText et = tb.findViewById(R.id.browser_addr);
if (et != null) et.setText(addr);
}
+ public void setButtonsVisibility(ToolBarView tb, boolean back, boolean forward) {
+ tb.findViewById(me.aap.utils.R.id.tool_bar_back_button).setVisibility(back ? VISIBLE : GONE);
+ tb.findViewById(R.id.browser_forward).setVisibility(forward ? VISIBLE : GONE);
+ }
+
private EditText createAddress(ToolBarView tb, WebBrowserFragment f) {
Context ctx = tb.getContext();
int p = (int) toPx(ctx, 2);
EditText t = createEditText(tb);
- ConstraintLayout.LayoutParams lp = setLayoutParams(t, MATCH_PARENT, WRAP_CONTENT);
+ ConstraintLayout.LayoutParams lp = setLayoutParams(t, 0, WRAP_CONTENT);
+ lp.horizontalWeight = 2;
t.setBackgroundResource(me.aap.utils.R.drawable.tool_bar_edittext_bg);
t.setOnKeyListener((v, keyCode, event) -> onKey(f, t, keyCode, event));
t.setMaxLines(1);
diff --git a/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeAddon.java b/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeAddon.java
index 310d7f61..552feb96 100644
--- a/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeAddon.java
+++ b/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeAddon.java
@@ -16,6 +16,7 @@
@SuppressWarnings("unused")
public class YoutubeAddon extends WebBrowserAddon {
private static final Pref YT_FORCE_DARK = Pref.b("YT_FORCE_DARK", false);
+ private static final Pref YT_DESKTOP_VERSION = Pref.b("YT_DESKTOP_VERSION", false);
private static final Pref> VIDEO_SCALE = Pref.s("VIDEO_SCALE", VideoScale.CONTAIN::prefName);
@Override
@@ -34,6 +35,11 @@ public Pref getForceDarkPref() {
return YT_FORCE_DARK;
}
+ @Override
+ public Pref getDesktopVersionPref() {
+ return YT_DESKTOP_VERSION;
+ }
+
VideoScale getScale() {
switch (getPreferenceStore().getStringPref(VIDEO_SCALE)) {
case "fill":
diff --git a/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeFragment.java b/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeFragment.java
index fb8fe68d..c478b938 100644
--- a/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeFragment.java
+++ b/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeFragment.java
@@ -131,4 +131,8 @@ protected YoutubeWebView getWebView() {
View v = getView();
return (v != null) ? v.findViewById(R.id.ytWebView) : null;
}
+
+ protected boolean isDesktopVersionSupported() {
+ return false;
+ }
}
diff --git a/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeWebView.java b/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeWebView.java
index a20b1116..901a08e0 100644
--- a/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeWebView.java
+++ b/modules/web/src/main/java/me/aap/fermata/addon/web/yt/YoutubeWebView.java
@@ -54,6 +54,17 @@ protected void pageLoaded(String uri) {
CookieManager.getInstance().flush();
}
+ protected void submitForm() {
+ if (!me.aap.fermata.BuildConfig.AUTO) return;
+ loadUrl("javascript:\n" +
+ "var e = new KeyboardEvent('keydown',\n" +
+ "{ code: 'Enter', key: 'Enter', keyCode: 13, view: window, bubbles: true });\n" +
+ "document.activeElement.dispatchEvent(e);\n" +
+ "e = new KeyboardEvent('keyup',\n" +
+ "{ code: 'Enter', key: 'Enter', keyCode: 13, view: window, bubbles: true });\n" +
+ "document.activeElement.dispatchEvent(e);");
+ }
+
void attachListeners() {
String debug = BuildConfig.DEBUG ? JS_EVENT + "(" + JS_VIDEO_FOUND + ", null);\n" : "";
String scale = getAddon().getScale().prefName();
diff --git a/modules/web/src/main/res/drawable/clear.xml b/modules/web/src/main/res/drawable/clear.xml
new file mode 100644
index 00000000..2cca6e3d
--- /dev/null
+++ b/modules/web/src/main/res/drawable/clear.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/modules/web/src/main/res/drawable/desktop.xml b/modules/web/src/main/res/drawable/desktop.xml
new file mode 100644
index 00000000..beb7bc05
--- /dev/null
+++ b/modules/web/src/main/res/drawable/desktop.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/modules/web/src/main/res/values-ru/strings.xml b/modules/web/src/main/res/values-ru/strings.xml
index d4b5c667..0bffcaac 100644
--- a/modules/web/src/main/res/values-ru/strings.xml
+++ b/modules/web/src/main/res/values-ru/strings.xml
@@ -1,7 +1,7 @@
Форсировать темный стиль, если поддерживается
- Вперед
+ Версия для ПК
Полноэкранный режим
Выйти из полноэкранного режима
Заполнить пропорционально
diff --git a/modules/web/src/main/res/values/idx.xml b/modules/web/src/main/res/values/idx.xml
index 08d58d48..d5a1171c 100644
--- a/modules/web/src/main/res/values/idx.xml
+++ b/modules/web/src/main/res/values/idx.xml
@@ -1,7 +1,9 @@
+
+
diff --git a/modules/web/src/main/res/values/strings.xml b/modules/web/src/main/res/values/strings.xml
index fc4fa5f0..7138c6e5 100644
--- a/modules/web/src/main/res/values/strings.xml
+++ b/modules/web/src/main/res/values/strings.xml
@@ -1,7 +1,7 @@
Force dark mode, if supported
- Go forward
+ Desktop version
Full screen
Exit full screen
URL