/* * This is the source code of Telegram for Android v. 5.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Nikolai Kudashov, 2013-2018. */ package org.telegram.ui; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.text.TextUtils; import android.view.MotionEvent; import android.view.View; import android.view.ViewParent; import android.webkit.CookieManager; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Components.ContextProgressView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.ShareAlert; import java.net.URLEncoder; import java.util.ArrayList; public class WebviewActivity extends BaseFragment { private WebView webView; private ActionBarMenuItem progressItem; private ContextProgressView progressView; private String currentUrl; private long currentDialogId; private String currentBot; private String currentGame; private String linkToCopy; private MessageObject currentMessageObject; private String short_param; private boolean loadStats; private final static int share = 1; private final static int open_in = 2; private static final int TYPE_GAME = 0; private static final int TYPE_STAT = 1; private int type; private class TelegramWebviewProxy { @JavascriptInterface public void postEvent(final String eventName, final String eventData) { AndroidUtilities.runOnUIThread(() -> { if (getParentActivity() == null) { return; } if (BuildVars.LOGS_ENABLED) { FileLog.d(eventName); } switch (eventName) { case "share_game": currentMessageObject.messageOwner.with_my_score = false; break; case "share_score": currentMessageObject.messageOwner.with_my_score = true; break; } showDialog(ShareAlert.createShareAlert(getParentActivity(), currentMessageObject, null, false, linkToCopy, false)); }); } } public Runnable typingRunnable = new Runnable() { @Override public void run() { if (currentMessageObject == null || getParentActivity() == null || typingRunnable == null) { return; } MessagesController.getInstance(currentAccount).sendTyping(currentMessageObject.getDialogId(), 0, 6, 0); AndroidUtilities.runOnUIThread(typingRunnable, 25000); } }; public WebviewActivity(String url, String botName, String gameName, String startParam, MessageObject messageObject) { super(); currentUrl = url; currentBot = botName; currentGame = gameName; currentMessageObject = messageObject; short_param = startParam; linkToCopy = "https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/" + currentBot + (TextUtils.isEmpty(startParam) ? "" : "?game=" + startParam); type = TYPE_GAME; } public WebviewActivity(String statUrl, long did) { super(); currentUrl = statUrl; currentDialogId = did; type = TYPE_STAT; } @Override public void onFragmentDestroy() { super.onFragmentDestroy(); AndroidUtilities.cancelRunOnUIThread(typingRunnable); webView.setLayerType(View.LAYER_TYPE_NONE, null); typingRunnable = null; try { ViewParent parent = webView.getParent(); if (parent != null) { ((FrameLayout) parent).removeView(webView); } webView.stopLoading(); webView.loadUrl("about:blank"); webView.destroy(); webView = null; } catch (Exception e) { FileLog.e(e); } } @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"}) @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { finishFragment(); } else if (id == share) { if (currentMessageObject != null) { currentMessageObject.messageOwner.with_my_score = false; showDialog(ShareAlert.createShareAlert(getParentActivity(), currentMessageObject, null, false, linkToCopy, false)); } } else if (id == open_in) { openGameInBrowser(currentUrl, currentMessageObject, getParentActivity(), short_param, currentBot); } } }); ActionBarMenu menu = actionBar.createMenu(); progressItem = menu.addItemWithWidth(share, R.drawable.baseline_share_24, AndroidUtilities.dp(54)); if (type == TYPE_GAME) { ActionBarMenuItem menuItem = menu.addItem(0, R.drawable.ic_ab_other); menuItem.addSubItem(open_in, R.drawable.baseline_open_in_browser_24, LocaleController.getString("OpenInExternalApp", R.string.OpenInExternalApp)); actionBar.setTitle(currentGame); actionBar.setSubtitle("@" + currentBot); progressView = new ContextProgressView(context, 1); progressItem.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); progressView.setAlpha(0.0f); progressView.setScaleX(0.1f); progressView.setScaleY(0.1f); progressView.setVisibility(View.INVISIBLE); } else if (type == TYPE_STAT) { actionBar.setBackgroundColor(Theme.getColor(Theme.key_player_actionBar)); actionBar.setItemsColor(Theme.getColor(Theme.key_player_actionBarItems), false); actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_player_actionBarSelector), false); actionBar.setTitleColor(Theme.getColor(Theme.key_player_actionBarTitle)); actionBar.setSubtitleColor(Theme.getColor(Theme.key_player_actionBarSubtitle)); actionBar.setTitle(LocaleController.getString("Statistics", R.string.Statistics)); progressView = new ContextProgressView(context, 3); progressItem.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); progressView.setAlpha(1.0f); progressView.setScaleX(1.0f); progressView.setScaleY(1.0f); progressView.setVisibility(View.VISIBLE); progressItem.getContentView().setVisibility(View.GONE); progressItem.setEnabled(false); } webView = new WebView(context); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setDomStorageEnabled(true); fragmentView = new FrameLayout(context); FrameLayout frameLayout = (FrameLayout) fragmentView; if (Build.VERSION.SDK_INT >= 19) { webView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } if (Build.VERSION.SDK_INT >= 21) { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptThirdPartyCookies(webView, true); if (type == TYPE_GAME) { webView.addJavascriptInterface(new TelegramWebviewProxy(), "TelegramWebviewProxy"); } } webView.setWebViewClient(new WebViewClient() { private boolean isInternalUrl(String url) { if (TextUtils.isEmpty(url)) { return false; } Uri uri = Uri.parse(url); if ("tg".equals(uri.getScheme())) { if (type == TYPE_STAT) { try { uri = Uri.parse(url.replace("tg:statsrefresh", "tg://telegram.org")); reloadStats(uri.getQueryParameter("params")); } catch (Throwable e) { FileLog.e(e); } } else { finishFragment(false); try { Intent intent = new Intent(Intent.ACTION_VIEW, uri); ComponentName componentName = new ComponentName(ApplicationLoader.applicationContext.getPackageName(), LaunchActivity.class.getName()); intent.setComponent(componentName); intent.putExtra(android.provider.Browser.EXTRA_APPLICATION_ID, ApplicationLoader.applicationContext.getPackageName()); ApplicationLoader.applicationContext.startActivity(intent); } catch (Exception e) { FileLog.e(e); } } return true; } return false; } @Override public void onLoadResource(WebView view, String url) { if (isInternalUrl(url)) { return; } super.onLoadResource(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return isInternalUrl(url) || super.shouldOverrideUrlLoading(view, url); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); if (progressView != null && progressView.getVisibility() == View.VISIBLE) { AnimatorSet animatorSet = new AnimatorSet(); if (type == TYPE_GAME) { progressItem.getContentView().setVisibility(View.VISIBLE); progressItem.setEnabled(true); animatorSet.playTogether( ObjectAnimator.ofFloat(progressView, "scaleX", 1.0f, 0.1f), ObjectAnimator.ofFloat(progressView, "scaleY", 1.0f, 0.1f), ObjectAnimator.ofFloat(progressView, "alpha", 1.0f, 0.0f), ObjectAnimator.ofFloat(progressItem.getContentView(), "scaleX", 0.0f, 1.0f), ObjectAnimator.ofFloat(progressItem.getContentView(), "scaleY", 0.0f, 1.0f), ObjectAnimator.ofFloat(progressItem.getContentView(), "alpha", 0.0f, 1.0f)); } else { animatorSet.playTogether( ObjectAnimator.ofFloat(progressView, "scaleX", 1.0f, 0.1f), ObjectAnimator.ofFloat(progressView, "scaleY", 1.0f, 0.1f), ObjectAnimator.ofFloat(progressView, "alpha", 1.0f, 0.0f)); } animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { if (type == TYPE_STAT) { progressItem.setVisibility(View.GONE); } else { progressView.setVisibility(View.INVISIBLE); } } }); animatorSet.setDuration(150); animatorSet.start(); } } }); frameLayout.addView(webView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); return fragmentView; } @Override public void onResume() { super.onResume(); AndroidUtilities.cancelRunOnUIThread(typingRunnable); typingRunnable.run(); } @Override protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) { if (isOpen && !backward && webView != null) { webView.loadUrl(currentUrl); } } @Override public boolean isSwipeBackEnabled(MotionEvent event) { return false; } public static boolean supportWebview() { String manufacturer = Build.MANUFACTURER; String model = Build.MODEL; if ("samsung".equals(manufacturer)) { if ("GT-I9500".equals(model)) { return false; } } return true; } private void reloadStats(String params) { if (loadStats) { return; } loadStats = true; TLRPC.TL_messages_getStatsURL req = new TLRPC.TL_messages_getStatsURL(); req.peer = MessagesController.getInstance(currentAccount).getInputPeer(currentDialogId); req.params = params != null ? params : ""; req.dark = Theme.getCurrentTheme().isDark(); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { loadStats = false; if (response != null) { TLRPC.TL_statsURL url = (TLRPC.TL_statsURL) response; webView.loadUrl(currentUrl = url.url); } })); } public static void openGameInBrowser(String urlStr, MessageObject messageObject, Activity parentActivity, String short_name, String username) { try { String url = urlStr; SharedPreferences sharedPreferences = ApplicationLoader.applicationContext.getSharedPreferences("botshare", Activity.MODE_PRIVATE); String existing = sharedPreferences.getString("" + messageObject.getId(), null); StringBuilder hash = new StringBuilder(existing != null ? existing : ""); StringBuilder addHash = new StringBuilder("tgShareScoreUrl=" + URLEncoder.encode("tgb://share_game_score?hash=", "UTF-8")); if (existing == null) { final char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); for (int i = 0; i < 20; i++) { hash.append(chars[Utilities.random.nextInt(chars.length)]); } } addHash.append(hash); int index = url.indexOf('#'); if (index < 0) { url += "#" + addHash; } else { String curHash = url.substring(index + 1); if (curHash.indexOf('=') >= 0 || curHash.indexOf('?') >= 0) { url += "&" + addHash; } else { if (curHash.length() > 0) { url += "?" + addHash; } else { url += addHash; } } } SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putInt(hash + "_date", (int) (System.currentTimeMillis() / 1000)); SerializedData serializedData = new SerializedData(messageObject.messageOwner.getObjectSize()); messageObject.messageOwner.serializeToStream(serializedData); editor.putString(hash + "_m", Utilities.bytesToHex(serializedData.toByteArray())); editor.putString(hash + "_link", "https://" + MessagesController.getInstance(messageObject.currentAccount).linkPrefix + "/" + username + (TextUtils.isEmpty(short_name) ? "" : "?game=" + short_name)); editor.commit(); Browser.openUrl(parentActivity, url, false); serializedData.cleanup(); } catch (Exception e) { FileLog.e(e); } } @Override public ArrayList getThemeDescriptions() { ArrayList themeDescriptions = new ArrayList<>(); if (type == TYPE_GAME) { themeDescriptions.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUBACKGROUND, null, null, null, null, Theme.key_actionBarDefaultSubmenuBackground)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM, null, null, null, null, Theme.key_actionBarDefaultSubmenuItem)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM | ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_actionBarDefaultSubmenuItemIcon)); themeDescriptions.add(new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressInner2)); themeDescriptions.add(new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressOuter2)); } else { themeDescriptions.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_player_actionBar)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_player_actionBarItems)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_player_actionBarTitle)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBTITLECOLOR, null, null, null, null, Theme.key_player_actionBarTitle)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_player_actionBarSelector)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUBACKGROUND, null, null, null, null, Theme.key_actionBarDefaultSubmenuBackground)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM, null, null, null, null, Theme.key_actionBarDefaultSubmenuItem)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM | ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_actionBarDefaultSubmenuItemIcon)); themeDescriptions.add(new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressInner4)); themeDescriptions.add(new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressOuter4)); } return themeDescriptions; } }