Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove tracking parameter from URLs #470

Merged
merged 4 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public class GlobalUserPreferences{
public static boolean mentionRebloggerAutomatically;
public static boolean showPostsWithoutAlt;
public static boolean showMediaPreview;
public static boolean removeTrackingParams;

public static SharedPreferences getPrefs(){
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
Expand Down Expand Up @@ -160,6 +161,7 @@ public static void load(){
mentionRebloggerAutomatically=prefs.getBoolean("mentionRebloggerAutomatically", false);
showPostsWithoutAlt=prefs.getBoolean("showPostsWithoutAlt", true);
showMediaPreview=prefs.getBoolean("showMediaPreview", true);
removeTrackingParams=prefs.getBoolean("removeTrackingParams", true);

theme=ThemePreference.values()[prefs.getInt("theme", 0)];

Expand Down Expand Up @@ -234,6 +236,7 @@ public static void save(){
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
.putBoolean("showPostsWithoutAlt", showPostsWithoutAlt)
.putBoolean("showMediaPreview", showMediaPreview)
.putBoolean("removeTrackingParams", removeTrackingParams)

.apply();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
import org.joinmastodon.android.utils.Tracking;
import org.joinmastodon.android.utils.TransferSpeedTracker;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.viewcontrollers.ComposeAutocompleteViewController;
Expand Down Expand Up @@ -1175,6 +1176,8 @@ private void publish(boolean preview){

private void actuallyPublish(boolean preview){
String text=mainEditText.getText().toString();
if(GlobalUserPreferences.removeTrackingParams)
text=Tracking.cleanUrlsInText(text);
CreateStatus.Request req=new CreateStatus.Request();
if("bottom".equals(postLang.encoding)){
text=new StatusTextEncoder(Bottom::encode).encode(text);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
private Instance instance;

//MOSHIDON
private CheckableListItem<Void> unlistedRepliesItem;
private CheckableListItem<Void> unlistedRepliesItem, removeTrackingParams;


@Override
Expand All @@ -38,7 +38,8 @@ public void onCreate(Bundle savedInstanceState){
privacy=self.source.privacy;
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),
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))
));

Expand Down Expand Up @@ -89,6 +90,7 @@ private void onPrivacyClick(ListItem<?> item_){
public void onPause(){
super.onPause();
GlobalUserPreferences.defaultToUnlistedReplies=unlistedRepliesItem.checked;
GlobalUserPreferences.removeTrackingParams=removeTrackingParams.checked;
GlobalUserPreferences.save();
AccountSession s=AccountSessionManager.get(accountID);
Account self=s.self;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
import org.joinmastodon.android.ui.sheets.MuteAccountConfirmationSheet;
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.utils.Tracking;
import org.parceler.Parcels;

import java.io.File;
Expand Down Expand Up @@ -196,6 +197,8 @@ private UiUtils() {
}

public static void launchWebBrowser(Context context, String url) {
if(GlobalUserPreferences.removeTrackingParams)
url=Tracking.removeTrackingParameters(url);
try {
if (GlobalUserPreferences.useCustomTabs) {
new CustomTabsIntent.Builder()
Expand Down Expand Up @@ -1480,6 +1483,8 @@ public void onError(ErrorResponse error) {
}

public static void copyText(View v, String text) {
if(GlobalUserPreferences.removeTrackingParams)
text=Tracking.cleanUrlsInText(text);
Context context = v.getContext();
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
Expand Down
107 changes: 107 additions & 0 deletions mastodon/src/main/java/org/joinmastodon/android/utils/Tracking.java
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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>
2 changes: 2 additions & 0 deletions mastodon/src/main/res/values/strings_mo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
<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_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 -->
<string name="mo_notification_audience_settings">Notification Audience</string>
Expand All @@ -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_disabled">UnifiedPush is not enabled. You will not receive any notifications.</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-->
<string name="mo_muted_accounts">Muted accounts</string>
Expand Down
Loading