From 50580df4cfacbb374c9d4b048efadf4702283774 Mon Sep 17 00:00:00 2001 From: swakwork Date: Thu, 28 Aug 2025 16:58:53 +0530 Subject: [PATCH 1/7] feat(Twitter): Added 'Native reader mode' patch --- .../revanced/integrations/twitter/Pref.java | 14 + .../revanced/integrations/twitter/Utils.java | 80 +++- .../patches/loggers/ResponseLogger.java | 7 +- .../twitter/settings/ActivityHook.java | 39 +- .../twitter/settings/ScreenBuilder.java | 35 ++ .../twitter/settings/Settings.java | 5 + .../twitter/settings/SettingsStatus.java | 6 +- .../settings/fragments/PageFragment.java | 21 +- .../readerMode/ReaderModeFragment.java | 112 +++++ .../readerMode/ReaderModeTemplate.java | 420 ++++++++++++++++++ .../fragments/readerMode/ReaderModeUtils.java | 198 +++++++++ .../twitter/settings/widgets/ButtonPref.java | 2 +- 12 files changed, 898 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeFragment.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java diff --git a/app/src/main/java/app/revanced/integrations/twitter/Pref.java b/app/src/main/java/app/revanced/integrations/twitter/Pref.java index 24b25b6ac1..0d8de73eb0 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/Pref.java +++ b/app/src/main/java/app/revanced/integrations/twitter/Pref.java @@ -58,6 +58,20 @@ public static boolean enableNativeTranslator() { return Utils.getBooleanPerf(Settings.NATIVE_TRANSLATOR); } + public static boolean enableNativeReaderMode() { + return Utils.getBooleanPerf(Settings.NATIVE_READER_MODE); + } + public static boolean hideNativeReaderPostTextOnlyMode() { + return Utils.getBooleanPerf(Settings.NATIVE_READER_MODE_TEXT_ONLY_MODE); + } + public static boolean hideNativeReaderHideQuotedPosts() { + return Utils.getBooleanPerf(Settings.NATIVE_READER_MODE_HIDE_QUOTED_POST); + } + + public static boolean hideNativeReaderNoGrok() { + return Utils.getBooleanPerf(Settings.NATIVE_READER_MODE_NO_GROK); + } + public static String translatorLanguage() { return Utils.getStringPref(Settings.NATIVE_TRANSLATOR_LANG); } diff --git a/app/src/main/java/app/revanced/integrations/twitter/Utils.java b/app/src/main/java/app/revanced/integrations/twitter/Utils.java index bb72c45931..7c08b0aa02 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/Utils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/Utils.java @@ -22,28 +22,35 @@ import org.json.JSONArray; import org.json.JSONObject; +import java.io.FileOutputStream; +import java.io.BufferedReader; import java.io.File; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.io.FileReader; +import java.io.BufferedReader; +import java.io.IOException; + @SuppressWarnings("unused") public class Utils { @SuppressLint("StaticFieldLeak") private static final Context ctx = app.revanced.integrations.shared.Utils.getContext(); private static final SharedPrefCategory sp = new SharedPrefCategory(Settings.SHARED_PREF_NAME); + private static final SharedPrefCategory defsp = new SharedPrefCategory(ctx.getPackageName() + "_preferences"); public static void openUrl(String url) { - Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse(url)); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setPackage(ctx.getPackageName()); ctx.startActivity(intent); } - public static void openDefaultLinks(){ + public static void openDefaultLinks() { Intent intent = new Intent(android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS); - intent.setData(Uri.parse("package:"+ctx.getPackageName())); + intent.setData(Uri.parse("package:" + ctx.getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ctx.startActivity(intent); } @@ -73,7 +80,6 @@ public static void startAppIconNNavIconActivity() { startActivityFromClassName(className); } - private static void startBookmarkActivity() { String className = "com.twitter.app.bookmarks.legacy.BookmarkActivity"; startActivityFromClassName(className); @@ -84,7 +90,7 @@ public static void startXSettings() { startActivityFromClassName(className); } - //thanks to @Ouxyl + // thanks to @Ouxyl public static boolean redirect(TabLayout$g g) { try { String tabName = g.c.toString(); @@ -249,8 +255,8 @@ private static String getPath(String publicFolder, String subFolder, String file return publicFolder + "/" + subFolder + "/" + filename; } - - private static void postDownload(String filename, File tempFile, File file, Intent intent, long downloadId, BroadcastReceiver broadcastReceiver) { + private static void postDownload(String filename, File tempFile, File file, Intent intent, long downloadId, + BroadcastReceiver broadcastReceiver) { long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (id == downloadId) { boolean result = tempFile.renameTo(file); @@ -293,8 +299,7 @@ public static void downloadFile(String url, String mediaName, String ext) { File tempFile = new File( Environment.getExternalStorageDirectory(), - getPath(publicFolder, subFolder, "temp_" + filename) - ); + getPath(publicFolder, subFolder, "temp_" + filename)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { ctx.registerReceiver(new BroadcastReceiver() { @@ -313,6 +318,62 @@ public void onReceive(Context context, Intent intent) { } } + public static int getTheme() { + // 0 = light, 1 = dark, 2 = dim + int theme = 0; + String three_state_night_mode = defsp.getString("three_state_night_mode", String.valueOf(theme)); + if (!(three_state_night_mode.equals("0"))) { + String dark_mode_appr = defsp.getString("dark_mode_appearance", "lights_out"); + if (dark_mode_appr.equals("lights_out")) + theme = 1; + else if (dark_mode_appr.equals("dim")) + theme = 2; + } + return theme; + } + + public static boolean writeFile(File fileName, byte[] data, boolean append) { + try { + FileOutputStream outputStream = new FileOutputStream(fileName, append); + outputStream.write(data); + outputStream.close(); + return true; + } catch (Exception e) { + logger(e.toString()); + } + return false; + } + + public static String readFile(File fileName) { + try { + if (!fileName.exists()) + return null; + + StringBuilder content = new StringBuilder(); + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(fileName)); + String line; + while ((line = reader.readLine()) != null) { + content.append(line); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignored) { + } + } + } + return content.toString(); + } catch (Exception e) { + logger(e.toString()); + } + return null; + } + public static void toast(String msg) { app.revanced.integrations.shared.Utils.showToastShort(msg); } @@ -321,5 +382,4 @@ public static void logger(Object j) { Log.d("piko", j.toString()); } - } diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java b/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java index 63f3f9156e..6fe89a699b 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java @@ -6,7 +6,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; -import java.io.FileOutputStream; import android.os.Environment; import app.revanced.integrations.twitter.Pref; @@ -51,11 +50,7 @@ private static boolean writeFile(byte[] data,boolean append){ } File outputFile = new File(pikoDir, "Server-Response-Log.txt"); - - FileOutputStream outputStream = new FileOutputStream(outputFile, append); - outputStream.write(data); - outputStream.close(); - return true; + return Utils.writeFile(outputFile,data,append); }catch (Exception e){ Utils.logger(e.toString()); } diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java b/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java index 509b9e7629..5e7137a032 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java @@ -13,9 +13,9 @@ import app.revanced.integrations.shared.Utils; import app.revanced.integrations.twitter.settings.featureflags.FeatureFlagsFragment; import app.revanced.integrations.twitter.settings.fragments.*; - +import app.revanced.integrations.twitter.settings.fragments.readerMode.ReaderModeFragment; +import app.revanced.integrations.twitter.settings.fragments.readerMode.ReaderModeUtils; import static app.revanced.integrations.shared.Utils.context; - @SuppressWarnings("deprecation") public class ActivityHook { public static Toolbar toolbar; @@ -26,6 +26,7 @@ public static boolean create(Activity act) { Intent intent = act.getIntent(); if (!(intent.getBooleanExtra(EXTRA_PIKO, false))) return false; + Bundle bundle = intent.getExtras(); Window window = act.getWindow(); if (Build.VERSION.SDK_INT >= 35) { @@ -48,31 +49,37 @@ public static boolean create(Activity act) { Fragment fragment = null; boolean addToBackStack = false; + String activity_name = bundle != null ? bundle.getString(Settings.ACT_NAME) : null; - if (intent.getBooleanExtra(EXTRA_PIKO_SETTINGS, false)) { + if (activity_name.equals(EXTRA_PIKO_SETTINGS)) { fragment = new SettingsFragment(); - } else if (intent.getBooleanExtra(Settings.FEATURE_FLAGS.key, false)) { + } else if (activity_name .equals( Settings.FEATURE_FLAGS.key) ){ fragment = new FeatureFlagsFragment(); - fragment.setArguments(intent.getExtras()); - } else if (intent.getBooleanExtra(Settings.PATCH_INFO.key, false)) { + } else if (activity_name .equals( Settings.PATCH_INFO.key)) { fragment = new SettingsAboutFragment(); - } else { + }else if (activity_name .equals( ReaderModeUtils.READER_MODE_KEY) ){ + fragment = new ReaderModeFragment(); + } else { fragment = new PageFragment(); - fragment.setArguments(intent.getExtras()); } if (fragment != null) { - startFragment(act, fragment, addToBackStack); + fragment.setArguments(bundle); + startFragment(act,activity_name, fragment, addToBackStack); return true; } return false; } - public static void startFragment(Activity act, Fragment fragment, boolean addToBackStack) { + public static void startFragment(Activity act, String activity_name, Fragment fragment, boolean addToBackStack) { act.setContentView(Utils.getResourceIdentifier("preference_fragment_activity", "layout")); toolbar = act.findViewById(Utils.getResourceIdentifier("toolbar", "id")); toolbar.setNavigationIcon(Utils.getResourceIdentifier("ic_vector_arrow_left", "drawable")); - toolbar.setTitle(Utils.getResourceString("piko_title_settings")); + if( activity_name.equals( ReaderModeUtils.READER_MODE_KEY )){ + toolbar.setTitle(Utils.getResourceString("piko_title_native_reader_mode")); + }else{ + toolbar.setTitle(Utils.getResourceString("piko_title_settings")); + } toolbar.setNavigationOnClickListener(view -> act.onBackPressed()); FragmentTransaction transaction = act.getFragmentManager().beginTransaction().replace(Utils.getResourceIdentifier("fragment_container", "id"), fragment); @@ -84,7 +91,7 @@ public static void startFragment(Activity act, Fragment fragment, boolean addToB public static void startActivity(String activity_name, Bundle bundle) throws Exception { Intent intent = new Intent(context, Class.forName("com.twitter.android.AuthorizeAppActivity")); - bundle.putBoolean(activity_name, true); + bundle.putString(Settings.ACT_NAME, activity_name); bundle.putBoolean(EXTRA_PIKO, true); intent.putExtras(bundle); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -93,11 +100,17 @@ public static void startActivity(String activity_name, Bundle bundle) throws Exc public static void startActivity(String activity_name) throws Exception { Bundle bundle = new Bundle(); - bundle.putBoolean(activity_name, true); + bundle.putString(Settings.ACT_NAME, activity_name); bundle.putBoolean(EXTRA_PIKO, true); startActivity(activity_name, bundle); } + public static void startReaderMode(String tweetId) throws Exception { + Bundle bundle = new Bundle(); + bundle.putString(ReaderModeUtils.ARG_TWEET_ID, tweetId); + startActivity(ReaderModeUtils.READER_MODE_KEY, bundle); + } + public static void startSettingsActivity() throws Exception { startActivity(EXTRA_PIKO_SETTINGS); } diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java b/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java index ee8dd01603..ab76db750a 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java @@ -372,6 +372,41 @@ public void buildNativeSection(boolean buildCategory){ ); } + + category = preferenceCategory(strRes("piko_title_native_reader_mode")); + + if (SettingsStatus.nativeReaderMode) { + addPreference(category, + helper.switchPreference( + strRes("piko_title_native_reader_mode"), + "", + Settings.NATIVE_READER_MODE + ) + ); + + addPreference(category, + helper.switchPreference( + strRes("piko_native_reader_mode_pref_text_only_mode"), + "", + Settings.NATIVE_READER_MODE_TEXT_ONLY_MODE + ) + ); + addPreference(category, + helper.switchPreference( + strRes("piko_native_reader_mode_pref_hide_quoted_posts"), + "", + Settings.NATIVE_READER_MODE_HIDE_QUOTED_POST + ) + ); + addPreference(category, + helper.switchPreference ( + strRes("piko_native_reader_mode_pref_no_grok"), + "", + Settings.NATIVE_READER_MODE_NO_GROK + ) + ); + + } } public void buildMiscSection(boolean buildCategory){ diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java b/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java index 600718c084..7efb3cc876 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java @@ -7,6 +7,7 @@ public class Settings extends BaseSettings { public static final String SHARED_PREF_NAME = "piko_settings"; + public static final String ACT_NAME = "activity_name"; public static final StringSetting VID_PUBLIC_FOLDER = new StringSetting("vid_public_folder", "Movies"); public static final StringSetting VID_SUBFOLDER = new StringSetting("vid_subfolder", "Twitter"); @@ -49,6 +50,10 @@ public class Settings extends BaseSettings { public static final BooleanSetting NATIVE_TRANSLATOR = new BooleanSetting("native_translator", true); public static final StringSetting NATIVE_TRANSLATOR_PROVIDERS = new StringSetting("native_translator_providers", "0"); public static final StringSetting NATIVE_TRANSLATOR_LANG = new StringSetting("native_translator_language", "en"); + public static final BooleanSetting NATIVE_READER_MODE = new BooleanSetting("native_reader_mode", true); + public static final BooleanSetting NATIVE_READER_MODE_TEXT_ONLY_MODE = new BooleanSetting("native_reader_mode_text_only_mode", false); + public static final BooleanSetting NATIVE_READER_MODE_HIDE_QUOTED_POST = new BooleanSetting("native_reader_mode_hide_quoted_post", false); + public static final BooleanSetting NATIVE_READER_MODE_NO_GROK = new BooleanSetting("native_reader_mode_no_grok", false); public static final BooleanSetting TIMELINE_DISABLE_AUTO_SCROLL = new BooleanSetting("timeline_disable_auto_scroll", true); public static final BooleanSetting TIMELINE_SHOW_SOURCE_LABEL = new BooleanSetting("timeline_show_source_label", false); diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/SettingsStatus.java b/app/src/main/java/app/revanced/integrations/twitter/settings/SettingsStatus.java index f73fac4c81..af3b7ee865 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/SettingsStatus.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/SettingsStatus.java @@ -68,6 +68,7 @@ public class SettingsStatus { public static boolean deleteFromDb = false; public static boolean nativeDownloader = false; public static boolean nativeTranslator = false; + public static boolean nativeReaderMode = false; public static boolean hideNudgeButton = false; public static boolean hideSocialProof = false; public static boolean customPostFontSize = false; @@ -108,6 +109,9 @@ public static void nativeTranslator() { nativeTranslator = true; } + public static void nativeReaderMode() { + nativeReaderMode = true; + } public static void deleteFromDb() { deleteFromDb = true; } @@ -350,7 +354,7 @@ public static boolean enableAdsSection() { } public static boolean enableNativeSection() { - return (nativeDownloader || nativeTranslator); + return (nativeDownloader || nativeTranslator || nativeReaderMode); } public static boolean enableDownloadSection() { diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java index 962a03622f..d6e6341e75 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java @@ -37,34 +37,35 @@ public void onCreate(@Nullable Bundle savedInstanceState) { ScreenBuilder screenBuilder = new ScreenBuilder(context, screen, helper); Bundle bundle = getArguments(); - if (bundle.getBoolean(Settings.PREMIUM_SECTION.key, false)) { + String activity_name = bundle != null ? bundle.getString(Settings.ACT_NAME) : null; + if (activity_name.equals(Settings.PREMIUM_SECTION.key)) { screenBuilder.buildPremiumSection(false); toolbarText = "piko_title_premium"; - }else if (bundle.getBoolean(Settings.DOWNLOAD_SECTION.key, false)) { + }else if (activity_name.equals(Settings.DOWNLOAD_SECTION.key)) { screenBuilder.buildDownloadSection(false); toolbarText = "piko_title_download"; - }else if (bundle.getBoolean(Settings.FLAGS_SECTION.key, false)) { + }else if (activity_name.equals(Settings.FLAGS_SECTION.key)) { screenBuilder.buildFeatureFlagsSection(false); toolbarText = "piko_title_feature_flags"; - }else if (bundle.getBoolean(Settings.ADS_SECTION.key, false)) { + }else if (activity_name.equals(Settings.ADS_SECTION.key)) { screenBuilder.buildAdsSection(false); toolbarText = "piko_title_ads"; - }else if (bundle.getBoolean(Settings.MISC_SECTION.key, false)) { + }else if (activity_name.equals(Settings.MISC_SECTION.key)) { screenBuilder.buildMiscSection(false); toolbarText = "piko_title_misc"; - }else if (bundle.getBoolean(Settings.CUSTOMISE_SECTION.key, false)) { + }else if (activity_name.equals(Settings.CUSTOMISE_SECTION.key)) { screenBuilder.buildCustomiseSection(false); toolbarText = "piko_title_customisation"; - }else if (bundle.getBoolean(Settings.TIMELINE_SECTION.key, false)) { + }else if (activity_name.equals(Settings.TIMELINE_SECTION.key)) { screenBuilder.buildTimelineSection(false); toolbarText = "piko_title_timeline"; - }else if (bundle.getBoolean(Settings.BACKUP_SECTION.key, false)) { + }else if (activity_name.equals(Settings.BACKUP_SECTION.key)) { screenBuilder.buildExportSection(false); toolbarText = "piko_title_backup"; - }else if (bundle.getBoolean(Settings.NATIVE_SECTION.key, false)) { + }else if (activity_name.equals(Settings.NATIVE_SECTION.key)) { screenBuilder.buildNativeSection(false); toolbarText = "piko_title_native"; - }else if (bundle.getBoolean(Settings.LOGGING_SECTION.key, false)) { + }else if (activity_name.equals(Settings.LOGGING_SECTION.key)) { screenBuilder.buildLoggingSection(false); toolbarText = "piko_title_logging"; } diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeFragment.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeFragment.java new file mode 100644 index 0000000000..572318497a --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeFragment.java @@ -0,0 +1,112 @@ +package app.revanced.integrations.twitter.settings.fragments.readerMode; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ProgressBar; +import app.revanced.integrations.shared.Utils; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.app.Fragment; +import android.graphics.Bitmap; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +public class ReaderModeFragment extends Fragment { + + private WebView webView; + + private String tweetId; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + tweetId = getArguments().getString(ReaderModeUtils.ARG_TWEET_ID); + } + } + + @Nullable + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + + View rootView = inflater.inflate(Utils.getResourceIdentifier("webview", "layout"), container, false); + + // View progressBarView = inflater.inflate(Utils.getResourceIdentifier("progress_bar", "layout"), container, false); + // ProgressBar progressBar = progressBarView.findViewById(Utils.getResourceIdentifier("progressbar", "id")); + // progressBar.setVisibility(View.VISIBLE); + + webView = rootView.findViewById(Utils.getResourceIdentifier("webview", "id")); + webView.getSettings().setJavaScriptEnabled(true); + webView.getSettings().setLoadWithOverviewMode(true); + webView.getSettings().setUseWideViewPort(true); + + webView.setWebViewClient(new WebViewClient() { + // @Override + // public void onPageStarted(WebView view, String url, Bitmap favicon) { + // progressBar.setVisibility(View.VISIBLE); + // } + @Override + public void onPageFinished(WebView view, String url) { + // progressBar.setVisibility(View.GONE); + webView.evaluateJavascript(ReaderModeUtils.injectJS(), null); + } + }); + + if (tweetId != null && !tweetId.isEmpty()) { + loadDynamicUrl(tweetId); + } else { + webView.loadData(ReaderModeUtils.NO_CONTENT,"text/html", "UTF-8"); + } + + return rootView; + } + + private void loadDynamicUrl(String tweetId) { + + new Thread(() -> { + final String finalHtml = ReaderModeUtils.buildHtml(tweetId); + + if (isAdded() && getActivity() != null) { + getActivity().runOnUiThread(() -> webView.loadDataWithBaseURL( + null, finalHtml, "text/html", "UTF-8", null)); + } + }).start(); + } + + private String getHtmlFromUrl(String urlString) { + StringBuilder content = new StringBuilder(); + HttpURLConnection conn = null; + BufferedReader reader = null; + + try { + URL url = new URL(urlString); + conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("User-Agent", "TelegramBot"); + + reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + content.append(line); + } + reader.close(); + + } catch (Exception e) { + e.printStackTrace(); + return null; + } finally { + if (conn != null) conn.disconnect(); + try { if (reader != null) reader.close(); } catch (Exception ignore) {} + } + return content.toString(); + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java new file mode 100644 index 0000000000..5ce67cad8e --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java @@ -0,0 +1,420 @@ +package app.revanced.integrations.twitter.settings.fragments.readerMode; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +import app.revanced.integrations.twitter.Utils; +import app.revanced.integrations.shared.StringRef; + +import org.json.JSONObject; +import org.json.JSONArray; + +public class ReaderModeTemplate { + + private static final String GROK_ANALYSE_LINK = "https://x.com/i/grok?text="; + private static final String GROK_ANALYSE_THREAD = StringRef.str("piko_native_reader_mode_grok_thread"); + private static final String GROK_ANALYSE_AUTHOR = StringRef.str("piko_native_reader_mode_grok_author"); + private static final String TITLE_SOURCE = StringRef.str("piko_native_reader_mode_source"); + private static final String TITLE_PUBLISHED = StringRef.str("piko_native_reader_mode_published"); + private static final String TITLE_TOT_POSTS = StringRef.str("piko_native_reader_mode_total_post"); + + private static String getGrokIcon(String text) { + return "\n" + + "\n" + + "\n" + + + " \n" + + + " \n" + + + " \n" + + "\n" + + "" + text + "\n" + + ""; + } + + private static String getColorScheme() { + int theme = Utils.getTheme(); + switch (theme) { + case 1: { + return ":root {\n" + + " --bg-body: #ffffff;\n" + + " --bg-container: #fefefe;\n" + + " --text-primary: #262626;\n" + + " --text-secondary: #606770;\n" + + " --text-accent: #1a73e8;\n" + + " --border-color: #dcdcdc;\n" + + " --media-shadow: rgba(26, 115, 232, 0.3);\n" + + " --highlight-bg: #e1ecff;\n" + + " --highlight-color: #1a56db;\n" + + " --quoted-bg: #f5f8ff;\n" + + " --quoted-border: #a3b4d9;\n" + + " --quoted-text: #38456d;\n" + + " --footer-border: #e1e4e8;\n" + + " --footer-text: #70757a;\n" + + " --link-color: #1a73e8;\n" + + " --link-hover: underline;\n" + + " --author-border: #d9d9d9;\n" + + " --author-desc: #5e6366;\n" + + " --author-handle: #65676b;\n" + + " }"; + } + case 2: { + return ":root {\n" + + " --bg-body: #15202b;\n" + + " --bg-container: #192734;\n" + + " --text-primary: #a3b1c2;\n" + + " --text-secondary: #7c8a9e;\n" + + " --text-accent: #6ea7ff;\n" + + " --border-color: #32475b;\n" + + " --media-shadow: rgba(44, 79, 131, 0.67);\n" + + " --highlight-bg: #24436e;\n" + + " --highlight-color: #afd1ff;\n" + + " --quoted-bg: #16202A;\n" + + " --quoted-border: #2b5285ff;\n" + + " --quoted-text: #9dbadb;\n" + + " --footer-border: #32475b;\n" + + " --footer-text: #73859a;\n" + + " --link-color: #0f3c89ff;\n" + + " --author-border: #32475b;\n" + + " --author-desc: #84a2c3;\n" + + " --author-handle: #7c8a9e;\n" + + "}\n"; + } + default: { + return ":root {\n" + + "--bg-body: #ffffff;\n" + + "--bg-container: #fefefe;\n" + + "--text-primary: #262626;\n" + + "--text-secondary: #606770;\n" + + "--text-accent: #1a73e8;\n" + + "--border-color: #dcdcdc;\n" + + "--media-shadow: rgba(26, 115, 232, 0.3);\n" + + "--highlight-bg: #e1ecff;\n" + + "--highlight-color: #1a56db;\n" + + "--quoted-bg: #f5f8ff;\n" + + "--quoted-border: #a3b4d9;\n" + + "--quoted-text: #38456d;\n" + + "--footer-border: #e1e4e8;\n" + + "--footer-text: #70757a;\n" + + "--link-color: #1a73e8;\n" + + "--link-hover: underline;\n" + + "--author-border: #d9d9d9;\n" + + "--author-desc: #5e6366;\n" + + "--author-handle: #65676b;\n" + + "}\n"; + } + } + + } + + public static String getHTMLHeader() { + + String colorScheme = getColorScheme(); + + String rest = "body {\n" + + " background: var(--bg-body);\n" + + " font-family: 'Georgia', 'Times New Roman', serif;\n" + + " color: var(--text-primary);\n" + + " margin: 0;\n" + + " padding: 0 0 3rem 0;\n" + + " transition: background 0.3s ease, color 0.3s ease;\n" + + "}\n" + + + ".news-container {\n" + + " max-width: 680px;\n" + + " margin: 3rem auto;\n" + + " background: var(--bg-container);\n" + + " box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);\n" + + " border-radius: 8px;\n" + + " padding: 2.3rem 2.3rem 2rem 2.3rem;\n" + + " color: var(--text-primary);\n" + + " transition: background 0.3s ease, color 0.3s ease;\n" + + "}\n" + + + ".news-header {\n" + + " border-bottom: 3px solid var(--border-color);\n" + + " margin-bottom: 1.3rem;\n" + + " padding-bottom: 1.4rem;\n" + + " display: flex;\n" + + " flex-direction: column;\n" + + " gap: 0.4em;\n" + + "}\n" + + + ".news-meta {\n" + + " font-size: 1em;\n" + + " color: var(--text-secondary);\n" + + " transition: color 0.3s ease;\n" + + "}\n" + + + ".author-block {\n" + + "display: flex;\n" + + "align-items: center;\n" + + "gap: 1rem;\n" + + "padding-bottom: 1rem;\n" + + "max-width: 400px;\n" + + "font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n" + + "}\n" + + + ".author-block img {\n" + + "width: 56px;\n" + + "height: 56px;\n" + + "border-radius: 50%;\n" + + "border: 2px solid #0078d4;\n" + + "object-fit: cover;\n" + + "}\n" + + + ".author-details {\n" + + "display: flex;\n" + + "flex-direction: column;\n" + + "}\n" + + + ".author-details .name {\n" + + "font-weight: 700;\n" + + "font-size: 1.1rem;\n" + + "color: var(--highlight-color);\n" + + "}\n" + + + ".author-details a {\n" + + "font-size: 0.9rem;\n" + + "color: var(--text-secondary);\n" + + "text-decoration: none;\n" + + "}\n" + + + ".article-body {\n" + + " font-size: 1.14rem;\n" + + " line-height: 1.7;\n" + + " margin-bottom: 2rem;\n" + + " color: var(--text-primary);\n" + + " transition: color 0.3s ease;\n" + + "}\n" + + + ".media {\n" + + " margin: 1.1em 0;\n" + + " text-align: center;\n" + + "}\n" + + + ".media img,\n" + + ".media video {\n" + + " max-width: 100%;\n" + + " border-radius: 10px;\n" + + " box-shadow: 0 0 12px var(--media-shadow);\n" + + " transition: box-shadow 0.3s ease;\n" + + "}\n" + + + ".highlight {\n" + + " font-weight: bold;\n" + + " background: var(--highlight-bg);\n" + + " padding: 0.08em 0.25em;\n" + + " border-radius: 4px;\n" + + " color: var(--highlight-color);\n" + + " transition: background 0.3s ease, color 0.3s ease;\n" + + "}\n" + + + ".quoted-section {\n" + + " background: var(--quoted-bg);\n" + + " border-left: 4px solid var(--quoted-border);\n" + + " padding: 1em 1em 1em 1.4em;\n" + + " margin: 1.9em 0 0.9em 0;\n" + + " border-radius: 7px;\n" + + " font-size: 1.02em;\n" + + " color: var(--quoted-text);\n" + + " transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;\n" + + "}\n" + + + ".quoted-author {\n" + + " font-weight: 600;\n" + + " color: var(--link-color);\n" + + " font-size: 0.97em;\n" + + " margin-bottom: 0.14em;\n" + + " display: block;\n" + + " transition: color 0.3s ease;\n" + + "}\n" + + + ".article-footer {\n" + + " margin-top: 2.6em;\n" + + " padding-top: 1.4em;\n" + + " border-top: 1.5px solid var(--footer-border);\n" + + " font-size: 0.95em;\n" + + " color: var(--footer-text);\n" + + " transition: border-color 0.3s ease, color 0.3s ease;\n" + + "}\n" + + + "a {\n" + + " color: var(--link-color);\n" + + " text-decoration: none;\n" + + " word-break: break-all;\n" + + " transition: color 0.3s ease;\n" + + "}\n" + + + "@media (max-width: 700px) {\n" + + " .news-container {\n" + + " padding: 1.2rem 0.8rem;\n" + + " }\n" + + "}\n"; + + return ""; + } + + private static String getHTMLTitle(JSONObject thread) throws Exception { + String html = ""; + String template = "
\n" + + " \n" + + "
\n" + + " {name}\n" + + " @{username}\n" + + "\n" + + getGrokIcon(GROK_ANALYSE_AUTHOR) + " ·\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + TITLE_PUBLISHED + ": {formattedDate} · " + TITLE_SOURCE + + ": X Thread\n" + + + "
\n" + + "
\n" + + TITLE_TOT_POSTS + ": {totalTweets}\n" + + "
\n" + + "
\n"; + + String tweetId = thread.getString("id"); + JSONObject author = thread.getJSONObject("author"); + String name = author.getString("name"); + String username = author.getString("username"); + String profilepic = author.getString("profileImageUrl"); + + long createdAt = thread.getLong("createdAt"); + LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(createdAt), ZoneId.systemDefault()); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd, yyyy"); + String formattedDate = dateTime.format(formatter); + + int totalTweets = thread.getInt("totalTweets"); + html = template + .replace("{formattedDate}", formattedDate) + .replace("{username}", username) + .replace("{name}", name) + .replace("{profilepic}", profilepic) + .replace("{tweetId}", tweetId) + .replace("{totalTweets}", String.valueOf(totalTweets)); + return html; + + } + + private static String getHTMLMedia(int type, JSONObject media) throws Exception { + String html = "
{mediaTag}

"; + String mediaTag = ""; + // 0= img, 1 = vid + if (type == 0) { + var src = media.getString("src"); + mediaTag = "\"image.jpg\"
"; + } else { + var thumbnail = media.getString("posterUrl"); + var videoUrl = media.getJSONArray("sources").getJSONObject(0).getString("url"); + mediaTag = "
\n"; + mediaTag = mediaTag.replace("{thumbnail}", thumbnail) + .replace("{videoUrl}", videoUrl); + + } + return html.replace("{mediaTag}", mediaTag); + } + + private static String getHTMLTweet(JSONObject tweet, boolean quoted) + throws Exception { + String html = "{content}"; + String content = ""; + + if (quoted) { + String tweetId = tweet.getString("id"); + JSONObject author = tweet.getJSONObject("author"); + String name = author.getString("name"); + String username = author.getString("username"); + html = "
\n" + + " {name} (@{username})
\n" + + " {content}\n" + + "\n" + + + "

"; + html = html.replace("{username}", username) + .replace("{name}", name) + .replace("{tweetId}", tweetId); + } + + if (!tweet.isNull("text")) { + content = tweet.getString("text") + "
"; + } + + if (!tweet.isNull("photos")) { + JSONArray images = tweet.getJSONArray("photos"); + for (int i = 0; i < images.length(); i++) { + JSONObject item = images.getJSONObject(i); + content += getHTMLMedia(0, item); + } + } + + if (!tweet.isNull("media")) { + JSONArray videos = tweet.getJSONArray("media"); + for (int i = 0; i < videos.length(); i++) { + JSONObject item = videos.getJSONObject(i); + content += getHTMLMedia(1, item); + } + } + + if (!tweet.isNull("quoted")) { + JSONObject quotedTweet = tweet.getJSONObject("quoted"); + content += getHTMLTweet(quotedTweet, true); + } + + return html.replace("{content}", content); + } + + private static String getHTMLFooter(JSONObject thread) throws Exception { + String tweetId = String.valueOf(thread.getLong("id")); + String grokLink = GROK_ANALYSE_LINK + "https://x.com/dummy/status/" + tweetId; + + String template = "
\n" + + "" + getGrokIcon(GROK_ANALYSE_THREAD) + " ·\n" + + " twitter-thread.com\n" + + "
\n"; + + return template + .replace("{grokit}", grokLink) + .replace("{tweetId}", tweetId); + } + + public static String generateHtml(JSONObject threads) throws Exception { + + String content = ""; + JSONObject thread = threads.getJSONObject("thread"); + + String title = getHTMLTitle(thread); + JSONArray tweets = thread.getJSONArray("tweets"); + for (int i = 0; i < tweets.length(); i++) { + JSONObject tweet = tweets.getJSONObject(i); + content += getHTMLTweet(tweet, false); + } + String footer = getHTMLFooter(thread); + return generateHtml(title + "\n" + content + "\n" + footer); + } + + public static String generateHtml(String content) { + String html = " \n" + + " {head}\n" + + " \n" + + "
\n" + + "{content}\n" + + "
\n" + + " \n" + + " \n"; + String head = getHTMLHeader(); + return html + .replace("{head}", head) + .replace("{content}", content); + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java new file mode 100644 index 0000000000..8b09273845 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java @@ -0,0 +1,198 @@ +package app.revanced.integrations.twitter.settings.fragments.readerMode; + +import android.content.Context; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import app.revanced.integrations.twitter.settings.ActivityHook; +import app.revanced.integrations.twitter.Utils; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import app.revanced.integrations.twitter.Pref; + +import java.io.File; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import android.os.Environment; +import java.net.HttpURLConnection; +import java.net.URL; +import java.io.IOException; +import java.net.MalformedURLException; + +import org.json.JSONObject; +import org.json.JSONArray; +import app.revanced.integrations.twitter.settings.fragments.readerMode.ReaderModeTemplate; + +public class ReaderModeUtils { + + private static final String THREADS_KEY = "threads"; + public static final String READER_MODE_KEY = "readerMode"; + public static final String ARG_TWEET_ID = "tweet_id"; + + public static final String NO_CONTENT = "

No content specified

"; + private static final Context ctx = app.revanced.integrations.shared.Utils.getContext(); + + private static String fontSize() { + return "if(document.getElementById('fontSizeControls')) return;" + + "var container = document.createElement('div');" + + "container.id = 'fontSizeControls';" + + "container.style.position = 'fixed';" + + "container.style.bottom = '10px';" + + "container.style.left = '50%';" + + "container.style.transform = 'translateX(-50%)';" + + "container.style.background = 'rgba(255,255,255,0.8)';" + + "container.style.padding = '8px 16px';" + + "container.style.borderRadius = '8px';" + + "container.style.zIndex = '9999';" + + "container.style.display = 'flex';" + + "container.style.gap = '10px';" + + "var decBtn = document.createElement('button');" + + "decBtn.innerText = 'A-';" + + "decBtn.style.fontSize = '25px';" + + "var incBtn = document.createElement('button');" + + "incBtn.innerText = 'A+';" + + "incBtn.style.fontSize = '25px';" + + "container.appendChild(decBtn);" + + "container.appendChild(incBtn);" + + "document.body.appendChild(container);" + + "var fontSize =100;" + + "decBtn.onclick = function() {" + + " if(fontSize > 50) {" + + " fontSize -= 10;" + + " document.body.style.fontSize = fontSize + '%';" + + " }" + + "};" + + "incBtn.onclick = function() {" + + " if(fontSize < 200) {" + + " fontSize += 10;" + + " document.body.style.fontSize = fontSize + '%';" + + " }" + + "};" + + "var isScrolling;" + + "window.addEventListener('scroll', function() {" + + " container.style.display = 'none';" + + " clearTimeout(isScrolling);" + + " isScrolling = setTimeout(function() {" + + " container.style.display = 'flex';" + + " }, 1000);" + + "});"; + } + + private static String preferenceJS() { + Boolean textOnlyMode = Pref.hideNativeReaderPostTextOnlyMode(); + Boolean hideQuotedPosts = Pref.hideNativeReaderHideQuotedPosts(); + Boolean noGrok = Pref.hideNativeReaderNoGrok(); + return "if(" + hideQuotedPosts + "){\n" + + "var nodes = document.querySelectorAll(\".quoted-section\");\n" + + "nodes.forEach(qp=>qp.style.display='none');\n" + + "}\n" + + "if(" + textOnlyMode + "){\n" + + "var nodes = document.querySelectorAll(\".media\");\n" + + "nodes.forEach(qp=>qp.style.display='none');\n" + + "}\n" + + "if(" + noGrok + "){\n" + + "var nodes = document.querySelectorAll(\".grokStub\");\n" + + "nodes.forEach(qp=>qp.style.display='none');\n" + + "}"; + } + + private static String getTweetId(Object tweet) + throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Long tweetId = (Long) tweet.getClass().getDeclaredMethod("getId").invoke(tweet); + return String.valueOf(tweetId); + } + + public static void launchReaderMode(Context activity, Object tweet) throws Exception { + String tweetId = getTweetId(tweet); + ActivityHook.startReaderMode(tweetId); + return; + } + + private static JSONObject getThreadInfo(String tweetId) throws Exception { + String api = "https://twitter-thread.com/api/unroll-thread?id=" + tweetId; + + StringBuilder content = new StringBuilder(); + HttpURLConnection conn = null; + BufferedReader reader = null; + + try { + URL url = new URL(api); + conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + + reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + content.append(line); + } + String data = content.toString(); + return new JSONObject(content.toString()); + + } catch (Exception e) { + e.printStackTrace(); + return null; + } finally { + if (conn != null) + conn.disconnect(); + try { + if (reader != null) + reader.close(); + } catch (Exception ignore) { + } + } + } + + private static File cacheFileDir(String tweetId) { + File cacheDir = ctx.getCacheDir(); + return new File(cacheDir, THREADS_KEY + "_" + tweetId + ".html"); + // return new File(cacheDir, THREADS_KEY + ".json"); + } + + private static boolean writeCacheFile(String tweetId, String data) { + try { + return Utils.writeFile(cacheFileDir(tweetId), data.getBytes(), false); + } catch (Exception e) { + Utils.logger(e.toString()); + } + return false; + } + + private static String readCacheFile(String tweetId) throws Exception { + try { + return Utils.readFile(cacheFileDir(tweetId)); + } catch (Exception e) { + Utils.logger(e.toString()); + } + return null; + } + + public static String buildHtml(String tweetId) { + String html = ""; + try { + html = readCacheFile(tweetId); + if (html != null) + return html; + JSONObject threadInfo = getThreadInfo(tweetId); + if (threadInfo != null) { + html = ReaderModeTemplate.generateHtml(threadInfo); + writeCacheFile(tweetId, html); + } + } catch (Exception e) { + html = ReaderModeTemplate.generateHtml(e.getMessage()); + } + return html; + } + + public static String injectJS() { + return "(function() {" + + fontSize() + preferenceJS()+ + "})();"; + } + + // class end +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java index 7dce55dade..641c5e992c 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java @@ -107,7 +107,7 @@ public boolean onPreferenceClick(Preference preference) { if (fragment != null) { fragment.setArguments(bundle); - ActivityHook.startFragment((Activity) context, fragment, true); + ActivityHook.startFragment((Activity) context, key,fragment, true); } } catch (Exception e) { Utils.logger(e); From 5f8fc1092f361a1be89eef4c49e35bcf108d8f5c Mon Sep 17 00:00:00 2001 From: swakwork Date: Mon, 1 Sep 2025 14:03:48 +0530 Subject: [PATCH 2/7] refactor(Twitter): Moved to `models` approach --- .../revanced/integrations/twitter/Utils.java | 37 +++- .../integrations/twitter/model/Debug.java | 125 +++++++++++ .../twitter/model/ExtMediaEntities.java | 66 ++++++ .../integrations/twitter/model/Media.java | 28 +++ .../integrations/twitter/model/Tweet.java | 128 +++++++++++ .../integrations/twitter/model/TweetInfo.java | 36 +++ .../integrations/twitter/model/Video.java | 67 ++++++ .../twitter/patches/NativeDownloader.java | 207 ++++-------------- .../patches/loggers/ResponseLogger.java | 25 +-- .../twitter/patches/translator/Constants.java | 22 -- .../patches/translator/NativeTranslator.java | 57 +---- .../readerMode/ReaderModeTemplate.java | 2 +- .../fragments/readerMode/ReaderModeUtils.java | 16 +- 13 files changed, 549 insertions(+), 267 deletions(-) create mode 100644 app/src/main/java/app/revanced/integrations/twitter/model/Debug.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/model/Media.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/model/Video.java diff --git a/app/src/main/java/app/revanced/integrations/twitter/Utils.java b/app/src/main/java/app/revanced/integrations/twitter/Utils.java index 7c08b0aa02..1bddfde80f 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/Utils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/Utils.java @@ -1,5 +1,6 @@ package app.revanced.integrations.twitter; +import app.revanced.integrations.twitter.model.Debug; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.DownloadManager; @@ -33,6 +34,7 @@ import java.io.BufferedReader; import java.io.IOException; +import java.lang.StackTraceElement; @SuppressWarnings("unused") public class Utils { @@ -332,6 +334,18 @@ else if (dark_mode_appr.equals("dim")) return theme; } + public static boolean pikoWriteFile(String fileName,String data,boolean append){ + File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + File pikoDir = new File(downloadsDir, "Piko"); + + if (!pikoDir.exists()) { + pikoDir.mkdirs(); + } + + File outputFile = new File(pikoDir, fileName); + return writeFile(outputFile,data.getBytes(),append); + } + public static boolean writeFile(File fileName, byte[] data, boolean append) { try { FileOutputStream outputStream = new FileOutputStream(fileName, append); @@ -378,8 +392,27 @@ public static void toast(String msg) { app.revanced.integrations.shared.Utils.showToastShort(msg); } - public static void logger(Object j) { - Log.d("piko", j.toString()); + public static void logger(Object e) { + String logName = "piko"; + Log.d(logName, String.valueOf(e)+"\n"); + if (e instanceof Exception) { + Exception ex = (Exception) e; + StackTraceElement[] stackTraceElements = ex.getStackTrace(); + for (StackTraceElement element : stackTraceElements) { + Log.d(logName, "Exception occurred at line " + element.getLineNumber() + " in " + element.getClassName() + + "." + element.getMethodName()); + } + } + } + + /*** THIS FUNCTION SHOULD BE USED ONLY WHILE DEVELOPMENT ***/ + public static void debugClass(Object obj) { + Debug cls = new Debug(obj); + try{ + cls.describeClass(); + }catch(Exception e){ + logger(e); + } } } diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/Debug.java b/app/src/main/java/app/revanced/integrations/twitter/model/Debug.java new file mode 100644 index 0000000000..7f95f7d486 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/model/Debug.java @@ -0,0 +1,125 @@ +package app.revanced.integrations.twitter.model; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import app.revanced.integrations.twitter.Utils; + +public class Debug { + protected Object obj; + + public Debug(Object obj) { + this.obj = obj; + } + + protected Class getObjClass() throws ClassNotFoundException { + return this.obj.getClass(); + } + + protected Object getField(Class cls, Object clsObj, String fieldName) throws Exception { + Field field = cls.getDeclaredField(fieldName); + field.setAccessible(true); + return (Object) field.get(clsObj); + } + + protected Object getField(Object clsObj, String fieldName) throws Exception { + return getField(clsObj.getClass(), clsObj, fieldName); + } + + protected Object getField(String fieldName) throws Exception { + return getField(this.obj, fieldName); + } + + protected Object getMethod(Object clsObj, String methodName) throws Exception { + return clsObj.getClass().getDeclaredMethod(methodName).invoke(clsObj); + } + + protected Object getMethod(String methodName) throws Exception { + return this.getMethod(this.obj, methodName); + } + + /*** THE BELOW FUNCTIONS SHOULD BE USED ONLY WHILE DEVELOPMENT ***/ + protected String describeFields() throws Exception { + String line = "----------------------------"; + StringBuilder sb = new StringBuilder(); + Class cls = this.getObjClass(); + + Field[] fields = cls.getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + String name = field.getName(); + String tyName = field.getType().getName(); + Object value; + sb.append(name) + .append(" - ") + .append(tyName) + .append("\n "); + + try { + value = field.get(this.obj); + } catch (IllegalAccessException e) { + value = "\n"; + } + sb.append(value) + .append("\n"+line+"\n"); + } + return sb.toString(); + } + + protected String describeMethods() throws Exception { + String line = "----------------------------"; + StringBuilder sb = new StringBuilder(); + Class cls = this.getObjClass(); + Method[] methods = cls.getDeclaredMethods(); + + for (Method method : methods) { + method.setAccessible(true); + sb.append(method.getName()) + .append(" - ") + .append(method.getReturnType().getSimpleName()) + .append("\n "); + + Parameter[] params = method.getParameters(); + if (params.length == 0) { + try { + Object result = method.invoke(this.obj); + sb.append(result); + } catch (Exception e) { + sb.append(""); + } + } else { + for (Parameter param : params) { + sb.append(param.getType().getSimpleName()) + .append(" ") + .append(param.getName()) + .append(", "); + } + } + sb.append("\n"+line+"\n"); + } + return sb.toString(); + } + + public void describeClass() throws Exception { + String className = this.getObjClass().getName(); + String line = "----------------------------"; + + String fields = this.describeFields(); + String methods = this.describeMethods(); + + StringBuilder sb = new StringBuilder(); + sb.append(line) + .append("\n" + className) + .append("\n" + line + "\n" + line) + .append("\nFIELDS\n" + line +"\n"+ fields) + .append("\n" + line + "\n" + line) + .append("\nMETHODS\n" + line + "\n"+methods) + .append("\n" + line + "\n" + line); + + String fileName = className+".txt"; + Utils.pikoWriteFile(fileName,sb.toString(),false); + Utils.toast("DONE: "+className); + + } +} diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java b/app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java new file mode 100644 index 0000000000..8eaad35778 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java @@ -0,0 +1,66 @@ +package app.revanced.integrations.twitter.model; +import app.revanced.integrations.twitter.Utils; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import app.revanced.integrations.twitter.model.Video; +import app.revanced.integrations.twitter.model.Media; +import app.revanced.integrations.twitter.Utils; + +// Lcom/twitter/model/core/entity/b0; +public class ExtMediaEntities extends Debug{ + + private Object obj; + + public ExtMediaEntities(Object obj) { + super(obj); + this.obj = obj; + } + + + public String getImageUrl() + throws Exception { + // q:String + return (String) super.getField("getThumbnailField"); + } + + public String getHighResImageUrl() + throws Exception { + return this.getImageUrl() + "?name=4096x4096&format=jpg"; + } + + public Video getHighResVideo() throws Exception { + // d() Lcom/twitter/model/core/entity/b0; + Object data = super.getMethod("highResVideoMethod"); + return data!=null ?new Video(data):null; + } + + public Media getMedia() throws Exception { + int type = 0; + String url = ""; + String ext = "jpg"; + + Video video = this.getHighResVideo(); + + if(video!=null){ + type = 1; + url = video.getMediaUrl(); + ext = video.getExtension(); + }else{ + url = this.getHighResImageUrl(); + } + return new Media(type,url,ext); + } + + @Override + public String toString(){ + try{ + return "ExtMediaEntities [getImageUrl()=" + getImageUrl() + ", getHighResImageUrl()=" + + getHighResImageUrl() + ", getHighResVideo()=" + getHighResVideo() + "]"; + }catch(Exception e){ + Utils.logger(e); + return e.getMessage(); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/Media.java b/app/src/main/java/app/revanced/integrations/twitter/model/Media.java new file mode 100644 index 0000000000..425b3bb1ca --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/model/Media.java @@ -0,0 +1,28 @@ +package app.revanced.integrations.twitter.model; + +import app.revanced.integrations.twitter.Utils; + +public class Media { + // 0-img, 1-video + public int type; + public String url; + public String ext; + + public Media(int type, String url, String ext) { + this.type = type; + this.url = url; + this.ext = ext; + } + + @Override + public String toString() { + + try { + return "Media [type=" + type + ", url=" + url + ", ext=" + ext + "]"; + } catch (Exception e) { + Utils.logger(e); + return e.getMessage(); + } + } + +} diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java b/app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java new file mode 100644 index 0000000000..2a53dc244c --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java @@ -0,0 +1,128 @@ +package app.revanced.integrations.twitter.model; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import app.revanced.integrations.twitter.model.ExtMediaEntities; +import app.revanced.integrations.twitter.model.Video; +import app.revanced.integrations.twitter.model.TweetInfo; +import app.revanced.integrations.twitter.model.Debug; +import java.util.ArrayList; +import java.util.List; +import java.util.*; +import app.revanced.integrations.twitter.Utils; + +// All comments based of 11.14.beta-0 +// Lcom/twitter/model/core/entity/e; +public class Tweet extends Debug { + private Object obj; + + public Tweet(Object obj) { + super(obj); + this.obj = obj; + } + + public Long getTweetId() throws Exception { + return (Long) super.getMethod("getId"); + } + + public String getTweetUsername() throws Exception { + return (String) super.getMethod("userNameMethod"); + } + + public String getTweetProfileName() throws Exception { + return (String) super.getMethod("profileNameMethod"); + } + + public Long getTweetUserId() throws Exception { + + return (Long) super.getMethod("userIdMethod"); + } + + public ArrayList getMedias() throws Exception { + ArrayList mediaData = new ArrayList(); + + // c()Lcom/twitter/model/core/entity/c0; + Object mediaRootObject = super.getMethod("mediaMethod"); + Class mediaRootObjectClass = mediaRootObject.getClass(); + + // Lcom/twitter/model/core/entity/s; + Class superClass = mediaRootObjectClass.getSuperclass(); + Object superClassInstance = superClass.cast(mediaRootObject); + + // a:List + List list = (List) super.getField(superClass, superClassInstance, "extMediaList"); + + assert list != null; + if (list.isEmpty()) { + return mediaData; + } + + for (Object item : list) { + ExtMediaEntities mediaObj = new ExtMediaEntities(item); + Media media = mediaObj.getMedia(); + mediaData.add(media); + } + return mediaData; + } + + public TweetInfo getTweetInfo() throws Exception { + Object data = super.getField("tweetInfo"); + return new TweetInfo(data); + } + + public String getTweetLang() throws Exception { + TweetInfo tweetInfo = this.getTweetInfo(); + return tweetInfo.getLang(); + } + + public String getLongText() throws Exception { + // j()Lcom/twitter/model/notetweet/b; + Object noteTweetObj = super.getMethod("noteTweetMethod"); + String data = noteTweetObj != null ? (String) super.getField(noteTweetObj, "longTextField") : null; + return data; + } + + public String getShortText() throws Exception { + // y()Lcom/twitter/model/core/entity/c1; + Object tweetObj = super.getMethod("tweetEntityClass"); + // getText() + Object data = super.getMethod(tweetObj, "getText"); + return (String) data; + } + + public String getText() throws Exception { + String text = ""; + try { + text = this.getLongText(); + if (text == null) { + text = this.getShortText(); + } + if (text.length() > 0) { + int mediaIndex = text.indexOf("pic.x.com"); + if (mediaIndex > 0) + text = text.substring(0, mediaIndex); + } + } catch (Exception e) { + Utils.logger(e); + text = e.getMessage(); + } + return text; + + } + + @Override + public String toString() { + try { + return "Tweet [getTweetId()=" + getTweetId() + ", getTweetUsername()=" + getTweetUsername() + + ", getTweetProfileName()=" + getTweetProfileName() + ", getTweetUserId()=" + getTweetUserId() + + ", getMedias()=" + getMedias() + ", getTweetInfo()=" + getTweetInfo() + ", getTweetLang()=" + + getTweetLang() + ", getLongText()=" + getLongText() + ", getShortText()=" + getShortText() + "]"; + + } catch (Exception e) { + Utils.logger(e); + return e.getMessage(); + } + + } +} diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java b/app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java new file mode 100644 index 0000000000..6efe76a093 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java @@ -0,0 +1,36 @@ +package app.revanced.integrations.twitter.model; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import app.revanced.integrations.twitter.model.Debug; +import app.revanced.integrations.twitter.Utils; + +// Lcom/twitter/model/core/entity/d; +public class TweetInfo extends Debug { + + private Object obj; + + public TweetInfo(Object obj) { + super(obj); + this.obj = obj; + } + + public String getLang() throws Exception { + // y:String + return (String) super.getField("tweetLang"); + } + + @Override + public String toString() { + try { + return "TweetInfo [getLang()=" + getLang() + "]"; + + } catch (Exception e) { + Utils.logger(e); + return e.getMessage(); + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/Video.java b/app/src/main/java/app/revanced/integrations/twitter/model/Video.java new file mode 100644 index 0000000000..f008f87eb6 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/model/Video.java @@ -0,0 +1,67 @@ +package app.revanced.integrations.twitter.model; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import app.revanced.integrations.twitter.model.Debug; +import app.revanced.integrations.twitter.Utils; + +// Lcom/twitter/media/av/model/b0; +public class Video extends Debug { + private Object obj; + + public Video(Object obj) { + super(obj); + this.obj = obj; + } + + public Integer getBitrate() + throws Exception { + return (Integer) super.getField("a"); + } + + public String getMediaUrl() + throws Exception { + return (String) super.getField("b"); + } + + public String getCodec() + throws Exception { + return (String) super.getField("c"); + } + + public String getThumbnail() + throws Exception { + return (String) super.getField("d"); + } + + public String getExtension() + throws Exception { + String codec = this.getCodec(); + if (codec.equals("video/mp4")) { + return "mp4"; + } + if (codec.equals("video/webm")) { + return "webm"; + } + if (codec.equals("application/x-mpegURL")) { + return "m3u8"; + } + return "unknown"; + } + + @Override + public String toString() { + try { + return "Video [getBitrate()=" + getBitrate() + ", getMediaUrl()=" + getMediaUrl() + ", getCodec()=" + + getCodec() + + ", getThumbnail()=" + getThumbnail() + ", getExtension()=" + getExtension() + "]"; + + } catch (Exception e) { + Utils.logger(e); + return e.getMessage(); + } + + } + +} diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java b/app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java index 0935f6da79..74d33f5836 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java @@ -11,7 +11,10 @@ import java.util.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.lang.StackTraceElement; +import app.revanced.integrations.twitter.model.Video; +import app.revanced.integrations.twitter.model.Media; +import app.revanced.integrations.twitter.model.Tweet; +import app.revanced.integrations.twitter.model.ExtMediaEntities; public class NativeDownloader { public static String downloadString() { @@ -31,97 +34,27 @@ private static String getExtension(String typ) { return "jpg"; } - private static String generateFileName(Object tweet) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException { - String tweetId = String.valueOf(getTweetId(tweet)); + private static String generateFileName(Tweet tweet) throws Exception { + String tweetId = ""+tweet.getTweetId(); int fileNameType = Pref.nativeDownloaderFileNameType(); switch (fileNameType) { case 1: - return getTweetUsername(tweet) + "_" + tweetId; + return tweet.getTweetUsername() + "_" + tweetId; case 2: - return getTweetProfileName(tweet) + "_" + tweetId; + return tweet.getTweetProfileName() + "_" + tweetId; case 3: - return getTweetUserId(tweet) + "_" + tweetId; + return tweet.getTweetUserId() + "_" + tweetId; case 5: return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); default: return tweetId; } } - private static ArrayList> getMediaData(Object yghObj) { - ArrayList> mediaData = new ArrayList<>(); - Class yghClazz = yghObj.getClass(); - Class superClass = yghClazz.getSuperclass(); + private static void alertBox(Context ctx, String filename, ArrayList mediaData) throws NoSuchFieldException, IllegalAccessException { + String photo = Utils.strRes("drafts_empty_photo"); + String video = Utils.strRes("drafts_empty_video"); - try { - Object superClassInstance = superClass.cast(yghObj); - List list; - - Field mediaListField = null; - for (Field field : superClass.getDeclaredFields()) { - if (List.class.isAssignableFrom(field.getType())) { - mediaListField = field; - mediaListField.setAccessible(true); - break; - } - } - - assert mediaListField != null; - list = (List) mediaListField.get(superClassInstance); - - assert list != null; - if (list.isEmpty()) { - return mediaData; - } - - Class itemClass = list.get(0).getClass(); - Class videoDataClass = getVideoDataClass(); - - for (Object item : list) { - Method[] itemMethods = itemClass.getDeclaredMethods(); - - Method getVideoDataMethod = Arrays.stream(itemMethods).filter(method -> method.getReturnType() == videoDataClass).findFirst().orElse(null); - - assert getVideoDataMethod != null; - Object videoData = getVideoDataMethod.invoke(item); - - HashMap data = new HashMap<>(); - - if (videoData != null) { - Field videoField = getVideoUrlField(videoDataClass); - String mediaUrl = (String) videoField.get(videoData); - - Field videoCDCField = getVideoCodecField(videoDataClass); - String c = (String) videoCDCField.get(videoData); - - String ext = getExtension(c); - - data.put("type", Utils.strRes("drafts_empty_video")); - data.put("ext", ext); - data.put("url", mediaUrl); - mediaData.add(data); - } else { - - Field mediaUrlField = getImageUrlField(itemClass); - String mediaUrl = (String) mediaUrlField.get(item); - String ext = "jpg"; - - data.put("type", Utils.strRes("drafts_empty_photo")); - data.put("ext", ext); - data.put("url", mediaUrl + "?name=4096x4096&format=jpg"); - - mediaData.add(data); - } - } - - } catch (Exception e) { - e.printStackTrace(); - } - - return mediaData; - } - - private static void alertBox(Context ctx, String filename, ArrayList> mediaData) throws NoSuchFieldException, IllegalAccessException { LinearLayout ln = new LinearLayout(ctx); ln.setOrientation(LinearLayout.VERTICAL); AlertDialog.Builder builder = new AlertDialog.Builder(ctx); @@ -130,24 +63,24 @@ private static void alertBox(Context ctx, String filename, ArrayList hashMap = mediaData.get(i); - String typ = hashMap.get("type"); + Media media = mediaData.get(i); + String typ = media.type == 0?photo:video; choices[i] = "• " + typ + " " + (i + 1); } builder.setItems(choices, (dialogInterface, which) -> { - HashMap media = mediaData.get(which); + Media media = mediaData.get(which); Utils.toast(Utils.strRes("download_started")); - Utils.downloadFile(media.get("url"), filename + (which + 1), media.get("ext")); + Utils.downloadFile(media.url, filename + (which + 1), media.ext); }); builder.setNegativeButton(Utils.strRes("piko_pref_native_downloader_download_all"), (dialogInterface, index) -> { Utils.toast(Utils.strRes("download_started")); int i = 1; - for (HashMap media : mediaData) { - Utils.downloadFile(media.get("url"), filename + i, media.get("ext")); + for (Media media : mediaData) { + Utils.downloadFile(media.url, filename + (index + 1), media.ext); i++; } dialogInterface.dismiss(); @@ -156,89 +89,33 @@ private static void alertBox(Context ctx, String filename, ArrayList media = tweet.getMedias(); + for(Media m:media){ + Utils.logger(m); + } + assert media != null; + if (media.isEmpty()) { + Utils.toast(Utils.strRes("piko_pref_native_downloader_no_media")); + return; + } - // downloader(Landroid/content/Context;Ljava/lang/Object;)V - public static void downloader(Context activity, Object tweet) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException { -try { - Object obj = getTweetMedia(tweet); - ArrayList> media = getMediaData(obj); - - assert media != null; - if (media.isEmpty()) { - Utils.toast(Utils.strRes("piko_pref_native_downloader_no_media")); - return; - } - - String fileName = generateFileName(tweet); - - if (media.size() == 1) { - HashMap item = media.get(0); - - Utils.toast(Utils.strRes("download_started")); - Utils.downloadFile(item.get("url"), fileName, item.get("ext")); - return; - } - - alertBox(activity, fileName + "-", media); -}catch(Exception ex){ - StackTraceElement[] stackTraceElements = ex.getStackTrace(); - for (StackTraceElement element : stackTraceElements) { - Utils.logger("Exception occurred at line " + element.getLineNumber() + " in " + element.getClassName() + "." + element.getMethodName()); - } -} - } - - private static Class tweetClass; - private static Class videoDataClass; - - public static Class getTweetClass() throws ClassNotFoundException { - if (tweetClass == null) tweetClass = Class.forName("tweetObjectClass"); - - return tweetClass; - } - - public static Long getTweetId(Object tweet) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return (Long) getTweetClass().getDeclaredMethod("idMethod").invoke(tweet); - } - - public static String getTweetUsername(Object tweet) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return (String) getTweetClass().getDeclaredMethod("getUserNamemethod").invoke(tweet); - } - - public static String getTweetProfileName(Object tweet) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return (String) getTweetClass().getDeclaredMethod("getTweetProfileName").invoke(tweet); - } - - public static Long getTweetUserId(Object tweet) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return (Long) getTweetClass().getDeclaredMethod("getTweetUserIdMethod").invoke(tweet); - } - - public static Object getTweetMedia(Object tweet) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return getTweetClass().getDeclaredMethod("getTweetMediaMethod").invoke(tweet); - } - - private static Field getField(Class mediaItemClass,String fieldName) throws NoSuchFieldException,ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Field field = mediaItemClass.getDeclaredField(fieldName); - field.setAccessible(true); - return field; - } - - private static Field getImageUrlField(Class mediaItemClass) throws NoSuchFieldException,ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return getField(mediaItemClass,"imageUrlFieldName"); - } - - private static Field getVideoUrlField(Class mediaItemClass) throws NoSuchFieldException,ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return getField(mediaItemClass,"videoUrlFieldName"); - } - - private static Field getVideoCodecField(Class mediaItemClass) throws NoSuchFieldException,ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - return getField(mediaItemClass,"videoCodecField"); - } + String fileName = generateFileName(tweet); - private static Class getVideoDataClass() throws ClassNotFoundException { - if (videoDataClass == null) videoDataClass = Class.forName("videoDataClass"); + if (media.size() == 1) { + Media item = media.get(0); + Utils.toast(Utils.strRes("download_started")); + Utils.downloadFile(item.url, fileName, item.ext); + return; + } - return videoDataClass; + alertBox(activity, fileName + "-", media); + } catch (Exception ex) { + Utils.logger(ex); + } } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java b/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java index 6fe89a699b..407ca50949 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/loggers/ResponseLogger.java @@ -16,7 +16,7 @@ public class ResponseLogger { static{ LOG_RES = Pref.serverResponseLogging(); if(Pref.serverResponseLoggingOverwriteFile()){ - writeFile("".getBytes(),false); + writeFile("",false); // Utils.logger("Cleared response log file!!!"); } } @@ -32,30 +32,17 @@ public static InputStream saveInputStream(InputStream inputStream) throws Excep } sb.append("\n"); inputStream.close(); - byte[] contentBytes = sb.toString().getBytes(); + String contentBytes = sb.toString(); if(!(sb.indexOf("session_token") == 2 || sb.indexOf("guest_token") == 2)){ writeFile(contentBytes,true); } - return new ByteArrayInputStream(contentBytes); + return new ByteArrayInputStream(contentBytes.getBytes()); } - private static boolean writeFile(byte[] data,boolean append){ - try { - File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); - File pikoDir = new File(downloadsDir, "Piko"); - - if (!pikoDir.exists()) { - pikoDir.mkdirs(); - } - - File outputFile = new File(pikoDir, "Server-Response-Log.txt"); - return Utils.writeFile(outputFile,data,append); - }catch (Exception e){ - Utils.logger(e.toString()); - } - - return false; + private static boolean writeFile(String data,boolean append){ + String fileName = "Server-Response-Log.txt"; + return Utils.pikoWriteFile(fileName,data,append); } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/Constants.java b/app/src/main/java/app/revanced/integrations/twitter/patches/translator/Constants.java index 8c022af58f..e0330c8504 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/Constants.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/translator/Constants.java @@ -3,9 +3,7 @@ import android.app.AlertDialog; import android.content.Context; import android.widget.LinearLayout; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -36,24 +34,4 @@ protected static String getLanguageNameFromLocale(String languageCode) { return "xxx"; } } - - protected static String getTweetInfoField(){ - return ""; - } - - protected static String getLangField(){ - return ""; - } - - protected static String getShortTextMethodName(){ - return ""; - } - - protected static String getLongTextMethodName(){ - return ""; - } - - protected static String getLongTextFieldName(){ - return ""; - } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java b/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java index 9267c2ea19..129fd70d9e 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java @@ -17,60 +17,16 @@ import app.revanced.integrations.twitter.patches.translator.providers.GTranslateV2; import app.revanced.integrations.twitter.Pref; +import app.revanced.integrations.twitter.model.Tweet; + public class NativeTranslator { - private static String getLang(Object tweet){ + public static void translate(Context activity, Object tweetObj) throws Exception { try{ - - Field tweetInfofield = tweet.getClass().getDeclaredField(Constants.getTweetInfoField()); - tweetInfofield.setAccessible(true); - Object tweetInfo = tweetInfofield.get(tweet); - - Field langfield = tweetInfo.getClass().getDeclaredField(Constants.getLangField()); - langfield.setAccessible(true); - String langShort = (String) langfield.get(tweetInfo); - return Constants.getLanguageNameFromLocale(langShort); - }catch (Exception ex){ - Utils.toast("Language detection failed: " + ex.getMessage()); - Utils.logger(ex.getMessage()); - } - return "xxx"; - - } - - private static String getShortText(Object tweet) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Object instance = tweet.getClass().getDeclaredMethod(Constants.getShortTextMethodName()).invoke(tweet); - Object text = instance.getClass().getDeclaredMethod("getText").invoke(instance); - return text!=null?(String) text : ""; - } - - private static String getLongText(Object tweet) throws ClassNotFoundException,NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Object instance = tweet.getClass().getDeclaredMethod(Constants.getLongTextMethodName()).invoke(tweet); - if(instance==null){ - return ""; - } - Field field = instance.getClass().getDeclaredField(Constants.getLongTextFieldName()); - field.setAccessible(true); - Object text = field.get(instance); - return text!=null?(String) text : ""; - } - - private static String getText(Object tweet) throws Exception{ - String text = getLongText(tweet); - if(text.equals("")){ - text = getShortText(tweet); - } - if(text.length()>0){ - int mediaIndex = text.indexOf("pic.x.com"); - if(mediaIndex>0) text = text.substring(0,mediaIndex); - } - return text; - } - - public static void translate(Context activity, Object tweet) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException { String text = ""; + Tweet tweet = new Tweet(tweetObj); try { - text = getText(tweet); + text = tweet.getText(); }catch (Exception e){ text = e.toString(); } @@ -83,7 +39,7 @@ public static void translate(Context activity, Object tweet) throws NoSuchMethod } String toLang = Pref.translatorLanguage(); - String tweetLang = getLang(tweet); + String tweetLang = tweet.getTweetLang(); // If both the tweet language and requested language are same. if(tweetLang.toLowerCase() == toLang.toLowerCase()){ @@ -121,5 +77,6 @@ public void onError(Exception e) { Utils.toast("Translation failed: " + e.getMessage()); } }); + }catch (Exception ex){Utils.logger(ex);} } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java index 5ce67cad8e..c19f3094ec 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java @@ -77,7 +77,7 @@ private static String getColorScheme() { " --quoted-text: #9dbadb;\n" + " --footer-border: #32475b;\n" + " --footer-text: #73859a;\n" + - " --link-color: #0f3c89ff;\n" + + " --link-color: #116fff;\n" + " --author-border: #32475b;\n" + " --author-desc: #84a2c3;\n" + " --author-handle: #7c8a9e;\n" + diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java index 8b09273845..9c925480c2 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java @@ -26,6 +26,7 @@ import org.json.JSONObject; import org.json.JSONArray; import app.revanced.integrations.twitter.settings.fragments.readerMode.ReaderModeTemplate; +import app.revanced.integrations.twitter.model.Tweet; public class ReaderModeUtils { @@ -100,14 +101,9 @@ private static String preferenceJS() { "}"; } - private static String getTweetId(Object tweet) - throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Long tweetId = (Long) tweet.getClass().getDeclaredMethod("getId").invoke(tweet); - return String.valueOf(tweetId); - } public static void launchReaderMode(Context activity, Object tweet) throws Exception { - String tweetId = getTweetId(tweet); + String tweetId = ""+new Tweet(tweet).getTweetId(); ActivityHook.startReaderMode(tweetId); return; } @@ -149,8 +145,12 @@ private static JSONObject getThreadInfo(String tweetId) throws Exception { private static File cacheFileDir(String tweetId) { File cacheDir = ctx.getCacheDir(); - return new File(cacheDir, THREADS_KEY + "_" + tweetId + ".html"); - // return new File(cacheDir, THREADS_KEY + ".json"); + File threadsDir = new File(cacheDir, "Threads"); + + if (!threadsDir.exists()) { + threadsDir.mkdirs(); + } + return new File(threadsDir, THREADS_KEY + "_" + tweetId + ".html"); } private static boolean writeCacheFile(String tweetId, String data) { From 590965882ca56ca48793321dc3a120c209093e8b Mon Sep 17 00:00:00 2001 From: swakwork Date: Mon, 1 Sep 2025 21:42:30 +0530 Subject: [PATCH 3/7] refactor(Twitter): consolidate native features --- .../NativeDownloader.java | 2 +- .../readerMode/ReaderModeFragment.java | 2 +- .../readerMode/ReaderModeTemplate.java | 194 ++++++++++-------- .../readerMode/ReaderModeUtils.java | 22 +- .../translator/Constants.java | 2 +- .../translator/DialogBox.java | 2 +- .../translator/NativeTranslator.java | 8 +- .../translator/providers/GTranslate.java | 2 +- .../translator/providers/GTranslateV2.java | 2 +- .../translator/providers/Translate.java | 2 +- .../twitter/settings/ActivityHook.java | 4 +- .../twitter/settings/widgets/ListPref.java | 2 +- 12 files changed, 137 insertions(+), 107 deletions(-) rename app/src/main/java/app/revanced/integrations/twitter/patches/{ => nativeFeatures}/NativeDownloader.java (98%) rename app/src/main/java/app/revanced/integrations/twitter/{settings/fragments => patches/nativeFeatures}/readerMode/ReaderModeFragment.java (98%) rename app/src/main/java/app/revanced/integrations/twitter/{settings/fragments => patches/nativeFeatures}/readerMode/ReaderModeTemplate.java (68%) rename app/src/main/java/app/revanced/integrations/twitter/{settings/fragments => patches/nativeFeatures}/readerMode/ReaderModeUtils.java (90%) rename app/src/main/java/app/revanced/integrations/twitter/patches/{ => nativeFeatures}/translator/Constants.java (93%) rename app/src/main/java/app/revanced/integrations/twitter/patches/{ => nativeFeatures}/translator/DialogBox.java (98%) rename app/src/main/java/app/revanced/integrations/twitter/patches/{ => nativeFeatures}/translator/NativeTranslator.java (87%) rename app/src/main/java/app/revanced/integrations/twitter/patches/{ => nativeFeatures}/translator/providers/GTranslate.java (97%) rename app/src/main/java/app/revanced/integrations/twitter/patches/{ => nativeFeatures}/translator/providers/GTranslateV2.java (97%) rename app/src/main/java/app/revanced/integrations/twitter/patches/{ => nativeFeatures}/translator/providers/Translate.java (86%) diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/NativeDownloader.java similarity index 98% rename from app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/NativeDownloader.java index 74d33f5836..02e8c74bda 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/NativeDownloader.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.patches; +package app.revanced.integrations.twitter.patches.nativeFeatures; import android.app.AlertDialog; import android.content.Context; diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeFragment.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java similarity index 98% rename from app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeFragment.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java index 572318497a..fca5dfd58c 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeFragment.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.settings.fragments.readerMode; +package app.revanced.integrations.twitter.patches.nativeFeatures.readerMode; import android.os.Bundle; import android.view.LayoutInflater; diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java similarity index 68% rename from app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java index c19f3094ec..38c9dd510d 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeTemplate.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.settings.fragments.readerMode; +package app.revanced.integrations.twitter.patches.nativeFeatures.readerMode; import java.time.Instant; import java.time.LocalDateTime; @@ -13,33 +13,25 @@ public class ReaderModeTemplate { - private static final String GROK_ANALYSE_LINK = "https://x.com/i/grok?text="; private static final String GROK_ANALYSE_THREAD = StringRef.str("piko_native_reader_mode_grok_thread"); private static final String GROK_ANALYSE_AUTHOR = StringRef.str("piko_native_reader_mode_grok_author"); private static final String TITLE_SOURCE = StringRef.str("piko_native_reader_mode_source"); private static final String TITLE_PUBLISHED = StringRef.str("piko_native_reader_mode_published"); private static final String TITLE_TOT_POSTS = StringRef.str("piko_native_reader_mode_total_post"); - private static String getGrokIcon(String text) { - return "\n" + - "\n" + - "\n" - + - " \n" - + - " \n" - + - " \n" + - "\n" + - "" + text + "\n" + - ""; + private static String getGrokIcon(String slug,String text) { + return "\n"+ + "\n"+ + "\n"+ + "\n"+ + "\n"+ + "\n"+ + ""+text+"\n"+ + "\n"; } private static String getColorScheme() { - int theme = Utils.getTheme(); - switch (theme) { - case 1: { - return ":root {\n" + + return ":root{\n" + " --bg-body: #ffffff;\n" + " --bg-container: #fefefe;\n" + " --text-primary: #262626;\n" + @@ -59,56 +51,48 @@ private static String getColorScheme() { " --author-border: #d9d9d9;\n" + " --author-desc: #5e6366;\n" + " --author-handle: #65676b;\n" + - " }"; - } - case 2: { - return ":root {\n" + - " --bg-body: #15202b;\n" + - " --bg-container: #192734;\n" + - " --text-primary: #a3b1c2;\n" + - " --text-secondary: #7c8a9e;\n" + - " --text-accent: #6ea7ff;\n" + - " --border-color: #32475b;\n" + - " --media-shadow: rgba(44, 79, 131, 0.67);\n" + - " --highlight-bg: #24436e;\n" + - " --highlight-color: #afd1ff;\n" + - " --quoted-bg: #16202A;\n" + - " --quoted-border: #2b5285ff;\n" + - " --quoted-text: #9dbadb;\n" + - " --footer-border: #32475b;\n" + - " --footer-text: #73859a;\n" + - " --link-color: #116fff;\n" + - " --author-border: #32475b;\n" + - " --author-desc: #84a2c3;\n" + - " --author-handle: #7c8a9e;\n" + + " }\n"+ + ":root.dark {\n" + + " --bg-body: #121212;\n" + + " --bg-container: #1e1e1e;\n" + + " --text-primary: #e0e0e0;\n" + + " --text-secondary: #8a9ba8;\n" + + " --text-accent: #80c8ff;\n" + + " --border-color: #333;\n" + + " --media-shadow: rgba(65, 105, 225, 0.8);\n" + + " --highlight-bg: #335577;\n" + + " --highlight-color: #c1d9ff;\n" + + " --quoted-bg: #222d3b;\n" + + " --quoted-border: #5a79a0;\n" + + " --quoted-text: #aac6ff;\n" + + " --footer-border: #333;\n" + + " --footer-text: #6a7b8d;\n" + + " --link-color: #7ab8ff;\n" + + " --link-hover: underline;\n" + + " --author-border: #2f3943;\n" + + " --author-desc: #89a1b0;\n" + + "}\n"+ + ":root.dim {\n" + + "--bg-body: #16202a ;\n"+ + "--bg-container: #192734;\n"+ + "--text-primary: #a3b1c2;\n"+ + "--text-secondary: #7c8a9e;\n"+ + "--text-accent: #6ea7ff;\n"+ + "--border-color: #32475b;\n"+ + "--media-shadow: rgba(77, 117, 179, 0.67);\n"+ + "--highlight-bg: #24436e;\n"+ + "--highlight-color: #afd1ff;\n"+ + "--quoted-bg: #223544;\n"+ + "--quoted-border: #5178ab;\n"+ + "--quoted-text: #9dbadb;\n"+ + "--footer-border: #32475b;\n"+ + "--footer-text: #73859a;\n"+ + "--link-color: #0f3c89ff;\n"+ + "--author-border: #32475b;\n"+ + "--author-desc: #84a2c3;\n"+ + "--author-handle: #7c8a9e;\n"+ "}\n"; } - default: { - return ":root {\n" + - "--bg-body: #ffffff;\n" + - "--bg-container: #fefefe;\n" + - "--text-primary: #262626;\n" + - "--text-secondary: #606770;\n" + - "--text-accent: #1a73e8;\n" + - "--border-color: #dcdcdc;\n" + - "--media-shadow: rgba(26, 115, 232, 0.3);\n" + - "--highlight-bg: #e1ecff;\n" + - "--highlight-color: #1a56db;\n" + - "--quoted-bg: #f5f8ff;\n" + - "--quoted-border: #a3b4d9;\n" + - "--quoted-text: #38456d;\n" + - "--footer-border: #e1e4e8;\n" + - "--footer-text: #70757a;\n" + - "--link-color: #1a73e8;\n" + - "--link-hover: underline;\n" + - "--author-border: #d9d9d9;\n" + - "--author-desc: #5e6366;\n" + - "--author-handle: #65676b;\n" + - "}\n"; - } - } - - } public static String getHTMLHeader() { @@ -241,19 +225,38 @@ public static String getHTMLHeader() { " color: var(--footer-text);\n" + " transition: border-color 0.3s ease, color 0.3s ease;\n" + "}\n" + - "a {\n" + " color: var(--link-color);\n" + " text-decoration: none;\n" + " word-break: break-all;\n" + " transition: color 0.3s ease;\n" + "}\n" + + ".article-footer a {\n" + + " font-size: 1rem;\n" + + "}\n"+ "@media (max-width: 700px) {\n" + " .news-container {\n" + " padding: 1.2rem 0.8rem;\n" + " }\n" + - "}\n"; + "}\n"+ + ".grok-button {\n" + + " display: inline-flex;\n" + + " align-items: center;\n" + + " gap: 0.5em;\n" + + " padding: 0.3em 1.2em;\n" + + " background-color:#000;\n" + + " border-radius: 9999px; /* oval shape */\n" + + " color: white;\n" + + " font-weight: 700;\n" + + " font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n" + + " font-size: 2rem;\n" + + " text-decoration: none;\n" + + " cursor: pointer;\n" + + " }\n" + + " .grok-icon svg {\n" + + " display: block;\n" + + " }"; return ""; @@ -266,18 +269,17 @@ private static String getHTMLTitle(JSONObject thread) throws Exception { "
\n" + " {name}\n" + " @{username}\n" + - "\n" + - getGrokIcon(GROK_ANALYSE_AUTHOR) + " ·\n" + - "
\n" + - " \n" + + " {grokUser}\n"+ + " \n" + "
\n" + "
\n" + TITLE_PUBLISHED + ": {formattedDate} · " + TITLE_SOURCE - + ": X Thread\n" + + ": X Thread\n" + "
\n" + "
\n" + TITLE_TOT_POSTS + ": {totalTweets}\n" + + "{grokPost}\n"+ "
\n" + "
\n"; @@ -286,6 +288,7 @@ private static String getHTMLTitle(JSONObject thread) throws Exception { String name = author.getString("name"); String username = author.getString("username"); String profilepic = author.getString("profileImageUrl"); + String postLink = "https://x.com/"+username+"/status/"+tweetId; long createdAt = thread.getLong("createdAt"); LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(createdAt), ZoneId.systemDefault()); @@ -299,14 +302,20 @@ private static String getHTMLTitle(JSONObject thread) throws Exception { .replace("{name}", name) .replace("{profilepic}", profilepic) .replace("{tweetId}", tweetId) + .replace("{postLink}", postLink) + .replace("{grokUser}", getGrokIcon("@"+username,GROK_ANALYSE_AUTHOR)) + .replace("{grokPost}", getGrokIcon(postLink,GROK_ANALYSE_THREAD)) .replace("{totalTweets}", String.valueOf(totalTweets)); return html; } - private static String getHTMLMedia(int type, JSONObject media) throws Exception { + private static String[] getHTMLMedia(int type, JSONObject media) throws Exception { String html = "
{mediaTag}

"; String mediaTag = ""; + // required to remove pic url. + String tco = media.getString("url"); + // 0= img, 1 = vid if (type == 0) { var src = media.getString("src"); @@ -321,13 +330,29 @@ private static String getHTMLMedia(int type, JSONObject media) throws Exception .replace("{videoUrl}", videoUrl); } - return html.replace("{mediaTag}", mediaTag); + return new String[]{tco,html.replace("{mediaTag}", mediaTag)}; + } + + private static String sanitizeText(JSONObject tweet)throws Exception{ + String text = tweet.getString("text"); + if (!tweet.isNull("urls")) { + JSONArray urls = tweet.getJSONArray("urls"); + for(int i=0;i\n" + " {name} (@{username})
\n" + + "{text}\n"+ " {content}\n" + "\n" + @@ -346,14 +372,17 @@ private static String getHTMLTweet(JSONObject tweet, boolean quoted) } if (!tweet.isNull("text")) { - content = tweet.getString("text") + "
"; + + content = sanitizeText(tweet) + "
"; } if (!tweet.isNull("photos")) { JSONArray images = tweet.getJSONArray("photos"); for (int i = 0; i < images.length(); i++) { JSONObject item = images.getJSONObject(i); - content += getHTMLMedia(0, item); + String[] cnt = getHTMLMedia(0, item); + String tco = cnt[0]; + content = content.replace(tco,"")+cnt[1]; } } @@ -361,7 +390,9 @@ private static String getHTMLTweet(JSONObject tweet, boolean quoted) JSONArray videos = tweet.getJSONArray("media"); for (int i = 0; i < videos.length(); i++) { JSONObject item = videos.getJSONObject(i); - content += getHTMLMedia(1, item); + String[] cnt = getHTMLMedia(1, item); + String tco = cnt[0]; + content = text.replace(tco,"")+cnt[1]; } } @@ -375,15 +406,14 @@ private static String getHTMLTweet(JSONObject tweet, boolean quoted) private static String getHTMLFooter(JSONObject thread) throws Exception { String tweetId = String.valueOf(thread.getLong("id")); - String grokLink = GROK_ANALYSE_LINK + "https://x.com/dummy/status/" + tweetId; + String postLink = "https://x.com/dummy/status/" + tweetId; String template = "
\n" + - "" + getGrokIcon(GROK_ANALYSE_THREAD) + " ·\n" + + getGrokIcon(postLink,GROK_ANALYSE_THREAD)+"\n"+ " twitter-thread.com\n" + "
\n"; return template - .replace("{grokit}", grokLink) .replace("{tweetId}", tweetId); } diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java similarity index 90% rename from app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java index 9c925480c2..1ff3ca1fb8 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/readerMode/ReaderModeUtils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java @@ -1,13 +1,9 @@ -package app.revanced.integrations.twitter.settings.fragments.readerMode; +package app.revanced.integrations.twitter.patches.nativeFeatures.readerMode; import android.content.Context; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import app.revanced.integrations.twitter.settings.ActivityHook; import app.revanced.integrations.twitter.Utils; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import app.revanced.integrations.twitter.Pref; import java.io.File; @@ -25,8 +21,8 @@ import org.json.JSONObject; import org.json.JSONArray; -import app.revanced.integrations.twitter.settings.fragments.readerMode.ReaderModeTemplate; import app.revanced.integrations.twitter.model.Tweet; +import app.revanced.integrations.twitter.patches.nativeFeatures.readerMode.ReaderModeTemplate; public class ReaderModeUtils { @@ -87,17 +83,21 @@ private static String preferenceJS() { Boolean textOnlyMode = Pref.hideNativeReaderPostTextOnlyMode(); Boolean hideQuotedPosts = Pref.hideNativeReaderHideQuotedPosts(); Boolean noGrok = Pref.hideNativeReaderNoGrok(); - return "if(" + hideQuotedPosts + "){\n" + + int theme = Utils.getTheme(); + String themeClass = (theme == 0?"":(theme==1?"dark":"dim")); + return "document.body.className='';\n" + + "\ndocument.body.classList.add(\""+themeClass+"\");\n"+ + "if(" + hideQuotedPosts + "){\n" + "var nodes = document.querySelectorAll(\".quoted-section\");\n" + - "nodes.forEach(qp=>qp.style.display='none');\n" + + "nodes.forEach(n=>n.style.display='none');\n" + "}\n" + "if(" + textOnlyMode + "){\n" + "var nodes = document.querySelectorAll(\".media\");\n" + - "nodes.forEach(qp=>qp.style.display='none');\n" + + "nodes.forEach(n=>n.style.display='none');\n" + "}\n" + "if(" + noGrok + "){\n" + - "var nodes = document.querySelectorAll(\".grokStub\");\n" + - "nodes.forEach(qp=>qp.style.display='none');\n" + + "var nodes = document.querySelectorAll(\".grok-button\");\n" + + "nodes.forEach(n=>n.style.display='none');\n" + "}"; } diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/Constants.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/Constants.java similarity index 93% rename from app/src/main/java/app/revanced/integrations/twitter/patches/translator/Constants.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/Constants.java index e0330c8504..9794e589be 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/Constants.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/Constants.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.patches.translator; +package app.revanced.integrations.twitter.patches.nativeFeatures.translator; import android.app.AlertDialog; import android.content.Context; diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/DialogBox.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/DialogBox.java similarity index 98% rename from app/src/main/java/app/revanced/integrations/twitter/patches/translator/DialogBox.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/DialogBox.java index eb6cabe18b..8b2cb23c73 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/DialogBox.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/DialogBox.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.patches.translator; +package app.revanced.integrations.twitter.patches.nativeFeatures.translator; import android.app.Dialog; import android.content.Context; diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/NativeTranslator.java similarity index 87% rename from app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/NativeTranslator.java index 129fd70d9e..32c5cecf8e 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/NativeTranslator.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.patches.translator; +package app.revanced.integrations.twitter.patches.nativeFeatures.translator; import android.app.AlertDialog; import android.content.Context; @@ -12,9 +12,9 @@ import app.revanced.integrations.twitter.Utils; import app.revanced.integrations.shared.StringRef; -import app.revanced.integrations.twitter.patches.translator.providers.Translate; -import app.revanced.integrations.twitter.patches.translator.providers.GTranslate; -import app.revanced.integrations.twitter.patches.translator.providers.GTranslateV2; +import app.revanced.integrations.twitter.patches.nativeFeatures.translator.providers.Translate; +import app.revanced.integrations.twitter.patches.nativeFeatures.translator.providers.GTranslate; +import app.revanced.integrations.twitter.patches.nativeFeatures.translator.providers.GTranslateV2; import app.revanced.integrations.twitter.Pref; import app.revanced.integrations.twitter.model.Tweet; diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/GTranslate.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/GTranslate.java similarity index 97% rename from app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/GTranslate.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/GTranslate.java index acd8414a57..a2b6354f8c 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/GTranslate.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/GTranslate.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.patches.translator.providers; +package app.revanced.integrations.twitter.patches.nativeFeatures.translator.providers; import android.os.AsyncTask; import java.io.BufferedReader; diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/GTranslateV2.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/GTranslateV2.java similarity index 97% rename from app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/GTranslateV2.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/GTranslateV2.java index 3df6c16db9..5308fc617b 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/GTranslateV2.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/GTranslateV2.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.patches.translator.providers; +package app.revanced.integrations.twitter.patches.nativeFeatures.translator.providers; import android.os.AsyncTask; import java.io.BufferedReader; diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/Translate.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/Translate.java similarity index 86% rename from app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/Translate.java rename to app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/Translate.java index ea5d5126c9..689cfd7624 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/providers/Translate.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/providers/Translate.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.twitter.patches.translator.providers; +package app.revanced.integrations.twitter.patches.nativeFeatures.translator.providers; diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java b/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java index 5e7137a032..4861e0b385 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java @@ -13,8 +13,8 @@ import app.revanced.integrations.shared.Utils; import app.revanced.integrations.twitter.settings.featureflags.FeatureFlagsFragment; import app.revanced.integrations.twitter.settings.fragments.*; -import app.revanced.integrations.twitter.settings.fragments.readerMode.ReaderModeFragment; -import app.revanced.integrations.twitter.settings.fragments.readerMode.ReaderModeUtils; +import app.revanced.integrations.twitter.patches.nativeFeatures.readerMode.ReaderModeFragment; +import app.revanced.integrations.twitter.patches.nativeFeatures.readerMode.ReaderModeUtils; import static app.revanced.integrations.shared.Utils.context; @SuppressWarnings("deprecation") public class ActivityHook { diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ListPref.java b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ListPref.java index a2f3a86519..d526fe3ddf 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ListPref.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ListPref.java @@ -7,7 +7,7 @@ import app.revanced.integrations.shared.Utils; import android.preference.Preference; import java.util.Locale; -import app.revanced.integrations.twitter.patches.translator.Constants; +import app.revanced.integrations.twitter.patches.nativeFeatures.translator.Constants; //import java.util.*; public class ListPref extends ListPreference { From 4e75e45439696dffc9ef79c457bb315271120890 Mon Sep 17 00:00:00 2001 From: swakwork Date: Mon, 1 Sep 2025 22:27:30 +0530 Subject: [PATCH 4/7] fix(Twitter): Settings title loading issue and refactor logic --- .../readerMode/ReaderModeUtils.java | 1 - .../twitter/settings/ActivityHook.java | 42 ++++++++++++---- .../twitter/settings/DeepLink.java | 7 ++- .../twitter/settings/ScreenBuilder.java | 9 ++-- .../twitter/settings/Settings.java | 48 ++++++++++++------- .../settings/fragments/PageFragment.java | 32 ------------- .../twitter/settings/widgets/ButtonPref.java | 18 +++---- .../twitter/settings/widgets/Helper.java | 8 ++-- 8 files changed, 83 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java index 1ff3ca1fb8..431a63d5e2 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java @@ -27,7 +27,6 @@ public class ReaderModeUtils { private static final String THREADS_KEY = "threads"; - public static final String READER_MODE_KEY = "readerMode"; public static final String ARG_TWEET_ID = "tweet_id"; public static final String NO_CONTENT = "

No content specified

"; diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java b/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java index 4861e0b385..ac979b1b4c 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/ActivityHook.java @@ -53,11 +53,11 @@ public static boolean create(Activity act) { if (activity_name.equals(EXTRA_PIKO_SETTINGS)) { fragment = new SettingsFragment(); - } else if (activity_name .equals( Settings.FEATURE_FLAGS.key) ){ + } else if (activity_name .equals( Settings.FEATURE_FLAGS) ){ fragment = new FeatureFlagsFragment(); - } else if (activity_name .equals( Settings.PATCH_INFO.key)) { + } else if (activity_name .equals( Settings.PATCH_INFO)) { fragment = new SettingsAboutFragment(); - }else if (activity_name .equals( ReaderModeUtils.READER_MODE_KEY) ){ + }else if (activity_name .equals( Settings.READER_MODE_KEY) ){ fragment = new ReaderModeFragment(); } else { fragment = new PageFragment(); @@ -75,11 +75,7 @@ public static void startFragment(Activity act, String activity_name, Fragment fr act.setContentView(Utils.getResourceIdentifier("preference_fragment_activity", "layout")); toolbar = act.findViewById(Utils.getResourceIdentifier("toolbar", "id")); toolbar.setNavigationIcon(Utils.getResourceIdentifier("ic_vector_arrow_left", "drawable")); - if( activity_name.equals( ReaderModeUtils.READER_MODE_KEY )){ - toolbar.setTitle(Utils.getResourceString("piko_title_native_reader_mode")); - }else{ - toolbar.setTitle(Utils.getResourceString("piko_title_settings")); - } + toolbar.setTitle(getTitle(activity_name)); toolbar.setNavigationOnClickListener(view -> act.onBackPressed()); FragmentTransaction transaction = act.getFragmentManager().beginTransaction().replace(Utils.getResourceIdentifier("fragment_container", "id"), fragment); @@ -89,6 +85,34 @@ public static void startFragment(Activity act, String activity_name, Fragment fr transaction.commit(); } + private static String getTitle(String activity_name){ + String toolbarText = "piko_title_settings"; + if (activity_name.equals(Settings.PREMIUM_SECTION)) { + toolbarText = "piko_title_premium"; + }else if (activity_name.equals(Settings.DOWNLOAD_SECTION)) { + toolbarText = "piko_title_download"; + }else if (activity_name.equals(Settings.FLAGS_SECTION)) { + toolbarText = "piko_title_feature_flags"; + }else if (activity_name.equals(Settings.ADS_SECTION)) { + toolbarText = "piko_title_ads"; + }else if (activity_name.equals(Settings.MISC_SECTION)) { + toolbarText = "piko_title_misc"; + }else if (activity_name.equals(Settings.CUSTOMISE_SECTION)) { + toolbarText = "piko_title_customisation"; + }else if (activity_name.equals(Settings.TIMELINE_SECTION)) { + toolbarText = "piko_title_timeline"; + }else if (activity_name.equals(Settings.BACKUP_SECTION)) { + toolbarText = "piko_title_backup"; + }else if (activity_name.equals(Settings.NATIVE_SECTION)) { + toolbarText = "piko_title_native"; + }else if (activity_name.equals(Settings.LOGGING_SECTION)) { + toolbarText = "piko_title_logging"; + }else if (activity_name.equals(Settings.READER_MODE_KEY)) { + toolbarText = "piko_title_native_reader_mode"; + } + return Utils.getResourceString(toolbarText); + } + public static void startActivity(String activity_name, Bundle bundle) throws Exception { Intent intent = new Intent(context, Class.forName("com.twitter.android.AuthorizeAppActivity")); bundle.putString(Settings.ACT_NAME, activity_name); @@ -108,7 +132,7 @@ public static void startActivity(String activity_name) throws Exception { public static void startReaderMode(String tweetId) throws Exception { Bundle bundle = new Bundle(); bundle.putString(ReaderModeUtils.ARG_TWEET_ID, tweetId); - startActivity(ReaderModeUtils.READER_MODE_KEY, bundle); + startActivity(Settings.READER_MODE_KEY, bundle); } public static void startSettingsActivity() throws Exception { diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java b/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java index 110735668e..97617c0787 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java @@ -5,7 +5,6 @@ import java.util.List; import android.net.Uri; import app.revanced.integrations.twitter.Utils; -import app.revanced.integrations.shared.settings.BooleanSetting; @SuppressWarnings("deprecation") public class DeepLink { @@ -27,7 +26,7 @@ public static boolean deeplink(Activity act) { boolean isPiko = mainPath.equals("piko") || mainPath.equals("pikosettings"); - BooleanSetting key = null; + String key = null; if(segmentSize == 2 && lastSegment.equals("settings")){ Utils.startXSettings(); return true; @@ -42,7 +41,7 @@ public static boolean deeplink(Activity act) { Bundle bundle = new Bundle(); bundle.putString(bundleFlagNameKey, bundleFlagName); bundle.putBoolean(bundleFlagValueKey, bundleFlagValue); - ActivityHook.startActivity(key.key,bundle); + ActivityHook.startActivity(key,bundle); return true; } @@ -70,7 +69,7 @@ public static boolean deeplink(Activity act) { } } if(key!=null){ - ActivityHook.startActivity(key.key); + ActivityHook.startActivity(key); return true; } }catch(Exception e){ diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java b/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java index ab76db750a..1640205bcd 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java @@ -8,7 +8,6 @@ import android.preference.Preference; import androidx.annotation.Nullable; import app.revanced.integrations.shared.Utils; -import app.revanced.integrations.shared.settings.BooleanSetting; import app.revanced.integrations.shared.settings.StringSetting; import com.twitter.ui.widget.LegacyTwitterPreferenceCategory; import android.view.View; @@ -77,7 +76,7 @@ public void buildPremiumSection(boolean buildCategory){ helper.buttonPreference( strRes("piko_pref_undo_posts_btn"), "", - Settings.PREMIUM_UNDO_POSTS + Settings.PREMIUM_UNDO_POSTS.key ) ); } @@ -87,7 +86,7 @@ public void buildPremiumSection(boolean buildCategory){ helper.buttonPreference( strRes("app_icon"), "", - Settings.PREMIUM_ICONS + Settings.PREMIUM_ICONS.key ) ); } @@ -97,7 +96,7 @@ public void buildPremiumSection(boolean buildCategory){ helper.buttonPreference( strRes("tab_customization_screen_title"), "", - Settings.PREMIUM_NAVBAR + Settings.PREMIUM_NAVBAR.key ) ); } @@ -299,7 +298,7 @@ public void buildAdsSection(boolean buildCategory){ helper.buttonPreference( strRes("piko_pref_del_from_db"), "", - Settings.ADS_DEL_FROM_DB, + Settings.ADS_DEL_FROM_DB.key, null, "#DE0025" ) diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java b/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java index 7efb3cc876..d13f3876a2 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java @@ -93,24 +93,36 @@ public class Settings extends BaseSettings { public static final BooleanSetting LOG_RES = new BooleanSetting("logging_response", false); public static final BooleanSetting LOG_RES_OVRD = new BooleanSetting("logging_response_overwrite_file", false); - public static final BooleanSetting EXPORT_PREF = new BooleanSetting("export_pref", true); - public static final BooleanSetting EXPORT_FLAGS = new BooleanSetting("export_flags", true); - public static final BooleanSetting IMPORT_PREF = new BooleanSetting("import_pref", true); - public static final BooleanSetting IMPORT_FLAGS = new BooleanSetting("import_flags", true); - public static final BooleanSetting PATCH_INFO = new BooleanSetting("patch_info", true); - public static final BooleanSetting RESET_PREF = new BooleanSetting("reset_pref", true); - public static final BooleanSetting RESET_FLAGS = new BooleanSetting("reset_flags", true); - public static final BooleanSetting FEATURE_FLAGS = new BooleanSetting("feature_flags", true); + public static final String EXPORT_PREF = "export_pref"; + public static final String EXPORT_FLAGS = "export_flags"; + public static final String IMPORT_PREF = "import_pref"; + public static final String IMPORT_FLAGS = "import_flags"; + public static final String PATCH_INFO = "patch_info"; + public static final String RESET_PREF = "reset_pref"; + public static final String RESET_FLAGS = "reset_flags"; + public static final String FEATURE_FLAGS = "feature_flags"; + +// public static final BooleanSetting PREMIUM_SECTION = new BooleanSetting("premium_section", true); +// public static final BooleanSetting DOWNLOAD_SECTION = new BooleanSetting("download_section", true); +// public static final BooleanSetting FLAGS_SECTION = new BooleanSetting("flags_section", true); +// public static final BooleanSetting ADS_SECTION = new BooleanSetting("ads_section", true); +// public static final BooleanSetting MISC_SECTION = new BooleanSetting("misc_section", true); +// public static final BooleanSetting CUSTOMISE_SECTION = new BooleanSetting("custommise_section", true); +// public static final BooleanSetting TIMELINE_SECTION = new BooleanSetting("timeline_section", true); +// public static final BooleanSetting LOGGING_SECTION = new BooleanSetting("logging_section", true); +// public static final BooleanSetting BACKUP_SECTION = new BooleanSetting("backup_section", true); +// public static final BooleanSetting NATIVE_SECTION = new BooleanSetting("native_section", true); + public static final String PREMIUM_SECTION = "premium_section"; + public static final String DOWNLOAD_SECTION = "download_section"; + public static final String FLAGS_SECTION = "flags_section"; + public static final String ADS_SECTION = "ads_section"; + public static final String MISC_SECTION = "misc_section"; + public static final String CUSTOMISE_SECTION = "custommise_section"; + public static final String TIMELINE_SECTION = "timeline_section"; + public static final String LOGGING_SECTION = "logging_section"; + public static final String BACKUP_SECTION = "backup_section"; + public static final String NATIVE_SECTION = "native_section"; + public static final String READER_MODE_KEY = "readerMode"; - public static final BooleanSetting PREMIUM_SECTION = new BooleanSetting("premium_section", true); - public static final BooleanSetting DOWNLOAD_SECTION = new BooleanSetting("download_section", true); - public static final BooleanSetting FLAGS_SECTION = new BooleanSetting("flags_section", true); - public static final BooleanSetting ADS_SECTION = new BooleanSetting("ads_section", true); - public static final BooleanSetting MISC_SECTION = new BooleanSetting("misc_section", true); - public static final BooleanSetting CUSTOMISE_SECTION = new BooleanSetting("custommise_section", true); - public static final BooleanSetting TIMELINE_SECTION = new BooleanSetting("timeline_section", true); - public static final BooleanSetting LOGGING_SECTION = new BooleanSetting("logging_section", true); - public static final BooleanSetting BACKUP_SECTION = new BooleanSetting("backup_section", true); - public static final BooleanSetting NATIVE_SECTION = new BooleanSetting("native_section", true); public static final BooleanSetting SINGLE_PAGE_SETTINGS = new BooleanSetting("single_page_settings", false); } diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java index d6e6341e75..f8570b3b9d 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java @@ -38,39 +38,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { Bundle bundle = getArguments(); String activity_name = bundle != null ? bundle.getString(Settings.ACT_NAME) : null; - if (activity_name.equals(Settings.PREMIUM_SECTION.key)) { - screenBuilder.buildPremiumSection(false); - toolbarText = "piko_title_premium"; - }else if (activity_name.equals(Settings.DOWNLOAD_SECTION.key)) { - screenBuilder.buildDownloadSection(false); - toolbarText = "piko_title_download"; - }else if (activity_name.equals(Settings.FLAGS_SECTION.key)) { - screenBuilder.buildFeatureFlagsSection(false); - toolbarText = "piko_title_feature_flags"; - }else if (activity_name.equals(Settings.ADS_SECTION.key)) { - screenBuilder.buildAdsSection(false); - toolbarText = "piko_title_ads"; - }else if (activity_name.equals(Settings.MISC_SECTION.key)) { - screenBuilder.buildMiscSection(false); - toolbarText = "piko_title_misc"; - }else if (activity_name.equals(Settings.CUSTOMISE_SECTION.key)) { - screenBuilder.buildCustomiseSection(false); - toolbarText = "piko_title_customisation"; - }else if (activity_name.equals(Settings.TIMELINE_SECTION.key)) { - screenBuilder.buildTimelineSection(false); - toolbarText = "piko_title_timeline"; - }else if (activity_name.equals(Settings.BACKUP_SECTION.key)) { - screenBuilder.buildExportSection(false); - toolbarText = "piko_title_backup"; - }else if (activity_name.equals(Settings.NATIVE_SECTION.key)) { - screenBuilder.buildNativeSection(false); - toolbarText = "piko_title_native"; - }else if (activity_name.equals(Settings.LOGGING_SECTION.key)) { - screenBuilder.buildLoggingSection(false); - toolbarText = "piko_title_logging"; - } - ActivityHook.toolbar.setTitle(StringRef.str(toolbarText)); // setSupportActionBar(ActivityHook.toolbar); setPreferenceScreen(screen); diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java index 641c5e992c..e90e586823 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java @@ -77,29 +77,29 @@ public boolean onPreferenceClick(Preference preference) { String key = getKey(); Bundle bundle = new Bundle(); Fragment fragment = null; - if (key.equals(Settings.EXPORT_PREF.key)) { + if (key.equals(Settings.EXPORT_PREF)) { bundle.putBoolean("featureFlag", false); fragment = new BackupPrefFragment(); - } else if (key.equals(Settings.EXPORT_FLAGS.key)) { + } else if (key.equals(Settings.EXPORT_FLAGS)) { bundle.putBoolean("featureFlag", true); fragment = new BackupPrefFragment(); - } else if (key.equals(Settings.IMPORT_PREF.key)) { + } else if (key.equals(Settings.IMPORT_PREF)) { bundle.putBoolean("featureFlag", false); fragment = new RestorePrefFragment(); - } else if (key.equals(Settings.IMPORT_FLAGS.key)) { + } else if (key.equals(Settings.IMPORT_FLAGS)) { bundle.putBoolean("featureFlag", true); fragment = new RestorePrefFragment(); - } else if (key.equals(Settings.PREMIUM_UNDO_POSTS.key)) { + } else if (key.equals(Settings.PREMIUM_UNDO_POSTS)) { Utils.startUndoPostActivity(); - } else if (key.equals(Settings.PREMIUM_ICONS.key)) { + } else if (key.equals(Settings.PREMIUM_ICONS)) { Utils.openUrl("https://www.x.com/settings/app_icon"); } else if (key.equals(Settings.PREMIUM_NAVBAR.key)) { Utils.openUrl("https://www.x.com/settings/custom_navigation"); - } else if (key.equals(Settings.RESET_PREF.key)) { + } else if (key.equals(Settings.RESET_PREF)) { Utils.deleteSharedPrefAB(context, false); - } else if (key.equals(Settings.RESET_FLAGS.key)) { + } else if (key.equals(Settings.RESET_FLAGS)) { Utils.deleteSharedPrefAB(context, true); - } else if (key.equals(Settings.ADS_DEL_FROM_DB.key)) { + } else if (key.equals(Settings.ADS_DEL_FROM_DB)) { DatabasePatch.showDialog(context); } else { ActivityHook.startActivity(key); diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/Helper.java b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/Helper.java index 24c737262c..18f90310dc 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/Helper.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/Helper.java @@ -46,15 +46,15 @@ public Preference switchPreference(String title, String summary, BooleanSetting return preference; } - public Preference buttonPreference(String title, String summary, BooleanSetting setting) { + public Preference buttonPreference(String title, String summary, String setting) { ButtonPref preference = new ButtonPref(context); preference.setTitle(title); preference.setSummary(summary); - preference.setKey(setting.key); + preference.setKey(setting); return preference; } - public Preference buttonPreference(String title, String summary, BooleanSetting setting, @Nullable String iconName, @Nullable String color) { + public Preference buttonPreference(String title, String summary, String setting, @Nullable String iconName, @Nullable String color) { ButtonPref preference = new ButtonPref(context, iconName); if (color != null) { @@ -65,7 +65,7 @@ public Preference buttonPreference(String title, String summary, BooleanSetting preference.setTitle(title); } preference.setSummary(summary); - preference.setKey(setting.key); + preference.setKey(setting); return preference; } From fd0252a299a7e542e169a635900a19475cf75e79 Mon Sep 17 00:00:00 2001 From: swakwork Date: Mon, 1 Sep 2025 23:06:12 +0530 Subject: [PATCH 5/7] fix(Twitter): Settings title loading issue and refactor logic --- .../integrations/twitter/settings/ScreenBuilder.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java b/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java index 1640205bcd..c067fe906b 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/ScreenBuilder.java @@ -404,6 +404,13 @@ public void buildNativeSection(boolean buildCategory){ Settings.NATIVE_READER_MODE_NO_GROK ) ); + addPreference(category, + helper.buttonPreference( + strRes("piko_native_reader_mode_cache_delete"), + "", + Settings.RESET_READER_MODE_CACHE + ) + ); } } From 062dd6b87f5dd44ae0eeca744267f2817e4f8744 Mon Sep 17 00:00:00 2001 From: swakwork Date: Tue, 2 Sep 2025 22:25:50 +0530 Subject: [PATCH 6/7] upd(Twitter): Added more features to Reader mode --- .../readerMode/ReaderModeFragment.java | 20 ++ .../readerMode/ReaderModeTemplate.java | 265 +++++++++++------- .../readerMode/ReaderModeUtils.java | 70 +++-- .../twitter/settings/DeepLink.java | 7 +- .../twitter/settings/Settings.java | 11 +- .../settings/fragments/PageFragment.java | 22 +- .../twitter/settings/widgets/ButtonPref.java | 4 + 7 files changed, 261 insertions(+), 138 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java index fca5dfd58c..2a5a1514fd 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java @@ -1,5 +1,7 @@ package app.revanced.integrations.twitter.patches.nativeFeatures.readerMode; + +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -16,6 +18,8 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import app.revanced.integrations.shared.StringRef; +import android.webkit.JavascriptInterface; public class ReaderModeFragment extends Fragment { @@ -23,6 +27,21 @@ public class ReaderModeFragment extends Fragment { private String tweetId; + // JavaScript interface class + public class WebAppInterface { + Context mContext; + + WebAppInterface(Context context) { + mContext = context; + } + + @JavascriptInterface + public void copyText(String text) { + Utils.setClipboard(text); + Utils.showToastShort(StringRef.str("link_copied_to_clipboard")); + } + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -46,6 +65,7 @@ public View onCreateView( webView = rootView.findViewById(Utils.getResourceIdentifier("webview", "id")); webView.getSettings().setJavaScriptEnabled(true); + webView.addJavascriptInterface(new WebAppInterface(getContext()), "Android"); webView.getSettings().setLoadWithOverviewMode(true); webView.getSettings().setUseWideViewPort(true); diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java index 38c9dd510d..1ab0e6d5d4 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java @@ -18,81 +18,86 @@ public class ReaderModeTemplate { private static final String TITLE_SOURCE = StringRef.str("piko_native_reader_mode_source"); private static final String TITLE_PUBLISHED = StringRef.str("piko_native_reader_mode_published"); private static final String TITLE_TOT_POSTS = StringRef.str("piko_native_reader_mode_total_post"); + private static final String SHARE_POST = StringRef.str("piko_native_reader_mode_copy_post"); - private static String getGrokIcon(String slug,String text) { - return "\n"+ - "\n"+ - "\n"+ - "\n"+ - "\n"+ - "\n"+ - ""+text+"\n"+ - "\n"; + private static String getGrokIcon(String slug, String text) { + return "\n" + + "\n" + + "\n" + + + "\n" + + + "\n" + + "\n" + + "" + text + "\n" + + "\n"; } private static String getColorScheme() { - return ":root{\n" + - " --bg-body: #ffffff;\n" + - " --bg-container: #fefefe;\n" + - " --text-primary: #262626;\n" + - " --text-secondary: #606770;\n" + - " --text-accent: #1a73e8;\n" + - " --border-color: #dcdcdc;\n" + - " --media-shadow: rgba(26, 115, 232, 0.3);\n" + - " --highlight-bg: #e1ecff;\n" + - " --highlight-color: #1a56db;\n" + - " --quoted-bg: #f5f8ff;\n" + - " --quoted-border: #a3b4d9;\n" + - " --quoted-text: #38456d;\n" + - " --footer-border: #e1e4e8;\n" + - " --footer-text: #70757a;\n" + - " --link-color: #1a73e8;\n" + - " --link-hover: underline;\n" + - " --author-border: #d9d9d9;\n" + - " --author-desc: #5e6366;\n" + - " --author-handle: #65676b;\n" + - " }\n"+ - ":root.dark {\n" + - " --bg-body: #121212;\n" + - " --bg-container: #1e1e1e;\n" + - " --text-primary: #e0e0e0;\n" + - " --text-secondary: #8a9ba8;\n" + - " --text-accent: #80c8ff;\n" + - " --border-color: #333;\n" + - " --media-shadow: rgba(65, 105, 225, 0.8);\n" + - " --highlight-bg: #335577;\n" + - " --highlight-color: #c1d9ff;\n" + - " --quoted-bg: #222d3b;\n" + - " --quoted-border: #5a79a0;\n" + - " --quoted-text: #aac6ff;\n" + - " --footer-border: #333;\n" + - " --footer-text: #6a7b8d;\n" + - " --link-color: #7ab8ff;\n" + - " --link-hover: underline;\n" + - " --author-border: #2f3943;\n" + - " --author-desc: #89a1b0;\n" + - "}\n"+ - ":root.dim {\n" + - "--bg-body: #16202a ;\n"+ - "--bg-container: #192734;\n"+ - "--text-primary: #a3b1c2;\n"+ - "--text-secondary: #7c8a9e;\n"+ - "--text-accent: #6ea7ff;\n"+ - "--border-color: #32475b;\n"+ - "--media-shadow: rgba(77, 117, 179, 0.67);\n"+ - "--highlight-bg: #24436e;\n"+ - "--highlight-color: #afd1ff;\n"+ - "--quoted-bg: #223544;\n"+ - "--quoted-border: #5178ab;\n"+ - "--quoted-text: #9dbadb;\n"+ - "--footer-border: #32475b;\n"+ - "--footer-text: #73859a;\n"+ - "--link-color: #0f3c89ff;\n"+ - "--author-border: #32475b;\n"+ - "--author-desc: #84a2c3;\n"+ - "--author-handle: #7c8a9e;\n"+ - "}\n"; - } + return ":root{\n" + + " --bg-body: #ffffff;\n" + + " --bg-container: #fefefe;\n" + + " --text-primary: #262626;\n" + + " --text-secondary: #606770;\n" + + " --text-accent: #1a73e8;\n" + + " --border-color: #dcdcdc;\n" + + " --media-shadow: rgba(26, 115, 232, 0.3);\n" + + " --highlight-bg: #e1ecff;\n" + + " --highlight-color: #1a56db;\n" + + " --quoted-bg: #f5f8ff;\n" + + " --quoted-border: #a3b4d9;\n" + + " --quoted-text: #38456d;\n" + + " --footer-border: #e1e4e8;\n" + + " --footer-text: #70757a;\n" + + " --link-color: #1a73e8;\n" + + " --link-hover: underline;\n" + + " --author-border: #d9d9d9;\n" + + " --author-desc: #5e6366;\n" + + " --author-handle: #65676b;\n" + + " }\n" + + "body.dark {\n" + + " --bg-body: #121212;\n" + + " --bg-container: #1e1e1e;\n" + + " --text-primary: #e0e0e0;\n" + + " --text-secondary: #8a9ba8;\n" + + " --text-accent: #80c8ff;\n" + + " --border-color: #333;\n" + + " --media-shadow: rgba(65, 105, 225, 0.8);\n" + + " --highlight-bg: #335577;\n" + + " --highlight-color: #c1d9ff;\n" + + " --quoted-bg: #2d2e2f;\n" + + " --quoted-border: #5a79a0;\n" + + " --quoted-text: #aac6ff;\n" + + " --footer-border: #333;\n" + + " --footer-text: #6a7b8d;\n" + + " --link-color: #7ab8ff;\n" + + " --link-hover: underline;\n" + + " --author-border: #2f3943;\n" + + " --author-desc: #89a1b0;\n" + + "}\n" + + "body.dim {\n" + + "--bg-body: #16202a ;\n" + + "--bg-container: #192734;\n" + + "--text-primary: #a3b1c2;\n" + + "--text-secondary: #7c8a9e;\n" + + "--text-accent: #6ea7ff;\n" + + "--border-color: #32475b;\n" + + "--media-shadow: rgba(77, 117, 179, 0.67);\n" + + "--highlight-bg: #24436e;\n" + + "--highlight-color: #afd1ff;\n" + + "--quoted-bg: #223544;\n" + + "--quoted-border: #5178ab;\n" + + "--quoted-text: #9dbadb;\n" + + "--footer-border: #32475b;\n" + + "--footer-text: #73859a;\n" + + "--link-color: #0070ff;\n" + + "--author-border: #32475b;\n" + + "--author-desc: #84a2c3;\n" + + "--author-handle: #7c8a9e;\n" + + "}\n"; + + } public static String getHTMLHeader() { @@ -231,32 +236,55 @@ public static String getHTMLHeader() { " word-break: break-all;\n" + " transition: color 0.3s ease;\n" + "}\n" + - ".article-footer a {\n" + - " font-size: 1rem;\n" + - "}\n"+ + ".article-footer a {\n" + + " font-size: 1rem;\n" + + "}\n" + "@media (max-width: 700px) {\n" + " .news-container {\n" + " padding: 1.2rem 0.8rem;\n" + " }\n" + - "}\n"+ + "}\n" + ".grok-button {\n" + - " display: inline-flex;\n" + - " align-items: center;\n" + - " gap: 0.5em;\n" + - " padding: 0.3em 1.2em;\n" + - " background-color:#000;\n" + - " border-radius: 9999px; /* oval shape */\n" + - " color: white;\n" + - " font-weight: 700;\n" + - " font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n" + - " font-size: 2rem;\n" + - " text-decoration: none;\n" + - " cursor: pointer;\n" + - " }\n" + - " .grok-icon svg {\n" + - " display: block;\n" + - " }"; + " display: inline-flex;\n" + + " align-items: center;\n" + + " gap: 0.5em;\n" + + " padding: 0.3em 1.2em;\n" + + " background-color:#000;\n" + + " border-radius: 9999px; /* oval shape */\n" + + " color: white;\n" + + " font-weight: 700;\n" + + " font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n" + + " font-size: 0.7rem;\n" + + " text-decoration: none;\n" + + " cursor: pointer;\n" + + " }\n" + + " .grok-icon svg {\n" + + " display: block;\n" + + " }" + + ".author-details .grok-button {\n" + + "font-size: 0.7rem;\n" + + "color: #fff;\n" + + "}\n" + + ".article-footer .grok-button{\n" + + "font-size: 0.7rem;\n" + + "color: #fff;\n" + + "}"+ + " .toggle-container {\n" + + " position: absolute;\n" + + " top: 0.5rem;\n" + + " right: 1rem;\n" + + " background: var(--bg-container);\n" + + " border-radius: 20px;\n" + + " box-shadow: 0 2px 8px rgba(0,0,0,0.5);\n" + + " padding: 0.4rem 0.8rem;\n" + + " font-family: sans-serif;\n" + + " color: var(--text-primary);\n" + + " cursor: pointer;\n" + + " user-select: none;\n" + + " transition: background 0.3s ease, color 0.3s ease;\n" + + " z-index: 1000;\n" + + " }"; return ""; @@ -269,7 +297,7 @@ private static String getHTMLTitle(JSONObject thread) throws Exception { "
\n" + " {name}\n" + " @{username}\n" + - " {grokUser}\n"+ + " {grokUser}\n" + "
\n" + "
\n" + "
\n" + @@ -279,7 +307,9 @@ private static String getHTMLTitle(JSONObject thread) throws Exception { "
\n" + "
\n" + TITLE_TOT_POSTS + ": {totalTweets}\n" + - "{grokPost}\n"+ + "
\n" + + "
\n" + + "{grokPost}\n" + "
\n" + "
\n"; @@ -288,7 +318,7 @@ private static String getHTMLTitle(JSONObject thread) throws Exception { String name = author.getString("name"); String username = author.getString("username"); String profilepic = author.getString("profileImageUrl"); - String postLink = "https://x.com/"+username+"/status/"+tweetId; + String postLink = "https://x.com/" + username + "/status/" + tweetId; long createdAt = thread.getLong("createdAt"); LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(createdAt), ZoneId.systemDefault()); @@ -303,8 +333,8 @@ private static String getHTMLTitle(JSONObject thread) throws Exception { .replace("{profilepic}", profilepic) .replace("{tweetId}", tweetId) .replace("{postLink}", postLink) - .replace("{grokUser}", getGrokIcon("@"+username,GROK_ANALYSE_AUTHOR)) - .replace("{grokPost}", getGrokIcon(postLink,GROK_ANALYSE_THREAD)) + .replace("{grokUser}", getGrokIcon("@" + username, GROK_ANALYSE_AUTHOR)) + .replace("{grokPost}", getGrokIcon(postLink, GROK_ANALYSE_THREAD)) .replace("{totalTweets}", String.valueOf(totalTweets)); return html; @@ -330,18 +360,18 @@ private static String[] getHTMLMedia(int type, JSONObject media) throws Exceptio .replace("{videoUrl}", videoUrl); } - return new String[]{tco,html.replace("{mediaTag}", mediaTag)}; + return new String[] { tco, html.replace("{mediaTag}", mediaTag) }; } - private static String sanitizeText(JSONObject tweet)throws Exception{ + private static String sanitizeText(JSONObject tweet) throws Exception { String text = tweet.getString("text"); if (!tweet.isNull("urls")) { JSONArray urls = tweet.getJSONArray("urls"); - for(int i=0;i\n" + " {name} (@{username})
\n" + - "{text}\n"+ + // "{text}\n" + " {content}\n" + "\n" + @@ -382,7 +411,7 @@ private static String getHTMLTweet(JSONObject tweet, boolean quoted) JSONObject item = images.getJSONObject(i); String[] cnt = getHTMLMedia(0, item); String tco = cnt[0]; - content = content.replace(tco,"")+cnt[1]; + content = content.replace(tco, "") + cnt[1]; } } @@ -392,7 +421,7 @@ private static String getHTMLTweet(JSONObject tweet, boolean quoted) JSONObject item = videos.getJSONObject(i); String[] cnt = getHTMLMedia(1, item); String tco = cnt[0]; - content = text.replace(tco,"")+cnt[1]; + content = content.replace(tco, "") + cnt[1]; } } @@ -405,11 +434,11 @@ private static String getHTMLTweet(JSONObject tweet, boolean quoted) } private static String getHTMLFooter(JSONObject thread) throws Exception { - String tweetId = String.valueOf(thread.getLong("id")); + String tweetId = thread.getString("id"); String postLink = "https://x.com/dummy/status/" + tweetId; String template = "
\n" + - getGrokIcon(postLink,GROK_ANALYSE_THREAD)+"\n"+ + getGrokIcon(postLink, GROK_ANALYSE_THREAD) + "\n" + " twitter-thread.com\n" + "
\n"; @@ -421,7 +450,7 @@ public static String generateHtml(JSONObject threads) throws Exception { String content = ""; JSONObject thread = threads.getJSONObject("thread"); - + String tweetId = thread.getString("id"); String title = getHTMLTitle(thread); JSONArray tweets = thread.getJSONArray("tweets"); for (int i = 0; i < tweets.length(); i++) { @@ -429,13 +458,31 @@ public static String generateHtml(JSONObject threads) throws Exception { content += getHTMLTweet(tweet, false); } String footer = getHTMLFooter(thread); - return generateHtml(title + "\n" + content + "\n" + footer); + return generateHtml(tweetId,title + "\n" + content + "\n" + footer); + } + + public static String generateHtml(String tweetId,String content) { + String script = ""+ + " \n" + + " \n"; + String btn = " \n" + + "
"+SHARE_POST+"
"; + String html = generateHtml(content); + + html = html.replace(" \n" + + " \n",script).replace(" \n",btn); + return html; } public static String generateHtml(String content) { String html = " \n" + " {head}\n" + - " \n" + + " \n" + "
\n" + "{content}\n" + "
\n" + diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java index 431a63d5e2..65d67a1bef 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java @@ -5,7 +5,7 @@ import app.revanced.integrations.twitter.settings.ActivityHook; import app.revanced.integrations.twitter.Utils; import app.revanced.integrations.twitter.Pref; - +import app.revanced.integrations.shared.StringRef; import java.io.File; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -82,11 +82,8 @@ private static String preferenceJS() { Boolean textOnlyMode = Pref.hideNativeReaderPostTextOnlyMode(); Boolean hideQuotedPosts = Pref.hideNativeReaderHideQuotedPosts(); Boolean noGrok = Pref.hideNativeReaderNoGrok(); - int theme = Utils.getTheme(); - String themeClass = (theme == 0?"":(theme==1?"dark":"dim")); - return "document.body.className='';\n" + - "\ndocument.body.classList.add(\""+themeClass+"\");\n"+ - "if(" + hideQuotedPosts + "){\n" + + + return "if(" + hideQuotedPosts + "){\n" + "var nodes = document.querySelectorAll(\".quoted-section\");\n" + "nodes.forEach(n=>n.style.display='none');\n" + "}\n" + @@ -100,9 +97,8 @@ private static String preferenceJS() { "}"; } - public static void launchReaderMode(Context activity, Object tweet) throws Exception { - String tweetId = ""+new Tweet(tweet).getTweetId(); + String tweetId = "" + new Tweet(tweet).getTweetId(); ActivityHook.startReaderMode(tweetId); return; } @@ -142,14 +138,36 @@ private static JSONObject getThreadInfo(String tweetId) throws Exception { } } - private static File cacheFileDir(String tweetId) { + private static File cacheDir() { File cacheDir = ctx.getCacheDir(); File threadsDir = new File(cacheDir, "Threads"); if (!threadsDir.exists()) { threadsDir.mkdirs(); } - return new File(threadsDir, THREADS_KEY + "_" + tweetId + ".html"); + return threadsDir; + } + + private static File cacheFileDir(String tweetId) { + return new File(cacheDir(), THREADS_KEY + "_" + tweetId + ".html"); + } + + public static void clearCache() { + File dir = cacheDir(); + boolean deleted = true; + if (dir != null && dir.isDirectory()) { + File[] children = dir.listFiles(); + if (children != null) { + for (File child : children) { + deleted = deleted && child.delete(); + } + } + } + if (deleted) { + Utils.toast(StringRef.str("piko_native_reader_mode_cache_delete_success")); + } else { + Utils.toast(StringRef.str("piko_native_reader_mode_cache_delete_failed")); + } } private static boolean writeCacheFile(String tweetId, String data) { @@ -171,25 +189,43 @@ private static String readCacheFile(String tweetId) throws Exception { } public static String buildHtml(String tweetId) { + // 0 = light, 1 = dark, 2 = dim String html = ""; + String defThemeClass = "{themeClassName}"; + String themeClass = defThemeClass; + int theme = Utils.getTheme(); + switch(theme){ + case 1:{ + themeClass = "dark"; + break; + }case 2:{ + themeClass = "dim"; + break; + }default:{ + themeClass = defThemeClass; + } + } try { html = readCacheFile(tweetId); - if (html != null) - return html; - JSONObject threadInfo = getThreadInfo(tweetId); - if (threadInfo != null) { - html = ReaderModeTemplate.generateHtml(threadInfo); - writeCacheFile(tweetId, html); + if (html == null) { + JSONObject threadInfo = getThreadInfo(tweetId); + if (threadInfo != null) { + html = ReaderModeTemplate.generateHtml(threadInfo); + writeCacheFile(tweetId, html); + }else{ + html = ReaderModeTemplate.generateHtml(NO_CONTENT); + } } } catch (Exception e) { html = ReaderModeTemplate.generateHtml(e.getMessage()); } + html = html.replace(defThemeClass, themeClass); return html; } public static String injectJS() { return "(function() {" + - fontSize() + preferenceJS()+ + fontSize() + preferenceJS() + "})();"; } diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java b/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java index 97617c0787..c6c86f3789 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java @@ -45,7 +45,12 @@ public static boolean deeplink(Activity act) { return true; } - }else if (isPiko) { + }else if(mainPath.equals("status")){ + String tweetId = deeplinkSegments.get(2); + ActivityHook.startReaderMode(tweetId); + return true; + } + else if (isPiko) { if(lastSegment.equals("premium")){ key = Settings.PREMIUM_SECTION; } else if (lastSegment.equals("download")) { diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java b/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java index d13f3876a2..317ed8b010 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/Settings.java @@ -101,17 +101,8 @@ public class Settings extends BaseSettings { public static final String RESET_PREF = "reset_pref"; public static final String RESET_FLAGS = "reset_flags"; public static final String FEATURE_FLAGS = "feature_flags"; + public static final String RESET_READER_MODE_CACHE = "reader_mode_cache"; -// public static final BooleanSetting PREMIUM_SECTION = new BooleanSetting("premium_section", true); -// public static final BooleanSetting DOWNLOAD_SECTION = new BooleanSetting("download_section", true); -// public static final BooleanSetting FLAGS_SECTION = new BooleanSetting("flags_section", true); -// public static final BooleanSetting ADS_SECTION = new BooleanSetting("ads_section", true); -// public static final BooleanSetting MISC_SECTION = new BooleanSetting("misc_section", true); -// public static final BooleanSetting CUSTOMISE_SECTION = new BooleanSetting("custommise_section", true); -// public static final BooleanSetting TIMELINE_SECTION = new BooleanSetting("timeline_section", true); -// public static final BooleanSetting LOGGING_SECTION = new BooleanSetting("logging_section", true); -// public static final BooleanSetting BACKUP_SECTION = new BooleanSetting("backup_section", true); -// public static final BooleanSetting NATIVE_SECTION = new BooleanSetting("native_section", true); public static final String PREMIUM_SECTION = "premium_section"; public static final String DOWNLOAD_SECTION = "download_section"; public static final String FLAGS_SECTION = "flags_section"; diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java index f8570b3b9d..b2b143a92e 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/fragments/PageFragment.java @@ -38,7 +38,27 @@ public void onCreate(@Nullable Bundle savedInstanceState) { Bundle bundle = getArguments(); String activity_name = bundle != null ? bundle.getString(Settings.ACT_NAME) : null; - + if (activity_name.equals(Settings.PREMIUM_SECTION)) { + screenBuilder.buildPremiumSection(false); + }else if (activity_name.equals(Settings.DOWNLOAD_SECTION)) { + screenBuilder.buildDownloadSection(false); + }else if (activity_name.equals(Settings.FLAGS_SECTION)) { + screenBuilder.buildFeatureFlagsSection(false); + }else if (activity_name.equals(Settings.ADS_SECTION)) { + screenBuilder.buildAdsSection(false); + }else if (activity_name.equals(Settings.MISC_SECTION)) { + screenBuilder.buildMiscSection(false); + }else if (activity_name.equals(Settings.CUSTOMISE_SECTION)) { + screenBuilder.buildCustomiseSection(false); + }else if (activity_name.equals(Settings.TIMELINE_SECTION)) { + screenBuilder.buildTimelineSection(false); + }else if (activity_name.equals(Settings.BACKUP_SECTION)) { + screenBuilder.buildExportSection(false); + }else if (activity_name.equals(Settings.NATIVE_SECTION)) { + screenBuilder.buildNativeSection(false); + }else if (activity_name.equals(Settings.LOGGING_SECTION)) { + screenBuilder.buildLoggingSection(false); + } // setSupportActionBar(ActivityHook.toolbar); setPreferenceScreen(screen); diff --git a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java index e90e586823..0a31a1523f 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java +++ b/app/src/main/java/app/revanced/integrations/twitter/settings/widgets/ButtonPref.java @@ -17,6 +17,8 @@ import app.revanced.integrations.twitter.settings.Settings; import app.revanced.integrations.twitter.settings.fragments.BackupPrefFragment; import app.revanced.integrations.twitter.settings.fragments.RestorePrefFragment; +import app.revanced.integrations.twitter.patches.nativeFeatures.readerMode.ReaderModeUtils; + public class ButtonPref extends Preference { private final Context context; @@ -101,6 +103,8 @@ public boolean onPreferenceClick(Preference preference) { Utils.deleteSharedPrefAB(context, true); } else if (key.equals(Settings.ADS_DEL_FROM_DB)) { DatabasePatch.showDialog(context); + }else if (key.equals(Settings.RESET_READER_MODE_CACHE)) { + ReaderModeUtils.clearCache(); } else { ActivityHook.startActivity(key); } From 3899c77071133ab2933b3dc8869657936240a824 Mon Sep 17 00:00:00 2001 From: swakwork Date: Tue, 2 Sep 2025 22:53:05 +0530 Subject: [PATCH 7/7] fix(Twitter): fix model's toString methods --- .../integrations/twitter/model/ExtMediaEntities.java | 4 ++-- .../app/revanced/integrations/twitter/model/Media.java | 2 +- .../app/revanced/integrations/twitter/model/Tweet.java | 8 ++++---- .../revanced/integrations/twitter/model/TweetInfo.java | 2 +- .../app/revanced/integrations/twitter/model/Video.java | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java b/app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java index 8eaad35778..6186fc3a32 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java +++ b/app/src/main/java/app/revanced/integrations/twitter/model/ExtMediaEntities.java @@ -55,8 +55,8 @@ public Media getMedia() throws Exception { @Override public String toString(){ try{ - return "ExtMediaEntities [getImageUrl()=" + getImageUrl() + ", getHighResImageUrl()=" - + getHighResImageUrl() + ", getHighResVideo()=" + getHighResVideo() + "]"; + return "ExtMediaEntities [getImageUrl()=" + this.getImageUrl() + ", getHighResImageUrl()=" + + this.getHighResImageUrl() + ", getHighResVideo()=" + this.getHighResVideo() + "]"; }catch(Exception e){ Utils.logger(e); return e.getMessage(); diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/Media.java b/app/src/main/java/app/revanced/integrations/twitter/model/Media.java index 425b3bb1ca..9745f6b3c0 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/model/Media.java +++ b/app/src/main/java/app/revanced/integrations/twitter/model/Media.java @@ -18,7 +18,7 @@ public Media(int type, String url, String ext) { public String toString() { try { - return "Media [type=" + type + ", url=" + url + ", ext=" + ext + "]"; + return "Media [type=" + this.type + ", url=" + this.url + ", ext=" + this.ext + "]"; } catch (Exception e) { Utils.logger(e); return e.getMessage(); diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java b/app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java index 2a53dc244c..44caff09a5 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java +++ b/app/src/main/java/app/revanced/integrations/twitter/model/Tweet.java @@ -114,10 +114,10 @@ public String getText() throws Exception { @Override public String toString() { try { - return "Tweet [getTweetId()=" + getTweetId() + ", getTweetUsername()=" + getTweetUsername() - + ", getTweetProfileName()=" + getTweetProfileName() + ", getTweetUserId()=" + getTweetUserId() - + ", getMedias()=" + getMedias() + ", getTweetInfo()=" + getTweetInfo() + ", getTweetLang()=" - + getTweetLang() + ", getLongText()=" + getLongText() + ", getShortText()=" + getShortText() + "]"; + return "Tweet [getTweetId()=" + this.getTweetId() + ", getTweetUsername()=" + this.getTweetUsername() + + ", getTweetProfileName()=" + this.getTweetProfileName() + ", getTweetUserId()=" + this.getTweetUserId() + + ", getMedias()=" + this.getMedias() + ", getTweetInfo()=" + this.getTweetInfo() + ", getTweetLang()=" + + this.getTweetLang() + ", getLongText()=" + this.getLongText() + ", getShortText()=" + this.getShortText() + "]"; } catch (Exception e) { Utils.logger(e); diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java b/app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java index 6efe76a093..70b9204efb 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java +++ b/app/src/main/java/app/revanced/integrations/twitter/model/TweetInfo.java @@ -24,7 +24,7 @@ public String getLang() throws Exception { @Override public String toString() { try { - return "TweetInfo [getLang()=" + getLang() + "]"; + return "TweetInfo [getLang()=" + this.getLang() + "]"; } catch (Exception e) { Utils.logger(e); diff --git a/app/src/main/java/app/revanced/integrations/twitter/model/Video.java b/app/src/main/java/app/revanced/integrations/twitter/model/Video.java index f008f87eb6..3fd5ffbe6c 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/model/Video.java +++ b/app/src/main/java/app/revanced/integrations/twitter/model/Video.java @@ -53,9 +53,9 @@ public String getExtension() @Override public String toString() { try { - return "Video [getBitrate()=" + getBitrate() + ", getMediaUrl()=" + getMediaUrl() + ", getCodec()=" - + getCodec() - + ", getThumbnail()=" + getThumbnail() + ", getExtension()=" + getExtension() + "]"; + return "Video [getBitrate()=" + this.getBitrate() + ", getMediaUrl()=" + this.getMediaUrl() + ", getCodec()=" + + this.getCodec() + + ", getThumbnail()=" + this.getThumbnail() + ", getExtension()=" + this.getExtension() + "]"; } catch (Exception e) { Utils.logger(e);