Merge pull request #470 from FineFindus/feat/tracking-urls
feat: remove tracking parameter from URLs
This commit is contained in:
commit
22585a2ec5
|
@ -80,6 +80,7 @@ public class GlobalUserPreferences{
|
||||||
public static boolean mentionRebloggerAutomatically;
|
public static boolean mentionRebloggerAutomatically;
|
||||||
public static boolean showPostsWithoutAlt;
|
public static boolean showPostsWithoutAlt;
|
||||||
public static boolean showMediaPreview;
|
public static boolean showMediaPreview;
|
||||||
|
public static boolean removeTrackingParams;
|
||||||
|
|
||||||
public static SharedPreferences getPrefs(){
|
public static SharedPreferences getPrefs(){
|
||||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||||
|
@ -160,6 +161,7 @@ public class GlobalUserPreferences{
|
||||||
mentionRebloggerAutomatically=prefs.getBoolean("mentionRebloggerAutomatically", false);
|
mentionRebloggerAutomatically=prefs.getBoolean("mentionRebloggerAutomatically", false);
|
||||||
showPostsWithoutAlt=prefs.getBoolean("showPostsWithoutAlt", true);
|
showPostsWithoutAlt=prefs.getBoolean("showPostsWithoutAlt", true);
|
||||||
showMediaPreview=prefs.getBoolean("showMediaPreview", true);
|
showMediaPreview=prefs.getBoolean("showMediaPreview", true);
|
||||||
|
removeTrackingParams=prefs.getBoolean("removeTrackingParams", true);
|
||||||
|
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
|
|
||||||
|
@ -234,6 +236,7 @@ public class GlobalUserPreferences{
|
||||||
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
||||||
.putBoolean("showPostsWithoutAlt", showPostsWithoutAlt)
|
.putBoolean("showPostsWithoutAlt", showPostsWithoutAlt)
|
||||||
.putBoolean("showMediaPreview", showMediaPreview)
|
.putBoolean("showMediaPreview", showMediaPreview)
|
||||||
|
.putBoolean("removeTrackingParams", removeTrackingParams)
|
||||||
|
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ import org.joinmastodon.android.ui.text.ComposeAutocompleteSpan;
|
||||||
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
|
import org.joinmastodon.android.utils.Tracking;
|
||||||
import org.joinmastodon.android.utils.TransferSpeedTracker;
|
import org.joinmastodon.android.utils.TransferSpeedTracker;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.viewcontrollers.ComposeAutocompleteViewController;
|
import org.joinmastodon.android.ui.viewcontrollers.ComposeAutocompleteViewController;
|
||||||
|
@ -1175,6 +1176,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||||
|
|
||||||
private void actuallyPublish(boolean preview){
|
private void actuallyPublish(boolean preview){
|
||||||
String text=mainEditText.getText().toString();
|
String text=mainEditText.getText().toString();
|
||||||
|
if(GlobalUserPreferences.removeTrackingParams)
|
||||||
|
text=Tracking.cleanUrlsInText(text);
|
||||||
CreateStatus.Request req=new CreateStatus.Request();
|
CreateStatus.Request req=new CreateStatus.Request();
|
||||||
if("bottom".equals(postLang.encoding)){
|
if("bottom".equals(postLang.encoding)){
|
||||||
text=new StatusTextEncoder(Bottom::encode).encode(text);
|
text=new StatusTextEncoder(Bottom::encode).encode(text);
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
|
||||||
private Instance instance;
|
private Instance instance;
|
||||||
|
|
||||||
//MOSHIDON
|
//MOSHIDON
|
||||||
private CheckableListItem<Void> unlistedRepliesItem;
|
private CheckableListItem<Void> unlistedRepliesItem, removeTrackingParams;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,7 +38,8 @@ public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
|
||||||
privacy=self.source.privacy;
|
privacy=self.source.privacy;
|
||||||
onDataLoaded(List.of(
|
onDataLoaded(List.of(
|
||||||
privacyItem=new ListItem<>(R.string.sk_settings_default_visibility, getPrivacyString(privacy), R.drawable.ic_fluent_eye_24_regular, this::onPrivacyClick, 0, false),
|
privacyItem=new ListItem<>(R.string.sk_settings_default_visibility, getPrivacyString(privacy), R.drawable.ic_fluent_eye_24_regular, this::onPrivacyClick, 0, false),
|
||||||
unlistedRepliesItem=new CheckableListItem<>(R.string.mo_change_default_reply_visibility_to_unlisted, R.string.mo_setting_default_reply_privacy_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.defaultToUnlistedReplies, R.drawable.ic_fluent_lock_open_24_regular, i->toggleCheckableItem(unlistedRepliesItem), true),
|
unlistedRepliesItem=new CheckableListItem<>(R.string.mo_change_default_reply_visibility_to_unlisted, R.string.mo_setting_default_reply_privacy_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.defaultToUnlistedReplies, R.drawable.ic_fluent_lock_open_24_regular, i->toggleCheckableItem(unlistedRepliesItem)),
|
||||||
|
removeTrackingParams=new CheckableListItem<>(R.string.mo_settings_remove_tracking_params, R.string.mo_settings_remove_tracking_params_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.removeTrackingParams, R.drawable.ic_fluent_eye_tracking_off_24_filled, i->toggleCheckableItem(removeTrackingParams), true),
|
||||||
lockedItem=new CheckableListItem<>(R.string.sk_settings_lock_account, 0, CheckableListItem.Style.SWITCH, self.locked, R.drawable.ic_fluent_person_available_24_regular, i->toggleCheckableItem(lockedItem))
|
lockedItem=new CheckableListItem<>(R.string.sk_settings_lock_account, 0, CheckableListItem.Style.SWITCH, self.locked, R.drawable.ic_fluent_person_available_24_regular, i->toggleCheckableItem(lockedItem))
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
|
||||||
public void onPause(){
|
public void onPause(){
|
||||||
super.onPause();
|
super.onPause();
|
||||||
GlobalUserPreferences.defaultToUnlistedReplies=unlistedRepliesItem.checked;
|
GlobalUserPreferences.defaultToUnlistedReplies=unlistedRepliesItem.checked;
|
||||||
|
GlobalUserPreferences.removeTrackingParams=removeTrackingParams.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
AccountSession s=AccountSessionManager.get(accountID);
|
AccountSession s=AccountSessionManager.get(accountID);
|
||||||
Account self=s.self;
|
Account self=s.self;
|
||||||
|
|
|
@ -125,6 +125,7 @@ import org.joinmastodon.android.ui.sheets.BlockAccountConfirmationSheet;
|
||||||
import org.joinmastodon.android.ui.sheets.MuteAccountConfirmationSheet;
|
import org.joinmastodon.android.ui.sheets.MuteAccountConfirmationSheet;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
import org.joinmastodon.android.utils.Tracking;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -196,6 +197,8 @@ public class UiUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void launchWebBrowser(Context context, String url) {
|
public static void launchWebBrowser(Context context, String url) {
|
||||||
|
if(GlobalUserPreferences.removeTrackingParams)
|
||||||
|
url=Tracking.removeTrackingParameters(url);
|
||||||
try {
|
try {
|
||||||
if (GlobalUserPreferences.useCustomTabs) {
|
if (GlobalUserPreferences.useCustomTabs) {
|
||||||
new CustomTabsIntent.Builder()
|
new CustomTabsIntent.Builder()
|
||||||
|
@ -1480,6 +1483,8 @@ public class UiUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void copyText(View v, String text) {
|
public static void copyText(View v, String text) {
|
||||||
|
if(GlobalUserPreferences.removeTrackingParams)
|
||||||
|
text=Tracking.cleanUrlsInText(text);
|
||||||
Context context = v.getContext();
|
Context context = v.getContext();
|
||||||
context.getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText(null, text));
|
context.getSystemService(ClipboardManager.class).setPrimaryClip(ClipData.newPlainText(null, text));
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || UiUtils.isMIUI()) { // Android 13+ SystemUI shows its own thing when you put things into the clipboard
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || UiUtils.isMIUI()) { // Android 13+ SystemUI shows its own thing when you put things into the clipboard
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package org.joinmastodon.android.utils;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Patterns;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
// Inspired by https://github.com/GeopJr/Tuba/blob/91a036edff9ab1ffb38d5b54a33023e5db551051/src/Utils/Tracking.vala
|
||||||
|
|
||||||
|
public class Tracking{
|
||||||
|
/* https://github.com/brave/brave-core/blob/face8d58ab81422480c8c05b9ba5d518e1a2d227/components/query_filter/utils.cc#L23-L119 */
|
||||||
|
private static final String[] TRACKING_IDS={
|
||||||
|
// Strip any utm_ based ones
|
||||||
|
"utm_",
|
||||||
|
// https://github.com/brave/brave-browser/issues/4239
|
||||||
|
"fbclid", "gclid", "msclkid", "mc_eid",
|
||||||
|
// New Facebook one
|
||||||
|
"mibexid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/9879
|
||||||
|
"dclid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/13644
|
||||||
|
"oly_anon_id", "oly_enc_id",
|
||||||
|
// https://github.com/brave/brave-browser/issues/11579
|
||||||
|
"_openstat",
|
||||||
|
// https://github.com/brave/brave-browser/issues/11817
|
||||||
|
"vero_conv", "vero_id",
|
||||||
|
// https://github.com/brave/brave-browser/issues/13647
|
||||||
|
"wickedid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/11578
|
||||||
|
"yclid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/8975
|
||||||
|
"__s",
|
||||||
|
// https://github.com/brave/brave-browser/issues/17451
|
||||||
|
"rb_clickid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/17452
|
||||||
|
"s_cid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/17507
|
||||||
|
"ml_subscriber", "ml_subscriber_hash",
|
||||||
|
// https://github.com/brave/brave-browser/issues/18020
|
||||||
|
"twclid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/18758
|
||||||
|
"gbraid", "wbraid",
|
||||||
|
// https://github.com/brave/brave-browser/issues/9019
|
||||||
|
"_hsenc", "__hssc", "__hstc", "__hsfp", "hsCtaTracking",
|
||||||
|
// https://github.com/brave/brave-browser/issues/22082
|
||||||
|
"oft_id", "oft_k", "oft_lk", "oft_d", "oft_c", "oft_ck", "oft_ids", "oft_sk",
|
||||||
|
// https://github.com/brave/brave-browser/issues/11580
|
||||||
|
"igshid",
|
||||||
|
// Instagram Threads
|
||||||
|
"ad_id", "adset_id", "campaign_id", "ad_name", "adset_name", "campaign_name", "placement",
|
||||||
|
// Reddit
|
||||||
|
"share_id", "ref", "ref_share",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to remove tracking parameters from a URL.
|
||||||
|
*
|
||||||
|
* @param url The original URL with tracking parameters
|
||||||
|
* @return The URL with the tracking parameters removed.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static String removeTrackingParameters(@NonNull String url){
|
||||||
|
Uri uri=Uri.parse(url);
|
||||||
|
if(uri==null)
|
||||||
|
return url;
|
||||||
|
Uri.Builder uriBuilder=uri.buildUpon().clearQuery();
|
||||||
|
|
||||||
|
// Iterate over existing parameters and add them back if they are not tracking parameters
|
||||||
|
for(String paramName : uri.getQueryParameterNames()){
|
||||||
|
if(!isTrackingParameter(paramName)){
|
||||||
|
for(String paramValue : uri.getQueryParameters(paramName)){
|
||||||
|
uriBuilder.appendQueryParameter(paramName, paramValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uriBuilder.build().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans URLs within the provided text, removing the tracking parameters from them.
|
||||||
|
*
|
||||||
|
* @param text The text that may contain URLs.
|
||||||
|
* @return The given text with cleaned URLs.
|
||||||
|
*/
|
||||||
|
public static String cleanUrlsInText(String text){
|
||||||
|
Matcher matcher=Patterns.WEB_URL.matcher(text);
|
||||||
|
StringBuffer sb=new StringBuffer();
|
||||||
|
|
||||||
|
while(matcher.find()){
|
||||||
|
String url=matcher.group();
|
||||||
|
matcher.appendReplacement(sb, removeTrackingParameters(url));
|
||||||
|
}
|
||||||
|
matcher.appendTail(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given parameter is used for tracking.
|
||||||
|
*/
|
||||||
|
private static boolean isTrackingParameter(String parameter){
|
||||||
|
return Arrays.stream(TRACKING_IDS).anyMatch(trackingId->parameter.toLowerCase().contains(trackingId));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M3.28,2.22a0.75,0.75 0,1 0,-1.06 1.06l0.127,0.127c-0.223,0.397 -0.35,0.855 -0.35,1.343v3.502l0.007,0.102a0.75,0.75 0,0 0,1.493 -0.102V4.75l0.007,-0.128 0.006,-0.051 3.675,3.675a7.573,7.573 0,0 0,-2.02 2.251,6.262 6.262,0 0,0 -0.358,0.716l-0.006,0.015c-0.002,0.005 -0.162,0.75 0.436,0.974a0.75,0.75 0,0 0,0.964 -0.436l0.001,-0.002 0.008,-0.02 0.044,-0.1c0.043,-0.09 0.11,-0.226 0.206,-0.391a6.072,6.072 0,0 1,1.8 -1.932l1.494,1.494a3.5,3.5 0,1 0,4.93 4.93l4.744,4.744a1.26,1.26 0,0 1,-0.18 0.013h-3.5l-0.103,0.007a0.75,0.75 0,0 0,0.102 1.493h3.5l0.168,-0.005a2.732,2.732 0,0 0,1.176 -0.345l0.128,0.128a0.75,0.75 0,0 0,1.061 -1.06L3.28,2.22ZM11.45,8.268 L10.122,6.94A9.051,9.051 0,0 1,12 6.75c2.726,0 4.535,1.1 5.655,2.22a7.573,7.573 0,0 1,1.18 1.527,6.294 6.294,0 0,1 0.34,0.67l0.018,0.046 0.006,0.015 0.002,0.005v0.002l0.001,0.002a0.75,0.75 0,0 1,-0.439 0.965,0.758 0.758,0 0,1 -0.965,-0.438l-0.008,-0.02s-0.023,-0.055 -0.044,-0.1a4.776,4.776 0,0 0,-0.206 -0.391,6.073 6.073,0 0,0 -0.945,-1.223c-0.88,-0.88 -2.32,-1.78 -4.595,-1.78a8.22,8.22 0,0 0,-0.55 0.018ZM21.997,18.815l-1.5,-1.5V15.75a0.75,0.75 0,0 1,1.493 -0.102l0.007,0.102v3.065ZM6.682,3.5 L5.182,2h3.065a0.75,0.75 0,0 1,0.102 1.493l-0.102,0.007H6.682ZM2.747,15a0.75,0.75 0,0 1,0.743 0.648l0.007,0.102v3.502l0.007,0.128a1.25,1.25 0,0 0,1.115 1.116l0.128,0.006h3.5l0.102,0.007a0.75,0.75 0,0 1,0 1.486l-0.102,0.007h-3.5l-0.167,-0.005a2.75,2.75 0,0 1,-2.578 -2.57l-0.005,-0.175V15.75l0.007,-0.102A0.75,0.75 0,0 1,2.747 15ZM19.247,2l0.168,0.005a2.75,2.75 0,0 1,2.577 2.57l0.005,0.175v3.502l-0.007,0.102a0.75,0.75 0,0 1,-1.486 0l-0.007,-0.102V4.75l-0.006,-0.128a1.25,1.25 0,0 0,-1.116 -1.116l-0.128,-0.006h-3.5l-0.102,-0.007a0.75,0.75 0,0 1,0 -1.486L15.747,2h3.5Z"
|
||||||
|
android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
|
@ -97,6 +97,7 @@
|
||||||
<string name="mo_setting_haptic_feedback_summary">Vibrate when interacting with posts</string>
|
<string name="mo_setting_haptic_feedback_summary">Vibrate when interacting with posts</string>
|
||||||
<string name="mo_swap_bookmark_with_reblog_summary">Bookmark or reblog posts from the notification</string>
|
<string name="mo_swap_bookmark_with_reblog_summary">Bookmark or reblog posts from the notification</string>
|
||||||
<string name="mo_settings_show_posts_without_alt_summary">Posts will be hidden in all timelines, but can be revealed in threads and notifications</string>
|
<string name="mo_settings_show_posts_without_alt_summary">Posts will be hidden in all timelines, but can be revealed in threads and notifications</string>
|
||||||
|
<string name="mo_settings_remove_tracking_params_summary">Strip tracking information from links</string>
|
||||||
|
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<string name="mo_notification_audience_settings">Notification Audience</string>
|
<string name="mo_notification_audience_settings">Notification Audience</string>
|
||||||
|
@ -117,6 +118,7 @@
|
||||||
<string name="mo_settings_unifiedpush_warning_no_distributors">No UnifiedPush Distributors installed. You will not receive any notifications.</string>
|
<string name="mo_settings_unifiedpush_warning_no_distributors">No UnifiedPush Distributors installed. You will not receive any notifications.</string>
|
||||||
<string name="mo_settings_unifiedpush_warning_disabled">UnifiedPush is not enabled. You will not receive any notifications.</string>
|
<string name="mo_settings_unifiedpush_warning_disabled">UnifiedPush is not enabled. You will not receive any notifications.</string>
|
||||||
<string name="mo_settings_unifiedpush_enable">Enable</string>
|
<string name="mo_settings_unifiedpush_enable">Enable</string>
|
||||||
|
<string name="mo_settings_remove_tracking_params">Private Links</string>
|
||||||
|
|
||||||
<!-- Temporary Strings. They exist in strings_sk.xml, but are not available on Megalodon's weblate-->
|
<!-- Temporary Strings. They exist in strings_sk.xml, but are not available on Megalodon's weblate-->
|
||||||
<string name="mo_muted_accounts">Muted accounts</string>
|
<string name="mo_muted_accounts">Muted accounts</string>
|
||||||
|
|
Loading…
Reference in New Issue