From 8d55001cf4100837028a3dec6402e23c37e3494c Mon Sep 17 00:00:00 2001 From: crimera Date: Wed, 15 May 2024 22:08:34 +0800 Subject: [PATCH 01/13] add `Download patch` --- .../instagram/patches/DownloadPatch.java | 48 +++++++++++++++++++ .../revanced/integrations/twitter/Utils.java | 4 ++ .../twitter/patches/FeatureSwitchPatch.java | 9 ++++ .../twitter/patches/customise/NavBar.java | 17 +++++++ stub/build.gradle.kts | 7 +++ .../api/schemas/MediaOptionStyle.java | 9 ++++ 6 files changed, 94 insertions(+) create mode 100644 app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java create mode 100644 app/src/main/java/app/revanced/integrations/twitter/patches/customise/NavBar.java create mode 100644 stub/src/main/java/com/instagram/api/schemas/MediaOptionStyle.java diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java new file mode 100644 index 0000000000..5c15ea2c51 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java @@ -0,0 +1,48 @@ +package app.revanced.integrations.instagram.patches; + +import app.revanced.integrations.twitter.Utils; +import com.instagram.api.schemas.MediaOptionStyle; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +class DownloadPatch { + // To be added in revanced patches + public static String feedOptionItemIconClassName() { + return ""; + } + + public static String feedItemClassName() { + return ""; + } + + public static Class feedItemIconClass; + public static Class feedItem; + + static { + try { + feedItemIconClass = Class.forName(feedOptionItemIconClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + + try { + feedItem = Class.forName(feedItemClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public static void addDownloadButton(List items) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { + Object item = feedItem + .getConstructor(MediaOptionStyle.class, feedItemIconClass, CharSequence.class) +// TODO dynamically get the Download icon field + .newInstance(MediaOptionStyle.A05, feedItemIconClass.getField("A0Z").get(feedItemIconClass), "Download"); + items.add(item); + } + + private void findNormalField() { + + } +} \ No newline at end of file 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 4d9650c4a5..39d397393d 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/Utils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/Utils.java @@ -29,6 +29,10 @@ private static void startActivity(Class cls) { ctx.startActivity(intent); } + public static void print(Object message) { + System.out.println("BRUH: "+message); + } + public static void startActivityFromClassName(String className){ try { Class clazz = Class.forName(className); diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/FeatureSwitchPatch.java b/app/src/main/java/app/revanced/integrations/twitter/patches/FeatureSwitchPatch.java index 0576c914c4..6da40da56f 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/patches/FeatureSwitchPatch.java +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/FeatureSwitchPatch.java @@ -2,6 +2,7 @@ import java.util.*; +import android.util.Log; import app.revanced.integrations.twitter.Pref; import app.revanced.integrations.twitter.Utils; import app.revanced.integrations.twitter.settings.Settings; @@ -38,9 +39,11 @@ private static void immersivePlayer() { } public static Object flagInfo(String flag, Object def) { +// Log.d("BRUH", "name: "+flag+" value: "+def.toString()+" type: "+def.getClass().getName()); try { if (FLAGS.containsKey(flag)) { Object val = FLAGS.get(flag); +// Log.d("BRUH", "name: "+flag+" value: "+val+" type: "+val.getClass().getName()); return val; } } catch (Exception ex) { @@ -58,5 +61,11 @@ public static void load() { addFlag(item[0], Boolean.valueOf(item[1])); } } + + addFlag("timeline_auto_refresh_on_foreground_timeout_millis", 99999.0); + addFlag("recent_search_limit_count", 10); + addFlag("home_timeline_start_at_top_min_background_minutes", 99999.0); + addFlag("home_timeline_start_at_top_warm_start_min_background_minutes", 99999.0); + addFlag("home_timeline_start_at_top_timeout_length", 99999.0); } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/twitter/patches/customise/NavBar.java b/app/src/main/java/app/revanced/integrations/twitter/patches/customise/NavBar.java new file mode 100644 index 0000000000..34e2d44ba5 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/twitter/patches/customise/NavBar.java @@ -0,0 +1,17 @@ +package app.revanced.integrations.twitter.patches.customise; + +import android.util.Log; + +import java.util.List; + +public class NavBar { + public static void printList(List list) { + for (Object o: list) { + print(o.getClass().getName()); + } + } + + public static void print(String a) { + System.out.println("BRUH"+ a); + } +} diff --git a/stub/build.gradle.kts b/stub/build.gradle.kts index d16c19b7a5..fb53193c05 100644 --- a/stub/build.gradle.kts +++ b/stub/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.android.library) + id("org.jetbrains.kotlin.android") } android { @@ -24,4 +25,10 @@ android { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } + kotlinOptions { + jvmTarget = "11" + } +} +dependencies { + implementation("androidx.core:core-ktx:+") } diff --git a/stub/src/main/java/com/instagram/api/schemas/MediaOptionStyle.java b/stub/src/main/java/com/instagram/api/schemas/MediaOptionStyle.java new file mode 100644 index 0000000000..a06a655f5b --- /dev/null +++ b/stub/src/main/java/com/instagram/api/schemas/MediaOptionStyle.java @@ -0,0 +1,9 @@ +package com.instagram.api.schemas; + +public enum MediaOptionStyle { +// TODO dynamically get the normal media type field + A05("normal"); + + MediaOptionStyle(String mediaOptionStyleUnspecified) { + } +} \ No newline at end of file From b4254c8d391ca3431007c29b0e0a2ac277d6caa5 Mon Sep 17 00:00:00 2001 From: crimera Date: Thu, 16 May 2024 20:57:04 +0800 Subject: [PATCH 02/13] call downloadPost on download clicked --- .../instagram/patches/DownloadPatch.java | 13 +++++++++---- .../app/revanced/integrations/twitter/Utils.java | 4 ---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java index 5c15ea2c51..acbfe98091 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java @@ -1,13 +1,13 @@ package app.revanced.integrations.instagram.patches; -import app.revanced.integrations.twitter.Utils; +import android.app.Activity; import com.instagram.api.schemas.MediaOptionStyle; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import java.util.List; -class DownloadPatch { +public class DownloadPatch { // To be added in revanced patches public static String feedOptionItemIconClassName() { return ""; @@ -39,10 +39,15 @@ public static void addDownloadButton(List items) throws IllegalAccessException, .getConstructor(MediaOptionStyle.class, feedItemIconClass, CharSequence.class) // TODO dynamically get the Download icon field .newInstance(MediaOptionStyle.A05, feedItemIconClass.getField("A0Z").get(feedItemIconClass), "Download"); + items.add(item); } - private void findNormalField() { + public static void downloadPost(Object p1, int p2, Object p3, Activity p4) { + System.out.println("BRUH p1: "+p1+", p2: "+p2+", p3: "+p3+", p4: "+p4); + } + public static void print(Object message) { + System.out.println("BRUH: "+message); } } \ No newline at end of file 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 39d397393d..4d9650c4a5 100644 --- a/app/src/main/java/app/revanced/integrations/twitter/Utils.java +++ b/app/src/main/java/app/revanced/integrations/twitter/Utils.java @@ -29,10 +29,6 @@ private static void startActivity(Class cls) { ctx.startActivity(intent); } - public static void print(Object message) { - System.out.println("BRUH: "+message); - } - public static void startActivityFromClassName(String className){ try { Class clazz = Class.forName(className); From bce8e4172a383531822bd98222f38482053d8d4b Mon Sep 17 00:00:00 2001 From: crimera Date: Thu, 16 May 2024 20:57:24 +0800 Subject: [PATCH 03/13] add todo --- .../revanced/integrations/instagram/patches/DownloadPatch.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java index acbfe98091..7c26796f64 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java @@ -44,6 +44,7 @@ public static void addDownloadButton(List items) throws IllegalAccessException, } public static void downloadPost(Object p1, int p2, Object p3, Activity p4) { +// TODO implement method System.out.println("BRUH p1: "+p1+", p2: "+p2+", p3: "+p3+", p4: "+p4); } From cd72e9b492e1c56bbf9f10b763f8a226b5ab808a Mon Sep 17 00:00:00 2001 From: crimera Date: Thu, 16 May 2024 21:00:44 +0800 Subject: [PATCH 04/13] suppress warnings --- .../revanced/integrations/instagram/patches/DownloadPatch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java index 7c26796f64..acafd55d9f 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java @@ -4,9 +4,9 @@ import com.instagram.api.schemas.MediaOptionStyle; import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; import java.util.List; +@SuppressWarnings("unused") public class DownloadPatch { // To be added in revanced patches public static String feedOptionItemIconClassName() { From 0c16c179d92afc909056e89a8636fafbd7bc6d83 Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 01:05:14 +0800 Subject: [PATCH 05/13] added getPhotoLink --- .../instagram/patches/DownloadPatch.java | 25 +++++++++++++++++-- .../model/mediasize/ExtendedImageUrl.java | 7 ++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 stub/src/main/java/com/instagram/model/mediasize/ExtendedImageUrl.java diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java index acafd55d9f..5d7b341e36 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java @@ -1,10 +1,15 @@ package app.revanced.integrations.instagram.patches; import android.app.Activity; +import android.content.Context; +import app.revanced.integrations.shared.Utils; import com.instagram.api.schemas.MediaOptionStyle; +import com.instagram.model.mediasize.ExtendedImageUrl; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.List; +import java.util.Objects; @SuppressWarnings("unused") public class DownloadPatch { @@ -43,9 +48,25 @@ public static void addDownloadButton(List items) throws IllegalAccessException, items.add(item); } - public static void downloadPost(Object p1, int p2, Object p3, Activity p4) { + public static void downloadPost(Object media, int p2, Object p3, Activity p4) throws InvocationTargetException, IllegalAccessException { // TODO implement method - System.out.println("BRUH p1: "+p1+", p2: "+p2+", p3: "+p3+", p4: "+p4); + System.out.println("BRUH p1: "+media+", p2: "+p2+", p3: "+p3+", p4: "+p4); +// TODO implement download a post with only 1 media + print("BRUH: "+getPhotoLink(media)); + } + + public static String getPhotoLink(Object media) throws InvocationTargetException, IllegalAccessException { + Method getExtendedImageUrl = null; + for (Method method : media.getClass().getMethods()) { + if (method.getParameterTypes().length == 0) {continue;} + + if (method.getReturnType() == ExtendedImageUrl.class && method.getParameterTypes()[0] == Context.class) { + getExtendedImageUrl = method; + } + } + + assert getExtendedImageUrl != null; + return ((ExtendedImageUrl) Objects.requireNonNull(getExtendedImageUrl.invoke(media, Utils.getContext()))).getUrl(); } public static void print(Object message) { diff --git a/stub/src/main/java/com/instagram/model/mediasize/ExtendedImageUrl.java b/stub/src/main/java/com/instagram/model/mediasize/ExtendedImageUrl.java new file mode 100644 index 0000000000..38072249c4 --- /dev/null +++ b/stub/src/main/java/com/instagram/model/mediasize/ExtendedImageUrl.java @@ -0,0 +1,7 @@ +package com.instagram.model.mediasize; + +public class ExtendedImageUrl { + public String getUrl() { + return ""; + } +} From 46552c54796cc21088eef24b418994c1f0912ed9 Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 09:21:25 +0800 Subject: [PATCH 06/13] implemented basic image downloading --- .../instagram/patches/DownloadPatch.java | 133 +++++++++++++++--- 1 file changed, 115 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java index 5d7b341e36..a3da24225c 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java @@ -1,19 +1,27 @@ package app.revanced.integrations.instagram.patches; import android.app.Activity; +import android.app.DownloadManager; import android.content.Context; +import android.net.Uri; +import android.os.Environment; +import android.widget.Toast; import app.revanced.integrations.shared.Utils; import com.instagram.api.schemas.MediaOptionStyle; import com.instagram.model.mediasize.ExtendedImageUrl; +import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.URISyntaxException; +import java.net.URL; import java.util.List; import java.util.Objects; @SuppressWarnings("unused") public class DownloadPatch { - // To be added in revanced patches + // To be added in revanced patches public static String feedOptionItemIconClassName() { return ""; } @@ -39,37 +47,126 @@ public static String feedItemClassName() { } } - public static void addDownloadButton(List items) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { + public static void addDownloadButton(List items) throws IllegalAccessException, InstantiationException, + NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Object item = feedItem .getConstructor(MediaOptionStyle.class, feedItemIconClass, CharSequence.class) -// TODO dynamically get the Download icon field - .newInstance(MediaOptionStyle.A05, feedItemIconClass.getField("A0Z").get(feedItemIconClass), "Download"); + // TODO dynamically get the Download icon field + .newInstance(MediaOptionStyle.A05, feedItemIconClass.getField("A0Z").get(feedItemIconClass), + "Download"); items.add(item); } - public static void downloadPost(Object media, int p2, Object p3, Activity p4) throws InvocationTargetException, IllegalAccessException { -// TODO implement method - System.out.println("BRUH p1: "+media+", p2: "+p2+", p3: "+p3+", p4: "+p4); -// TODO implement download a post with only 1 media - print("BRUH: "+getPhotoLink(media)); + public static void downloadPost(Object media, int p2, Object p3, Activity p4) throws URISyntaxException { + + // TODO implement download a post with only 1 media + startDownload(getPhotoLink(media)); + } + + public static void startDownload(URL link) throws URISyntaxException { + String filename = link.getPath().split("/")[link.getPath().split("/").length - 1]; + String publicFolderDirectory = Environment.DIRECTORY_PICTURES; + String folder = "Instagram"; + + File imageFile = new File(Environment.getExternalStoragePublicDirectory(publicFolderDirectory), filename); + + if (imageFile.exists()) { + return; + } + + DownloadManager mgr = (DownloadManager) Utils.getContext().getSystemService(Context.DOWNLOAD_SERVICE); + + mgr.enqueue(new DownloadManager.Request(Uri.parse(link.toURI().toString())) + .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | + DownloadManager.Request.NETWORK_MOBILE) + .setAllowedOverRoaming(false) + .setTitle("Demo") + .setDescription("Something useful. No, really.") + .setDestinationInExternalPublicDir(publicFolderDirectory, + folder+"/"+filename)); + } + + public static void downloadPhoto(URL link) { + HttpURLConnection connection; + try { + connection = (HttpURLConnection) link.openConnection(); + connection.connect(); + + InputStream inputStream = connection.getInputStream(); + + File imageFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "test.png"); + if (!imageFile.createNewFile()) { + Toast.makeText(Utils.getContext(), "Failed to create file", Toast.LENGTH_SHORT).show(); + } + + FileOutputStream fos = new FileOutputStream(imageFile); + fos.write(readAllBytes(inputStream)); + + fos.close(); + + Toast.makeText(Utils.getContext(), "File downloaded", Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + Toast.makeText(Utils.getContext(), "Error downloading", Toast.LENGTH_SHORT).show(); + } } - public static String getPhotoLink(Object media) throws InvocationTargetException, IllegalAccessException { + public static byte[] readAllBytes(InputStream inputStream) throws IOException { + final int bufLen = 4 * 0x400; // 4KB + byte[] buf = new byte[bufLen]; + int readLen; + IOException exception = null; + + try { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) + outputStream.write(buf, 0, readLen); + + return outputStream.toByteArray(); + } + } catch (IOException e) { + exception = e; + throw e; + } finally { + if (exception == null) inputStream.close(); + else try { + inputStream.close(); + } catch (IOException e) { + exception.addSuppressed(e); + } + } + } + + public static URL getPhotoLink(Object media) { Method getExtendedImageUrl = null; - for (Method method : media.getClass().getMethods()) { - if (method.getParameterTypes().length == 0) {continue;} + URL url = null; - if (method.getReturnType() == ExtendedImageUrl.class && method.getParameterTypes()[0] == Context.class) { - getExtendedImageUrl = method; + try { + for (Method method : media.getClass().getMethods()) { + if (method.getParameterTypes().length == 0) { + continue; + } + + if (method.getReturnType() == ExtendedImageUrl.class + && method.getParameterTypes()[0] == Context.class) { + getExtendedImageUrl = method; + } } + + assert getExtendedImageUrl != null; + String urlString = ((ExtendedImageUrl) Objects + .requireNonNull(getExtendedImageUrl.invoke(media, Utils.getContext()))) + .getUrl(); + + url = new URL(urlString); + } catch (Exception e) { + e.printStackTrace(); } - assert getExtendedImageUrl != null; - return ((ExtendedImageUrl) Objects.requireNonNull(getExtendedImageUrl.invoke(media, Utils.getContext()))).getUrl(); + return url; } public static void print(Object message) { - System.out.println("BRUH: "+message); + System.out.println("BRUH: " + message); } -} \ No newline at end of file +} From 7a1874b0c85464c5ead9bdd422cef8586c8e2b4d Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 13:34:07 +0800 Subject: [PATCH 07/13] implemented download media list (for photos only) --- .../patches/{ => download}/DownloadPatch.java | 52 +++++++++++++++---- .../patches/download/MediaHelpers.java | 7 +++ 2 files changed, 50 insertions(+), 9 deletions(-) rename app/src/main/java/app/revanced/integrations/instagram/patches/{ => download}/DownloadPatch.java (78%) create mode 100644 app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java similarity index 78% rename from app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java rename to app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java index a3da24225c..8c8f89e8e8 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java @@ -1,4 +1,4 @@ -package app.revanced.integrations.instagram.patches; +package app.revanced.integrations.instagram.patches.download; import android.app.Activity; import android.app.DownloadManager; @@ -11,6 +11,7 @@ import com.instagram.model.mediasize.ExtendedImageUrl; import java.io.*; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.HttpURLConnection; @@ -25,13 +26,17 @@ public class DownloadPatch { public static String feedOptionItemIconClassName() { return ""; } - public static String feedItemClassName() { return ""; } + public static String mediaListSourceClass() { + return ""; + } + public static String listFieldName() {return ""; } public static Class feedItemIconClass; public static Class feedItem; + public static Class mediaListSourceClass; static { try { @@ -45,6 +50,13 @@ public static String feedItemClassName() { } catch (ClassNotFoundException e) { throw new RuntimeException(e); } + + + try { + mediaListSourceClass = Class.forName(mediaListSourceClass()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } } public static void addDownloadButton(List items) throws IllegalAccessException, InstantiationException, @@ -58,18 +70,40 @@ public static void addDownloadButton(List items) throws IllegalAccessException, items.add(item); } - public static void downloadPost(Object media, int p2, Object p3, Activity p4) throws URISyntaxException { + public static void downloadPost(Object media, int p2, Object p3, Activity p4) throws URISyntaxException, NoSuchFieldException, IllegalAccessException { + // TODO fingerprint them fields + Field mediaField = null; + + // Get media field + for (Field field : media.getClass().getFields()) { + if (field.getType() == mediaListSourceClass.getInterfaces()[0]) { + mediaField = field; + } + } + + assert mediaField != null; + Object _mediaList = mediaField.get(media); + _mediaList = mediaListSourceClass.cast(_mediaList); + + List mediaList = (List) _mediaList.getClass().getField(listFieldName()).get(_mediaList); - // TODO implement download a post with only 1 media - startDownload(getPhotoLink(media)); + print(mediaList); + // TODO implement choose to download all, or only download the current media + if (mediaList != null) { + for (Object image: mediaList) { + startDownload(getPhotoLink(image)); + } + } else { + startDownload(getPhotoLink(media)); + } } public static void startDownload(URL link) throws URISyntaxException { String filename = link.getPath().split("/")[link.getPath().split("/").length - 1]; String publicFolderDirectory = Environment.DIRECTORY_PICTURES; - String folder = "Instagram"; + String subPath = "Instagram/" + filename; - File imageFile = new File(Environment.getExternalStoragePublicDirectory(publicFolderDirectory), filename); + File imageFile = new File(Environment.getExternalStoragePublicDirectory(publicFolderDirectory), subPath); if (imageFile.exists()) { return; @@ -84,7 +118,7 @@ public static void startDownload(URL link) throws URISyntaxException { .setTitle("Demo") .setDescription("Something useful. No, really.") .setDestinationInExternalPublicDir(publicFolderDirectory, - folder+"/"+filename)); + subPath)); } public static void downloadPhoto(URL link) { @@ -169,4 +203,4 @@ public static URL getPhotoLink(Object media) { public static void print(Object message) { System.out.println("BRUH: " + message); } -} +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java new file mode 100644 index 0000000000..106f3ac68f --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java @@ -0,0 +1,7 @@ +package app.revanced.integrations.instagram.patches.download; + +public class MediaHelpers { + public static boolean isVideo() { + return false; + } +} From 6c2edc5d0c91b92eb95446721f22eaeace65c395 Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 13:34:52 +0800 Subject: [PATCH 08/13] add todo --- .../integrations/instagram/patches/download/DownloadPatch.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java index 8c8f89e8e8..2c3bfe710c 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java @@ -72,6 +72,7 @@ public static void addDownloadButton(List items) throws IllegalAccessException, public static void downloadPost(Object media, int p2, Object p3, Activity p4) throws URISyntaxException, NoSuchFieldException, IllegalAccessException { // TODO fingerprint them fields + // TODO support video downloading Field mediaField = null; // Get media field From f4f20aebda8808723aef141bfeda398f774031c7 Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 14:19:12 +0800 Subject: [PATCH 09/13] fix incompatibility with official patches --- .../app/revanced/integrations/shared/Logger.java | 8 ++++++++ .../app/revanced/integrations/shared/Utils.java | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/src/main/java/app/revanced/integrations/shared/Logger.java b/app/src/main/java/app/revanced/integrations/shared/Logger.java index f8c5c87eee..766f2b5009 100644 --- a/app/src/main/java/app/revanced/integrations/shared/Logger.java +++ b/app/src/main/java/app/revanced/integrations/shared/Logger.java @@ -136,6 +136,14 @@ public static void printException(@NonNull LogMessage message, @Nullable Throwab } } + /** + * Logging to use if {@link BaseSettings#DEBUG} or {@link Utils#getContext()} may not be initialized. + * Normally this method should not be used. + */ + public static void initializationInfo(@NonNull Class callingClass, @NonNull String message) { + Log.i(REVANCED_LOG_PREFIX + callingClass.getSimpleName(), message); + } + /** * Logging to use if {@link BaseSettings#DEBUG} or {@link Utils#context} may not be initialized. * Always logs even if Debugging is not enabled. diff --git a/app/src/main/java/app/revanced/integrations/shared/Utils.java b/app/src/main/java/app/revanced/integrations/shared/Utils.java index cac761c7f5..beebb24f29 100644 --- a/app/src/main/java/app/revanced/integrations/shared/Utils.java +++ b/app/src/main/java/app/revanced/integrations/shared/Utils.java @@ -259,6 +259,20 @@ public static void setClipboard(@NonNull String text) { clipboard.setPrimaryClip(clip); } + public static void setContext(Context appContext) { + context = appContext; + // In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies. + // Calling the regular printDebug method here can cause a Settings context null pointer exception, + // even though the context is already set before the call. + // + // The initialization logger methods do not directly or indirectly + // reference the Context or any Settings and are unaffected by this problem. + // + // Info level also helps debug if a patch hook is called before + // the context is set since debug logging is off by default. + Logger.initializationInfo(Utils.class, "Set context: " + appContext); + } + public static boolean isTablet() { return context.getResources().getConfiguration().smallestScreenWidthDp >= 600; } From 30b1a87dafbb2d705626b4c2a4abd846d8f793e7 Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 19:06:34 +0800 Subject: [PATCH 10/13] add dialog chooser for posts with multiple media --- .../patches/download/DownloadPatch.java | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java index 2c3bfe710c..0c5b0df477 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java @@ -1,6 +1,7 @@ package app.revanced.integrations.instagram.patches.download; import android.app.Activity; +import android.app.AlertDialog; import android.app.DownloadManager; import android.content.Context; import android.net.Uri; @@ -26,13 +27,18 @@ public class DownloadPatch { public static String feedOptionItemIconClassName() { return ""; } + public static String feedItemClassName() { return ""; } + public static String mediaListSourceClass() { return ""; } - public static String listFieldName() {return ""; } + + public static String listFieldName() { + return ""; + } public static Class feedItemIconClass; public static Class feedItem; @@ -70,7 +76,7 @@ public static void addDownloadButton(List items) throws IllegalAccessException, items.add(item); } - public static void downloadPost(Object media, int p2, Object p3, Activity p4) throws URISyntaxException, NoSuchFieldException, IllegalAccessException { + public static void downloadPost(Object media, int index, Object p3, Activity act) throws URISyntaxException, NoSuchFieldException, IllegalAccessException { // TODO fingerprint them fields // TODO support video downloading Field mediaField = null; @@ -88,17 +94,36 @@ public static void downloadPost(Object media, int p2, Object p3, Activity p4) th List mediaList = (List) _mediaList.getClass().getField(listFieldName()).get(_mediaList); - print(mediaList); - // TODO implement choose to download all, or only download the current media + // TODO Support video downloads if (mediaList != null) { - for (Object image: mediaList) { - startDownload(getPhotoLink(image)); - } + downloadDialog(mediaList, index, act); } else { startDownload(getPhotoLink(media)); } } + public static void downloadDialog(List mediaList, int index, Activity activity) { + // TODO use instagram dialog + AlertDialog.Builder d = new AlertDialog.Builder(activity) + .setTitle("Download") + .setItems(new String[]{"Current", "Download all"}, (dialogInterface, i) -> { + Toast.makeText(activity, "Download started", Toast.LENGTH_SHORT).show(); + try { + if (i == 0) { + startDownload(getPhotoLink(mediaList.get(index))); + } else { + for (Object media: mediaList) { + startDownload(getPhotoLink(media)); + } + } + } catch (URISyntaxException e) { + e.printStackTrace(); + Toast.makeText(Utils.getContext(), "Download failed", Toast.LENGTH_SHORT).show(); + } + }); + d.show(); + } + public static void startDownload(URL link) throws URISyntaxException { String filename = link.getPath().split("/")[link.getPath().split("/").length - 1]; String publicFolderDirectory = Environment.DIRECTORY_PICTURES; @@ -106,9 +131,7 @@ public static void startDownload(URL link) throws URISyntaxException { File imageFile = new File(Environment.getExternalStoragePublicDirectory(publicFolderDirectory), subPath); - if (imageFile.exists()) { - return; - } + if (imageFile.exists()) {return;} DownloadManager mgr = (DownloadManager) Utils.getContext().getSystemService(Context.DOWNLOAD_SERVICE); @@ -120,6 +143,8 @@ public static void startDownload(URL link) throws URISyntaxException { .setDescription("Something useful. No, really.") .setDestinationInExternalPublicDir(publicFolderDirectory, subPath)); + + Toast.makeText(Utils.getContext(), "Download completed", Toast.LENGTH_SHORT).show(); } public static void downloadPhoto(URL link) { @@ -153,12 +178,12 @@ public static byte[] readAllBytes(InputStream inputStream) throws IOException { IOException exception = null; try { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) - outputStream.write(buf, 0, readLen); - - return outputStream.toByteArray(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) { + outputStream.write(buf, 0, readLen); } + + return outputStream.toByteArray(); } catch (IOException e) { exception = e; throw e; From 14165db0be01f22aa8000c868a8d8a631581af4a Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 23:23:45 +0800 Subject: [PATCH 11/13] added video downloads --- .../patches/download/DownloadPatch.java | 77 +++++++++++-------- .../patches/download/MediaHelpers.java | 7 -- 2 files changed, 46 insertions(+), 38 deletions(-) delete mode 100644 app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java index 0c5b0df477..4063b31cc7 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java @@ -16,6 +16,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.List; @@ -76,7 +77,7 @@ public static void addDownloadButton(List items) throws IllegalAccessException, items.add(item); } - public static void downloadPost(Object media, int index, Object p3, Activity act) throws URISyntaxException, NoSuchFieldException, IllegalAccessException { + public static void downloadPost(Object media, int index, Object p3, Activity act) throws URISyntaxException, NoSuchFieldException, IllegalAccessException, MalformedURLException, InvocationTargetException { // TODO fingerprint them fields // TODO support video downloading Field mediaField = null; @@ -98,7 +99,7 @@ public static void downloadPost(Object media, int index, Object p3, Activity act if (mediaList != null) { downloadDialog(mediaList, index, act); } else { - startDownload(getPhotoLink(media)); + downloadMedia(media); } } @@ -107,16 +108,16 @@ public static void downloadDialog(List mediaList, int index, Activity activity) AlertDialog.Builder d = new AlertDialog.Builder(activity) .setTitle("Download") .setItems(new String[]{"Current", "Download all"}, (dialogInterface, i) -> { - Toast.makeText(activity, "Download started", Toast.LENGTH_SHORT).show(); try { if (i == 0) { - startDownload(getPhotoLink(mediaList.get(index))); + downloadMedia(mediaList.get(index)); } else { - for (Object media: mediaList) { - startDownload(getPhotoLink(media)); + for (Object media : mediaList) { + downloadMedia(media); } } - } catch (URISyntaxException e) { + } catch (URISyntaxException | MalformedURLException | InvocationTargetException | + IllegalAccessException e) { e.printStackTrace(); Toast.makeText(Utils.getContext(), "Download failed", Toast.LENGTH_SHORT).show(); } @@ -124,14 +125,27 @@ public static void downloadDialog(List mediaList, int index, Activity activity) d.show(); } - public static void startDownload(URL link) throws URISyntaxException { + public static void downloadMedia(Object media) throws URISyntaxException, MalformedURLException, InvocationTargetException, IllegalAccessException { + if (isVideo(media)) { + startDownload(getVideoLink(media)); + } else { + startDownload(getPhotoLink(media)); + } + } + + // TODO add a new destination parameter so we can use the same download function for video and photo + public static void startDownload(String url) throws URISyntaxException, MalformedURLException { + URL link = new URL(url); + String filename = link.getPath().split("/")[link.getPath().split("/").length - 1]; String publicFolderDirectory = Environment.DIRECTORY_PICTURES; String subPath = "Instagram/" + filename; File imageFile = new File(Environment.getExternalStoragePublicDirectory(publicFolderDirectory), subPath); - if (imageFile.exists()) {return;} + if (imageFile.exists()) { + return; + } DownloadManager mgr = (DownloadManager) Utils.getContext().getSystemService(Context.DOWNLOAD_SERVICE); @@ -139,8 +153,9 @@ public static void startDownload(URL link) throws URISyntaxException { .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE) .setAllowedOverRoaming(false) - .setTitle("Demo") - .setDescription("Something useful. No, really.") + .setTitle(filename) + .setDescription("Downloading...") + .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) .setDestinationInExternalPublicDir(publicFolderDirectory, subPath)); @@ -197,33 +212,33 @@ public static byte[] readAllBytes(InputStream inputStream) throws IOException { } } - public static URL getPhotoLink(Object media) { + private static String getVideoLink(Object media) { + return ""; + } + + public static String getPhotoLink(Object media) throws InvocationTargetException, IllegalAccessException { Method getExtendedImageUrl = null; URL url = null; - try { - for (Method method : media.getClass().getMethods()) { - if (method.getParameterTypes().length == 0) { - continue; - } - - if (method.getReturnType() == ExtendedImageUrl.class - && method.getParameterTypes()[0] == Context.class) { - getExtendedImageUrl = method; - } + for (Method method : media.getClass().getMethods()) { + if (method.getParameterTypes().length == 0) { + continue; } - assert getExtendedImageUrl != null; - String urlString = ((ExtendedImageUrl) Objects - .requireNonNull(getExtendedImageUrl.invoke(media, Utils.getContext()))) - .getUrl(); - - url = new URL(urlString); - } catch (Exception e) { - e.printStackTrace(); + if (method.getReturnType() == ExtendedImageUrl.class + && method.getParameterTypes()[0] == Context.class) { + getExtendedImageUrl = method; + } } - return url; + assert getExtendedImageUrl != null; + return ((ExtendedImageUrl) Objects + .requireNonNull(getExtendedImageUrl.invoke(media, Utils.getContext()))) + .getUrl(); + } + + public static boolean isVideo(Object media) { + return true; } public static void print(Object message) { diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java deleted file mode 100644 index 106f3ac68f..0000000000 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/download/MediaHelpers.java +++ /dev/null @@ -1,7 +0,0 @@ -package app.revanced.integrations.instagram.patches.download; - -public class MediaHelpers { - public static boolean isVideo() { - return false; - } -} From 6e6f1fa7751bc4cdd9032da14344f1f12766d180 Mon Sep 17 00:00:00 2001 From: crimera Date: Sat, 18 May 2024 23:25:16 +0800 Subject: [PATCH 12/13] remove todo --- .../integrations/instagram/patches/download/DownloadPatch.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java index 4063b31cc7..b6af938912 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java @@ -95,7 +95,6 @@ public static void downloadPost(Object media, int index, Object p3, Activity act List mediaList = (List) _mediaList.getClass().getField(listFieldName()).get(_mediaList); - // TODO Support video downloads if (mediaList != null) { downloadDialog(mediaList, index, act); } else { From 10037962a1f823b0215b407472f376c87dfde924 Mon Sep 17 00:00:00 2001 From: crimera Date: Tue, 21 May 2024 16:27:19 +0800 Subject: [PATCH 13/13] separate picture, and video downloads --- .../patches/download/DownloadPatch.java | 61 ++----------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java index b6af938912..6bf84ebb61 100644 --- a/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java +++ b/app/src/main/java/app/revanced/integrations/instagram/patches/download/DownloadPatch.java @@ -78,8 +78,6 @@ public static void addDownloadButton(List items) throws IllegalAccessException, } public static void downloadPost(Object media, int index, Object p3, Activity act) throws URISyntaxException, NoSuchFieldException, IllegalAccessException, MalformedURLException, InvocationTargetException { - // TODO fingerprint them fields - // TODO support video downloading Field mediaField = null; // Get media field @@ -126,19 +124,18 @@ public static void downloadDialog(List mediaList, int index, Activity activity) public static void downloadMedia(Object media) throws URISyntaxException, MalformedURLException, InvocationTargetException, IllegalAccessException { if (isVideo(media)) { - startDownload(getVideoLink(media)); + startDownload(getVideoLink(media), Environment.DIRECTORY_MOVIES, "Instagram"); } else { - startDownload(getPhotoLink(media)); + startDownload(getPhotoLink(media), Environment.DIRECTORY_PICTURES, "Instagram"); } } - // TODO add a new destination parameter so we can use the same download function for video and photo - public static void startDownload(String url) throws URISyntaxException, MalformedURLException { + public static void startDownload(String url, String publicFolder, String subpath) throws URISyntaxException, MalformedURLException { URL link = new URL(url); String filename = link.getPath().split("/")[link.getPath().split("/").length - 1]; String publicFolderDirectory = Environment.DIRECTORY_PICTURES; - String subPath = "Instagram/" + filename; + String subPath =subpath + "/" + filename; File imageFile = new File(Environment.getExternalStoragePublicDirectory(publicFolderDirectory), subPath); @@ -161,56 +158,6 @@ public static void startDownload(String url) throws URISyntaxException, Malforme Toast.makeText(Utils.getContext(), "Download completed", Toast.LENGTH_SHORT).show(); } - public static void downloadPhoto(URL link) { - HttpURLConnection connection; - try { - connection = (HttpURLConnection) link.openConnection(); - connection.connect(); - - InputStream inputStream = connection.getInputStream(); - - File imageFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "test.png"); - if (!imageFile.createNewFile()) { - Toast.makeText(Utils.getContext(), "Failed to create file", Toast.LENGTH_SHORT).show(); - } - - FileOutputStream fos = new FileOutputStream(imageFile); - fos.write(readAllBytes(inputStream)); - - fos.close(); - - Toast.makeText(Utils.getContext(), "File downloaded", Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - Toast.makeText(Utils.getContext(), "Error downloading", Toast.LENGTH_SHORT).show(); - } - } - - public static byte[] readAllBytes(InputStream inputStream) throws IOException { - final int bufLen = 4 * 0x400; // 4KB - byte[] buf = new byte[bufLen]; - int readLen; - IOException exception = null; - - try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) { - outputStream.write(buf, 0, readLen); - } - - return outputStream.toByteArray(); - } catch (IOException e) { - exception = e; - throw e; - } finally { - if (exception == null) inputStream.close(); - else try { - inputStream.close(); - } catch (IOException e) { - exception.addSuppressed(e); - } - } - } - private static String getVideoLink(Object media) { return ""; }