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..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; @@ -22,28 +23,36 @@ 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; + +import java.lang.StackTraceElement; @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 +82,6 @@ public static void startAppIconNNavIconActivity() { startActivityFromClassName(className); } - private static void startBookmarkActivity() { String className = "com.twitter.app.bookmarks.legacy.BookmarkActivity"; startActivityFromClassName(className); @@ -84,7 +92,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 +257,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 +301,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,13 +320,99 @@ 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 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); + 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); } - 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..6186fc3a32 --- /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()=" + this.getImageUrl() + ", getHighResImageUrl()=" + + this.getHighResImageUrl() + ", getHighResVideo()=" + this.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..9745f6b3c0 --- /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=" + 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 new file mode 100644 index 0000000000..44caff09a5 --- /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()=" + 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); + 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..70b9204efb --- /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()=" + this.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..3fd5ffbe6c --- /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()=" + this.getBitrate() + ", getMediaUrl()=" + this.getMediaUrl() + ", getCodec()=" + + this.getCodec() + + ", getThumbnail()=" + this.getThumbnail() + ", getExtension()=" + this.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 deleted file mode 100644 index 0935f6da79..0000000000 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/NativeDownloader.java +++ /dev/null @@ -1,244 +0,0 @@ -package app.revanced.integrations.twitter.patches; - -import android.app.AlertDialog; -import android.content.Context; -import android.widget.LinearLayout; -import app.revanced.integrations.twitter.Utils; -import app.revanced.integrations.twitter.Pref; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.lang.StackTraceElement; - -public class NativeDownloader { - public static String downloadString() { - return Utils.strRes("piko_pref_native_downloader_alert_title"); - } - - private static String getExtension(String typ) { - if (typ.equals("video/mp4")) { - return "mp4"; - } - if (typ.equals("video/webm")) { - return "webm"; - } - if (typ.equals("application/x-mpegURL")) { - return "m3u8"; - } - return "jpg"; - } - - private static String generateFileName(Object tweet) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException { - String tweetId = String.valueOf(getTweetId(tweet)); - int fileNameType = Pref.nativeDownloaderFileNameType(); - switch (fileNameType) { - case 1: - return getTweetUsername(tweet) + "_" + tweetId; - case 2: - return getTweetProfileName(tweet) + "_" + tweetId; - case 3: - return getTweetUserId(tweet) + "_" + 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(); - - 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); - builder.setTitle(Utils.strRes("piko_pref_native_downloader_alert_title")); - - int n = mediaData.size(); - String[] choices = new String[n]; - for (int i = 0; i < n; i++) { - HashMap hashMap = mediaData.get(i); - String typ = hashMap.get("type"); - choices[i] = "• " + typ + " " + (i + 1); - } - - builder.setItems(choices, (dialogInterface, which) -> { - HashMap media = mediaData.get(which); - - Utils.toast(Utils.strRes("download_started")); - Utils.downloadFile(media.get("url"), filename + (which + 1), media.get("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")); - i++; - } - dialogInterface.dismiss(); - }); - - builder.show(); - } - - - // 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"); - } - - private static Class getVideoDataClass() throws ClassNotFoundException { - if (videoDataClass == null) videoDataClass = Class.forName("videoDataClass"); - - return videoDataClass; - } - -} \ 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 63f3f9156e..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 @@ -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; @@ -17,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!!!"); } } @@ -33,34 +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"); - - FileOutputStream outputStream = new FileOutputStream(outputFile, append); - outputStream.write(data); - outputStream.close(); - return true; - }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/nativeFeatures/NativeDownloader.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/NativeDownloader.java new file mode 100644 index 0000000000..02e8c74bda --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/NativeDownloader.java @@ -0,0 +1,121 @@ +package app.revanced.integrations.twitter.patches.nativeFeatures; + +import android.app.AlertDialog; +import android.content.Context; +import android.widget.LinearLayout; +import app.revanced.integrations.twitter.Utils; +import app.revanced.integrations.twitter.Pref; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +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() { + return Utils.strRes("piko_pref_native_downloader_alert_title"); + } + + private static String getExtension(String typ) { + if (typ.equals("video/mp4")) { + return "mp4"; + } + if (typ.equals("video/webm")) { + return "webm"; + } + if (typ.equals("application/x-mpegURL")) { + return "m3u8"; + } + return "jpg"; + } + + private static String generateFileName(Tweet tweet) throws Exception { + String tweetId = ""+tweet.getTweetId(); + int fileNameType = Pref.nativeDownloaderFileNameType(); + switch (fileNameType) { + case 1: + return tweet.getTweetUsername() + "_" + tweetId; + case 2: + return tweet.getTweetProfileName() + "_" + tweetId; + case 3: + return tweet.getTweetUserId() + "_" + tweetId; + case 5: + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")); + default: + return tweetId; + } + } + + 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"); + + LinearLayout ln = new LinearLayout(ctx); + ln.setOrientation(LinearLayout.VERTICAL); + AlertDialog.Builder builder = new AlertDialog.Builder(ctx); + builder.setTitle(Utils.strRes("piko_pref_native_downloader_alert_title")); + + int n = mediaData.size(); + String[] choices = new String[n]; + for (int i = 0; i < n; i++) { + Media media = mediaData.get(i); + String typ = media.type == 0?photo:video; + choices[i] = "• " + typ + " " + (i + 1); + } + + builder.setItems(choices, (dialogInterface, which) -> { + Media media = mediaData.get(which); + + Utils.toast(Utils.strRes("download_started")); + 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 (Media media : mediaData) { + Utils.downloadFile(media.url, filename + (index + 1), media.ext); + i++; + } + dialogInterface.dismiss(); + }); + + builder.show(); + } + + public static void downloader(Context activity, Object tweetObj) throws NoSuchMethodException, + InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException { + try { + Tweet tweet = new Tweet(tweetObj); + 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; + } + + String fileName = generateFileName(tweet); + + if (media.size() == 1) { + Media item = media.get(0); + Utils.toast(Utils.strRes("download_started")); + Utils.downloadFile(item.url, fileName, item.ext); + return; + } + + 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/nativeFeatures/readerMode/ReaderModeFragment.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java new file mode 100644 index 0000000000..2a5a1514fd --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeFragment.java @@ -0,0 +1,132 @@ +package app.revanced.integrations.twitter.patches.nativeFeatures.readerMode; + + +import android.content.Context; +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; +import app.revanced.integrations.shared.StringRef; +import android.webkit.JavascriptInterface; + +public class ReaderModeFragment extends Fragment { + + private WebView webView; + + 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); + 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.addJavascriptInterface(new WebAppInterface(getContext()), "Android"); + 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/patches/nativeFeatures/readerMode/ReaderModeTemplate.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java new file mode 100644 index 0000000000..1ab0e6d5d4 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeTemplate.java @@ -0,0 +1,497 @@ +package app.revanced.integrations.twitter.patches.nativeFeatures.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_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 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 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" + + "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() { + + 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" + + ".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" + + ".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: 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 ""; + } + + private static String getHTMLTitle(JSONObject thread) throws Exception { + String html = ""; + String template = "
\n" + + " \n" + + "
\n" + + " {name}\n" + + " @{username}\n" + + " {grokUser}\n" + + "
\n" + + "
\n" + + "
\n" + + TITLE_PUBLISHED + ": {formattedDate} · " + TITLE_SOURCE + + ": X Thread\n" + + + "
\n" + + "
\n" + + TITLE_TOT_POSTS + ": {totalTweets}\n" + + "
\n" + + "
\n" + + "{grokPost}\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"); + String postLink = "https://x.com/" + username + "/status/" + tweetId; + + 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("{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 { + 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"); + 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 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 < urls.length(); i++) { + JSONObject url = urls.getJSONObject(i); + String tco = url.getString("url"); + String expUrl = url.getString("expandedUrl"); + text = text.replace(tco, expUrl); + } + + } + return text; + } + + 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" + + // "{text}\n" + + " {content}\n" + + "\n" + + + "

"; + html = html.replace("{username}", username) + .replace("{name}", name) + .replace("{tweetId}", tweetId); + } + + if (!tweet.isNull("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); + String[] cnt = getHTMLMedia(0, item); + String tco = cnt[0]; + content = content.replace(tco, "") + cnt[1]; + } + } + + if (!tweet.isNull("media")) { + JSONArray videos = tweet.getJSONArray("media"); + for (int i = 0; i < videos.length(); i++) { + JSONObject item = videos.getJSONObject(i); + String[] cnt = getHTMLMedia(1, item); + String tco = cnt[0]; + content = content.replace(tco, "") + cnt[1]; + } + } + + 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 = thread.getString("id"); + String postLink = "https://x.com/dummy/status/" + tweetId; + + String template = "
\n" + + getGrokIcon(postLink, GROK_ANALYSE_THREAD) + "\n" + + " twitter-thread.com\n" + + "
\n"; + + return template + .replace("{tweetId}", tweetId); + } + + 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++) { + JSONObject tweet = tweets.getJSONObject(i); + content += getHTMLTweet(tweet, false); + } + String footer = getHTMLFooter(thread); + 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" + + "{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/patches/nativeFeatures/readerMode/ReaderModeUtils.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java new file mode 100644 index 0000000000..65d67a1bef --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/readerMode/ReaderModeUtils.java @@ -0,0 +1,233 @@ +package app.revanced.integrations.twitter.patches.nativeFeatures.readerMode; + +import android.content.Context; +import java.lang.reflect.InvocationTargetException; +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; +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.model.Tweet; +import app.revanced.integrations.twitter.patches.nativeFeatures.readerMode.ReaderModeTemplate; + +public class ReaderModeUtils { + + private static final String THREADS_KEY = "threads"; + 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(n=>n.style.display='none');\n" + + "}\n" + + "if(" + textOnlyMode + "){\n" + + "var nodes = document.querySelectorAll(\".media\");\n" + + "nodes.forEach(n=>n.style.display='none');\n" + + "}\n" + + "if(" + noGrok + "){\n" + + "var nodes = document.querySelectorAll(\".grok-button\");\n" + + "nodes.forEach(n=>n.style.display='none');\n" + + "}"; + } + + public static void launchReaderMode(Context activity, Object tweet) throws Exception { + String tweetId = "" + new Tweet(tweet).getTweetId(); + 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 cacheDir() { + File cacheDir = ctx.getCacheDir(); + File threadsDir = new File(cacheDir, "Threads"); + + if (!threadsDir.exists()) { + threadsDir.mkdirs(); + } + 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) { + 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) { + // 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) { + 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() + + "})();"; + } + + // class end +} \ 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/nativeFeatures/translator/Constants.java similarity index 67% 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 8c022af58f..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,11 +1,9 @@ -package app.revanced.integrations.twitter.patches.translator; +package app.revanced.integrations.twitter.patches.nativeFeatures.translator; 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/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/nativeFeatures/translator/NativeTranslator.java b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/NativeTranslator.java new file mode 100644 index 0000000000..32c5cecf8e --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/nativeFeatures/translator/NativeTranslator.java @@ -0,0 +1,82 @@ +package app.revanced.integrations.twitter.patches.nativeFeatures.translator; + +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.HashMap; +import java.util.List; + +import app.revanced.integrations.twitter.Utils; +import app.revanced.integrations.shared.StringRef; +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; + +public class NativeTranslator { + + public static void translate(Context activity, Object tweetObj) throws Exception { + try{ + String text = ""; + Tweet tweet = new Tweet(tweetObj); + try { + text = tweet.getText(); + }catch (Exception e){ + text = e.toString(); + } + // If text is empty. + if(text == ""){ + String translatedText = StringRef.str("piko_native_translator_zero_text"); + DialogBox dialogBox = new DialogBox(activity,"",translatedText); + dialogBox.show(); + return; + } + + String toLang = Pref.translatorLanguage(); + String tweetLang = tweet.getTweetLang(); + + // If both the tweet language and requested language are same. + if(tweetLang.toLowerCase() == toLang.toLowerCase()){ + String translatedText = StringRef.str("translate_tweet_same_language",toLang); + DialogBox dialogBox = new DialogBox(activity,"",translatedText); + dialogBox.show(); + return; + } + int providerCode = Pref.natveTranslatorProvider(); + + Translate translator; + switch (providerCode){ + case 1: { + translator = new GTranslateV2(); + break; + } + default: { + translator = new GTranslate(); + } + } + String providerName = translator.getProviderName(); + String header = StringRef.str("translate_tweet_link_label",tweetLang,providerName); + + translator.translate(text, toLang, new Translate.TranslationCallback() { + @Override + public void onTranslationComplete(String translatedText) { + // Update UI with translated text + DialogBox dialogBox = new DialogBox(activity,header,translatedText); + dialogBox.show(); + } + + @Override + public void onError(Exception e) { + // Handle translation error + 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/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/patches/translator/NativeTranslator.java b/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java deleted file mode 100644 index 9267c2ea19..0000000000 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/translator/NativeTranslator.java +++ /dev/null @@ -1,125 +0,0 @@ -package app.revanced.integrations.twitter.patches.translator; - -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.HashMap; -import java.util.List; - -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.Pref; - -public class NativeTranslator { - - private static String getLang(Object tweet){ - 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 = ""; - try { - text = getText(tweet); - }catch (Exception e){ - text = e.toString(); - } - // If text is empty. - if(text == ""){ - String translatedText = StringRef.str("piko_native_translator_zero_text"); - DialogBox dialogBox = new DialogBox(activity,"",translatedText); - dialogBox.show(); - return; - } - - String toLang = Pref.translatorLanguage(); - String tweetLang = getLang(tweet); - - // If both the tweet language and requested language are same. - if(tweetLang.toLowerCase() == toLang.toLowerCase()){ - String translatedText = StringRef.str("translate_tweet_same_language",toLang); - DialogBox dialogBox = new DialogBox(activity,"",translatedText); - dialogBox.show(); - return; - } - int providerCode = Pref.natveTranslatorProvider(); - - Translate translator; - switch (providerCode){ - case 1: { - translator = new GTranslateV2(); - break; - } - default: { - translator = new GTranslate(); - } - } - String providerName = translator.getProviderName(); - String header = StringRef.str("translate_tweet_link_label",tweetLang,providerName); - - translator.translate(text, toLang, new Translate.TranslationCallback() { - @Override - public void onTranslationComplete(String translatedText) { - // Update UI with translated text - DialogBox dialogBox = new DialogBox(activity,header,translatedText); - dialogBox.show(); - } - - @Override - public void onError(Exception e) { - // Handle translation error - Utils.toast("Translation failed: " + e.getMessage()); - } - }); - } -} \ No newline at end of file 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..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 @@ -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.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 { 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,33 @@ 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) ){ fragment = new FeatureFlagsFragment(); - fragment.setArguments(intent.getExtras()); - } else if (intent.getBooleanExtra(Settings.PATCH_INFO.key, false)) { + } else if (activity_name .equals( Settings.PATCH_INFO)) { fragment = new SettingsAboutFragment(); - } else { + }else if (activity_name .equals( Settings.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")); + toolbar.setTitle(getTitle(activity_name)); toolbar.setNavigationOnClickListener(view -> act.onBackPressed()); FragmentTransaction transaction = act.getFragmentManager().beginTransaction().replace(Utils.getResourceIdentifier("fragment_container", "id"), fragment); @@ -82,9 +85,37 @@ public static void startFragment(Activity act, Fragment fragment, boolean addToB 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.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 +124,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(Settings.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/DeepLink.java b/app/src/main/java/app/revanced/integrations/twitter/settings/DeepLink.java index 110735668e..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 @@ -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,11 +41,16 @@ 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; } - }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")) { @@ -70,7 +74,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 ee8dd01603..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 @@ -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" ) @@ -372,6 +371,48 @@ 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 + ) + ); + addPreference(category, + helper.buttonPreference( + strRes("piko_native_reader_mode_cache_delete"), + "", + Settings.RESET_READER_MODE_CACHE + ) + ); + + } } 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..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 @@ -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); @@ -88,24 +93,27 @@ 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 String RESET_READER_MODE_CACHE = "reader_mode_cache"; + + 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/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..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 @@ -37,39 +37,28 @@ 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)) { screenBuilder.buildPremiumSection(false); - toolbarText = "piko_title_premium"; - }else if (bundle.getBoolean(Settings.DOWNLOAD_SECTION.key, false)) { + }else if (activity_name.equals(Settings.DOWNLOAD_SECTION)) { screenBuilder.buildDownloadSection(false); - toolbarText = "piko_title_download"; - }else if (bundle.getBoolean(Settings.FLAGS_SECTION.key, false)) { + }else if (activity_name.equals(Settings.FLAGS_SECTION)) { 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)) { screenBuilder.buildAdsSection(false); - toolbarText = "piko_title_ads"; - }else if (bundle.getBoolean(Settings.MISC_SECTION.key, false)) { + }else if (activity_name.equals(Settings.MISC_SECTION)) { screenBuilder.buildMiscSection(false); - toolbarText = "piko_title_misc"; - }else if (bundle.getBoolean(Settings.CUSTOMISE_SECTION.key, false)) { + }else if (activity_name.equals(Settings.CUSTOMISE_SECTION)) { screenBuilder.buildCustomiseSection(false); - toolbarText = "piko_title_customisation"; - }else if (bundle.getBoolean(Settings.TIMELINE_SECTION.key, false)) { + }else if (activity_name.equals(Settings.TIMELINE_SECTION)) { screenBuilder.buildTimelineSection(false); - toolbarText = "piko_title_timeline"; - }else if (bundle.getBoolean(Settings.BACKUP_SECTION.key, false)) { + }else if (activity_name.equals(Settings.BACKUP_SECTION)) { screenBuilder.buildExportSection(false); - toolbarText = "piko_title_backup"; - }else if (bundle.getBoolean(Settings.NATIVE_SECTION.key, false)) { + }else if (activity_name.equals(Settings.NATIVE_SECTION)) { screenBuilder.buildNativeSection(false); - toolbarText = "piko_title_native"; - }else if (bundle.getBoolean(Settings.LOGGING_SECTION.key, false)) { + }else if (activity_name.equals(Settings.LOGGING_SECTION)) { 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 7dce55dade..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; @@ -77,37 +79,39 @@ 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 if (key.equals(Settings.RESET_READER_MODE_CACHE)) { + ReaderModeUtils.clearCache(); } else { ActivityHook.startActivity(key); } 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); 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; } 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 {