Remove built-in browser

maths
Thomas 2 years ago
parent 403fb1aba7
commit 95beb7f3c1

@ -166,9 +166,6 @@
android:scheme="fedilab" />
</intent-filter>
</activity>
<activity
android:name=".activities.WebviewConnectActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.StatusHistoryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -193,9 +190,6 @@
<activity
android:name=".activities.FollowRequestActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.WebviewActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.ProfileActivity"
android:configChanges="keyboardHidden|orientation|screenSize"

@ -125,7 +125,6 @@ import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.BottomMenu;
import app.fedilab.android.client.entities.app.DomainsBlock;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.StatusCache;
@ -749,10 +748,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
}
}).start();
}
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
if (embedded_browser) {
DomainsBlock.updateDomains(BaseMainActivity.this);
}
}
protected abstract void rateThisApp();

@ -15,11 +15,16 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.helper.Helper.PREF_USER_TOKEN;
import static app.fedilab.android.helper.MastodonHelper.REDIRECT_CONTENT_WEB;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.FrameLayout;
@ -32,8 +37,10 @@ import org.jetbrains.annotations.NotNull;
import java.util.regex.Matcher;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.ui.fragment.login.FragmentLoginMain;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
@ -50,6 +57,33 @@ public class LoginActivity extends BaseActivity {
private final int PICK_IMPORT = 5557;
public static boolean requestedAdmin;
@SuppressLint("ApplySharedPref")
public static void proceedLogin(Activity activity, Account account) {
new Thread(() -> {
try {
//update the database
new Account(activity).insertOrUpdate(account);
Handler mainHandler = new Handler(Looper.getMainLooper());
BaseMainActivity.currentToken = account.token;
BaseMainActivity.currentUserID = account.user_id;
BaseMainActivity.api = Account.API.MASTODON;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(PREF_USER_TOKEN, account.token);
editor.commit();
//The user is now authenticated, it will be redirected to MainActivity
Runnable myRunnable = () -> {
Intent mainActivity = new Intent(activity, MainActivity.class);
activity.startActivity(mainActivity);
activity.finish();
};
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
private void manageItent(Intent intent) {
if (intent != null && intent.getData() != null && intent.getData().toString().contains(REDIRECT_CONTENT_WEB + "?code=")) {
@ -84,10 +118,10 @@ public class LoginActivity extends BaseActivity {
AdminVM adminVM = new ViewModelProvider(LoginActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(LoginActivity.this, adminAccount -> {
account.admin = adminAccount != null;
WebviewConnectActivity.proceedLogin(LoginActivity.this, account);
proceedLogin(LoginActivity.this, account);
});
} else {
WebviewConnectActivity.proceedLogin(LoginActivity.this, account);
proceedLogin(LoginActivity.this, account);
}
});
} else {
@ -98,7 +132,6 @@ public class LoginActivity extends BaseActivity {
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@ -129,9 +162,6 @@ public class LoginActivity extends BaseActivity {
public boolean onCreateOptionsMenu(@NotNull Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_login, menu);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
menu.findItem(R.id.action_custom_tabs).setChecked(!embedded_browser);
return true;
}
@ -145,13 +175,6 @@ public class LoginActivity extends BaseActivity {
if (id == R.id.action_proxy) {
Intent intent = new Intent(LoginActivity.this, ProxyActivity.class);
startActivity(intent);
} else if (id == R.id.action_custom_tabs) {
item.setChecked(!item.isChecked());
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean(getString(R.string.SET_EMBEDDED_BROWSER), !item.isChecked());
editor.apply();
return false;
} else if (id == R.id.action_request_admin) {
item.setChecked(!item.isChecked());
requestedAdmin = item.isChecked();

@ -1,252 +0,0 @@
package app.fedilab.android.activities;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityWebviewBinding;
import app.fedilab.android.helper.CountDrawable;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.webview.CustomWebview;
import app.fedilab.android.webview.FedilabWebChromeClient;
import app.fedilab.android.webview.FedilabWebViewClient;
import es.dmoral.toasty.Toasty;
public class WebviewActivity extends BaseActivity {
private String url;
private boolean peertubeLink;
private CustomWebview webView;
private FedilabWebViewClient FedilabWebViewClient;
private ActivityWebviewBinding binding;
private Menu defaultMenu;
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityWebviewBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
Bundle b = getIntent().getExtras();
if (b != null) {
url = b.getString("url", null);
peertubeLink = b.getBoolean("peertubeLink", false);
}
if (url == null)
finish();
if (getSupportActionBar() != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
webView = Helper.initializeWebview(WebviewActivity.this, R.id.webview, null);
setTitle("");
webView.getSettings().setJavaScriptEnabled(true);
FedilabWebChromeClient FedilabWebChromeClient = new FedilabWebChromeClient(WebviewActivity.this, webView, binding.webviewContainer, binding.videoLayout);
FedilabWebChromeClient.setOnToggledFullscreen(fullscreen -> {
if (fullscreen) {
binding.videoLayout.setVisibility(View.VISIBLE);
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
getWindow().setAttributes(attrs);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
} else {
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
getWindow().setAttributes(attrs);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
binding.videoLayout.setVisibility(View.GONE);
}
});
webView.setWebChromeClient(FedilabWebChromeClient);
FedilabWebViewClient = new FedilabWebViewClient(WebviewActivity.this);
webView.setWebViewClient(FedilabWebViewClient);
webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(WebviewActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(WebviewActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(WebviewActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE);
} else {
Helper.manageDownloads(WebviewActivity.this, url);
}
} else {
Helper.manageDownloads(WebviewActivity.this, url);
}
});
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
url = "http://" + url;
webView.loadUrl(url);
}
public void setCount(Context context, String count) {
if (defaultMenu != null && !peertubeLink) {
MenuItem menuItem = defaultMenu.findItem(R.id.action_block);
LayerDrawable icon = (LayerDrawable) menuItem.getIcon();
CountDrawable badge;
// Reuse drawable if possible
Drawable reuse = icon.findDrawableByLayerId(R.id.ic_block_count);
if (reuse instanceof CountDrawable) {
badge = (CountDrawable) reuse;
} else {
badge = new CountDrawable(context);
}
badge.setCount(count);
icon.mutate();
icon.setDrawableByLayerId(R.id.ic_block_count, badge);
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (!peertubeLink)
setCount(WebviewActivity.this, "0");
defaultMenu = menu;
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onCreateOptionsMenu(@NotNull Menu menu) {
getMenuInflater().inflate(R.menu.main_webview, menu);
defaultMenu = menu;
if (peertubeLink) {
menu.findItem(R.id.action_go).setVisible(false);
menu.findItem(R.id.action_block).setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
finish();
return true;
} else if (itemId == R.id.action_block) {
List<String> domains = FedilabWebViewClient.getDomains();
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(WebviewActivity.this, R.layout.domains_blocked);
arrayAdapter.addAll(domains);
AlertDialog.Builder builder = new AlertDialog.Builder(WebviewActivity.this, Helper.dialogStyle());
builder.setTitle(R.string.list_of_blocked_domains);
builder.setNegativeButton(R.string.close, (dialog, which) -> dialog.dismiss());
builder.setAdapter(arrayAdapter, (dialog, which) -> {
String strName = arrayAdapter.getItem(which);
assert strName != null;
Toasty.info(WebviewActivity.this, strName, Toast.LENGTH_LONG).show();
});
builder.show();
return true;
} else if (itemId == R.id.action_go) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
try {
startActivity(browserIntent);
} catch (Exception e) {
Toasty.error(WebviewActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
return true;
}
return super.onOptionsItemSelected(item);
}
public void setUrl(String newUrl) {
this.url = newUrl;
}
@Override
public void onPause() {
super.onPause();
if (webView != null)
webView.onPause();
}
@Override
public void onResume() {
super.onResume();
if (webView != null)
webView.onResume();
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (webView != null)
webView.destroy();
}
}

@ -1,325 +0,0 @@
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
package app.fedilab.android.activities;
import static app.fedilab.android.activities.LoginActivity.apiLogin;
import static app.fedilab.android.activities.LoginActivity.client_idLogin;
import static app.fedilab.android.activities.LoginActivity.client_secretLogin;
import static app.fedilab.android.activities.LoginActivity.currentInstanceLogin;
import static app.fedilab.android.activities.LoginActivity.requestedAdmin;
import static app.fedilab.android.activities.LoginActivity.softwareLogin;
import static app.fedilab.android.helper.Helper.PREF_USER_TOKEN;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.databinding.ActivityWebviewConnectBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.viewmodel.mastodon.OauthVM;
import es.dmoral.toasty.Toasty;
public class WebviewConnectActivity extends BaseActivity {
private ActivityWebviewConnectBinding binding;
private AlertDialog alert;
private String login_url;
@SuppressWarnings("deprecation")
public static void clearCookies(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
} else {
CookieSyncManager cookieSyncMngr = CookieSyncManager.createInstance(context);
cookieSyncMngr.startSync();
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
cookieManager.removeSessionCookie();
cookieSyncMngr.stopSync();
cookieSyncMngr.sync();
}
}
@SuppressLint("ApplySharedPref")
public static void proceedLogin(Activity activity, Account account) {
new Thread(() -> {
try {
//update the database
new Account(activity).insertOrUpdate(account);
Handler mainHandler = new Handler(Looper.getMainLooper());
BaseMainActivity.currentToken = account.token;
BaseMainActivity.currentUserID = account.user_id;
BaseMainActivity.api = Account.API.MASTODON;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(PREF_USER_TOKEN, account.token);
editor.commit();
//The user is now authenticated, it will be redirected to MainActivity
Runnable myRunnable = () -> {
Intent mainActivity = new Intent(activity, MainActivity.class);
activity.startActivity(mainActivity);
activity.finish();
};
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
@SuppressLint("SetJavaScriptEnabled")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(WebviewConnectActivity.this);
binding = ActivityWebviewConnectBinding.inflate(getLayoutInflater());
View rootView = binding.getRoot();
setContentView(rootView);
Bundle b = getIntent().getExtras();
if (b != null) {
login_url = b.getString("login_url");
requestedAdmin = b.getBoolean("requestedAdmin", false);
}
if (login_url == null)
finish();
clearCookies(WebviewConnectActivity.this);
binding.webviewConnect.getSettings().setJavaScriptEnabled(true);
// String user_agent = sharedpreferences.getString(getString(R.string.SET_CUSTOM_USER_AGENT), Helper.USER_AGENT);
// binding.webviewConnect.getSettings().setUserAgentString(user_agent);
binding.webviewConnect.getSettings().setDomStorageEnabled(true);
CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webviewConnect, true);
final ProgressBar pbar = findViewById(R.id.progress_bar);
binding.webviewConnect.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int progress) {
if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
pbar.setVisibility(ProgressBar.VISIBLE);
}
pbar.setProgress(progress);
if (progress == 100) {
pbar.setVisibility(ProgressBar.GONE);
}
}
});
binding.webviewConnect.setWebViewClient(new WebViewClient() {
/* @Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String x_xsrf_token = null;
String x_csrf_token = null;
if (request.getUrl().toString().contains("accounts/verify_credentials")) {
String cookies = CookieManager.getInstance().getCookie(request.getUrl().toString());
Map<String, String> requestHeaders = request.getRequestHeaders();
Iterator<Map.Entry<String, String>> it = requestHeaders.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (pair.getKey().compareTo("X-XSRF-TOKEN") == 0) {
x_xsrf_token = pair.getValue();
}
if (pair.getKey().compareTo("X-CSRF-TOKEN") == 0) {
x_csrf_token = pair.getValue();
}
it.remove();
}
if (x_xsrf_token != null && x_csrf_token != null) {
String finalX_xsrf_token = x_xsrf_token;
String finalX_csrf_token = x_csrf_token;
new Handler(Looper.getMainLooper()).post(() -> {
view.stopLoading();
SharedPreferences sharedpreferences1 = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences1.edit();
String token = "X-XSRF-TOKEN= " + finalX_xsrf_token + ";X-CSRF-TOKEN= " + finalX_csrf_token + "|" + cookies;
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
editor.commit();
view.setVisibility(View.GONE);
//Update the account with the token;
new UpdateAccountInfoAsyncTask(WebviewConnectActivity.this, token, clientId, clientSecret, null, instance, social);
});
}
}
return super.shouldInterceptRequest(view, request);
}*/
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (request.getUrl().toString().contains(currentInstanceLogin + "/api/v1")) {
request.getRequestHeaders();
Map<String, String> requestHeaders = request.getRequestHeaders();
Iterator<Map.Entry<String, String>> it = requestHeaders.entrySet().iterator();
String token = null;
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (pair.getKey().equals("Authorization")) {
token = pair.getValue();
break;
}
it.remove();
}
if (token != null) {
AccountsVM accountsVM = new ViewModelProvider(WebviewConnectActivity.this).get(AccountsVM.class);
String finalToken = token;
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
accountsVM.getConnectedAccount(currentInstanceLogin, finalToken).observe(WebviewConnectActivity.this, mastodonAccount -> {
if (mastodonAccount != null) {
Account account = new Account();
account.client_id = client_idLogin;
account.client_secret = client_secretLogin;
account.token = finalToken;
account.api = apiLogin;
account.software = softwareLogin;
account.instance = currentInstanceLogin;
account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id;
//We check if user have really moderator rights
if (requestedAdmin) {
AdminVM adminVM = new ViewModelProvider(WebviewConnectActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(WebviewConnectActivity.this, adminAccount -> {
account.admin = adminAccount != null;
proceedLogin(WebviewConnectActivity.this, account);
});
} else {
proceedLogin(WebviewConnectActivity.this, account);
}
} else {
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
}
});
};
mainHandler.post(myRunnable);
}
}
return super.shouldInterceptRequest(view, request);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
super.shouldOverrideUrlLoading(view, url);
if (url.contains(Helper.REDIRECT_CONTENT_WEB)) {
Matcher matcher = Helper.codePattern.matcher(url);
if (!matcher.find()) {
return false;
}
String code = matcher.group(1);
OauthVM oauthVM = new ViewModelProvider(WebviewConnectActivity.this).get(OauthVM.class);
//API call to get the user token
String scope = requestedAdmin ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES;
oauthVM.createToken(currentInstanceLogin, "authorization_code", client_idLogin, client_secretLogin, Helper.REDIRECT_CONTENT_WEB, scope, code)
.observe(WebviewConnectActivity.this, tokenObj -> {
if (tokenObj != null) {
Account account = new Account();
account.client_id = client_idLogin;
account.client_secret = client_secretLogin;
account.token = tokenObj.token_type + " " + tokenObj.access_token;
account.api = apiLogin;
account.software = softwareLogin;
account.instance = currentInstanceLogin;
//API call to retrieve account information for the new token
AccountsVM accountsVM = new ViewModelProvider(WebviewConnectActivity.this).get(AccountsVM.class);
accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(WebviewConnectActivity.this, mastodonAccount -> {
if (mastodonAccount != null) {
account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id;
//We check if user have really moderator rights
if (requestedAdmin) {
AdminVM adminVM = new ViewModelProvider(WebviewConnectActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(WebviewConnectActivity.this, adminAccount -> {
account.admin = adminAccount != null;
proceedLogin(WebviewConnectActivity.this, account);
});
} else {
proceedLogin(WebviewConnectActivity.this, account);
}
} else {
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
}
});
} else {
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_token), Toasty.LENGTH_SHORT).show();
}
});
return true;
} else {
return false;
}
}
});
binding.webviewConnect.loadUrl(login_url);
}
@Override
public void onBackPressed() {
if (binding.webviewConnect.canGoBack()) {
binding.webviewConnect.goBack();
} else {
super.onBackPressed();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (alert != null) {
alert.dismiss();
alert = null;
}
binding.webviewConnect.destroy();
}
}

@ -18,7 +18,6 @@ import static android.content.Context.DOWNLOAD_SERVICE;
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.activities.BaseActivity.currentThemeId;
import static app.fedilab.android.helper.LogoHelper.getNotificationIcon;
import static app.fedilab.android.webview.ProxyHelper.setProxy;
import android.annotation.SuppressLint;
import android.app.Activity;
@ -68,11 +67,8 @@ import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.webkit.CookieManager;
import android.webkit.MimeTypeMap;
import android.webkit.URLUtil;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@ -152,7 +148,6 @@ import app.fedilab.android.activities.ComposeActivity;
import app.fedilab.android.activities.LoginActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.activities.ProfileActivity;
import app.fedilab.android.activities.WebviewActivity;
import app.fedilab.android.broadcastreceiver.ToastMessage;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Status;
@ -169,7 +164,6 @@ import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.viewmodel.mastodon.OauthVM;
import app.fedilab.android.watermark.androidwm.WatermarkBuilder;
import app.fedilab.android.watermark.androidwm.bean.WatermarkText;
import app.fedilab.android.webview.CustomWebview;
import es.dmoral.toasty.Toasty;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
@ -412,60 +406,6 @@ public class Helper {
patternHashMap = Collections.unmodifiableMap(aMap);
}
/***
* Initialize a CustomWebview
* @param activity Activity - activity containing the webview
* @param webviewId int - webview id
* @param rootView View - the root view that will contain the webview
* @return {@link CustomWebview}
*/
public static CustomWebview initializeWebview(Activity activity, int webviewId, View rootView) {
CustomWebview webView;
if (rootView == null) {
webView = activity.findViewById(webviewId);
} else {
webView = rootView.findViewById(webviewId);
}
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
boolean javascript = sharedpreferences.getBoolean(activity.getString(R.string.SET_JAVASCRIPT), true);
webView.getSettings().setJavaScriptEnabled(javascript);
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setSupportZoom(true);
webView.getSettings().setDisplayZoomControls(false);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setAllowContentAccess(true);
webView.getSettings().setSaveFormData(true);
webView.getSettings().setLoadsImagesAutomatically(true);
webView.getSettings().setSupportMultipleWindows(false);
webView.getSettings().setMediaPlaybackRequiresUserGesture(true);
//String user_agent = sharedpreferences.getString(activity.getString(R.string.SET_CUSTOM_USER_AGENT), USER_AGENT);
// webView.getSettings().setUserAgentString(user_agent);
boolean cookies = sharedpreferences.getBoolean(activity.getString(R.string.SET_COOKIES), false);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptThirdPartyCookies(webView, cookies);
webView.setBackgroundColor(Color.TRANSPARENT);
webView.getSettings().setAppCacheEnabled(true);
webView.getSettings().setDatabaseEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
webView.setWebChromeClient(new WebChromeClient() {
@Override
public Bitmap getDefaultVideoPoster() {
return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
}
});
boolean proxyEnabled = sharedpreferences.getBoolean(activity.getString(R.string.SET_PROXY_ENABLED), false);
if (proxyEnabled) {
String host = sharedpreferences.getString(activity.getString(R.string.SET_PROXY_HOST), "127.0.0.1");
int port = sharedpreferences.getInt(activity.getString(R.string.SET_PROXY_PORT), 8118);
setProxy(activity, webView, host, port, WebviewActivity.class.getName());
}
return webView;
}
/**
* Manage downloads with URLs
@ -719,30 +659,16 @@ public class Helper {
if (url == null) {
return;
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean embedded_browser = sharedpreferences.getBoolean(context.getString(R.string.SET_EMBEDDED_BROWSER), true);
if (embedded_browser && !url.toLowerCase().startsWith("gemini://")) {
Intent intent = new Intent(context, WebviewActivity.class);
Bundle b = new Bundle();
String finalUrl = url;
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
finalUrl = "http://" + url;
b.putString("url", finalUrl);
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://") && !url.toLowerCase().startsWith("gemini://")) {
url = "http://" + url;
}
intent.setData(Uri.parse(url));
try {
context.startActivity(intent);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://") && !url.toLowerCase().startsWith("gemini://")) {
url = "http://" + url;
}
intent.setData(Uri.parse(url));
try {
context.startActivity(intent);
} catch (Exception e) {
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
}

@ -50,7 +50,6 @@ import java.net.URLEncoder;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.ProxyActivity;
import app.fedilab.android.activities.WebviewConnectActivity;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.InstanceSocial;
import app.fedilab.android.databinding.FragmentLoginMainBinding;
@ -184,36 +183,14 @@ public class FragmentLoginMain extends Fragment {
PopupMenu popupMenu = new PopupMenu(requireActivity(), binding.menuIcon);
MenuInflater menuInflater = popupMenu.getMenuInflater();
menuInflater.inflate(R.menu.main_login, popupMenu.getMenu());
MenuItem customTabItem = popupMenu.getMenu().findItem(R.id.action_custom_tabs);
MenuItem adminTabItem = popupMenu.getMenu().findItem(R.id.action_request_admin);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
customTabItem.setChecked(!embedded_browser);
adminTabItem.setChecked(requestedAdmin);
popupMenu.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.action_proxy) {
Intent intent = new Intent(requireActivity(), ProxyActivity.class);
startActivity(intent);
} else if (itemId == R.id.action_custom_tabs) {
boolean checked = !embedded_browser;
item.setChecked(!item.isChecked());
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean(getString(R.string.SET_EMBEDDED_BROWSER), checked);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
item.setActionView(new View(requireContext()));
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return false;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
return false;
}
});
editor.apply();
} else if (itemId == R.id.action_request_admin) {
item.setChecked(!item.isChecked());
@ -267,24 +244,13 @@ public class FragmentLoginMain extends Fragment {
client_idLogin = app.client_id;
client_secretLogin = app.client_secret;
String redirectUrl = MastodonHelper.authorizeURL(currentInstanceLogin, client_idLogin, requestedAdmin);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
if (embedded_browser) {
Intent i = new Intent(requireActivity(), WebviewConnectActivity.class);
i.putExtra("login_url", redirectUrl);
i.putExtra("requestedAdmin", requestedAdmin);
startActivity(i);
requireActivity().finish();
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(redirectUrl));
try {
startActivity(intent);
} catch (Exception e) {
Toasty.error(requireActivity(), getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(redirectUrl));
try {
startActivity(intent);
} catch (Exception e) {
Toasty.error(requireActivity(), getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
} else {
Toasty.error(requireActivity(), getString(R.string.client_error), Toasty.LENGTH_SHORT).show();

@ -24,7 +24,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.ImageView;
import androidx.annotation.NonNull;
@ -53,9 +52,6 @@ import app.fedilab.android.databinding.FragmentSlideMediaBinding;
import app.fedilab.android.helper.CacheDataSourceFactory;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
import app.fedilab.android.webview.CustomWebview;
import app.fedilab.android.webview.FedilabWebChromeClient;
import app.fedilab.android.webview.FedilabWebViewClient;
public class FragmentMedia extends Fragment {
@ -67,7 +63,6 @@ public class FragmentMedia extends Fragment {
private boolean canSwipe;
private Attachment attachment;
private boolean swipeEnabled;
private CustomWebview webview_video;
private FragmentSlideMediaBinding binding;
@ -90,7 +85,6 @@ public class FragmentMedia extends Fragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
swipeEnabled = true;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
url = attachment.url;
binding.mediaPicture.setOnMatrixChangeListener(rect -> {
@ -218,39 +212,6 @@ public class FragmentMedia extends Fragment {
loadVideo(url, type);
}
break;
case "web":
binding.loader.setVisibility(View.GONE);
binding.mediaPicture.setVisibility(View.GONE);
webview_video = Helper.initializeWebview(requireActivity(), R.id.webview_video, binding.getRoot());
webview_video.setVisibility(View.VISIBLE);
FedilabWebChromeClient fedilabWebChromeClient = new FedilabWebChromeClient(requireActivity(), webview_video, binding.mainMediaFrame, binding.videoLayout);
fedilabWebChromeClient.setOnToggledFullscreen(fullscreen -> {
if (fullscreen) {
binding.videoLayout.setVisibility(View.VISIBLE);
WindowManager.LayoutParams attrs = (requireActivity()).getWindow().getAttributes();
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
(requireActivity()).getWindow().setAttributes(attrs);
(requireActivity()).getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
} else {
WindowManager.LayoutParams attrs = (requireActivity()).getWindow().getAttributes();
attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
(requireActivity()).getWindow().setAttributes(attrs);
(requireActivity()).getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
binding.videoLayout.setVisibility(View.GONE);
}
});
webview_video.getSettings().setAllowFileAccess(true);
webview_video.setWebChromeClient(fedilabWebChromeClient);
webview_video.getSettings().setDomStorageEnabled(true);
webview_video.getSettings().setAppCacheEnabled(true);
// String user_agent = sharedpreferences.getString(getString(R.string.SET_CUSTOM_USER_AGENT), Helper.USER_AGENT);
// webview_video.getSettings().setUserAgentString(user_agent);
webview_video.getSettings().setMediaPlaybackRequiresUserGesture(false);
webview_video.setWebViewClient(new FedilabWebViewClient(requireActivity()));
webview_video.loadUrl(attachment.url);
break;
}
}
@ -298,9 +259,6 @@ public class FragmentMedia extends Fragment {
if (player != null) {
player.setPlayWhenReady(false);
}
if (webview_video != null) {
webview_video.onPause();
}
}
@ -313,9 +271,6 @@ public class FragmentMedia extends Fragment {
}
} catch (Exception ignored) {
}
if (webview_video != null) {
webview_video.destroy();
}
if (timer != null) {
timer.cancel();
timer = null;
@ -328,11 +283,6 @@ public class FragmentMedia extends Fragment {
if (player != null) {
player.setPlayWhenReady(true);
}
if (webview_video != null) {
webview_video.onResume();
}
}
private void scheduleStartPostponedTransition(final ImageView imageView) {

@ -1,52 +0,0 @@
package app.fedilab.android.webview;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.util.AttributeSet;
import android.webkit.WebView;
public class CustomWebview extends WebView {
public CustomWebview(Context context) {
super(getFixedContext(context));
}
public CustomWebview(Context context, AttributeSet attrs) {
super(getFixedContext(context), attrs);
}
public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr) {
super(getFixedContext(context), attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(getFixedContext(context), attrs, defStyleAttr, defStyleRes);
}
@SuppressWarnings("deprecation")
public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) {
super(getFixedContext(context), attrs, defStyleAttr, privateBrowsing);
}
public static Context getFixedContext(Context context) {
return context.createConfigurationContext(new Configuration());
}
}

@ -1,228 +0,0 @@
package app.fedilab.android.webview;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.app.Activity;
import android.graphics.Bitmap;
import android.media.MediaPlayer;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import app.fedilab.android.R;
public class FedilabWebChromeClient extends WebChromeClient implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
private final CustomWebview webView;
private final View activityNonVideoView;
private final ViewGroup activityVideoView;
private final ProgressBar pbar;
private final Activity activity;
private FrameLayout videoViewContainer;
private CustomViewCallback videoViewCallback;
private ToggledFullscreenCallback toggledFullscreenCallback;
private boolean isVideoFullscreen;
public FedilabWebChromeClient(Activity activity, CustomWebview webView, FrameLayout activityNonVideoView, ViewGroup activityVideoView) {
this.activity = activity;
this.isVideoFullscreen = false;
this.webView = webView;
this.pbar = activity.findViewById(R.id.progress_bar);
this.activityNonVideoView = activityNonVideoView;
this.activityVideoView = activityVideoView;
}
@Override
public void onProgressChanged(WebView view, int progress) {
if (pbar != null) {
if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
pbar.setVisibility(ProgressBar.VISIBLE);
}
pbar.setProgress(progress);
if (progress == 100) {
pbar.setVisibility(ProgressBar.GONE);
}
}
}
@Override
public Bitmap getDefaultVideoPoster() {
return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
LayoutInflater mInflater = LayoutInflater.from(activity);
ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar();
if (actionBar != null) {
View webview_actionbar = mInflater.inflate(R.layout.webview_actionbar, new LinearLayout(activity), false);
TextView webview_title = webview_actionbar.findViewById(R.id.webview_title);
webview_title.setText(view.getTitle());
ImageView webview_favicon = webview_actionbar.findViewById(R.id.webview_favicon);
if (icon != null)
webview_favicon.setImageBitmap(icon);
actionBar.setCustomView(webview_actionbar);
actionBar.setDisplayShowCustomEnabled(true);
} else {
activity.setTitle(view.getTitle());
}
}
/**
* Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen)
*
* @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback
*/
public void setOnToggledFullscreen(ToggledFullscreenCallback callback) {
this.toggledFullscreenCallback = callback;
}
//FULLSCREEN VIDEO
//Code from https://stackoverflow.com/a/16179544/3197259
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
if (view instanceof FrameLayout) {
if (((AppCompatActivity) activity).getSupportActionBar() != null)
//noinspection ConstantConditions
((AppCompatActivity) activity).getSupportActionBar().hide();
// A video wants to be shown
FrameLayout frameLayout = (FrameLayout) view;
View focusedChild = frameLayout.getFocusedChild();
// Save video related variables
isVideoFullscreen = true;
this.videoViewContainer = frameLayout;
this.videoViewCallback = callback;
// Hide the non-video view, add the video view, and show it
activityNonVideoView.setVisibility(View.INVISIBLE);
activityVideoView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
activityVideoView.setVisibility(View.VISIBLE);
if (focusedChild instanceof android.widget.VideoView) {
// android.widget.VideoView (typically API level <11)
android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;
// Handle all the required events
videoView.setOnCompletionListener(this);
videoView.setOnErrorListener(this);
} else {
// Other classes, including:
// - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 11-18)
// - android.webkit.HTML5VideoFullScreen$VideoTextureView, which inherits from android.view.TextureView (typically API level 11-18)
// - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 19+)
// Handle HTML5 video ended event only if the class is a SurfaceView
// Test case: TextureView of Sony Xperia T API level 16 doesn't work fullscreen when loading the javascript below
if (webView != null && webView.getSettings().getJavaScriptEnabled() && focusedChild instanceof SurfaceView) {
// Run javascript code that detects the video end and notifies the Javascript interface
String js = "javascript:";
js += "var _ytrp_html5_video_last;";
js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
{
js += "_ytrp_html5_video_last = _ytrp_html5_video;";
js += "function _ytrp_html5_video_ended() {";
{
js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
}
js += "}";
js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
}
js += "}";
webView.loadUrl(js);
}
}
// Notify full-screen change
if (toggledFullscreenCallback != null) {
toggledFullscreenCallback.toggledFullscreen(true);
}
}
}
// Available in API level 14+, deprecated in API level 18+
@Override
@SuppressWarnings("deprecation")
public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {
onShowCustomView(view, callback);
}
@Override
public void onHideCustomView() {
if (((AppCompatActivity) activity).getSupportActionBar() != null)
//noinspection ConstantConditions
((AppCompatActivity) activity).getSupportActionBar().show();
// This method should be manually called on video end in all cases because it's not always called automatically.
// This method must be manually called on back key press (from this class' onBackPressed() method).
if (isVideoFullscreen) {
// Hide the video view, remove it, and show the non-video view
activityVideoView.setVisibility(View.INVISIBLE);
activityVideoView.removeView(videoViewContainer);
activityNonVideoView.setVisibility(View.VISIBLE);
// Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium.")) {
videoViewCallback.onCustomViewHidden();
}
// Reset video related variables
isVideoFullscreen = false;
videoViewContainer = null;
videoViewCallback = null;
// Notify full-screen change
if (toggledFullscreenCallback != null) {
toggledFullscreenCallback.toggledFullscreen(false);
}
}
}
// Video will start loading
@Override
public View getVideoLoadingProgressView() {
return super.getVideoLoadingProgressView();
}
// Video finished playing, only called in the case of android.widget.VideoView (typically API level <11)
@Override
public void onCompletion(MediaPlayer mp) {
onHideCustomView();
}
// Error while playing video, only called in the case of android.widget.VideoView (typically API level <11)
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false; // By returning false, onCompletion() will be called
}
public interface ToggledFullscreenCallback {
void toggledFullscreen(boolean fullscreen);
}
}

@ -1,163 +0,0 @@
package app.fedilab.android.webview;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.client.entities.app.DomainsBlock.trackingDomains;
import android.app.Activity;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.SslErrorHandler;
import android.webkit.URLUtil;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R;
import app.fedilab.android.activities.WebviewActivity;
public class FedilabWebViewClient extends WebViewClient {
private final Activity activity;
public List<String> domains = new ArrayList<>();
private int count = 0;
public FedilabWebViewClient(Activity activity) {
this.activity = activity;
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
@Override
public WebResourceResponse shouldInterceptRequest(final WebView view, String url) {
if (trackingDomains != null) {
URI uri;
try {
uri = new URI(url);
String domain = uri.getHost();
if (domain != null) {
domain = domain.startsWith("www.") ? domain.substring(4) : domain;
}
if (domain != null && trackingDomains.contains(domain)) {
if (activity instanceof WebviewActivity) {
count++;
domains.add(url);
((WebviewActivity) activity).setCount(activity, String.valueOf(count));
}
ByteArrayInputStream nothing = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", nothing);
}
} catch (URISyntaxException e) {
try {
if (url.length() > 50) {
url = url.substring(0, 50);
}
uri = new URI(url);
String domain = uri.getHost();
if (domain != null) {
domain = domain.startsWith("www.") ? domain.substring(4) : domain;
}
if (domain != null && trackingDomains.contains(domain)) {
if (activity instanceof WebviewActivity) {
count++;
domains.add(url);
((WebviewActivity) activity).setCount(activity, String.valueOf(count));
}
ByteArrayInputStream nothing = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", nothing);
}
} catch (URISyntaxException ignored) {
}
}
}
return super.shouldInterceptRequest(view, url);
}
public List<String> getDomains() {
return this.domains;
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if (BuildConfig.DONATIONS) {
if (view.getUrl() != null && view.getUrl().endsWith(".onion")) {
handler.proceed();
} else {
super.onReceivedSslError(view, handler, error);
}
} else {
super.onReceivedSslError(view, handler, error);
}
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (URLUtil.isNetworkUrl(url)) {
return false;
} else {
view.stopLoading();
view.goBack();
}
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
count = 0;
domains = new ArrayList<>();
domains.clear();
ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar();
LayoutInflater mInflater = LayoutInflater.from(activity);
if (actionBar != null) {
View webview_actionbar = mInflater.inflate(R.layout.webview_actionbar, new LinearLayout(activity), false);
TextView webview_title = webview_actionbar.findViewById(R.id.webview_title);
webview_title.setText(url);
actionBar.setCustomView(webview_actionbar);
actionBar.setDisplayShowCustomEnabled(true);
} else {
activity.setTitle(url);
}
//Changes the url in webview activity so that it can be opened with an external app
try {
if (activity instanceof WebviewActivity)
((WebviewActivity) activity).setUrl(url);
} catch (Exception ignore) {
}
}
}

@ -1,166 +0,0 @@
package app.fedilab.android.webview;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Proxy;
import android.util.ArrayMap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ProxyHelper {
public static void setProxy(Context context, CustomWebview webview, String host, int port, String applicationClassName) {
setProxyKKPlus(context, webview, host, port, applicationClassName);
}
@SuppressWarnings("all")
private static boolean setProxyICS(CustomWebview webview, String host, int port) {
try {
Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
Class params[] = new Class[1];
params[0] = Class.forName("android.net.ProxyProperties");
Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
Class wv = Class.forName("android.webkit.WebView");
Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webview);
Class wvc = Class.forName("android.webkit.WebViewCore");
Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
Class bf = Class.forName("android.webkit.BrowserFrame");
Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
Class ppclass = Class.forName("android.net.ProxyProperties");
Class pparams[] = new Class[3];
pparams[0] = String.class;
pparams[1] = int.class;
pparams[2] = String.class;
Constructor ppcont = ppclass.getConstructor(pparams);
updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null));
return true;
} catch (Exception ex) {
return false;
}
}
/**
* Set Proxy for Android 4.1 - 4.3.
*/
@SuppressWarnings("all")
private static boolean setProxyJB(CustomWebview webview, String host, int port) {
try {
Class wvcClass = Class.forName("android.webkit.WebViewClassic");
Class wvParams[] = new Class[1];
wvParams[0] = Class.forName("android.webkit.WebView");
Method fromWebView = wvcClass.getDeclaredMethod("fromWebView", wvParams);
Object webViewClassic = fromWebView.invoke(null, webview);
Class wv = Class.forName("android.webkit.WebViewClassic");
Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore");
Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webViewClassic);
Class wvc = Class.forName("android.webkit.WebViewCore");
Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame");
Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance);
Class bf = Class.forName("android.webkit.BrowserFrame");
Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge");
Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame);
Class ppclass = Class.forName("android.net.ProxyProperties");
Class pparams[] = new Class[3];
pparams[0] = String.class;
pparams[1] = int.class;
pparams[2] = String.class;
Constructor ppcont = ppclass.getConstructor(pparams);
Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge");
Class params[] = new Class[1];
params[0] = Class.forName("android.net.ProxyProperties");
Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params);
updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null));
} catch (Exception ex) {
return false;
}
return true;
}
// from https://stackoverflow.com/questions/19979578/android-webview-set-proxy-programatically-kitkat
@SuppressLint("NewApi")
@SuppressWarnings("all")
private static void setProxyKKPlus(Context appContext, CustomWebview webView, String host, int port, String applicationClassName) {
System.setProperty("http.proxyHost", host);
System.setProperty("http.proxyPort", port + "");
System.setProperty("https.proxyHost", host);
System.setProperty("https.proxyPort", port + "");
try {
Class applictionCls = Class.forName("android.app.Application");
Field loadedApkField = applictionCls.getDeclaredField("mLoadedApk");
loadedApkField.setAccessible(true);
Object loadedApk = loadedApkField.get(appContext);
Class loadedApkCls = Class.forName("android.app.LoadedApk");
Field receiversField = loadedApkCls.getDeclaredField("mReceivers");
receiversField.setAccessible(true);
ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
for (Object receiverMap : receivers.values()) {
for (Object rec : ((ArrayMap) receiverMap).keySet()) {
Class clazz = rec.getClass();
if (clazz.getName().contains("ProxyChangeListener")) {
Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
onReceiveMethod.invoke(rec, appContext, intent);
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private static Object getFieldValueSafely(Field field, Object classInstance) throws IllegalArgumentException, IllegalAccessException {
boolean oldAccessibleValue = field.isAccessible();
field.setAccessible(true);
Object result = field.get(classInstance);
field.setAccessible(oldAccessibleValue);
return result;
}
}

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2021 Thomas Schneider
This file is a part of Fedilab
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with Fedilab; if not,
see <http://www.gnu.org/licenses>
-->
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.WebviewActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|enterAlways">
<ImageView
android:id="@+id/favicon"
android:layout_width="32dp"
android:layout_height="32dp"
android:contentDescription="@string/favicon" />
<TextView
android:id="@+id/toolbar_title"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="10dp"
android:padding="1dp" />
<FrameLayout
android:id="@+id/webview_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<app.fedilab.android.webview.CustomWebview
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white" />
</FrameLayout>
<!-- View where the video will be shown when video goes fullscreen -->
<RelativeLayout
android:id="@+id/videoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:visibility="gone" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.drawerlayout.widget.DrawerLayout>

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2022 Thomas Schneider
This file is a part of Fedilab
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 3 of the
License, or (at your option) any later version.
Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with Fedilab; if not,
see <http://www.gnu.org/licenses>
-->
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="10dp"
android:padding="2dp" />
<app.fedilab.android.webview.CustomWebview
android:id="@+id/webviewConnect"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</androidx.drawerlayout.widget.DrawerLayout>

@ -43,19 +43,6 @@
android:textSize="12sp" />
</RelativeLayout>
<FrameLayout
android:id="@+id/main_media_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true">
<app.fedilab.android.webview.CustomWebview
android:id="@+id/webview_video"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</FrameLayout>
<RelativeLayout
android:id="@+id/videoLayout"
android:layout_width="match_parent"

@ -16,11 +16,6 @@
android:id="@+id/action_proxy"
android:title="@string/proxy_set"
app:showAsAction="never" />
<item
android:id="@+id/action_custom_tabs"
android:checkable="true"
android:title="@string/custom_tabs"
app:actionViewClass="android.widget.CheckBox" />
<item
android:id="@+id/action_request_admin"
android:checkable="true"

@ -774,7 +774,6 @@
<string name="SET_PROXY_TYPE" translatable="false">SET_PROXY_TYPE</string>
<string name="SET_PROXY_HOST" translatable="false">SET_PROXY_HOST</string>
<string name="SET_PROXY_PORT" translatable="false">SET_PROXY_PORT</string>
<string name="SET_EMBEDDED_BROWSER" translatable="false">SET_EMBEDDED_BROWSER</string>
<string name="SET_DEFAULT_LOCALE_NEW" translatable="false">SET_DEFAULT_LOCALE_NEW</string>
<string name="SET_SEND_CRASH_REPORTS" translatable="false">SET_SEND_CRASH_REPORTS</string>
<string name="SET_DISABLE_GIF" translatable="false">SET_DISABLE_GIF</string>

@ -52,12 +52,6 @@
app:summary="@string/set_clear_cache_exit_indication"
app:title="@string/set_clear_cache_exit" />
<SwitchPreferenceCompat
app:defaultValue="true"
app:iconSpaceReserved="false"
app:key="@string/SET_EMBEDDED_BROWSER"
app:singleLineTitle="false"
app:title="@string/embedded_browser" />
<SwitchPreferenceCompat
app:defaultValue="false"

Loading…
Cancel
Save