From 508c8df68b167e93d90a84820e02c19d00816016 Mon Sep 17 00:00:00 2001 From: M66B Date: Sun, 4 Apr 2021 12:55:47 +0200 Subject: [PATCH] Refactoring --- .../eu/faircode/email/AdapterMessage.java | 402 +-------------- .../email/FragmentDialogOpenLink.java | 458 ++++++++++++++++++ 2 files changed, 459 insertions(+), 401 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index b1bd4abecf..48e35da52e 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -65,7 +65,6 @@ import android.text.TextUtils; import android.text.TextWatcher; import android.text.format.DateUtils; import android.text.method.ArrowKeyMovementMethod; -import android.text.method.LinkMovementMethod; import android.text.style.BackgroundColorSpan; import android.text.style.DynamicDrawableSpan; import android.text.style.ForegroundColorSpan; @@ -91,7 +90,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.ConversationAction; import android.view.textclassifier.ConversationActions; @@ -119,7 +117,6 @@ import androidx.core.content.FileProvider; import androidx.core.content.pm.ShortcutInfoCompat; import androidx.core.content.pm.ShortcutManagerCompat; import androidx.core.graphics.ColorUtils; -import androidx.core.util.PatternsCompat; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.lifecycle.Lifecycle; @@ -156,7 +153,6 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; -import java.net.IDN; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.text.Collator; @@ -293,34 +289,6 @@ public class AdapterMessage extends RecyclerView.Adapter PARANOID_QUERY = Collections.unmodifiableList(Arrays.asList( - // https://en.wikipedia.org/wiki/UTM_parameters - "icid", // Adobe - "gclid", // Google - "gclsrc", // Google ads - "dclid", // DoubleClick (Google) - "fbclid", // Facebook - "igshid", // Instagram - - "mc_cid", // MailChimp - "mc_eid", // MailChimp - - "zanpid", // Zanox (Awin) - - "kclickid" // https://support.freespee.com/hc/en-us/articles/202577831-Kenshoo-integration - )); - - // https://github.com/snarfed/granary/blob/master/granary/facebook.py#L1789 - - private static final List FACEBOOK_WHITELIST_PATH = Collections.unmodifiableList(Arrays.asList( - "/nd/", "/n/", "/story.php" - )); - - private static final List FACEBOOK_WHITELIST_QUERY = Collections.unmodifiableList(Arrays.asList( - "story_fbid", "fbid", "id", "comment_id" - )); - // https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml private static final List IMAP_KEYWORDS_BLACKLIST = Collections.unmodifiableList(Arrays.asList( "$MDNSent".toLowerCase(Locale.ROOT), // https://tools.ietf.org/html/rfc3503 @@ -4385,7 +4353,7 @@ public class AdapterMessage extends RecyclerView.Adapter>() { - @Override - protected void onPreExecute(Bundle args) { - btnOwner.setEnabled(false); - pbWait.setVisibility(View.VISIBLE); - grpOwner.setVisibility(View.GONE); - } - - @Override - protected void onPostExecute(Bundle args) { - btnOwner.setEnabled(true); - pbWait.setVisibility(View.GONE); - grpOwner.setVisibility(View.VISIBLE); - } - - @Override - protected Pair onExecute(Context context, Bundle args) throws Throwable { - Uri uri = args.getParcelable("uri"); - return IPInfo.getOrganization(uri, context); - } - - @Override - protected void onExecuted(Bundle args, Pair data) { - tvHost.setText(data.first); - tvOwner.setText(data.second.name == null ? "?" : data.second.name); - ApplicationEx.getMainHandler().post(new Runnable() { - @Override - public void run() { - dview.scrollTo(0, tvOwner.getBottom()); - } - }); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - tvHost.setText(ex.getClass().getName()); - tvOwner.setText(ex.getMessage()); - } - }.execute(FragmentDialogLink.this, args, "link:owner"); - } - }); - - tvTitle.setText(title); - tvTitle.setVisibility(TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE); - - String host = uri.getHost(); - String puny; - try { - puny = IDN.toASCII(host); - } catch (Throwable ex) { - Log.e(ex); - puny = host; - } - - if (host != null && !host.equals(puny)) { - etLink.setText(uri.buildUpon().encodedAuthority(puny).build().toString()); - etLink.setTextColor(Helper.resolveColor(context, R.attr.colorWarning)); - } else - etLink.setText(uri.toString()); - - grpDifferent.setVisibility(host == null || - uriTitle == null || uriTitle.getHost() == null || - uriTitle.getHost().equalsIgnoreCase(host) - ? View.GONE : View.VISIBLE); - - boolean disconnect_links = prefs.getBoolean("disconnect_links", true); - List categories = null; - if (disconnect_links) - categories = DisconnectBlacklist.getCategories(uri.getHost()); - if (categories != null) - tvDisconnectCategories.setText(TextUtils.join(", ", categories)); - tvDisconnect.setVisibility(categories == null ? View.GONE : View.VISIBLE); - tvDisconnectCategories.setVisibility(categories == null ? View.GONE : View.VISIBLE); - - return new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Uri uri = Uri.parse(etLink.getText().toString()); - Helper.view(context, uri, false); - } - }) - .setNeutralButton(R.string.title_browse, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Uri uri = Uri.parse(etLink.getText().toString()); - Helper.view(context, uri, true, true); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - - private static Uri sanitize(Uri uri) { - boolean changed = false; - - Uri url; - Uri.Builder builder; - if (uri.getHost() != null && - uri.getHost().endsWith("safelinks.protection.outlook.com") && - !TextUtils.isEmpty(uri.getQueryParameter("url"))) { - changed = true; - url = Uri.parse(uri.getQueryParameter("url")); - } else if ("https".equals(uri.getScheme()) && - "www.google.com".equals(uri.getHost()) && - uri.getPath() != null && - uri.getPath().startsWith("/amp/")) { - // https://blog.amp.dev/2017/02/06/whats-in-an-amp-url/ - Uri result = null; - - String u = uri.toString(); - u = u.replace("https://www.google.com/amp/", ""); - - int p = u.indexOf("/"); - while (p > 0) { - String segment = u.substring(0, p); - if (segment.contains(".")) { - result = Uri.parse("https://" + u); - break; - } - - u = u.substring(p + 1); - p = u.indexOf("/"); - } - - changed = (result != null); - url = (result == null ? uri : result); - } else - url = uri; - - if (url.isOpaque()) - return uri; - - builder = url.buildUpon(); - - builder.clearQuery(); - String host = uri.getHost(); - String path = uri.getPath(); - if (host != null) - host = host.toLowerCase(Locale.ROOT); - if (path != null) - path = path.toLowerCase(Locale.ROOT); - boolean first = "www.facebook.com".equals(host); - for (String key : url.getQueryParameterNames()) { - // https://en.wikipedia.org/wiki/UTM_parameters - // https://docs.oracle.com/en/cloud/saas/marketing/eloqua-user/Help/EloquaAsynchronousTrackingScripts/EloquaTrackingParameters.htm - String lkey = key.toLowerCase(Locale.ROOT); - if (PARANOID_QUERY.contains(lkey) || - lkey.startsWith("utm_") || - lkey.startsWith("elq") || - ((host != null && host.endsWith("facebook.com")) && - !first && - FACEBOOK_WHITELIST_PATH.contains(path) && - !FACEBOOK_WHITELIST_QUERY.contains(lkey)) || - ("store.steampowered.com".equals(host) && - "snr".equals(lkey))) - changed = true; - else if (!TextUtils.isEmpty(key)) - for (String value : url.getQueryParameters(key)) { - Log.i("Query " + key + "=" + value); - Uri suri = Uri.parse(value); - if ("http".equals(suri.getScheme()) || "https".equals(suri.getScheme())) { - Uri s = sanitize(suri); - if (s != null) { - changed = true; - value = s.toString(); - } - } - builder.appendQueryParameter(key, value); - } - first = false; - } - - return (changed ? builder.build() : null); - } - - private static Uri secure(Uri uri, boolean https) { - String scheme = uri.getScheme(); - if (https ? "http".equals(scheme) : "https".equals(scheme)) { - Uri.Builder builder = uri.buildUpon(); - builder.scheme(https ? "https" : "http"); - - String authority = uri.getEncodedAuthority(); - if (authority != null) { - authority = authority.replace(https ? ":80" : ":443", https ? ":443" : ":80"); - builder.encodedAuthority(authority); - } - - return builder.build(); - } else - return uri; - } - } - public static class FragmentDialogJunk extends FragmentDialogBase { @NonNull @Override diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java b/app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java new file mode 100644 index 0000000000..272c29ae0c --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogOpenLink.java @@ -0,0 +1,458 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail 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. + + FairEmail 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 FairEmail. If not, see . + + Copyright 2018-2021 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.LinkMovementMethod; +import android.util.Pair; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.constraintlayout.widget.Group; +import androidx.core.util.PatternsCompat; +import androidx.preference.PreferenceManager; + +import java.net.IDN; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public class FragmentDialogOpenLink extends FragmentDialogBase { + // https://github.com/newhouse/url-tracking-stripper + private static final List PARANOID_QUERY = Collections.unmodifiableList(Arrays.asList( + // https://en.wikipedia.org/wiki/UTM_parameters + "icid", // Adobe + "gclid", // Google + "gclsrc", // Google ads + "dclid", // DoubleClick (Google) + "fbclid", // Facebook + "igshid", // Instagram + + "mc_cid", // MailChimp + "mc_eid", // MailChimp + + "zanpid", // Zanox (Awin) + + "kclickid" // https://support.freespee.com/hc/en-us/articles/202577831-Kenshoo-integration + )); + + // https://github.com/snarfed/granary/blob/master/granary/facebook.py#L1789 + + private static final List FACEBOOK_WHITELIST_PATH = Collections.unmodifiableList(Arrays.asList( + "/nd/", "/n/", "/story.php" + )); + + private static final List FACEBOOK_WHITELIST_QUERY = Collections.unmodifiableList(Arrays.asList( + "story_fbid", "fbid", "id", "comment_id" + )); + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Uri uri = getArguments().getParcelable("uri"); + String _title = getArguments().getString("title"); + if (_title != null) + _title = _title.replace("\uFFFC", ""); // Object replacement character + if (TextUtils.isEmpty(_title)) + _title = null; + final String title = _title; + + // Preload web view + Helper.customTabsWarmup(getContext()); + + // Process link + final Uri sanitized; + if (uri.isOpaque()) + sanitized = uri; + else { + Uri s = sanitize(uri); + sanitized = (s == null ? uri : s); + } + + // Process title + final Uri uriTitle; + if (title != null && PatternsCompat.WEB_URL.matcher(title).matches()) + uriTitle = Uri.parse(title.contains("://") ? title : "http://" + title); + else + uriTitle = null; + + // Get views + final View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_open_link, null); + final TextView tvTitle = dview.findViewById(R.id.tvTitle); + final ImageButton ibDifferent = dview.findViewById(R.id.ibDifferent); + final EditText etLink = dview.findViewById(R.id.etLink); + final TextView tvDisconnect = dview.findViewById(R.id.tvDisconnect); + final TextView tvDisconnectCategories = dview.findViewById(R.id.tvDisconnectCategories); + final ImageButton ibShare = dview.findViewById(R.id.ibShare); + final ImageButton ibCopy = dview.findViewById(R.id.ibCopy); + final CheckBox cbSecure = dview.findViewById(R.id.cbSecure); + final CheckBox cbSanitize = dview.findViewById(R.id.cbSanitize); + final Button btnOwner = dview.findViewById(R.id.btnOwner); + final TextView tvOwnerRemark = dview.findViewById(R.id.tvOwnerRemark); + final ContentLoadingProgressBar pbWait = dview.findViewById(R.id.pbWait); + final TextView tvHost = dview.findViewById(R.id.tvHost); + final TextView tvOwner = dview.findViewById(R.id.tvOwner); + final CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); + final Group grpDifferent = dview.findViewById(R.id.grpDifferent); + final Group grpOwner = dview.findViewById(R.id.grpOwner); + + final Context context = getContext(); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + ibDifferent.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + etLink.setText(uriTitle.toString()); + } + }); + + etLink.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + } + + @Override + public void onTextChanged(CharSequence text, int i, int i1, int i2) { + } + + @Override + public void afterTextChanged(Editable editable) { + Uri uri = Uri.parse(editable.toString()); + + boolean secure = (!uri.isOpaque() && + "https".equals(uri.getScheme())); + boolean hyperlink = (!uri.isOpaque() && + ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme()))); + + cbSecure.setTag(secure); + cbSecure.setChecked(secure); + + cbSecure.setText( + secure ? R.string.title_link_https : R.string.title_link_http); + cbSecure.setTextColor(Helper.resolveColor(context, + secure ? android.R.attr.textColorSecondary : R.attr.colorWarning)); + cbSecure.setTypeface( + secure ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD); + + cbSecure.setVisibility(hyperlink ? View.VISIBLE : View.GONE); + } + }); + + etLink.setHorizontallyScrolling(false); + etLink.setMaxLines(10); + etLink.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + ((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_POSITIVE).performClick(); + return true; + } else + return false; + } + }); + + ibShare.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent send = new Intent(); + send.setAction(Intent.ACTION_SEND); + send.putExtra(Intent.EXTRA_TEXT, etLink.getText().toString()); + send.setType("text/plain"); + startActivity(Intent.createChooser(send, title)); + } + }); + + ibCopy.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ClipboardManager clipboard = + (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + if (clipboard != null) { + ClipData clip = ClipData.newPlainText(title, etLink.getText().toString()); + clipboard.setPrimaryClip(clip); + + ToastEx.makeText(context, R.string.title_clipboard_copied, Toast.LENGTH_LONG).show(); + } + } + }); + + cbSecure.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + boolean tag = (Boolean) compoundButton.getTag(); + if (tag == checked) + return; + + Uri uri = Uri.parse(etLink.getText().toString()); + etLink.setText(secure(uri, checked).toString()); + } + }); + + cbSanitize.setVisibility(uri.equals(sanitized) ? View.GONE : View.VISIBLE); + + cbSanitize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + etLink.setText(secure(checked ? sanitized : uri, cbSecure.isChecked()).toString()); + } + }); + + cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + prefs.edit().putBoolean(uri.getHost() + ".confirm_link", !isChecked).apply(); + } + }); + + tvOwnerRemark.setMovementMethod(LinkMovementMethod.getInstance()); + cbNotAgain.setText(context.getString(R.string.title_no_ask_for_again, uri.getHost())); + cbNotAgain.setVisibility( + "https".equals(uri.getScheme()) && !TextUtils.isEmpty(uri.getHost()) + ? View.VISIBLE : View.GONE); + pbWait.setVisibility(View.GONE); + grpOwner.setVisibility(View.GONE); + + btnOwner.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Bundle args = new Bundle(); + args.putParcelable("uri", uri); + + new SimpleTask>() { + @Override + protected void onPreExecute(Bundle args) { + btnOwner.setEnabled(false); + pbWait.setVisibility(View.VISIBLE); + grpOwner.setVisibility(View.GONE); + } + + @Override + protected void onPostExecute(Bundle args) { + btnOwner.setEnabled(true); + pbWait.setVisibility(View.GONE); + grpOwner.setVisibility(View.VISIBLE); + } + + @Override + protected Pair onExecute(Context context, Bundle args) throws Throwable { + Uri uri = args.getParcelable("uri"); + return IPInfo.getOrganization(uri, context); + } + + @Override + protected void onExecuted(Bundle args, Pair data) { + tvHost.setText(data.first); + tvOwner.setText(data.second.name == null ? "?" : data.second.name); + ApplicationEx.getMainHandler().post(new Runnable() { + @Override + public void run() { + dview.scrollTo(0, tvOwner.getBottom()); + } + }); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + tvHost.setText(ex.getClass().getName()); + tvOwner.setText(ex.getMessage()); + } + }.execute(FragmentDialogOpenLink.this, args, "link:owner"); + } + }); + + tvTitle.setText(title); + tvTitle.setVisibility(TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE); + + String host = uri.getHost(); + String thost = (uriTitle == null ? null : uriTitle.getHost()); + + String puny; + try { + puny = IDN.toASCII(host); + } catch (Throwable ex) { + Log.e(ex); + puny = host; + } + + if (host != null && !host.equals(puny)) { + etLink.setText(uri.buildUpon().encodedAuthority(puny).build().toString()); + grpDifferent.setVisibility(View.VISIBLE); + } else { + etLink.setText(uri.toString()); + + grpDifferent.setVisibility( + host == null || thost == null || host.equalsIgnoreCase(thost) + ? View.GONE : View.VISIBLE); + } + + boolean disconnect_links = prefs.getBoolean("disconnect_links", true); + List categories = null; + if (disconnect_links) + categories = DisconnectBlacklist.getCategories(uri.getHost()); + if (categories != null) + tvDisconnectCategories.setText(TextUtils.join(", ", categories)); + tvDisconnect.setVisibility(categories == null ? View.GONE : View.VISIBLE); + tvDisconnectCategories.setVisibility(categories == null ? View.GONE : View.VISIBLE); + + return new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Uri uri = Uri.parse(etLink.getText().toString()); + Helper.view(context, uri, false); + } + }) + .setNeutralButton(R.string.title_browse, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Uri uri = Uri.parse(etLink.getText().toString()); + Helper.view(context, uri, true, true); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } + + private static Uri sanitize(Uri uri) { + boolean changed = false; + + Uri url; + Uri.Builder builder; + if (uri.getHost() != null && + uri.getHost().endsWith("safelinks.protection.outlook.com") && + !TextUtils.isEmpty(uri.getQueryParameter("url"))) { + changed = true; + url = Uri.parse(uri.getQueryParameter("url")); + } else if ("https".equals(uri.getScheme()) && + "www.google.com".equals(uri.getHost()) && + uri.getPath() != null && + uri.getPath().startsWith("/amp/")) { + // https://blog.amp.dev/2017/02/06/whats-in-an-amp-url/ + Uri result = null; + + String u = uri.toString(); + u = u.replace("https://www.google.com/amp/", ""); + + int p = u.indexOf("/"); + while (p > 0) { + String segment = u.substring(0, p); + if (segment.contains(".")) { + result = Uri.parse("https://" + u); + break; + } + + u = u.substring(p + 1); + p = u.indexOf("/"); + } + + changed = (result != null); + url = (result == null ? uri : result); + } else + url = uri; + + if (url.isOpaque()) + return uri; + + builder = url.buildUpon(); + + builder.clearQuery(); + String host = uri.getHost(); + String path = uri.getPath(); + if (host != null) + host = host.toLowerCase(Locale.ROOT); + if (path != null) + path = path.toLowerCase(Locale.ROOT); + boolean first = "www.facebook.com".equals(host); + for (String key : url.getQueryParameterNames()) { + // https://en.wikipedia.org/wiki/UTM_parameters + // https://docs.oracle.com/en/cloud/saas/marketing/eloqua-user/Help/EloquaAsynchronousTrackingScripts/EloquaTrackingParameters.htm + String lkey = key.toLowerCase(Locale.ROOT); + if (PARANOID_QUERY.contains(lkey) || + lkey.startsWith("utm_") || + lkey.startsWith("elq") || + ((host != null && host.endsWith("facebook.com")) && + !first && + FACEBOOK_WHITELIST_PATH.contains(path) && + !FACEBOOK_WHITELIST_QUERY.contains(lkey)) || + ("store.steampowered.com".equals(host) && + "snr".equals(lkey))) + changed = true; + else if (!TextUtils.isEmpty(key)) + for (String value : url.getQueryParameters(key)) { + Log.i("Query " + key + "=" + value); + Uri suri = Uri.parse(value); + if ("http".equals(suri.getScheme()) || "https".equals(suri.getScheme())) { + Uri s = sanitize(suri); + if (s != null) { + changed = true; + value = s.toString(); + } + } + builder.appendQueryParameter(key, value); + } + first = false; + } + + return (changed ? builder.build() : null); + } + + private static Uri secure(Uri uri, boolean https) { + String scheme = uri.getScheme(); + if (https ? "http".equals(scheme) : "https".equals(scheme)) { + Uri.Builder builder = uri.buildUpon(); + builder.scheme(https ? "https" : "http"); + + String authority = uri.getEncodedAuthority(); + if (authority != null) { + authority = authority.replace(https ? ":80" : ":443", https ? ":443" : ":80"); + builder.encodedAuthority(authority); + } + + return builder.build(); + } else + return uri; + } +} \ No newline at end of file