diff --git a/README.md b/README.md index b1f513c..7fe3da7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,54 @@ # SnapTools -Snaptools for Snapchat 10.52.0.0 Beta +A fork of [Jaqxues'](https://github.com/jaqxues/) SnapTools repository. +This repository contains the source for 10.52.0.0 Beta and 10.48.5.0 Beta. +You can download the APK [here](https://github.com/azenyx/SnapTools_DataProvider/blob/master/Packs/Files/STModulePack_1.0.0.5_Premium_10.52.0.0_Beta.jar) --If for some reason you would like to donate direct your donations to Jaqxues as it wouldnt be possible without him --https://www.paypal.me/jaqxues +Check out the [SnapTools Website](https://snaptools.io) for a brief rundown of the project! + +I will be going through the project and adding additional javadocs as the project deserves a more professional format. If there is any specific code or file that you wish for me to clear up, please feel free to [Open An Issue](https://github.com/Andrerm124/SnapTools_OpenSource/issues/new) thread and I'll do my best to provide information on the topic, however be sure to check out the Points Of Interest section below for the more important systems in the app. + +It should be noted that this source is not intended to be built directly into a directly functional APK, it will require adjustments as certain required files have been redacted due to containing private information (Such as [google_services.json](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/app/google-services.json), [RedactedClasses](https://github.com/Andrerm124/SnapTools_OpenSource/tree/master/app/src/main/java/com/ljmu/andre/snaptools/RedactedClasses), and the certificates located in the [Debug](https://github.com/Andrerm124/SnapTools_OpenSource/tree/master/app/src/debug/assets) and [Release](https://github.com/Andrerm124/SnapTools_OpenSource/tree/master/app/src/release/assets) Assets folders) + +# SnapTools Media +[![Shtuff](https://img.youtube.com/vi/mIkM8KTjoWs/0.jpg)](https://www.youtube.com/watch?v=mIkM8KTjoWs) + +A user created promotional video by [John Luke](https://www.youtube.com/channel/UCVQavYHPmuzDu5eELNC3oWg) + +# Points of Interest +### Framework/ModulePack System +[HookManager](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/app/src/main/java/com/ljmu/andre/snaptools/HookManager.java) + +The entry point for Xposed based code. +*Todo: Add more comments.* + +[FrameworkManager](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/app/src/main/java/com/ljmu/andre/snaptools/Framework/FrameworkManager.java) + +Responsible for managing the loading and injecting of ModulePacks. + +[ModulePack](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/app/src/main/java/com/ljmu/andre/snaptools/Framework/ModulePack.java) + +The interface between the framework and the dynamically loaded ModulePackImpl code. + +[ModulePackImpl](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulePackImpl.java) + +An implementation of ModulePack with the loading and injection systems in place to manage the internal hooking code. + +[HookResolver](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookResolver.java) + +Responsible for efficiently generating and caching references to Hooks and HookClasses. + +### General +[MainActivity](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/app/src/main/java/com/ljmu/andre/snaptools/MainActivity.java) + +The majority of the application initialisation and EventBus subscriptions. + +### Custom IntelliJ Plugins + +The following are some support plugins that I created with the intent of automating or making workflow easier. If people are interested I will make separate repositories for these plugins. + +- [StringEncryptorPlugin](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/StringEncryptorPlugin.jar) - A plugin that provides an **Encrypt Strings** function in the right click menu (And can also be bound to a key). This function will automatically replace string literals with the **decryptString(...)** function (Or reverse an already encrypted string), however the encryption key has been hardcoded into the plugin as it was only intended for personal use. If this library is sought after, let me know and I'll fix it up! +- [ApkVersionPlugin](https://github.com/Andrerm124/SnapTools_OpenSource/blob/master/ApkVersionPlugin.jar) - A plugin that scans for **@FrameworkVersion(versionInt)** annotations on methods and displays an error to the developer when there are no conditional checks for when the build version of the app is higher than the build version of the method being called. Again, this has hardcoded values as I never anticipated making the plugin public source. If this is something that interests you, let me know and I'll do what I can! +- [MappingMerger](https://github.com/Andrerm124/SnapTools_OpenSource/tree/master/app/mapping_merger) - As proguard doesn't offer the ability to maintain static and incremental obfuscation, I had to create my own scripts to automatically merge the previously generated proguard file with the **pre-proguard mappings** file (The file that tells proguard what to name each function) and have the ability to re-include any lost mappings from previous builds as proguard strips out unused mappings that we may need for alternate builds. An implementation (Although crude) can be seen [HERE](https://github.com/Andrerm124/SnapTools_OpenSource/blob/cfa45c5adec117f2b57977688450e909f29211ca/app/build.gradle#L187) +### Parting Words + +Finally I would like to take a short moment to say that I have enjoyed every moment of this project thoroughly, even though at times it can be extremely stressful. The community, the friends I've made, and the things I've learnt from this venture has been truly exceptional and hopefully it has been as enjoyable for those that got to experience it as it was for me - Jaqxues diff --git a/STModulePack_1.0.0.3_Premium_10.52.0.0_Beta_unsigned.jar b/STModulePack_1.0.0.3_Premium_10.52.0.0_Beta_unsigned.jar deleted file mode 100644 index cc44fcf..0000000 Binary files a/STModulePack_1.0.0.3_Premium_10.52.0.0_Beta_unsigned.jar and /dev/null differ diff --git a/STModulePack_1.1.0.2_Premium_10.82.5.0_unsigned.jar b/STModulePack_1.1.0.2_Premium_10.82.5.0_unsigned.jar deleted file mode 100644 index 2359344..0000000 Binary files a/STModulePack_1.1.0.2_Premium_10.82.5.0_unsigned.jar and /dev/null differ diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ChatSaving.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ChatSaving.java index ce211da..34846b0 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ChatSaving.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ChatSaving.java @@ -1,380 +1,379 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.app.Activity; -import android.content.Context; - - -import com.ljmu.andre.CBIDatabase.CBITable; -import com.ljmu.andre.GsonPreferences.Preferences; -import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.ModulePack.Databases.ChatDatabase; -import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.ChatObject; -import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.ConversationObject; -import com.ljmu.andre.snaptools.ModulePack.Fragments.ChatManagerFragment; -import com.ljmu.andre.snaptools.ModulePack.Utils.FieldMapper; -import com.ljmu.andre.snaptools.Utils.ContextHelper; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import de.robv.android.xposed.XposedHelpers; -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_BODY_METADATA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_HEADER_METADATA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_METADATA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_ISSAVED_INAPP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_MESSAGE_VIEW_MEASURE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_READ; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_READ_SECOND; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_WRITE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_WRITE_SECOND; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_NOTIFICATION; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_SAVE_INAPP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_USERNAME; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_SAVING_LINKER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_SAVING_LINKER_CHAT_REF; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.NOTIFICATION_TYPE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.*; - - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class ChatSaving extends ModuleHelper { - private static final int MAX_CHAT_MESSAGE_ENTRIES = 500; - private CBITable chatTable; - private CBITable conversationTable; - private String yourUsername; - public String typing; - public ChatSaving(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - @Override - public FragmentHelper[] getUIFragments() { - return new ChatManagerFragment[]{new ChatManagerFragment()}; - } - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - /*findAndHookMethod( - "ify", snapClassLoader, - "a", findClass("com.snapchat.android.core.structure.fragment.SnapchatFragment", snapClassLoader), - new ST_MethodHook() { - @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - Object snapchatFragment = param.args[0]; - Timber.d("SnapchatFragment: " + snapchatFragment.getClass() + " | " + snapchatFragment); - } - } - ); - - findAndHookMethod( - "com.snapchat.android.app.feature.messaging.chat.fragment.ChatV3Fragment", snapClassLoader, - "onCreateView", LayoutInflater.class, ViewGroup.class, Bundle.class, - new ST_MethodHook() { - @Override protected void after(MethodHookParam param) throws Throwable { - View buttonContainer = (View) param.getResult(); - - - TableLayout menuTableLayout = getView(buttonContainer, "chat_menu_table"); - } - } - );*/ - - if (getPref(BLOCK_TYPING_NOTIFICATIONS)) { - hookMethod( - CHAT_NOTIFICATION, - new ST_MethodHook() { - @Override - protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - Enum notificationType = getObjectField(NOTIFICATION_TYPE, param.args[0]); - String name = notificationType.name(); - - Timber.d("Notification inbound: " + notificationType); - - if (name.contains("TYPING")) - param.setResult(null); - } - } - ); - } - - if (getPref(CHANGE_TYPING_NOTIFICATIONS)) { - Map notificationTexts = getPref(CUSTOM_NOTIFICATION_TEXTS); - hookMethod( - CHAT_NOTIFICATION, - new ST_MethodHook() { - @Override - protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - Enum notificationType = getObjectField(NOTIFICATION_TYPE, param.args[0]); - // names: - // SNAP, CHAT, TYPING, CHAT_SCREENSHOT, STATUS_BAR, ADD, - // SCREENSHOT, REPLAY, SAVE_CAMERA_ROLL, ADDFRIEND, INITIATE_AUDIO, - // INITIATE_VIDEO, ABANDON_AUDIO, ABANDON_VIDEO - String name = notificationType.name(); - - Timber.d("Notification inbound: %s", notificationType); - - String nickname = (String) XposedHelpers.getObjectField(param.args[0], "o"); - // Nickname of SENDER (First & Last name or name user has set) - String username = (String) XposedHelpers.getObjectField(param.args[0], "p"); - // Username of SENDER (The name would use to add) - String recipient = (String) XposedHelpers.getObjectField(param.args[0], "q"); - // Username of the account the message is being sent to - String source = (String) XposedHelpers.getObjectField(param.args[0], "d"); - // Not too sure what source is, returns null - - String text = notificationTexts.get(name); - if (text == null) return; - XposedHelpers.setObjectField(param.args[0],"r", syntaxReplacer(text, nickname, username, recipient, name)); - } - } - ); - } - - if (getPref(STORE_CHAT_MESSAGES)) { - try { - ChatDatabase.init(snapContext); - - chatTable = ChatDatabase.getTable(ChatObject.class); - conversationTable = ChatDatabase.getTable(ConversationObject.class); - - // =========================================================================== - - Class chatClass = HookResolver.resolveHookClass(CHAT_METADATA); - Class chatSuperClass = chatClass.getSuperclass(); - Class headerClass = HookResolver.resolveHookClass(CHAT_HEADER_METADATA); - Class bodyClass = HookResolver.resolveHookClass(CHAT_BODY_METADATA); - - // =========================================================================== - - FieldMapper.initMapper("Chat", - chatClass); - FieldMapper.initMapper("ChatSuper", - chatSuperClass); - FieldMapper.initMapper("Header", - headerClass); - FieldMapper.initMapper("Body", - bodyClass); - - // =========================================================================== - - //hookAllMethods("oet", snapClassLoader, false, false); - hookMethod( - CHAT_METADATA_READ, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - try { - Object chat = param.getResult(); - handleChatLogging(chat); - } catch (Throwable t) { - Timber.e(t); - } - } - } - ); - - hookMethod( - CHAT_METADATA_WRITE, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - try { - Object chat = param.args[1]; - handleChatLogging(chat); - } catch (Throwable t) { - Timber.e(t); - } - } - }); - - hookMethod( - CHAT_METADATA_READ_SECOND, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - try { - Object chat = param.getResult(); - handleChatLogging(chat); - } catch (Throwable t) { - Timber.e(t); - } - } - } - ); - - hookMethod( - CHAT_METADATA_WRITE_SECOND, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - try { - Object chat = param.args[1]; - handleChatLogging(chat); - } catch (Throwable t) { - Timber.e(t); - } - } - }); - } catch (HookNotFoundException e) { - Timber.e(e); - moduleLoadState.fail(); - } catch (Throwable t) { - Timber.e(t, "Unknown error"); - } - } - - Set intArray = Collections.newSetFromMap(new LinkedHashMap() { - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > MAX_CHAT_MESSAGE_ENTRIES; - } - }); - - if (getPref(SAVE_CHAT_IN_SC)) { - hookMethod( - CHAT_MESSAGE_VIEW_MEASURE, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - ContextHelper.getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - Object chatLinker = getObjectField(CHAT_SAVING_LINKER, param.thisObject); - - if (chatLinker == null) { - Timber.w("Null Chat Linker"); - return; - } - - Object chat = getObjectField(CHAT_SAVING_LINKER_CHAT_REF, chatLinker); - - if (chat == null) { - Timber.w("Null Chat Object"); - return; - } - - Boolean isSaved = callHook(CHAT_ISSAVED_INAPP, chat); - int hashCode = chat.hashCode(); - - if (!isSaved) { - synchronized (intArray) { - if (intArray.contains(hashCode)) { - return; - } - } - - callHook(CHAT_SAVE_INAPP, param.thisObject); - } else { - synchronized (intArray) { - intArray.add(hashCode); - } - } - - } catch (Throwable t) { - Timber.w("Chat Saving Error: " + t); - - } - } - }); - } - }); - } - } - - - - private static String syntaxReplacer(String text, String nickname, String username, String receiver, String type){ - type = type.substring(0,1).toUpperCase() + type.substring(1).toLowerCase(); - return text.replace("{username}", username) - .replace("{nickname}", nickname) - .replace("{receiver}", receiver) - .replace("{type}", type) - .replace("\\n", "\n"); - } - - private void handleChatLogging(Object chat) { - FieldMapper chatMapper = FieldMapper.getMapper("Chat"); - FieldMapper chatSuperMapper = FieldMapper.getMapper("ChatSuper"); - FieldMapper headerMapper = FieldMapper.getMapper("Header"); - FieldMapper bodyMapper = FieldMapper.getMapper("Body"); - - String messageId = chatMapper.getFieldVal(chat, "chat_message_id"); - - if (chatTable.contains(messageId)) - return; - - ChatObject newChatObject = new ChatObject(); - - // Set Body Text ============================================================= - Object body = chatMapper.getFieldVal(chat, "body"); - newChatObject.text = bodyMapper.getFieldVal(body, "text"); - - String type = bodyMapper.getFieldVal(body, "type"); - - if (!type.equals("text")) { - if (newChatObject.text == null) - newChatObject.text = "<" + type + ">"; - else - newChatObject.text = "<" + type + ">\n" + newChatObject.text; - } - - if (newChatObject.text == null) { - Timber.w("Null text for chat... Probably not a message"); - return; - } - - // Set Message ID ============================================================ - newChatObject.chat_message_id = messageId; - // Set Timestamp =========================================== - newChatObject.timestamp = chatSuperMapper.getFieldVal(chat, "timestamp"); - // Set Sender/Receiver ======================================================= - Object header = chatSuperMapper.getFieldVal(chat, "header"); - newChatObject.from = headerMapper.getFieldVal(header, "from"); - newChatObject.to = headerMapper.getFieldVal(header, "to"); - newChatObject.conv_id = headerMapper.getFieldVal(header, "conv_id"); - newChatObject.sentByYou = yourUsername.equals(newChatObject.from); - // =========================================================================== - - if (!newChatObject.isCompleted()) { - Timber.w("Chat Object not completed!"); - return; - } - - if (chatTable.insert(newChatObject)) { - ConversationObject conversation = conversationTable.getFirst("conversation_id", newChatObject.conv_id); - - if (conversation == null) - conversation = new ConversationObject(); - - conversation.setUsers(newChatObject.from, newChatObject.to); - conversation.timestamp = newChatObject.timestamp; - conversation.conv_id = newChatObject.conv_id; - conversation.yourUsername = yourUsername; - conversationTable.insert(conversation); - } else - Timber.w("Chat object not inserted"); - - Timber.d("Created new chat object: " + newChatObject.toString()); - } - - @Override - public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { - // Giving time Snapchat time to initialize - try { - yourUsername = callStaticHook(GET_USERNAME); - } catch (Exception e) { - Timber.e(e, "Unable to get UserName"); - moduleLoadState.fail(); - } - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.app.Activity; +import android.content.Context; + + +import com.ljmu.andre.CBIDatabase.CBITable; +import com.ljmu.andre.GsonPreferences.Preferences; +import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.ModulePack.Databases.ChatDatabase; +import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.ChatObject; +import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.ConversationObject; +import com.ljmu.andre.snaptools.ModulePack.Fragments.ChatManagerFragment; +import com.ljmu.andre.snaptools.ModulePack.Utils.FieldMapper; +import com.ljmu.andre.snaptools.Utils.ContextHelper; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import de.robv.android.xposed.XposedHelpers; +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_BODY_METADATA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_HEADER_METADATA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_METADATA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_ISSAVED_INAPP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_MESSAGE_VIEW_MEASURE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_READ; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_READ_SECOND; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_WRITE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_METADATA_WRITE_SECOND; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_NOTIFICATION; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_SAVE_INAPP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_USERNAME; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_SAVING_LINKER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_SAVING_LINKER_CHAT_REF; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.NOTIFICATION_TYPE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.*; + + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class ChatSaving extends ModuleHelper { + private static final int MAX_CHAT_MESSAGE_ENTRIES = 500; + private CBITable chatTable; + private CBITable conversationTable; + private String yourUsername; + public String typing; + public ChatSaving(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + @Override + public FragmentHelper[] getUIFragments() { + return new ChatManagerFragment[]{new ChatManagerFragment()}; + } + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + /*findAndHookMethod( + "ify", snapClassLoader, + "a", findClass("com.snapchat.android.core.structure.fragment.SnapchatFragment", snapClassLoader), + new ST_MethodHook() { + @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + Object snapchatFragment = param.args[0]; + Timber.d("SnapchatFragment: " + snapchatFragment.getClass() + " | " + snapchatFragment); + } + } + ); + + findAndHookMethod( + "com.snapchat.android.app.feature.messaging.chat.fragment.ChatV3Fragment", snapClassLoader, + "onCreateView", LayoutInflater.class, ViewGroup.class, Bundle.class, + new ST_MethodHook() { + @Override protected void after(MethodHookParam param) throws Throwable { + View buttonContainer = (View) param.getResult(); + + + TableLayout menuTableLayout = getView(buttonContainer, "chat_menu_table"); + } + } + );*/ + + if (getPref(BLOCK_TYPING_NOTIFICATIONS)) { + hookMethod( + CHAT_NOTIFICATION, + new ST_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + Enum notificationType = getObjectField(NOTIFICATION_TYPE, param.args[0]); + String name = notificationType.name(); + + Timber.d("Notification inbound: " + notificationType); + + if (name.contains("TYPING")) + param.setResult(null); + } + } + ); + } + + if (getPref(CHANGE_TYPING_NOTIFICATIONS)) { + Map notificationTexts = getPref(CUSTOM_NOTIFICATION_TEXTS); + hookMethod( + CHAT_NOTIFICATION, + new ST_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + Enum notificationType = getObjectField(NOTIFICATION_TYPE, param.args[0]); + // names: + // SNAP, CHAT, TYPING, CHAT_SCREENSHOT, STATUS_BAR, ADD, + // SCREENSHOT, REPLAY, SAVE_CAMERA_ROLL, ADDFRIEND, INITIATE_AUDIO, + // INITIATE_VIDEO, ABANDON_AUDIO, ABANDON_VIDEO + String name = notificationType.name(); + + Timber.d("Notification inbound: %s", notificationType); + + String nickname = (String) XposedHelpers.getObjectField(param.args[0], "o"); + // Nickname of SENDER (First & Last name or name user has set) + String username = (String) XposedHelpers.getObjectField(param.args[0], "p"); + // Username of SENDER (The name would use to add) + String recipient = (String) XposedHelpers.getObjectField(param.args[0], "q"); + // Username of the account the message is being sent to + String source = (String) XposedHelpers.getObjectField(param.args[0], "d"); + // Not too sure what source is, returns null + + String text = notificationTexts.get(name); + if (text == null) return; + XposedHelpers.setObjectField(param.args[0],"r", syntaxReplacer(text, nickname, username, recipient, name)); + } + } + ); + } + + if (getPref(STORE_CHAT_MESSAGES)) { + try { + ChatDatabase.init(snapContext); + + chatTable = ChatDatabase.getTable(ChatObject.class); + conversationTable = ChatDatabase.getTable(ConversationObject.class); + + // =========================================================================== + + Class chatClass = HookResolver.resolveHookClass(CHAT_METADATA); + Class chatSuperClass = chatClass.getSuperclass(); + Class headerClass = HookResolver.resolveHookClass(CHAT_HEADER_METADATA); + Class bodyClass = HookResolver.resolveHookClass(CHAT_BODY_METADATA); + + // =========================================================================== + + FieldMapper.initMapper("Chat", + chatClass); + FieldMapper.initMapper("ChatSuper", + chatSuperClass); + FieldMapper.initMapper("Header", + headerClass); + FieldMapper.initMapper("Body", + bodyClass); + + // =========================================================================== + + //hookAllMethods("oet", snapClassLoader, false, false); + hookMethod( + CHAT_METADATA_READ, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + try { + Object chat = param.getResult(); + handleChatLogging(chat); + } catch (Throwable t) { + Timber.e(t); + } + } + } + ); + + hookMethod( + CHAT_METADATA_WRITE, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + try { + Object chat = param.args[1]; + handleChatLogging(chat); + } catch (Throwable t) { + Timber.e(t); + } + } + }); + + hookMethod( + CHAT_METADATA_READ_SECOND, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + try { + Object chat = param.getResult(); + handleChatLogging(chat); + } catch (Throwable t) { + Timber.e(t); + } + } + } + ); + + hookMethod( + CHAT_METADATA_WRITE_SECOND, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + try { + Object chat = param.args[1]; + handleChatLogging(chat); + } catch (Throwable t) { + Timber.e(t); + } + } + }); + } catch (HookNotFoundException e) { + Timber.e(e); + moduleLoadState.fail(); + } catch (Throwable t) { + Timber.e(t, "Unknown error"); + } + } + + Set intArray = Collections.newSetFromMap(new LinkedHashMap() { + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_CHAT_MESSAGE_ENTRIES; + } + }); + + if (getPref(SAVE_CHAT_IN_SC)) { + hookMethod( + CHAT_MESSAGE_VIEW_MEASURE, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + ContextHelper.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + try { + Object chatLinker = getObjectField(CHAT_SAVING_LINKER, param.thisObject); + + if (chatLinker == null) { + Timber.w("Null Chat Linker"); + return; + } + + Object chat = getObjectField(CHAT_SAVING_LINKER_CHAT_REF, chatLinker); + + if (chat == null) { + Timber.w("Null Chat Object"); + return; + } + + Boolean isSaved = callHook(CHAT_ISSAVED_INAPP, chat); + int hashCode = chat.hashCode(); + + if (!isSaved) { + synchronized (intArray) { + if (intArray.contains(hashCode)) { + return; + } + } + + callHook(CHAT_SAVE_INAPP, param.thisObject); + } else { + synchronized (intArray) { + intArray.add(hashCode); + } + } + + } catch (Throwable t) { + Timber.w(t); + } + } + }); + } + }); + } + } + + + + private static String syntaxReplacer(String text, String nickname, String username, String receiver, String type){ + type = type.substring(0,1).toUpperCase() + type.substring(1).toLowerCase(); + return text.replace("{username}", username) + .replace("{nickname}", nickname) + .replace("{receiver}", receiver) + .replace("{type}", type) + .replace("\\n", "\n"); + } + + private void handleChatLogging(Object chat) { + FieldMapper chatMapper = FieldMapper.getMapper("Chat"); + FieldMapper chatSuperMapper = FieldMapper.getMapper("ChatSuper"); + FieldMapper headerMapper = FieldMapper.getMapper("Header"); + FieldMapper bodyMapper = FieldMapper.getMapper("Body"); + + String messageId = chatMapper.getFieldVal(chat, "chat_message_id"); + + if (chatTable.contains(messageId)) + return; + + ChatObject newChatObject = new ChatObject(); + + // Set Body Text ============================================================= + Object body = chatMapper.getFieldVal(chat, "body"); + newChatObject.text = bodyMapper.getFieldVal(body, "text"); + + String type = bodyMapper.getFieldVal(body, "type"); + + if (!type.equals("text")) { + if (newChatObject.text == null) + newChatObject.text = "<" + type + ">"; + else + newChatObject.text = "<" + type + ">\n" + newChatObject.text; + } + + if (newChatObject.text == null) { + Timber.w("Null text for chat... Probably not a message"); + return; + } + + // Set Message ID ============================================================ + newChatObject.chat_message_id = messageId; + // Set Timestamp =========================================== + newChatObject.timestamp = chatSuperMapper.getFieldVal(chat, "timestamp"); + // Set Sender/Receiver ======================================================= + Object header = chatSuperMapper.getFieldVal(chat, "header"); + newChatObject.from = headerMapper.getFieldVal(header, "from"); + newChatObject.to = headerMapper.getFieldVal(header, "to"); + newChatObject.conv_id = headerMapper.getFieldVal(header, "conv_id"); + newChatObject.sentByYou = yourUsername.equals(newChatObject.from); + // =========================================================================== + + if (!newChatObject.isCompleted()) { + Timber.w("Chat Object not completed!"); + return; + } + + if (chatTable.insert(newChatObject)) { + ConversationObject conversation = conversationTable.getFirst("conversation_id", newChatObject.conv_id); + + if (conversation == null) + conversation = new ConversationObject(); + + conversation.setUsers(newChatObject.from, newChatObject.to); + conversation.timestamp = newChatObject.timestamp; + conversation.conv_id = newChatObject.conv_id; + conversation.yourUsername = yourUsername; + conversationTable.insert(conversation); + } else + Timber.w("Chat object not inserted"); + + Timber.d("Created new chat object: " + newChatObject.toString()); + } + + @Override + public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { + // Giving time Snapchat time to initialize + try { + yourUsername = callStaticHook(GET_USERNAME); + } catch (Exception e) { + Timber.e(e, "Unable to get UserName"); + moduleLoadState.fail(); + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/CustomFilters.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/CustomFilters.java index f56e3e7..78679e5 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/CustomFilters.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/CustomFilters.java @@ -1,789 +1,789 @@ -//package com.ljmu.andre.snaptools.ModulePack; -// -//import android.annotation.SuppressLint; -//import android.app.Activity; -//import android.content.BroadcastReceiver; -//import android.content.Context; -//import android.content.Intent; -//import android.content.IntentFilter; -//import android.graphics.Bitmap; -//import android.graphics.BitmapFactory; -//import android.graphics.BitmapFactory.Options; -//import android.graphics.Color; -//import android.graphics.Rect; -//import android.graphics.drawable.BitmapDrawable; -//import android.graphics.drawable.Drawable; -//import android.os.Bundle; -//import android.os.Handler; -//import android.os.Looper; -//import android.os.Message; -//import android.view.Display; -//import android.view.KeyEvent; -//import android.view.MotionEvent; -//import android.view.View; -//import android.view.ViewGroup; -//import android.widget.FrameLayout.LayoutParams; -//import android.widget.ImageButton; -//import android.widget.ImageView; -//import android.widget.LinearLayout; -//import android.widget.RelativeLayout; -//import android.widget.TextView; -// -//import com.google.common.base.Optional; -//import com.ljmu.andre.CBIDatabase.CBITable; -//import com.ljmu.andre.CBIDatabase.Utils.QueryBuilder; -//import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; -//import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -//import com.ljmu.andre.snaptools.ModulePack.Databases.FiltersDatabase; -//import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.FilterObject; -//import com.ljmu.andre.snaptools.ModulePack.Fragments.FiltersManagerFragment; -//import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.NowPlayingView; -//import com.ljmu.andre.snaptools.ModulePack.Networking.Helpers.TrackAlbumArtManager; -//import com.ljmu.andre.snaptools.ModulePack.Utils.FieldMapper; -//import com.ljmu.andre.snaptools.ModulePack.Utils.TrackMetaData; -//import com.ljmu.andre.snaptools.Networking.WebResponse.ObjectResultListener; -//import com.ljmu.andre.snaptools.Utils.AnimationUtils; -//import com.ljmu.andre.snaptools.Utils.Assert; -//import com.ljmu.andre.snaptools.Utils.Constants; -//import com.ljmu.andre.snaptools.Utils.ContextHelper; -//import com.ljmu.andre.snaptools.Utils.CustomObservers.SimpleObserver; -//import com.ljmu.andre.snaptools.Utils.ResourceUtils; -//import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; -// -//import java.io.File; -//import java.util.ArrayList; -//import java.util.Collection; -//import java.util.Iterator; -//import java.util.List; -//import java.util.concurrent.Callable; -// -//import io.reactivex.Observable; -//import io.reactivex.android.schedulers.AndroidSchedulers; -//import io.reactivex.schedulers.Schedulers; -//import timber.log.Timber; -// -//import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -//import static com.ljmu.andre.GsonPreferences.Preferences.putPref; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.FILTER_METADATA; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SERIALIZABLE_FILTER_METADATA; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CREATE_FILTER_METADATA; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CREATE_GEOFILTER_VIEW; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FILTER_LOAD_METADATA; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GEOFILTER_SHOULD_SUBSAMPLE; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GEOFILTER_TAPPED; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_GEOFILTER_CONTENT_VIEW; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.FILTER_METADATA_CACHE; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.FILTER_SERIALIZABLE_METADATA; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.GEOFILTER_VIEW_CREATION_ARG3; -//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FILTERS_PATH; -//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FILTER_NOW_PLAYING_ENABLED; -//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.NOW_PLAYING_BOTTOM_MARGIN; -//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.NOW_PLAYING_IMAGE_SIZE; -//import static com.ljmu.andre.snaptools.ModulePack.Utils.PackPreferenceHelpers.getFilterScaleType; -//import static com.ljmu.andre.snaptools.Utils.ContextHelper.getModuleContext; -//import static com.ljmu.andre.snaptools.Utils.FrameworkViewFactory.getSelectableBackgroundId; -//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getDSLView; -//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getDrawable; -//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getId; -//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getIdFromString; -//import static com.ljmu.andre.snaptools.Utils.XposedUtils.logStackTrace; -//import static de.robv.android.xposed.XposedHelpers.callMethod; -//import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField; -//import static de.robv.android.xposed.XposedHelpers.newInstance; -//import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField; -// -///** -// * This class was created by Andre R M (SID: 701439) -// * It and its contents are free to use by all -// */ -// -//public class CustomFilters extends ModuleHelper { -// private static final int BACKGROUND_FILTER_SAMPLE_SIZE = 10; -// private static final String FILTER_IMAGEVIEW_ID = "custom_filter_view"; -// private static final String FILTER_FILE_ID = "custom_filter_path"; -// private static final Object FILTER_LOCK = new Object(); -// private String filtersPath; -// private Object duplicateInjectReference; -// private View nowPlayingView; -// private NowPlayingView playerViewProvider; -// private TrackMetaData trackMetaData; -// private ImageView nowPlayingSettingsView; -// private boolean isDraggingPlayer; -// -// public CustomFilters(String name, boolean canBeDisabled) { -// super(name, canBeDisabled); -// } -// -// @Override -// public FragmentHelper[] getUIFragments() { -// -// return new FragmentHelper[]{new FiltersManagerFragment()}; -// } -// -// @Override -// public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { -// FiltersDatabase.init(snapContext); -// filtersPath = getPref(FILTERS_PATH); -// -// CBITable filterTable = FiltersDatabase.getTable(FilterObject.class); -// Collection filterObjects = filterTable.getAll(new QueryBuilder() -// .addSelection("is_active", "1")); -// -// List filterMetaData = new ArrayList<>(filterObjects.size()); -// -// try { -// Class filterMetadataClass = HookResolver.resolveHookClass(FILTER_METADATA); -// Class serializableFilterMetadataClass = HookResolver.resolveHookClass(SERIALIZABLE_FILTER_METADATA); -// FieldMapper mapper = FieldMapper.initMapper("Filter", serializableFilterMetadataClass); -// -// hookMethod( -// CREATE_FILTER_METADATA, -// new ST_MethodHook() { -// @Override -// protected void after(MethodHookParam param) throws Throwable { -// Object result = callMethod(param.getResult(), "d"); -// Timber.d("MGR ITEM: " + result); -// -// synchronized (FILTER_LOCK) { -// if (result.getClass().equals(filterMetadataClass) && filterMetaData.isEmpty()) { -// int requiredMetaDataCount = filterObjects.size(); -// -// if (getPref(FILTER_NOW_PLAYING_ENABLED)) -// requiredMetaDataCount++; -// -// for (int i = 0; i < requiredMetaDataCount; i++) { -// Object geoMetaDataCache = getObjectField(FILTER_METADATA_CACHE, param.thisObject); -// Object serializableMetaData = getObjectField(FILTER_SERIALIZABLE_METADATA, geoMetaDataCache); -// -// // Retrieve the original settings ============================================ -// String oldId = mapper.getFieldVal(serializableMetaData, "filter_id"); -// Boolean wasDynamic = mapper.getFieldVal(serializableMetaData, "is_dynamic_geofilter"); -// -// // Assign our custom settings ================================================ -// mapper.setField(serializableMetaData, "filter_id", "Custom_" + i); -// mapper.setField(serializableMetaData, "is_dynamic_geofilter", true); -// -// // Create our custom metadata ================================================ -// Object builtMetaData = newInstance(filterMetadataClass, serializableMetaData); -// filterMetaData.add(builtMetaData); -// -// // Reset the original settings =============================================== -// mapper.setField(serializableMetaData, "filter_id", oldId); -// mapper.setField(serializableMetaData, "is_dynamic_geofilter", wasDynamic); -// } -// } -// } -// } -// } -// ); -// } catch (HookNotFoundException e) { -// Timber.e(e); -// moduleLoadState.fail(); -// } -// -// hookMethod( -// GEOFILTER_SHOULD_SUBSAMPLE, -// new ST_MethodHook() { -// @SuppressWarnings("Guava") -// @Override -// protected void before(MethodHookParam param) throws Throwable { -// RelativeLayout geofilterLayout = (RelativeLayout) param.thisObject; -// Timber.d("Should SubSample? " + param.args[0]); -// -// if (getAdditionalInstanceField(geofilterLayout, "is_now_playing") != null) { -// if (nowPlayingSettingsView == null) -// return; -// -// Timber.d("It's a now playing filter"); -// -// ContextHelper.getActivity().runOnUiThread(() -> { -// if (!(boolean) param.args[0]) -// AnimationUtils.collapse(nowPlayingSettingsView, 2); -// else -// AnimationUtils.expand(nowPlayingSettingsView, 2); -// }); -// -// return; -// } -// -// String filterFilePath = (String) getAdditionalInstanceField(geofilterLayout, FILTER_FILE_ID); -// -// if (filterFilePath != null) { -// Timber.d("Is custom filter: " + filterFilePath); -// //Timber.d("Is rendered: " + param.args[1]); -// -// ImageView filterImageView = getDSLView(geofilterLayout, FILTER_IMAGEVIEW_ID); -// File filterFile = new File(filterFilePath); -// -// if (filterImageView == null) { -// Timber.w("Couldn't find filter imageview for [Filter: %s]", filterFile.getName()); -// return; -// } -// -// if (!filterFile.exists()) { -// Timber.w("Filter file doesn't exist? " + filterFilePath); -// return; -// } -// -// if (!(boolean) param.args[0]) { -// Options bitmapFactoryOptions = new Options(); -// bitmapFactoryOptions.inSampleSize = BACKGROUND_FILTER_SAMPLE_SIZE; -// Bitmap decodedBitmap = BitmapFactory.decodeFile(filterFilePath, bitmapFactoryOptions); -// -// ContextHelper.getActivity().runOnUiThread(() -> { -// Drawable viewDrawable = filterImageView.getDrawable(); -// -// if (viewDrawable != null && viewDrawable instanceof BitmapDrawable) { -// Bitmap drawableBitmap = ((BitmapDrawable) viewDrawable).getBitmap(); -// if (drawableBitmap != null) -// drawableBitmap.recycle(); -// } -// -// filterImageView.setImageBitmap(decodedBitmap); -// }); -// } else { -// Observable.fromCallable((Callable>) () -> { -// try { -// return Optional.fromNullable(BitmapFactory.decodeFile(filterFilePath)); -// } catch (Exception ignored) { -// Timber.w("Couldn't decode high resolution filter image: " + filterFile.getName()); -// } -// -// return Optional.absent(); -// }).subscribeOn(Schedulers.computation()) -// .observeOn(AndroidSchedulers.mainThread()) -// .subscribe(new SimpleObserver>() { -// @Override -// public void onNext(Optional bitmapOptional) { -// if (bitmapOptional.isPresent()) { -// Drawable viewDrawable = filterImageView.getDrawable(); -// -// if (viewDrawable != null && viewDrawable instanceof BitmapDrawable) { -// Bitmap drawableBitmap = ((BitmapDrawable) viewDrawable).getBitmap(); -// if (drawableBitmap != null) -// drawableBitmap.recycle(); -// } -// -// filterImageView.setImageBitmap(bitmapOptional.get()); -// } -// } -// }); -// } -// } -// } -// } -// ); -// -// hookMethod( -// FILTER_LOAD_METADATA, -// new ST_MethodHook() { -// @Override -// protected void after(MethodHookParam param) throws Throwable { -// synchronized (FILTER_LOCK) { -// Timber.d("Attempting to load filter views"); -// Collection geoFilterList = (Collection) param.getResult(); -// Object noIdeaWhatThisVarIs = getObjectField(GEOFILTER_VIEW_CREATION_ARG3, param.thisObject); -// -// if (noIdeaWhatThisVarIs == duplicateInjectReference) { -// Timber.i("Tried to re-inject filters into same list"); -// return; -// } -// -// if (!filterObjects.isEmpty() && filterMetaData.isEmpty()) { -// Timber.w("Expected metadata for custom filters"); -// return; -// } -// -// duplicateInjectReference = noIdeaWhatThisVarIs; -// -// Iterator filterObjectIterator = filterObjects.iterator(); -// int index = -1; -// while (filterObjectIterator.hasNext()) { -// index++; -// -// try { -// FilterObject filterObject = filterObjectIterator.next(); -// if (filterObject == null) { -// Timber.w("Null FilterObject? The fuck's that about?"); -// continue; -// } -// -// File imageFile = new File(filtersPath, filterObject.getFileName()); -// -// if (!imageFile.exists()) { -// Timber.w("Filter Image doesn't exist? [Filter: %s]", filterObject.getFileName()); -// filterObjectIterator.remove(); -// filterTable.delete(filterObject); -// continue; -// } -// -// if (index >= filterMetaData.size()) { -// Timber.w( -// "Filter Index exceeded MetaData Size [Index: %s][MetaDataSize: %s][ActiveFilters: %s]", -// index, filterMetaData.size(), filterObjects.size() -// ); -// continue; -// } -// -// //Timber.d("Index: " + index + " | " + filterMetaData.size()); -// //Timber.d("Binding: " + XposedHelpers.getObjectField(filterMetaData.get(index), "a") + " TO " + filterObject.getFileName()); -// Object geofilterViewHolder = callStaticHook(CREATE_GEOFILTER_VIEW, filterMetaData.get(index), param.args[1], noIdeaWhatThisVarIs); -// RelativeLayout geofilterView = callHook(GET_GEOFILTER_CONTENT_VIEW, geofilterViewHolder); -// geofilterView.removeAllViews(); -// -// ImageView testView = new ImageView(ContextHelper.getActivity()); -// testView.setId(getIdFromString(FILTER_IMAGEVIEW_ID)); -// testView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); -// testView.setScaleType(getFilterScaleType()); -// geofilterView.addView(testView); -// -// setAdditionalInstanceField(geofilterView, FILTER_FILE_ID, imageFile.getAbsolutePath()); -// geoFilterList.add(geofilterViewHolder); -// } catch (Throwable t) { -// Timber.e(t); -// } -// } -// -// if ((boolean) getPref(FILTER_NOW_PLAYING_ENABLED) && !filterMetaData.isEmpty()) { -// try { -// setupNowPlayingSettings(ContextHelper.getActivity()); -// } catch (Throwable t) { -// Timber.e(t, "Couldn't create Filter Settings"); -// } -// -// Timber.d("Adding Now PLaying Filter"); -// -// Object geofilterViewHolder = callStaticHook(CREATE_GEOFILTER_VIEW, filterMetaData.get(filterMetaData.size() - 1), param.args[1], noIdeaWhatThisVarIs); -// RelativeLayout geofilterView = callHook(GET_GEOFILTER_CONTENT_VIEW, geofilterViewHolder); -// geofilterView.removeAllViews(); -// View nowPlayingMainContainer = getPlayerViewProvider(ContextHelper.getActivity()).getCurrentPlayerView(ContextHelper.getActivity()); -// -// nowPlayingView = getDSLView(nowPlayingMainContainer, "now_playing_container"); -// updateNowPlaying(ContextHelper.getActivity()); -// -// geofilterView.addView(nowPlayingMainContainer); -// setAdditionalInstanceField(geofilterView, "is_now_playing", true); -// geoFilterList.add(geofilterViewHolder); -// } -// } -// } -// }); -// -// hookMethod( -// GEOFILTER_TAPPED, -// new ST_MethodHook() { -// @Override -// protected void after(MethodHookParam param) throws Throwable { -// Timber.d("Found a tap: " + param.thisObject); -// -// logStackTrace(); -// -// if (getAdditionalInstanceField(param.thisObject, "is_now_playing") != null) { -// Timber.d("It's a now playing filter"); -// -// if (nowPlayingView == null || nowPlayingView.getVisibility() != View.VISIBLE) { -// param.setResult(false); -// Timber.d("It's not visible"); -// return; -// } -// -// Rect rect = new Rect(); -// if (!nowPlayingView.getGlobalVisibleRect(rect)) { -// param.setResult(false); -// Timber.d("Couldn't get filter bounds"); -// return; -// } -// -// MotionEvent motionEvent = (MotionEvent) param.args[0]; -// if (!rect.contains((int) motionEvent.getX(), (int) motionEvent.getY())) { -// param.setResult(false); -// Timber.d("It's not within the bounds"); -// return; -// } -// -// RelativeLayout geofilterView = (RelativeLayout) param.thisObject; -// geofilterView.removeAllViews(); -// -// View nowPlayingMainContainer = getPlayerViewProvider(ContextHelper.getActivity()).getPlayerView(ContextHelper.getActivity(), true); -// -// nowPlayingView = getDSLView(nowPlayingMainContainer, "now_playing_container"); -// updateNowPlaying(ContextHelper.getActivity()); -// -// geofilterView.addView(nowPlayingMainContainer); -// Timber.d("Now playing tapped " + param.args[0]); -// param.setResult(true); -// } -// } -// } -// ); -// } -// -// private void setupNowPlayingSettings(Activity snapActivity) { -// LinearLayout verticalButtonContainer = (LinearLayout) snapActivity.findViewById(getId(snapActivity, "vertical_button_container")); -// -// if (verticalButtonContainer == null) { -// Timber.w("VerticalButtonContainer not found, not assigning NowPlaying Settings button"); -// return; -// } -// -// Context moduleContext = getModuleContext(snapActivity); -// Assert.notNull("Null Context", moduleContext); -// -// /** -// * =========================================================================== -// * Create the filter settings button -// * =========================================================================== -// */ -// nowPlayingSettingsView = new ImageView(moduleContext); -// nowPlayingSettingsView.setBackgroundResource(getSelectableBackgroundId(snapActivity)); -// nowPlayingSettingsView.setImageResource(getDrawable(moduleContext, "settings_96")); -// nowPlayingSettingsView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); -// nowPlayingSettingsView.setColorFilter(Color.WHITE); -// nowPlayingSettingsView.setScaleX(0.6f); -// nowPlayingSettingsView.setScaleY(0.6f); -// nowPlayingSettingsView.setVisibility(View.GONE); -// -// nowPlayingSettingsView.setOnClickListener((View v) -> buildPlayerSettingsView(snapActivity)); -// verticalButtonContainer.addView(nowPlayingSettingsView); -// } -// -// @SuppressLint("ClickableViewAccessibility") -// private void buildPlayerSettingsView(Activity snapActivity) { -// if (nowPlayingView != null) -// nowPlayingView.setVisibility(View.GONE); -// -// NowPlayingView playerProvider = getPlayerViewProvider(snapActivity); -// ViewGroup playerView = getDSLView(playerProvider.getCurrentPlayerView(snapActivity), "now_playing_container"); -// -// /** -// * =========================================================================== -// * Build the white overlay and add it to the preview frame -// * =========================================================================== -// */ -// ViewGroup contentContainer = (ViewGroup) snapActivity.findViewById(getId(snapActivity, "snap_preview_frame_layout")); -// RelativeLayout overlay = playerProvider.getPlayerPositionController(snapActivity, (seekBar, progress) -> { -// View artView = getDSLView(playerView, "now_playing_art"); -// ViewGroup.LayoutParams artParams = artView.getLayoutParams(); -// artParams.width = progress; -// artParams.height = progress; -// artView.setLayoutParams(artParams); -// }); -// overlay.setFocusableInTouchMode(true); -// overlay.requestFocus(); -// contentContainer.addView(overlay); -// // =========================================================================== -// -// ViewGroup overlayPlayerContainer = getDSLView(overlay, "player_container"); -// View bottomMarginView = getDSLView(overlay, "bottom_margin_line"); -// -// // =========================================================================== -// playerView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); -// ((ViewGroup) playerView.getParent()).removeView(playerView); -// overlayPlayerContainer.addView(playerView); -// -// // =========================================================================== -// -// ImageButton cancelButton = getDSLView(overlay, "button_close_player_settings"); -// cancelButton.setOnClickListener(v -> removeOverlayView(contentContainer, overlay)); -// -// // =========================================================================== -// -// Handler touchHandler = new Handler(Looper.getMainLooper()); -// -// /** -// * =========================================================================== -// * Setup our touch listener to move the player -// * =========================================================================== -// */ -// //noinspection AndroidLintClickableViewAccessibility -// overlay.setOnTouchListener((v, event) -> { -// Timber.d("TouchEvent: " + event); -// -// /** -// * =========================================================================== -// * Check if our initial press is on the player view -// * - Never return touch events to Snapchat -// * =========================================================================== -// */ -// if (event.getAction() == MotionEvent.ACTION_DOWN) { -// Rect rect = new Rect(); -// if (!playerView.getGlobalVisibleRect(rect)) { -// return true; -// } -// -// if (!rect.contains((int) event.getX(), (int) event.getY())) { -// return true; -// } -// -// isDraggingPlayer = true; -// touchHandler.sendMessageDelayed(Message.obtain(touchHandler, 1), 250); -// } -// -// if (!isDraggingPlayer) -// return true; -// -// // =========================================================================== -// Display display = snapActivity.getWindowManager().getDefaultDisplay(); -// playerView.measure(display.getWidth(), display.getHeight()); -// // =========================================================================== -// -// int playerHeight = playerView.getMeasuredHeight(); //view height -// int parentHeight = overlay.getHeight(); //view height -// -// // =========================================================================== -// -// /** -// * =========================================================================== -// * Set the height of our "margin" line -// * =========================================================================== -// */ -// int marginHeight = (int) (parentHeight - (event.getY() + (playerHeight / 2))); -// -// if (marginHeight > parentHeight - playerHeight) -// marginHeight = parentHeight - playerHeight; -// else if (marginHeight < 0) -// marginHeight = 0; -// -// RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) bottomMarginView.getLayoutParams(); -// layoutParams.height = marginHeight; -// bottomMarginView.setLayoutParams(layoutParams); -// // =========================================================================== -// -// if (event.getAction() == MotionEvent.ACTION_UP) { -// isDraggingPlayer = false; -// -// if (touchHandler.hasMessages(1)) { -// v.performClick(); -// Timber.d("View %s got click", v); -// } -// -// putPref(NOW_PLAYING_BOTTOM_MARGIN, marginHeight); -// return true; -// } -// -// return true; -// }); -// -// /** -// * =========================================================================== -// * Override the Back Button to close the overlay -// * =========================================================================== -// */ -// overlay.setOnKeyListener((v, keyCode, event) -> { -// if (keyCode == KeyEvent.KEYCODE_BACK) { -// if (event.getAction() == MotionEvent.ACTION_UP) -// removeOverlayView(contentContainer, overlay); -// -// return true; -// } -// -// return false; -// }); -// } -// -// private void removeOverlayView(ViewGroup contentContainer, View overlay) { -// refreshNowPlayingViewMetrics(); -// contentContainer.removeView(overlay); -// -// if (nowPlayingView != null) -// nowPlayingView.setVisibility(View.VISIBLE); -// } -// -// private NowPlayingView getPlayerViewProvider(Activity activity) { -// if (playerViewProvider == null) { -// playerViewProvider = new NowPlayingView(); -// initMediaReceiver(activity); -// } -// -// return playerViewProvider; -// } -// -// private void refreshNowPlayingViewMetrics() { -// if (nowPlayingView != null) { -// RelativeLayout.LayoutParams playerParams = (RelativeLayout.LayoutParams) nowPlayingView.getLayoutParams(); -// playerParams.bottomMargin = getPref(NOW_PLAYING_BOTTOM_MARGIN); -// nowPlayingView.setLayoutParams(playerParams); -// -// int imageSize = getPref(NOW_PLAYING_IMAGE_SIZE); -// View artView = getDSLView(nowPlayingView, "now_playing_art"); -// ViewGroup.LayoutParams artParams = artView.getLayoutParams(); -// artParams.width = imageSize; -// artParams.height = imageSize; -// artView.setLayoutParams(artParams); -// } -// } -// -// private void updateNowPlaying(Context context) { -// if (nowPlayingView == null) { -// return; -// } -// -// String title = trackMetaData == null ? "Unknown" : trackMetaData.getTitle(); -// String artist = trackMetaData == null ? "Unknown" : trackMetaData.getArtist(); -// -// ResourceUtils.getDSLView(nowPlayingView, "now_playing_title").setText( -// title -// ); -// -// ResourceUtils.getDSLView(nowPlayingView, "now_playing_artist").setText( -// artist -// ); -// -// View artworkImageView = getDSLView(nowPlayingView, "now_playing_art"); -// -// if (trackMetaData != null) { -// if (trackMetaData.getArtwork() != null && !trackMetaData.getArtwork().isRecycled()) { -// artworkImageView.setBackground( -// new BitmapDrawable(context.getResources(), trackMetaData.getArtwork()) -// ); -// return; -// } -// -// TrackAlbumArtManager.getAlbumArt(trackMetaData, new ObjectResultListener() { -// @Override -// public void success(String message, Bitmap object) { -// trackMetaData.setArtwork(object); -// -// ResourceUtils.getDSLView(nowPlayingView, "now_playing_art").setBackground( -// new BitmapDrawable(context.getResources(), object) -// ); -// } -// -// @Override -// public void error(String message, Throwable t, int errorCode) { -// if (t != null) -// Timber.e(t, message); -// else -// Timber.w(message); -// -// trackMetaData.setArtwork(null); -// artworkImageView.setBackgroundResource( -// Constants.getApkVersionCode() >= 65 ? -// getDrawable(getModuleContext(context), "music_record_primary_dark") -// : getDrawable(getModuleContext(context), "delete") -// ); -// } -// }); -// } else { -// artworkImageView.setBackgroundResource( -// Constants.getApkVersionCode() >= 65 ? -// getDrawable(getModuleContext(context), "music_record_primary_dark") -// : getDrawable(getModuleContext(context), "delete") -// ); -// } -// } -// -// private void initMediaReceiver(Activity activity) { -// IntentFilter iF = new IntentFilter(); -// -// iF.addAction("com.andrew.apollo.metachanged"); -// -// iF.addAction("com.android.music.queuechanged"); -// iF.addAction("com.android.music.playstatechanged"); -// iF.addAction("com.android.music.playbackcomplete"); -// iF.addAction("com.android.music.metachanged"); -// //HTC Music -// iF.addAction("com.htc.music.playstatechanged"); -// iF.addAction("com.htc.music.playbackcomplete"); -// iF.addAction("com.htc.music.metachanged"); -// //MIUI Player -// iF.addAction("com.miui.player.playstatechanged"); -// iF.addAction("com.miui.player.playbackcomplete"); -// iF.addAction("com.miui.player.metachanged"); -// //Real -// iF.addAction("com.real.IMP.playstatechanged"); -// iF.addAction("com.real.IMP.playbackcomplete"); -// iF.addAction("com.real.IMP.metachanged"); -// //SEMC Music Player -// iF.addAction("com.sonyericsson.music.playbackcontrol.ACTION_TRACK_STARTED"); -// iF.addAction("com.sonyericsson.music.playbackcontrol.ACTION_PAUSED"); -// iF.addAction("com.sonyericsson.music.TRACK_COMPLETED"); -// iF.addAction("com.sonyericsson.music.metachanged"); -// iF.addAction("com.sonyericsson.music.playbackcomplete"); -// iF.addAction("com.sonyericsson.music.playstatechanged"); -// //rdio -// iF.addAction("com.rdio.android.metachanged"); -// iF.addAction("com.rdio.android.playstatechanged"); -// //Samsung Music Player -// iF.addAction("com.samsung.sec.android.MusicPlayer.playstatechanged"); -// iF.addAction("com.samsung.sec.android.MusicPlayer.playbackcomplete"); -// iF.addAction("com.samsung.sec.android.MusicPlayer.metachanged"); -// iF.addAction("com.sec.android.app.music.playstatechanged"); -// iF.addAction("com.sec.android.app.music.playbackcomplete"); -// iF.addAction("com.sec.android.app.music.metachanged"); -// //Winamp -// iF.addAction("com.nullsoft.winamp.playstatechanged"); -// iF.addAction("com.nullsoft.winamp.metachanged"); -// //Amazon -// iF.addAction("com.amazon.mp3.playstatechanged"); -// iF.addAction("com.amazon.mp3.metachanged"); -// //Rhapsody -// iF.addAction("com.rhapsody.playstatechanged"); -// //PowerAmp -// iF.addAction("com.maxmpz.audioplayer.playstatechanged"); -// //Last.fm -// iF.addAction("fm.last.android.metachanged"); -// iF.addAction("fm.last.android.playbackpaused"); -// iF.addAction("fm.last.android.playbackcomplete"); -// //A simple last.fm scrobbler -// iF.addAction("com.adam.aslfms.notify.playstatechanged"); -// //Scrobble Droid -// iF.addAction("net.jjc1138.android.scrobbler.action.MUSIC_STATUS"); -// //Spotify -// iF.addAction("com.spotify.music.playbackstatechanged"); -// //Poweramp -// iF.addAction("com.maxmpz.audioplayer.TRACK_CHANGED"); -// -// Timber.d("Registering receiver"); -// activity.registerReceiver(new BroadcastReceiver() { -// @Override -// public void onReceive(Context context, Intent intent) { -// Timber.d("Received: " + intent.toString()); -// Bundle bundle = intent.getExtras(); -// -// if (bundle != null) { -// for (String key : bundle.keySet()) { -// Object value = bundle.get(key); -// -// if (value == null) -// value = "NULL"; -// -// Timber.d("TrackEvent: " + String.format("%s %s (%s)", key, -// value.toString(), value.getClass().getName())); -// } -// } -// -// TrackMetaData receivedTrackMetaData; -// -// try { -// receivedTrackMetaData = TrackMetaData.Builder -// .fromIntent(intent) -// .build(); -// } catch (NullPointerException e) { -// Timber.w("Track Broadcast not built: " + e.getMessage()); -// return; -// } -// -// if (receivedTrackMetaData == null) { -// Timber.e("Null TrackMetaData received"); -// return; -// } -// -// if (!receivedTrackMetaData.isPlaying()) { -// Timber.d("Track not playing... Skipping any updates"); -// return; -// } -// -// if (trackMetaData == null || !trackMetaData.equals(receivedTrackMetaData)) { -// Timber.d("Current track differs with intent track"); -// if (trackMetaData != null) -// trackMetaData.setArtwork(null); -// -// trackMetaData = receivedTrackMetaData; -// updateNowPlaying(activity); -// } else -// Timber.d("Intent track matches current track"); -// } -// }, iF); -// } -//} +//package com.ljmu.andre.snaptools.ModulePack; +// +//import android.annotation.SuppressLint; +//import android.app.Activity; +//import android.content.BroadcastReceiver; +//import android.content.Context; +//import android.content.Intent; +//import android.content.IntentFilter; +//import android.graphics.Bitmap; +//import android.graphics.BitmapFactory; +//import android.graphics.BitmapFactory.Options; +//import android.graphics.Color; +//import android.graphics.Rect; +//import android.graphics.drawable.BitmapDrawable; +//import android.graphics.drawable.Drawable; +//import android.os.Bundle; +//import android.os.Handler; +//import android.os.Looper; +//import android.os.Message; +//import android.view.Display; +//import android.view.KeyEvent; +//import android.view.MotionEvent; +//import android.view.View; +//import android.view.ViewGroup; +//import android.widget.FrameLayout.LayoutParams; +//import android.widget.ImageButton; +//import android.widget.ImageView; +//import android.widget.LinearLayout; +//import android.widget.RelativeLayout; +//import android.widget.TextView; +// +//import com.google.common.base.Optional; +//import com.ljmu.andre.CBIDatabase.CBITable; +//import com.ljmu.andre.CBIDatabase.Utils.QueryBuilder; +//import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; +//import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +//import com.ljmu.andre.snaptools.ModulePack.Databases.FiltersDatabase; +//import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.FilterObject; +//import com.ljmu.andre.snaptools.ModulePack.Fragments.FiltersManagerFragment; +//import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.NowPlayingView; +//import com.ljmu.andre.snaptools.ModulePack.Networking.Helpers.TrackAlbumArtManager; +//import com.ljmu.andre.snaptools.ModulePack.Utils.FieldMapper; +//import com.ljmu.andre.snaptools.ModulePack.Utils.TrackMetaData; +//import com.ljmu.andre.snaptools.Networking.WebResponse.ObjectResultListener; +//import com.ljmu.andre.snaptools.Utils.AnimationUtils; +//import com.ljmu.andre.snaptools.Utils.Assert; +//import com.ljmu.andre.snaptools.Utils.Constants; +//import com.ljmu.andre.snaptools.Utils.ContextHelper; +//import com.ljmu.andre.snaptools.Utils.CustomObservers.SimpleObserver; +//import com.ljmu.andre.snaptools.Utils.ResourceUtils; +//import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; +// +//import java.io.File; +//import java.util.ArrayList; +//import java.util.Collection; +//import java.util.Iterator; +//import java.util.List; +//import java.util.concurrent.Callable; +// +//import io.reactivex.Observable; +//import io.reactivex.android.schedulers.AndroidSchedulers; +//import io.reactivex.schedulers.Schedulers; +//import timber.log.Timber; +// +//import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +//import static com.ljmu.andre.GsonPreferences.Preferences.putPref; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.FILTER_METADATA; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SERIALIZABLE_FILTER_METADATA; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CREATE_FILTER_METADATA; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CREATE_GEOFILTER_VIEW; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FILTER_LOAD_METADATA; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GEOFILTER_SHOULD_SUBSAMPLE; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GEOFILTER_TAPPED; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_GEOFILTER_CONTENT_VIEW; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.FILTER_METADATA_CACHE; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.FILTER_SERIALIZABLE_METADATA; +//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.GEOFILTER_VIEW_CREATION_ARG3; +//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FILTERS_PATH; +//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FILTER_NOW_PLAYING_ENABLED; +//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.NOW_PLAYING_BOTTOM_MARGIN; +//import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.NOW_PLAYING_IMAGE_SIZE; +//import static com.ljmu.andre.snaptools.ModulePack.Utils.PackPreferenceHelpers.getFilterScaleType; +//import static com.ljmu.andre.snaptools.Utils.ContextHelper.getModuleContext; +//import static com.ljmu.andre.snaptools.Utils.FrameworkViewFactory.getSelectableBackgroundId; +//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getDSLView; +//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getDrawable; +//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getId; +//import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getIdFromString; +//import static com.ljmu.andre.snaptools.Utils.XposedUtils.logStackTrace; +//import static de.robv.android.xposed.XposedHelpers.callMethod; +//import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField; +//import static de.robv.android.xposed.XposedHelpers.newInstance; +//import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField; +// +///** +// * This class was created by Andre R M (SID: 701439) +// * It and its contents are free to use by all +// */ +// +//public class CustomFilters extends ModuleHelper { +// private static final int BACKGROUND_FILTER_SAMPLE_SIZE = 10; +// private static final String FILTER_IMAGEVIEW_ID = "custom_filter_view"; +// private static final String FILTER_FILE_ID = "custom_filter_path"; +// private static final Object FILTER_LOCK = new Object(); +// private String filtersPath; +// private Object duplicateInjectReference; +// private View nowPlayingView; +// private NowPlayingView playerViewProvider; +// private TrackMetaData trackMetaData; +// private ImageView nowPlayingSettingsView; +// private boolean isDraggingPlayer; +// +// public CustomFilters(String name, boolean canBeDisabled) { +// super(name, canBeDisabled); +// } +// +// @Override +// public FragmentHelper[] getUIFragments() { +// +// return new FragmentHelper[]{new FiltersManagerFragment()}; +// } +// +// @Override +// public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { +// FiltersDatabase.init(snapContext); +// filtersPath = getPref(FILTERS_PATH); +// +// CBITable filterTable = FiltersDatabase.getTable(FilterObject.class); +// Collection filterObjects = filterTable.getAll(new QueryBuilder() +// .addSelection("is_active", "1")); +// +// List filterMetaData = new ArrayList<>(filterObjects.size()); +// +// try { +// Class filterMetadataClass = HookResolver.resolveHookClass(FILTER_METADATA); +// Class serializableFilterMetadataClass = HookResolver.resolveHookClass(SERIALIZABLE_FILTER_METADATA); +// FieldMapper mapper = FieldMapper.initMapper("Filter", serializableFilterMetadataClass); +// +// hookMethod( +// CREATE_FILTER_METADATA, +// new ST_MethodHook() { +// @Override +// protected void after(MethodHookParam param) throws Throwable { +// Object result = callMethod(param.getResult(), "d"); +// Timber.d("MGR ITEM: " + result); +// +// synchronized (FILTER_LOCK) { +// if (result.getClass().equals(filterMetadataClass) && filterMetaData.isEmpty()) { +// int requiredMetaDataCount = filterObjects.size(); +// +// if (getPref(FILTER_NOW_PLAYING_ENABLED)) +// requiredMetaDataCount++; +// +// for (int i = 0; i < requiredMetaDataCount; i++) { +// Object geoMetaDataCache = getObjectField(FILTER_METADATA_CACHE, param.thisObject); +// Object serializableMetaData = getObjectField(FILTER_SERIALIZABLE_METADATA, geoMetaDataCache); +// +// // Retrieve the original settings ============================================ +// String oldId = mapper.getFieldVal(serializableMetaData, "filter_id"); +// Boolean wasDynamic = mapper.getFieldVal(serializableMetaData, "is_dynamic_geofilter"); +// +// // Assign our custom settings ================================================ +// mapper.setField(serializableMetaData, "filter_id", "Custom_" + i); +// mapper.setField(serializableMetaData, "is_dynamic_geofilter", true); +// +// // Create our custom metadata ================================================ +// Object builtMetaData = newInstance(filterMetadataClass, serializableMetaData); +// filterMetaData.add(builtMetaData); +// +// // Reset the original settings =============================================== +// mapper.setField(serializableMetaData, "filter_id", oldId); +// mapper.setField(serializableMetaData, "is_dynamic_geofilter", wasDynamic); +// } +// } +// } +// } +// } +// ); +// } catch (HookNotFoundException e) { +// Timber.e(e); +// moduleLoadState.fail(); +// } +// +// hookMethod( +// GEOFILTER_SHOULD_SUBSAMPLE, +// new ST_MethodHook() { +// @SuppressWarnings("Guava") +// @Override +// protected void before(MethodHookParam param) throws Throwable { +// RelativeLayout geofilterLayout = (RelativeLayout) param.thisObject; +// Timber.d("Should SubSample? " + param.args[0]); +// +// if (getAdditionalInstanceField(geofilterLayout, "is_now_playing") != null) { +// if (nowPlayingSettingsView == null) +// return; +// +// Timber.d("It's a now playing filter"); +// +// ContextHelper.getActivity().runOnUiThread(() -> { +// if (!(boolean) param.args[0]) +// AnimationUtils.collapse(nowPlayingSettingsView, 2); +// else +// AnimationUtils.expand(nowPlayingSettingsView, 2); +// }); +// +// return; +// } +// +// String filterFilePath = (String) getAdditionalInstanceField(geofilterLayout, FILTER_FILE_ID); +// +// if (filterFilePath != null) { +// Timber.d("Is custom filter: " + filterFilePath); +// //Timber.d("Is rendered: " + param.args[1]); +// +// ImageView filterImageView = getDSLView(geofilterLayout, FILTER_IMAGEVIEW_ID); +// File filterFile = new File(filterFilePath); +// +// if (filterImageView == null) { +// Timber.w("Couldn't find filter imageview for [Filter: %s]", filterFile.getName()); +// return; +// } +// +// if (!filterFile.exists()) { +// Timber.w("Filter file doesn't exist? " + filterFilePath); +// return; +// } +// +// if (!(boolean) param.args[0]) { +// Options bitmapFactoryOptions = new Options(); +// bitmapFactoryOptions.inSampleSize = BACKGROUND_FILTER_SAMPLE_SIZE; +// Bitmap decodedBitmap = BitmapFactory.decodeFile(filterFilePath, bitmapFactoryOptions); +// +// ContextHelper.getActivity().runOnUiThread(() -> { +// Drawable viewDrawable = filterImageView.getDrawable(); +// +// if (viewDrawable != null && viewDrawable instanceof BitmapDrawable) { +// Bitmap drawableBitmap = ((BitmapDrawable) viewDrawable).getBitmap(); +// if (drawableBitmap != null) +// drawableBitmap.recycle(); +// } +// +// filterImageView.setImageBitmap(decodedBitmap); +// }); +// } else { +// Observable.fromCallable((Callable>) () -> { +// try { +// return Optional.fromNullable(BitmapFactory.decodeFile(filterFilePath)); +// } catch (Exception ignored) { +// Timber.w("Couldn't decode high resolution filter image: " + filterFile.getName()); +// } +// +// return Optional.absent(); +// }).subscribeOn(Schedulers.computation()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribe(new SimpleObserver>() { +// @Override +// public void onNext(Optional bitmapOptional) { +// if (bitmapOptional.isPresent()) { +// Drawable viewDrawable = filterImageView.getDrawable(); +// +// if (viewDrawable != null && viewDrawable instanceof BitmapDrawable) { +// Bitmap drawableBitmap = ((BitmapDrawable) viewDrawable).getBitmap(); +// if (drawableBitmap != null) +// drawableBitmap.recycle(); +// } +// +// filterImageView.setImageBitmap(bitmapOptional.get()); +// } +// } +// }); +// } +// } +// } +// } +// ); +// +// hookMethod( +// FILTER_LOAD_METADATA, +// new ST_MethodHook() { +// @Override +// protected void after(MethodHookParam param) throws Throwable { +// synchronized (FILTER_LOCK) { +// Timber.d("Attempting to load filter views"); +// Collection geoFilterList = (Collection) param.getResult(); +// Object noIdeaWhatThisVarIs = getObjectField(GEOFILTER_VIEW_CREATION_ARG3, param.thisObject); +// +// if (noIdeaWhatThisVarIs == duplicateInjectReference) { +// Timber.i("Tried to re-inject filters into same list"); +// return; +// } +// +// if (!filterObjects.isEmpty() && filterMetaData.isEmpty()) { +// Timber.w("Expected metadata for custom filters"); +// return; +// } +// +// duplicateInjectReference = noIdeaWhatThisVarIs; +// +// Iterator filterObjectIterator = filterObjects.iterator(); +// int index = -1; +// while (filterObjectIterator.hasNext()) { +// index++; +// +// try { +// FilterObject filterObject = filterObjectIterator.next(); +// if (filterObject == null) { +// Timber.w("Null FilterObject? The fuck's that about?"); +// continue; +// } +// +// File imageFile = new File(filtersPath, filterObject.getFileName()); +// +// if (!imageFile.exists()) { +// Timber.w("Filter Image doesn't exist? [Filter: %s]", filterObject.getFileName()); +// filterObjectIterator.remove(); +// filterTable.delete(filterObject); +// continue; +// } +// +// if (index >= filterMetaData.size()) { +// Timber.w( +// "Filter Index exceeded MetaData Size [Index: %s][MetaDataSize: %s][ActiveFilters: %s]", +// index, filterMetaData.size(), filterObjects.size() +// ); +// continue; +// } +// +// //Timber.d("Index: " + index + " | " + filterMetaData.size()); +// //Timber.d("Binding: " + XposedHelpers.getObjectField(filterMetaData.get(index), "a") + " TO " + filterObject.getFileName()); +// Object geofilterViewHolder = callStaticHook(CREATE_GEOFILTER_VIEW, filterMetaData.get(index), param.args[1], noIdeaWhatThisVarIs); +// RelativeLayout geofilterView = callHook(GET_GEOFILTER_CONTENT_VIEW, geofilterViewHolder); +// geofilterView.removeAllViews(); +// +// ImageView testView = new ImageView(ContextHelper.getActivity()); +// testView.setId(getIdFromString(FILTER_IMAGEVIEW_ID)); +// testView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); +// testView.setScaleType(getFilterScaleType()); +// geofilterView.addView(testView); +// +// setAdditionalInstanceField(geofilterView, FILTER_FILE_ID, imageFile.getAbsolutePath()); +// geoFilterList.add(geofilterViewHolder); +// } catch (Throwable t) { +// Timber.e(t); +// } +// } +// +// if ((boolean) getPref(FILTER_NOW_PLAYING_ENABLED) && !filterMetaData.isEmpty()) { +// try { +// setupNowPlayingSettings(ContextHelper.getActivity()); +// } catch (Throwable t) { +// Timber.e(t, "Couldn't create Filter Settings"); +// } +// +// Timber.d("Adding Now PLaying Filter"); +// +// Object geofilterViewHolder = callStaticHook(CREATE_GEOFILTER_VIEW, filterMetaData.get(filterMetaData.size() - 1), param.args[1], noIdeaWhatThisVarIs); +// RelativeLayout geofilterView = callHook(GET_GEOFILTER_CONTENT_VIEW, geofilterViewHolder); +// geofilterView.removeAllViews(); +// View nowPlayingMainContainer = getPlayerViewProvider(ContextHelper.getActivity()).getCurrentPlayerView(ContextHelper.getActivity()); +// +// nowPlayingView = getDSLView(nowPlayingMainContainer, "now_playing_container"); +// updateNowPlaying(ContextHelper.getActivity()); +// +// geofilterView.addView(nowPlayingMainContainer); +// setAdditionalInstanceField(geofilterView, "is_now_playing", true); +// geoFilterList.add(geofilterViewHolder); +// } +// } +// } +// }); +// +// hookMethod( +// GEOFILTER_TAPPED, +// new ST_MethodHook() { +// @Override +// protected void after(MethodHookParam param) throws Throwable { +// Timber.d("Found a tap: " + param.thisObject); +// +// logStackTrace(); +// +// if (getAdditionalInstanceField(param.thisObject, "is_now_playing") != null) { +// Timber.d("It's a now playing filter"); +// +// if (nowPlayingView == null || nowPlayingView.getVisibility() != View.VISIBLE) { +// param.setResult(false); +// Timber.d("It's not visible"); +// return; +// } +// +// Rect rect = new Rect(); +// if (!nowPlayingView.getGlobalVisibleRect(rect)) { +// param.setResult(false); +// Timber.d("Couldn't get filter bounds"); +// return; +// } +// +// MotionEvent motionEvent = (MotionEvent) param.args[0]; +// if (!rect.contains((int) motionEvent.getX(), (int) motionEvent.getY())) { +// param.setResult(false); +// Timber.d("It's not within the bounds"); +// return; +// } +// +// RelativeLayout geofilterView = (RelativeLayout) param.thisObject; +// geofilterView.removeAllViews(); +// +// View nowPlayingMainContainer = getPlayerViewProvider(ContextHelper.getActivity()).getPlayerView(ContextHelper.getActivity(), true); +// +// nowPlayingView = getDSLView(nowPlayingMainContainer, "now_playing_container"); +// updateNowPlaying(ContextHelper.getActivity()); +// +// geofilterView.addView(nowPlayingMainContainer); +// Timber.d("Now playing tapped " + param.args[0]); +// param.setResult(true); +// } +// } +// } +// ); +// } +// +// private void setupNowPlayingSettings(Activity snapActivity) { +// LinearLayout verticalButtonContainer = (LinearLayout) snapActivity.findViewById(getId(snapActivity, "vertical_button_container")); +// +// if (verticalButtonContainer == null) { +// Timber.w("VerticalButtonContainer not found, not assigning NowPlaying Settings button"); +// return; +// } +// +// Context moduleContext = getModuleContext(snapActivity); +// Assert.notNull("Null Context", moduleContext); +// +// /** +// * =========================================================================== +// * Create the filter settings button +// * =========================================================================== +// */ +// nowPlayingSettingsView = new ImageView(moduleContext); +// nowPlayingSettingsView.setBackgroundResource(getSelectableBackgroundId(snapActivity)); +// nowPlayingSettingsView.setImageResource(getDrawable(moduleContext, "settings_96")); +// nowPlayingSettingsView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); +// nowPlayingSettingsView.setColorFilter(Color.WHITE); +// nowPlayingSettingsView.setScaleX(0.6f); +// nowPlayingSettingsView.setScaleY(0.6f); +// nowPlayingSettingsView.setVisibility(View.GONE); +// +// nowPlayingSettingsView.setOnClickListener((View v) -> buildPlayerSettingsView(snapActivity)); +// verticalButtonContainer.addView(nowPlayingSettingsView); +// } +// +// @SuppressLint("ClickableViewAccessibility") +// private void buildPlayerSettingsView(Activity snapActivity) { +// if (nowPlayingView != null) +// nowPlayingView.setVisibility(View.GONE); +// +// NowPlayingView playerProvider = getPlayerViewProvider(snapActivity); +// ViewGroup playerView = getDSLView(playerProvider.getCurrentPlayerView(snapActivity), "now_playing_container"); +// +// /** +// * =========================================================================== +// * Build the white overlay and add it to the preview frame +// * =========================================================================== +// */ +// ViewGroup contentContainer = (ViewGroup) snapActivity.findViewById(getId(snapActivity, "snap_preview_frame_layout")); +// RelativeLayout overlay = playerProvider.getPlayerPositionController(snapActivity, (seekBar, progress) -> { +// View artView = getDSLView(playerView, "now_playing_art"); +// ViewGroup.LayoutParams artParams = artView.getLayoutParams(); +// artParams.width = progress; +// artParams.height = progress; +// artView.setLayoutParams(artParams); +// }); +// overlay.setFocusableInTouchMode(true); +// overlay.requestFocus(); +// contentContainer.addView(overlay); +// // =========================================================================== +// +// ViewGroup overlayPlayerContainer = getDSLView(overlay, "player_container"); +// View bottomMarginView = getDSLView(overlay, "bottom_margin_line"); +// +// // =========================================================================== +// playerView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); +// ((ViewGroup) playerView.getParent()).removeView(playerView); +// overlayPlayerContainer.addView(playerView); +// +// // =========================================================================== +// +// ImageButton cancelButton = getDSLView(overlay, "button_close_player_settings"); +// cancelButton.setOnClickListener(v -> removeOverlayView(contentContainer, overlay)); +// +// // =========================================================================== +// +// Handler touchHandler = new Handler(Looper.getMainLooper()); +// +// /** +// * =========================================================================== +// * Setup our touch listener to move the player +// * =========================================================================== +// */ +// //noinspection AndroidLintClickableViewAccessibility +// overlay.setOnTouchListener((v, event) -> { +// Timber.d("TouchEvent: " + event); +// +// /** +// * =========================================================================== +// * Check if our initial press is on the player view +// * - Never return touch events to Snapchat +// * =========================================================================== +// */ +// if (event.getAction() == MotionEvent.ACTION_DOWN) { +// Rect rect = new Rect(); +// if (!playerView.getGlobalVisibleRect(rect)) { +// return true; +// } +// +// if (!rect.contains((int) event.getX(), (int) event.getY())) { +// return true; +// } +// +// isDraggingPlayer = true; +// touchHandler.sendMessageDelayed(Message.obtain(touchHandler, 1), 250); +// } +// +// if (!isDraggingPlayer) +// return true; +// +// // =========================================================================== +// Display display = snapActivity.getWindowManager().getDefaultDisplay(); +// playerView.measure(display.getWidth(), display.getHeight()); +// // =========================================================================== +// +// int playerHeight = playerView.getMeasuredHeight(); //view height +// int parentHeight = overlay.getHeight(); //view height +// +// // =========================================================================== +// +// /** +// * =========================================================================== +// * Set the height of our "margin" line +// * =========================================================================== +// */ +// int marginHeight = (int) (parentHeight - (event.getY() + (playerHeight / 2))); +// +// if (marginHeight > parentHeight - playerHeight) +// marginHeight = parentHeight - playerHeight; +// else if (marginHeight < 0) +// marginHeight = 0; +// +// RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) bottomMarginView.getLayoutParams(); +// layoutParams.height = marginHeight; +// bottomMarginView.setLayoutParams(layoutParams); +// // =========================================================================== +// +// if (event.getAction() == MotionEvent.ACTION_UP) { +// isDraggingPlayer = false; +// +// if (touchHandler.hasMessages(1)) { +// v.performClick(); +// Timber.d("View %s got click", v); +// } +// +// putPref(NOW_PLAYING_BOTTOM_MARGIN, marginHeight); +// return true; +// } +// +// return true; +// }); +// +// /** +// * =========================================================================== +// * Override the Back Button to close the overlay +// * =========================================================================== +// */ +// overlay.setOnKeyListener((v, keyCode, event) -> { +// if (keyCode == KeyEvent.KEYCODE_BACK) { +// if (event.getAction() == MotionEvent.ACTION_UP) +// removeOverlayView(contentContainer, overlay); +// +// return true; +// } +// +// return false; +// }); +// } +// +// private void removeOverlayView(ViewGroup contentContainer, View overlay) { +// refreshNowPlayingViewMetrics(); +// contentContainer.removeView(overlay); +// +// if (nowPlayingView != null) +// nowPlayingView.setVisibility(View.VISIBLE); +// } +// +// private NowPlayingView getPlayerViewProvider(Activity activity) { +// if (playerViewProvider == null) { +// playerViewProvider = new NowPlayingView(); +// initMediaReceiver(activity); +// } +// +// return playerViewProvider; +// } +// +// private void refreshNowPlayingViewMetrics() { +// if (nowPlayingView != null) { +// RelativeLayout.LayoutParams playerParams = (RelativeLayout.LayoutParams) nowPlayingView.getLayoutParams(); +// playerParams.bottomMargin = getPref(NOW_PLAYING_BOTTOM_MARGIN); +// nowPlayingView.setLayoutParams(playerParams); +// +// int imageSize = getPref(NOW_PLAYING_IMAGE_SIZE); +// View artView = getDSLView(nowPlayingView, "now_playing_art"); +// ViewGroup.LayoutParams artParams = artView.getLayoutParams(); +// artParams.width = imageSize; +// artParams.height = imageSize; +// artView.setLayoutParams(artParams); +// } +// } +// +// private void updateNowPlaying(Context context) { +// if (nowPlayingView == null) { +// return; +// } +// +// String title = trackMetaData == null ? "Unknown" : trackMetaData.getTitle(); +// String artist = trackMetaData == null ? "Unknown" : trackMetaData.getArtist(); +// +// ResourceUtils.getDSLView(nowPlayingView, "now_playing_title").setText( +// title +// ); +// +// ResourceUtils.getDSLView(nowPlayingView, "now_playing_artist").setText( +// artist +// ); +// +// View artworkImageView = getDSLView(nowPlayingView, "now_playing_art"); +// +// if (trackMetaData != null) { +// if (trackMetaData.getArtwork() != null && !trackMetaData.getArtwork().isRecycled()) { +// artworkImageView.setBackground( +// new BitmapDrawable(context.getResources(), trackMetaData.getArtwork()) +// ); +// return; +// } +// +// TrackAlbumArtManager.getAlbumArt(trackMetaData, new ObjectResultListener() { +// @Override +// public void success(String message, Bitmap object) { +// trackMetaData.setArtwork(object); +// +// ResourceUtils.getDSLView(nowPlayingView, "now_playing_art").setBackground( +// new BitmapDrawable(context.getResources(), object) +// ); +// } +// +// @Override +// public void error(String message, Throwable t, int errorCode) { +// if (t != null) +// Timber.e(t, message); +// else +// Timber.w(message); +// +// trackMetaData.setArtwork(null); +// artworkImageView.setBackgroundResource( +// Constants.getApkVersionCode() >= 65 ? +// getDrawable(getModuleContext(context), "music_record_primary_dark") +// : getDrawable(getModuleContext(context), "delete") +// ); +// } +// }); +// } else { +// artworkImageView.setBackgroundResource( +// Constants.getApkVersionCode() >= 65 ? +// getDrawable(getModuleContext(context), "music_record_primary_dark") +// : getDrawable(getModuleContext(context), "delete") +// ); +// } +// } +// +// private void initMediaReceiver(Activity activity) { +// IntentFilter iF = new IntentFilter(); +// +// iF.addAction("com.andrew.apollo.metachanged"); +// +// iF.addAction("com.android.music.queuechanged"); +// iF.addAction("com.android.music.playstatechanged"); +// iF.addAction("com.android.music.playbackcomplete"); +// iF.addAction("com.android.music.metachanged"); +// //HTC Music +// iF.addAction("com.htc.music.playstatechanged"); +// iF.addAction("com.htc.music.playbackcomplete"); +// iF.addAction("com.htc.music.metachanged"); +// //MIUI Player +// iF.addAction("com.miui.player.playstatechanged"); +// iF.addAction("com.miui.player.playbackcomplete"); +// iF.addAction("com.miui.player.metachanged"); +// //Real +// iF.addAction("com.real.IMP.playstatechanged"); +// iF.addAction("com.real.IMP.playbackcomplete"); +// iF.addAction("com.real.IMP.metachanged"); +// //SEMC Music Player +// iF.addAction("com.sonyericsson.music.playbackcontrol.ACTION_TRACK_STARTED"); +// iF.addAction("com.sonyericsson.music.playbackcontrol.ACTION_PAUSED"); +// iF.addAction("com.sonyericsson.music.TRACK_COMPLETED"); +// iF.addAction("com.sonyericsson.music.metachanged"); +// iF.addAction("com.sonyericsson.music.playbackcomplete"); +// iF.addAction("com.sonyericsson.music.playstatechanged"); +// //rdio +// iF.addAction("com.rdio.android.metachanged"); +// iF.addAction("com.rdio.android.playstatechanged"); +// //Samsung Music Player +// iF.addAction("com.samsung.sec.android.MusicPlayer.playstatechanged"); +// iF.addAction("com.samsung.sec.android.MusicPlayer.playbackcomplete"); +// iF.addAction("com.samsung.sec.android.MusicPlayer.metachanged"); +// iF.addAction("com.sec.android.app.music.playstatechanged"); +// iF.addAction("com.sec.android.app.music.playbackcomplete"); +// iF.addAction("com.sec.android.app.music.metachanged"); +// //Winamp +// iF.addAction("com.nullsoft.winamp.playstatechanged"); +// iF.addAction("com.nullsoft.winamp.metachanged"); +// //Amazon +// iF.addAction("com.amazon.mp3.playstatechanged"); +// iF.addAction("com.amazon.mp3.metachanged"); +// //Rhapsody +// iF.addAction("com.rhapsody.playstatechanged"); +// //PowerAmp +// iF.addAction("com.maxmpz.audioplayer.playstatechanged"); +// //Last.fm +// iF.addAction("fm.last.android.metachanged"); +// iF.addAction("fm.last.android.playbackpaused"); +// iF.addAction("fm.last.android.playbackcomplete"); +// //A simple last.fm scrobbler +// iF.addAction("com.adam.aslfms.notify.playstatechanged"); +// //Scrobble Droid +// iF.addAction("net.jjc1138.android.scrobbler.action.MUSIC_STATUS"); +// //Spotify +// iF.addAction("com.spotify.music.playbackstatechanged"); +// //Poweramp +// iF.addAction("com.maxmpz.audioplayer.TRACK_CHANGED"); +// +// Timber.d("Registering receiver"); +// activity.registerReceiver(new BroadcastReceiver() { +// @Override +// public void onReceive(Context context, Intent intent) { +// Timber.d("Received: " + intent.toString()); +// Bundle bundle = intent.getExtras(); +// +// if (bundle != null) { +// for (String key : bundle.keySet()) { +// Object value = bundle.get(key); +// +// if (value == null) +// value = "NULL"; +// +// Timber.d("TrackEvent: " + String.format("%s %s (%s)", key, +// value.toString(), value.getClass().getName())); +// } +// } +// +// TrackMetaData receivedTrackMetaData; +// +// try { +// receivedTrackMetaData = TrackMetaData.Builder +// .fromIntent(intent) +// .build(); +// } catch (NullPointerException e) { +// Timber.w("Track Broadcast not built: " + e.getMessage()); +// return; +// } +// +// if (receivedTrackMetaData == null) { +// Timber.e("Null TrackMetaData received"); +// return; +// } +// +// if (!receivedTrackMetaData.isPlaying()) { +// Timber.d("Track not playing... Skipping any updates"); +// return; +// } +// +// if (trackMetaData == null || !trackMetaData.equals(receivedTrackMetaData)) { +// Timber.d("Current track differs with intent track"); +// if (trackMetaData != null) +// trackMetaData.setArtwork(null); +// +// trackMetaData = receivedTrackMetaData; +// updateNowPlaying(activity); +// } else +// Timber.d("Intent track matches current track"); +// } +// }, iF); +// } +//} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ForcedHooks.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ForcedHooks.java index afe2def..4d7d5f9 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ForcedHooks.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ForcedHooks.java @@ -1,682 +1,682 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.Context; - -import com.ljmu.andre.GsonPreferences.Preferences.Preference; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import de.robv.android.xposed.XC_MethodHook.MethodHookParam; -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_BOOLEAN; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_FLOAT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_INT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_LONG; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_STRING; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.ERROR_SUPPRESS_DOWNLOADER_RUNNABLE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.NETWORK_EXECUTE_SYNC; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_ANIMATED_CONTENT_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_CAMERA2_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_CAPTIONV2_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_CHAT_VIDEO_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_EMOJIBRUSH_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_FPS_OVERLAY_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_GIPHY_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_HANDSFREEREC_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_INSIGHTS_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_MULTI_SNAP_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_SKYFILTERS_STATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_ADVERTS_BLOCKED; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_DISCOVER_BLOCKED; -import static com.ljmu.andre.snaptools.Utils.FrameworkPreferencesDef.DISABLED_MODULES; -import static com.ljmu.andre.snaptools.Utils.PreferenceHelpers.collectionContains; -import static de.robv.android.xposed.XposedHelpers.callMethod; -import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; -import static de.robv.android.xposed.XposedHelpers.findClass; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class ForcedHooks extends ModuleHelper { - - private boolean miscChangesEnabled; - - public ForcedHooks(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - @Override - public FragmentHelper[] getUIFragments() { - return new FragmentHelper[0]; - } - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - boolean blockDiscovery = getPref(STORY_BLOCKER_DISCOVER_BLOCKED); - boolean blockAds = getPref(STORY_BLOCKER_ADVERTS_BLOCKED); - - miscChangesEnabled = !collectionContains(DISABLED_MODULES, "Misc Changes"); - - /** - * =========================================================================== - * Returns TRUE to mark SC as DEBUG build - * =========================================================================== - */ -// findAndHookMethod( -// "aqly", snapClassLoader, -// "p", XC_MethodReplacement.returnConstant(true) -// ); - - -// findAndHookMethod( -// "tfj", snapClassLoader, -// "a", findClass("tfj$c", snapClassLoader), -// new ST_MethodHook() { -// @Override protected void after(MethodHookParam param) throws Throwable { -// Timber.d("Inbound debug item: " + param.args[0] + " | " + param.getResult()); -// -// Enum debugEnum = (Enum) param.args[0]; -// -// switch (debugEnum.name()) { -// case "CROP_SNAP_ENABLED": -// param.setResult(true); -// break; -// } -// } -// } -// ); - - //Forced Chronological Friends Feed -// findAndHookMethod( -// "ifj", snapClassLoader, -// "a", List.class, findClass("sda", snapClassLoader), -// new HookWrapper((HookBefore) param -> XposedHelpers.setObjectField(param.thisObject, "a", null)) -// ); - - /* - Snapchat Experiments - */ - Boolean insightsMode = transformBoolean(FORCE_INSIGHTS_STATE); - String multiSnapMode = transformOverwrite(FORCE_MULTI_SNAP_STATE); - Boolean videoChatMode = transformBoolean(FORCE_CHAT_VIDEO_STATE); - String animatedContentMode = transformOverwrite(FORCE_ANIMATED_CONTENT_STATE); - Boolean giphyMode = transformBoolean(FORCE_GIPHY_STATE); - String captionV2Mode = transformOverwrite(FORCE_CAPTIONV2_STATE); - String camera2Mode = transformOverwrite(FORCE_CAMERA2_STATE); - Boolean camera2ModeBool = transformBoolean(FORCE_CAMERA2_STATE); - String handsFreeMode = transformOtherOnOff(FORCE_HANDSFREEREC_STATE, "FULLY_ENABLED", "DISABLED", null); - Boolean fpsOverlayMode = transformBoolean(FORCE_FPS_OVERLAY_STATE); - String skyFilterMode = transformOverwrite(FORCE_SKYFILTERS_STATE); - Boolean emojiBrushMode = transformBoolean(FORCE_EMOJIBRUSH_STATE); - - HookAfter experimentDebugHook = null; - -// if (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) -// experimentDebugHook = this::handleExperimentPrinting; - - findAndHookMethod( - "apjq", snapClassLoader, - "a", "apjr", Object.class, - new HookWrapper( - param -> { - String key = (String) callMethod(param.args[0], "a"); - - switch (key) { - /** - * =========================================================================== - * Experiments - * =========================================================================== - */ -// case "developerOptionCheetahMode": -// handleExperiment(param, cheetahMode); -// break; -// case "chat_v10": -// handleExperiment(param, chatV10Mode); -// break; - case "developerOptionsImpalaForceShowInsights": - handleExperiment(param, insightsMode); - break; - case "magikarp_overwrite": - handleExperiment(param, multiSnapMode); - break; - case "chat_video_enabled": - handleExperiment(param, videoChatMode); - break; - case "animated_content_overwrite": - handleExperiment(param, animatedContentMode); - break; - case "giphy_in_preview": - handleExperiment(param, giphyMode); - break; - case "caption_v2_overwrite": - handleExperiment(param, captionV2Mode); - break; - case "camera2_overwrite_state": - handleExperiment(param, camera2Mode); - break; - case "developerOptionsHandsFreeRecordingMode": - handleExperiment(param, handsFreeMode); - break; - case "developerOptionsShouldShowFpsOverlay": - handleExperiment(param, fpsOverlayMode); - break; - case "sky_filters_overwrite": - handleExperiment(param, skyFilterMode); - break; - case "emoji_brush": - handleExperiment(param, emojiBrushMode); - - break; - - - /** - * =========================================================================== - * Forced Enabled Settings - * =========================================================================== - */ - case "nycEnableStreaming": - case "developerOptionsNycSearchStreamingAbtestOverride": - case "developerOptionsNycPublicStoryStreamingAbtestOverride": - param.setResult("FALSE"); - break; - case "discover_feed_tab_mode": - param.setResult("FORCE_ON"); - break; - case "discover_feed_should_show_subscribed_tab_nux": - param.setResult(true); - break; - case "enable geofilters": - param.setResult(true); - break; - } - }, - experimentDebugHook - ) - ); - - ST_MethodHook abTestHook = new HookWrapper( - (HookBefore) param -> { - String groupName = (String) param.args[0]; - String experimentName = (String) param.args[1]; - - String key = groupName + ":" + experimentName; - - switch (key) { - case "DOWNSCALE_TAKE_PICTURE_API_PHOTO_BEFORE_SEND:ENABLED": - handleExperiment(param, camera2ModeBool); - break; - case "ADS_HOLDOUT_01:SHOW_ADS": - case "ADS_HOLDOUT_01:ADS_IN_AA": - if (blockAds) - param.setResult(false); - break; - case "CRASHLYTICS:USE_CRASHLYTICS": - param.setResult(false); - break; - case "PUBLIC_STORY_STREAMING_ANDROID:enable": - case "STREAMING_PROMOTED_STORIES_ANDROID:enable": - param.setResult(false); - break; - } - }, -// (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) ? -// (HookAfter) param -> handleABTestPrinting(param) : null - null - ); - - hookMethod( - AB_TEST_CHECK_STRING, - abTestHook - ); - - hookMethod( - AB_TEST_CHECK_INT, - abTestHook - ); - - hookMethod( - AB_TEST_CHECK_LONG, - abTestHook - ); - - hookMethod( - AB_TEST_CHECK_BOOLEAN, - abTestHook - ); - - hookMethod( - AB_TEST_CHECK_FLOAT, - abTestHook - ); - -// hookMethod( -// AB_TEST_CHECK_VALUE, -// new HookWrapper((HookAfter) param -> { -// Timber.d("RAK: [p1: %s][p2: %s][p3: %s][r: %s]", param.args[0], param.args[1], param.args[2], param.getResult()); -// -// switch ((String) param.args[0]) { -//// case "CHEETAH_ANDROID": -//// if (param.args[1].equals("CHEETAH_MODE")) -//// param.setResult(forceCheetah ? "FULL_CHEETAH" : "OLD_DESIGN"); -//// break; -//// case "DIRECT_FILE_SNAP_UPLOAD": -//// if (param.args[1].equals("ENABLED")) -//// param.setResult("true"); -//// break; -//// case "LANDING_PAGE_CAMERA_VIEW": -//// param.setResult("true"); -//// break; -// case "DOWNSCALE_TAKE_PICTURE_API_PHOTO_BEFORE_SEND": -// if (param.args[1].equals("ENABLED")) -// param.setResult("false"); -// break; -//// case "ANDROID_CAMERA2_AND_TAKEPICTURE_API_v2": -//// if (param.args[1].equals("enable")) -//// param.setResult("true"); -//// break; -//// case "CAMERA_FRAGMENT_V2": -//// if (param.args[1].equals("ENABLED")) -//// param.setResult("true"); -//// break; -// case "PUBLIC_STORY_STREAMING_ANDROID": -// if (param.args[1].equals("enable")) -// param.setResult("false"); -// return; -// case "STREAMING_PROMOTED_STORIES_ANDROID": -// if (param.args[1].equals("enable")) -// param.setResult("false"); -// return; -//// case "ASYM_VIEW_PAGER": -//// if (param.args[1].equals("ENABLED")) -//// param.setResult("true"); -//// break; -//// case "SECURE_CHAT_SESSION_V2": -//// if (param.args[1].equals("ENABLED")) -//// param.setResult("true"); -//// break; -// } -// -// switch ((String) param.args[1]) { -// case "SHOW_ADS": -// case "ADS_IN_AA": -// if (getPref(STORY_BLOCKER_ADVERTS_BLOCKED)) -// param.setResult("false"); -// return; -// case "ENABLE_MULTI_SNAP": -// param.setResult("true"); -// return; -// case "ENABLE_PREVIEW_V2": -// param.setResult("true"); -// return; -// case "USE_CRASHLYTICS": -// param.setResult("false"); -// return; -//// case "RELEASE_DELAY_TIME_WHEN_USER_SWIPE_INTO_CONVERSATION": -//// param.setResult("5000"); -//// break; -// case "ENCODING_QUALITY": -// param.setResult("100"); -// break; -// case "DF_TAB_ANDROID": -// if (blockDiscovery) -// param.setResult("true"); -// break; -//// case "USE_SERVER_SIDE_PRECACHING": -//// param.setResult("false"); -//// break; -//// case "MEDIA_PLAYER_IMPL_EXOPLAYER": -//// param.setResult("true"); -//// break; -//// case "SHOULD_SHOW_SPONSORED_SECTION": -//// param.setResult("false"); -//// break; -//// case "fps": -//// param.setResult("30"); -//// break; -// } -// -// })); - - - hookMethod( - NETWORK_EXECUTE_SYNC, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - String url = (String) callMethod(param.thisObject, "getUrl"); - - Timber.d("Network URL: " + url); - - if (url.endsWith("logout")) { - Timber.d("Blocking logout"); - param.setResult(null); - } - } - } - ); - // Error Suppression for the hook above - hookMethod( - ERROR_SUPPRESS_DOWNLOADER_RUNNABLE, - new HookWrapper((HookAfter) param -> { - if (param.getThrowable() != null) { - Timber.d("Download Runnable Error Suppression"); - param.setThrowable(null); - } - }) - ); - - /** - * =========================================================================== - * Just used as a fatal crash prevention... Likely just moves the issue - * =========================================================================== - */ -// XposedHelpers.findAndHookMethod( -// "htt", snapClassLoader, -// "a", -// new ST_MethodHook() { -// @Override protected void after(MethodHookParam param) throws Throwable { -// if (param.getThrowable() != null) { -// Timber.e(new Throwable( -// "Error raised from lens: " + -// XposedHelpers.getObjectField(param.thisObject, "d").toString(), -// param.getThrowable() -// )); -// -// param.setResult(false); -// } -// } -// } -// ); - - /** - * =========================================================================== - * Just used as a fatal crash prevention... Likely just moves the issue - * =========================================================================== - */ -// XposedHelpers.findAndHookMethod( -// "hto", snapClassLoader, -// "c", String.class, -// new ST_MethodHook() { -// @Override protected void after(MethodHookParam param) throws Throwable { -// if (param.getThrowable() != null) { -// Timber.e(new Throwable( -// "Error raised checking lens set content", -// param.getThrowable() -// )); -// -// param.setResult(false); -// } -// } -// } -// ); - } - - private String transformOtherString(Preference preference) { - if (!miscChangesEnabled) - return null; - - String preferenceValue = getPref(preference); - - if (preferenceValue == null || preferenceValue.equals("Default")) { - return null; - } - - return preferenceValue; - } - - private Boolean transformBoolean(Preference preference) { - if (!miscChangesEnabled) - return null; - - String preferenceValue = getPref(preference); - - if (preferenceValue != null) { - if (preferenceValue.equals("On")) { - return true; - } else if (preferenceValue.equals("Off")) { - return false; - } - } - - return null; - } - - private String transformOverwrite(Preference preference) { - if (!miscChangesEnabled) - return null; - - String preferenceValue = getPref(preference); - - if (preferenceValue != null) { - if (preferenceValue.equals("On")) { - return "FORCE_ENABLED"; - } else if (preferenceValue.equals("Off")) { - return "FORCE_DISABLED"; - } - } - - return null; - - } - - private T transformOtherOnOff(Preference preference, T on, T off, T def) { - if (!miscChangesEnabled) - return null; - - String preferenceValue = getPref(preference); - - if (preferenceValue != null) { - if (preferenceValue.equals("On")) { - return on; - } else if (preferenceValue.equals("Off")) { - return off; - } - } - - return def; - } - - private void handleExperiment(MethodHookParam param, Object experimentMode) { - if (!miscChangesEnabled) - return; - - if (experimentMode != null) - param.setResult(experimentMode); - } - - private void handleABTestPrinting(MethodHookParam param) { - String groupName = (String) param.args[0]; - String experimentName = (String) param.args[1]; - - Object result = param.getResult(); - - switch (groupName) { - case "DISCOVER_V2": - break; - default: - Timber.d("ABTest [Group: %s][Exp: %s][Def: %s][Res: %s]", groupName, experimentName, param.args[2], - result + (result != null ? "(" + result.getClass() + ")" : "")); - } - } - - private void handleExperimentPrinting(MethodHookParam param) { - { - String key = (String) callMethod(param.args[0], "a"); - Object defaultValue = param.args[1]; - Object result = param.getResult(); - - switch (key) { - /** - * =========================================================================== - * Tests - * =========================================================================== - */ - case "cold_start_stabilization": - param.setResult("Rainbow"); - break; - case "is_official_user": - param.setResult(true); - break; - - case "is_logged_in": - case "is_registering": - case "username": - case "birthday": - case "enable_shake_to_report": - case "nycSettingsGhostMode": - case "nycSettingsGhostModeDuration": - case "nycSettingsAudience": - case "nycSettingsCustomFriendIds": - case "nycHasOnboarded": - case "nycDontShareState": - case "memories_year_end_story_badge": - case "developerOptionsNycTrackActivity": - case "BITMASK_AVATAR_ID": - case "has_seen_new_user_onboard_ui": - case "birthday_in_millis": - case "birthday_this_year_in_millis": - case "has_seen_snap_onboarding_message": - case "memories_year_end_story": - case "perf_bandwidthsampler_version": - case "perf_downloadmanager_hyperrequest_enabled": - case "perf_downloadmanager_hyperrequest_disabled": - case "user_id": - case "BITMOJI_SELFIE_ID": - case "daily_client_id_timestamp": - case "daily_client_id": - case "last_lenses_enabled_date": - case "auth_token": - case "perf_preferred_network_interface": - case "email": - case "perf_enable_detailed_timing_metrics": - case "story_count": - case "enable_featured_official_stories": - case "ad_track_user_data": - case "ad_preferences": - case "registered_in_cheetah": - case "developerOptionMockLocation": - case "developerOptionMockLocationNYCOffice": - case "enable_resumable_download": - case "delta_fetch_one_on_one_conversations": - case "perf_hyper_callback_feature_executor": - case "cheetah_selfie_id": - case "web_attachments_overwrite": - case "nycSharingNotificationLastSeenTimestamp": - case "nycLastOpenMapTimestamp": - case "nycSharingNotificationCount": - case "nycHasSeenSharingNotification": - case "nycSettingsShowDevToolTipOnce": - case "search_seeen_p2s_tooltip_count": - case "account_creation_millis": - case "has_seen_cheetah_camera_onboarding_my_story_management_tooltip": - case "has_seen_cheetah_onboarding_my_story_view_tooltip": - case "has_given_access_to_contacts": - case "is_popular_user": - case "profile_v3_phone_number_verification_prompt": - case "friendmoji_blocked_keys": - case "phone_number": - case "last_profile_fragment_exit_timestamp": - case "last_seen_added_me_timestamp": - case "bitmojiFetchInWebp": - case "FRIEND_FEED_AST_GROUP": - case "developerOptionsSnapAdsEnablePromotedStories": - case "developerOptionsCheetahFreeformRankingTreatments": - case "developer_option_lens_on_preview_tweak": - case "has_seen_swipe_filters_onboarding_message": - case "transcoding_overwrite_state": - case "transcodingState": - case "video_filters_overwrite_state": - case "video_filters": - case "usps_geofencing_v2": - case "usps_multi_location": - case "sticker_picker_hometab_overwrite": - case "context_filter_metadata_timestamp": - case "captureRotation": - case "show_geofilter_tools_enabled": - case "checksum_friends": - case "checksum_updates": - case "checksum_conversations": - case "checksums_study_settings": - case "last_time_low_sensitivity_unlockables_request": - case "backup_fastlane": - case "last_scan_unlocked_lenses_check_time": - case "next_force_scheduled_lenses_check_time": - case "should_force_low_sensitivity_request": - case "is_first_all_updates_on_app_upgrade": - case "geofilter_allow_concurrent_requests": - case "friends_sync_token": - case "FRIEND_FEED_LAST_FULL_SYNC_TIMESTAMP": - case "developerOptionsShouldShowLocationToast": - case "enable_usps_geolens": - case "has_clicked_to_turn_off_lens": - case "usps_gtq_migration_plan": - case "developer_option_force_camera_30_fps": - case "gles3_allowed": - case "is_using_multiple_frame_buffer_recording": - case "reg_user_complete_timestamp": - case "reg_last_transition_action": - case "reg_last_page": - case "developerOptionsShowFrameDispatcherBufferUsage": - case "snapads_ad_session_id": - case "snap_tag_image": - case "has_profile_images": - case "address_book_version": - case "is_device_whitelisted_for_lenses_on_backend": - case "PENDING_CLIENT_PROPERTIES_V1": - case "square_tos_accepted": - case "has_seen_send_to_quick_add_dialog": - case "has_seen_send_to_sms_snap_alert_v2": - case "has_seen_auto_friend_invite_alert": - case "tos_version_6_accepted": - case "spectacles_tos_accepted": - case "has_seen_camera_module_lens": - case "has_seen_camera_module_scan": - case "clipboard_detection_enabled": - case "search_our_story_attribution_tos_accepted": - case "has_used_memories_search": - case "number_of_enter_memories": - case "image_player_reset_timestamp": - case "transcoding_reset_timestamp": - case "cheetah_partial_story_response": - case "cash_customer_allowed": - case "last_seen_new_friends_tooltip_timestamp": - case "gcm_registration_id": - case "notificationsEnabled": - case "-1572590044has_given_access_to_contacts": - case "story_privacy_setting": - case "last_checked_trophycase_timestamp": - case "display_name_pop_up_count": - case "last_identity_action_timestamp": - case "developerOptionsDiscoverCustomRegion": - case "developerOptionsDiscoverCustomCountry": - case "suggested_friend_sync_version_v2": - case "use_unsigned_receipt": - case "dirty_video_rendering_overwrite_state": - case "dirty_video_rendering": - case "video_decoder_texcoord_transformation": - case "developerOptionsSetDecoderOperatingRate": - case "snapchatInfiniteVideoPreference": - case "should_override_track_url": - case "FRIEND_FEED_WARM_START_BACKGROUND_TIME_THRESHOLD_SECONDS": - case "FRIEND_FEED_WARM_START_LAST_FULL_RANKING_THRESHOLD_SECONDS": - case "has_seen_sound_tools_tooltip": - case "has_seen_auto_sticker_generation_tooltip": - case "developerOptionsCustomEndpoint": - case "app_application_open_client_ts": - case "last_permission_report_timestamp": - case "is_snapchat_contact_permission_synced": - case "reg_started": - break; - default: - Timber.d( - "New preference check: " + key + " | " + defaultValue + " | " - + result + (result != null ? "(" + result.getClass() + ")" : "") - ); - } - } - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.Context; + +import com.ljmu.andre.GsonPreferences.Preferences.Preference; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import de.robv.android.xposed.XC_MethodHook.MethodHookParam; +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_BOOLEAN; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_FLOAT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_INT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_LONG; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.AB_TEST_CHECK_STRING; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.ERROR_SUPPRESS_DOWNLOADER_RUNNABLE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.NETWORK_EXECUTE_SYNC; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_ANIMATED_CONTENT_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_CAMERA2_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_CAPTIONV2_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_CHAT_VIDEO_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_EMOJIBRUSH_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_FPS_OVERLAY_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_GIPHY_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_HANDSFREEREC_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_INSIGHTS_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_MULTI_SNAP_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_SKYFILTERS_STATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_ADVERTS_BLOCKED; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_DISCOVER_BLOCKED; +import static com.ljmu.andre.snaptools.Utils.FrameworkPreferencesDef.DISABLED_MODULES; +import static com.ljmu.andre.snaptools.Utils.PreferenceHelpers.collectionContains; +import static de.robv.android.xposed.XposedHelpers.callMethod; +import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; +import static de.robv.android.xposed.XposedHelpers.findClass; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class ForcedHooks extends ModuleHelper { + + private boolean miscChangesEnabled; + + public ForcedHooks(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + @Override + public FragmentHelper[] getUIFragments() { + return new FragmentHelper[0]; + } + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + boolean blockDiscovery = getPref(STORY_BLOCKER_DISCOVER_BLOCKED); + boolean blockAds = getPref(STORY_BLOCKER_ADVERTS_BLOCKED); + + miscChangesEnabled = !collectionContains(DISABLED_MODULES, "Misc Changes"); + + /** + * =========================================================================== + * Returns TRUE to mark SC as DEBUG build + * =========================================================================== + */ +// findAndHookMethod( +// "aqly", snapClassLoader, +// "p", XC_MethodReplacement.returnConstant(true) +// ); + + +// findAndHookMethod( +// "tfj", snapClassLoader, +// "a", findClass("tfj$c", snapClassLoader), +// new ST_MethodHook() { +// @Override protected void after(MethodHookParam param) throws Throwable { +// Timber.d("Inbound debug item: " + param.args[0] + " | " + param.getResult()); +// +// Enum debugEnum = (Enum) param.args[0]; +// +// switch (debugEnum.name()) { +// case "CROP_SNAP_ENABLED": +// param.setResult(true); +// break; +// } +// } +// } +// ); + + //Forced Chronological Friends Feed +// findAndHookMethod( +// "ifj", snapClassLoader, +// "a", List.class, findClass("sda", snapClassLoader), +// new HookWrapper((HookBefore) param -> XposedHelpers.setObjectField(param.thisObject, "a", null)) +// ); + + /* + Snapchat Experiments + */ + Boolean insightsMode = transformBoolean(FORCE_INSIGHTS_STATE); + String multiSnapMode = transformOverwrite(FORCE_MULTI_SNAP_STATE); + Boolean videoChatMode = transformBoolean(FORCE_CHAT_VIDEO_STATE); + String animatedContentMode = transformOverwrite(FORCE_ANIMATED_CONTENT_STATE); + Boolean giphyMode = transformBoolean(FORCE_GIPHY_STATE); + String captionV2Mode = transformOverwrite(FORCE_CAPTIONV2_STATE); + String camera2Mode = transformOverwrite(FORCE_CAMERA2_STATE); + Boolean camera2ModeBool = transformBoolean(FORCE_CAMERA2_STATE); + String handsFreeMode = transformOtherOnOff(FORCE_HANDSFREEREC_STATE, "FULLY_ENABLED", "DISABLED", null); + Boolean fpsOverlayMode = transformBoolean(FORCE_FPS_OVERLAY_STATE); + String skyFilterMode = transformOverwrite(FORCE_SKYFILTERS_STATE); + Boolean emojiBrushMode = transformBoolean(FORCE_EMOJIBRUSH_STATE); + + HookAfter experimentDebugHook = null; + +// if (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) +// experimentDebugHook = this::handleExperimentPrinting; + + findAndHookMethod( + "axbl", snapClassLoader, + "a", "axbm", Object.class, + new HookWrapper( + param -> { + String key = (String) callMethod(param.args[0], "a"); + + switch (key) { + /** + * =========================================================================== + * Experiments + * =========================================================================== + */ +// case "developerOptionCheetahMode": +// handleExperiment(param, cheetahMode); +// break; +// case "chat_v10": +// handleExperiment(param, chatV10Mode); +// break; + case "developerOptionsImpalaForceShowInsights": + handleExperiment(param, insightsMode); + break; + case "magikarp_overwrite": + handleExperiment(param, multiSnapMode); + break; + case "chat_video_enabled": + handleExperiment(param, videoChatMode); + break; + case "animated_content_overwrite": + handleExperiment(param, animatedContentMode); + break; + case "giphy_in_preview": + handleExperiment(param, giphyMode); + break; + case "caption_v2_overwrite": + handleExperiment(param, captionV2Mode); + break; + case "camera2_overwrite_state": + handleExperiment(param, camera2Mode); + break; + case "developerOptionsHandsFreeRecordingMode": + handleExperiment(param, handsFreeMode); + break; + case "developerOptionsShouldShowFpsOverlay": + handleExperiment(param, fpsOverlayMode); + break; + case "sky_filters_overwrite": + handleExperiment(param, skyFilterMode); + break; + case "emoji_brush": + handleExperiment(param, emojiBrushMode); + + break; + + + /** + * =========================================================================== + * Forced Enabled Settings + * =========================================================================== + */ + case "nycEnableStreaming": + case "developerOptionsNycSearchStreamingAbtestOverride": + case "developerOptionsNycPublicStoryStreamingAbtestOverride": + param.setResult("FALSE"); + break; + case "discover_feed_tab_mode": + param.setResult("FORCE_ON"); + break; + case "discover_feed_should_show_subscribed_tab_nux": + param.setResult(true); + break; + case "enable geofilters": + param.setResult(true); + break; + } + }, + experimentDebugHook + ) + ); + + ST_MethodHook abTestHook = new HookWrapper( + (HookBefore) param -> { + String groupName = (String) param.args[0]; + String experimentName = (String) param.args[1]; + + String key = groupName + ":" + experimentName; + + switch (key) { + case "DOWNSCALE_TAKE_PICTURE_API_PHOTO_BEFORE_SEND:ENABLED": + handleExperiment(param, camera2ModeBool); + break; + case "ADS_HOLDOUT_01:SHOW_ADS": + case "ADS_HOLDOUT_01:ADS_IN_AA": + if (blockAds) + param.setResult(false); + break; + case "CRASHLYTICS:USE_CRASHLYTICS": + param.setResult(false); + break; + case "PUBLIC_STORY_STREAMING_ANDROID:enable": + case "STREAMING_PROMOTED_STORIES_ANDROID:enable": + param.setResult(false); + break; + } + }, +// (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) ? +// (HookAfter) param -> handleABTestPrinting(param) : null + null + ); + + hookMethod( + AB_TEST_CHECK_STRING, + abTestHook + ); + + hookMethod( + AB_TEST_CHECK_INT, + abTestHook + ); + + hookMethod( + AB_TEST_CHECK_LONG, + abTestHook + ); + + hookMethod( + AB_TEST_CHECK_BOOLEAN, + abTestHook + ); + + hookMethod( + AB_TEST_CHECK_FLOAT, + abTestHook + ); + +// hookMethod( +// AB_TEST_CHECK_VALUE, +// new HookWrapper((HookAfter) param -> { +// Timber.d("RAK: [p1: %s][p2: %s][p3: %s][r: %s]", param.args[0], param.args[1], param.args[2], param.getResult()); +// +// switch ((String) param.args[0]) { +//// case "CHEETAH_ANDROID": +//// if (param.args[1].equals("CHEETAH_MODE")) +//// param.setResult(forceCheetah ? "FULL_CHEETAH" : "OLD_DESIGN"); +//// break; +//// case "DIRECT_FILE_SNAP_UPLOAD": +//// if (param.args[1].equals("ENABLED")) +//// param.setResult("true"); +//// break; +//// case "LANDING_PAGE_CAMERA_VIEW": +//// param.setResult("true"); +//// break; +// case "DOWNSCALE_TAKE_PICTURE_API_PHOTO_BEFORE_SEND": +// if (param.args[1].equals("ENABLED")) +// param.setResult("false"); +// break; +//// case "ANDROID_CAMERA2_AND_TAKEPICTURE_API_v2": +//// if (param.args[1].equals("enable")) +//// param.setResult("true"); +//// break; +//// case "CAMERA_FRAGMENT_V2": +//// if (param.args[1].equals("ENABLED")) +//// param.setResult("true"); +//// break; +// case "PUBLIC_STORY_STREAMING_ANDROID": +// if (param.args[1].equals("enable")) +// param.setResult("false"); +// return; +// case "STREAMING_PROMOTED_STORIES_ANDROID": +// if (param.args[1].equals("enable")) +// param.setResult("false"); +// return; +//// case "ASYM_VIEW_PAGER": +//// if (param.args[1].equals("ENABLED")) +//// param.setResult("true"); +//// break; +//// case "SECURE_CHAT_SESSION_V2": +//// if (param.args[1].equals("ENABLED")) +//// param.setResult("true"); +//// break; +// } +// +// switch ((String) param.args[1]) { +// case "SHOW_ADS": +// case "ADS_IN_AA": +// if (getPref(STORY_BLOCKER_ADVERTS_BLOCKED)) +// param.setResult("false"); +// return; +// case "ENABLE_MULTI_SNAP": +// param.setResult("true"); +// return; +// case "ENABLE_PREVIEW_V2": +// param.setResult("true"); +// return; +// case "USE_CRASHLYTICS": +// param.setResult("false"); +// return; +//// case "RELEASE_DELAY_TIME_WHEN_USER_SWIPE_INTO_CONVERSATION": +//// param.setResult("5000"); +//// break; +// case "ENCODING_QUALITY": +// param.setResult("100"); +// break; +// case "DF_TAB_ANDROID": +// if (blockDiscovery) +// param.setResult("true"); +// break; +//// case "USE_SERVER_SIDE_PRECACHING": +//// param.setResult("false"); +//// break; +//// case "MEDIA_PLAYER_IMPL_EXOPLAYER": +//// param.setResult("true"); +//// break; +//// case "SHOULD_SHOW_SPONSORED_SECTION": +//// param.setResult("false"); +//// break; +//// case "fps": +//// param.setResult("30"); +//// break; +// } +// +// })); + + + hookMethod( + NETWORK_EXECUTE_SYNC, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + String url = (String) callMethod(param.thisObject, "getUrl"); + + Timber.d("Network URL: " + url); + + if (url.endsWith("logout")) { + Timber.d("Blocking logout"); + param.setResult(null); + } + } + } + ); + // Error Suppression for the hook above + hookMethod( + ERROR_SUPPRESS_DOWNLOADER_RUNNABLE, + new HookWrapper((HookAfter) param -> { + if (param.getThrowable() != null) { + Timber.d("Download Runnable Error Suppression"); + param.setThrowable(null); + } + }) + ); + + /** + * =========================================================================== + * Just used as a fatal crash prevention... Likely just moves the issue + * =========================================================================== + */ +// XposedHelpers.findAndHookMethod( +// "htt", snapClassLoader, +// "a", +// new ST_MethodHook() { +// @Override protected void after(MethodHookParam param) throws Throwable { +// if (param.getThrowable() != null) { +// Timber.e(new Throwable( +// "Error raised from lens: " + +// XposedHelpers.getObjectField(param.thisObject, "d").toString(), +// param.getThrowable() +// )); +// +// param.setResult(false); +// } +// } +// } +// ); + + /** + * =========================================================================== + * Just used as a fatal crash prevention... Likely just moves the issue + * =========================================================================== + */ +// XposedHelpers.findAndHookMethod( +// "hto", snapClassLoader, +// "c", String.class, +// new ST_MethodHook() { +// @Override protected void after(MethodHookParam param) throws Throwable { +// if (param.getThrowable() != null) { +// Timber.e(new Throwable( +// "Error raised checking lens set content", +// param.getThrowable() +// )); +// +// param.setResult(false); +// } +// } +// } +// ); + } + + private String transformOtherString(Preference preference) { + if (!miscChangesEnabled) + return null; + + String preferenceValue = getPref(preference); + + if (preferenceValue == null || preferenceValue.equals("Default")) { + return null; + } + + return preferenceValue; + } + + private Boolean transformBoolean(Preference preference) { + if (!miscChangesEnabled) + return null; + + String preferenceValue = getPref(preference); + + if (preferenceValue != null) { + if (preferenceValue.equals("On")) { + return true; + } else if (preferenceValue.equals("Off")) { + return false; + } + } + + return null; + } + + private String transformOverwrite(Preference preference) { + if (!miscChangesEnabled) + return null; + + String preferenceValue = getPref(preference); + + if (preferenceValue != null) { + if (preferenceValue.equals("On")) { + return "FORCE_ENABLED"; + } else if (preferenceValue.equals("Off")) { + return "FORCE_DISABLED"; + } + } + + return null; + + } + + private T transformOtherOnOff(Preference preference, T on, T off, T def) { + if (!miscChangesEnabled) + return null; + + String preferenceValue = getPref(preference); + + if (preferenceValue != null) { + if (preferenceValue.equals("On")) { + return on; + } else if (preferenceValue.equals("Off")) { + return off; + } + } + + return def; + } + + private void handleExperiment(MethodHookParam param, Object experimentMode) { + if (!miscChangesEnabled) + return; + + if (experimentMode != null) + param.setResult(experimentMode); + } + + private void handleABTestPrinting(MethodHookParam param) { + String groupName = (String) param.args[0]; + String experimentName = (String) param.args[1]; + + Object result = param.getResult(); + + switch (groupName) { + case "DISCOVER_V2": + break; + default: + Timber.d("ABTest [Group: %s][Exp: %s][Def: %s][Res: %s]", groupName, experimentName, param.args[2], + result + (result != null ? "(" + result.getClass() + ")" : "")); + } + } + + private void handleExperimentPrinting(MethodHookParam param) { + { + String key = (String) callMethod(param.args[0], "a"); + Object defaultValue = param.args[1]; + Object result = param.getResult(); + + switch (key) { + /** + * =========================================================================== + * Tests + * =========================================================================== + */ + case "cold_start_stabilization": + param.setResult("Rainbow"); + break; + case "is_official_user": + param.setResult(true); + break; + + case "is_logged_in": + case "is_registering": + case "username": + case "birthday": + case "enable_shake_to_report": + case "nycSettingsGhostMode": + case "nycSettingsGhostModeDuration": + case "nycSettingsAudience": + case "nycSettingsCustomFriendIds": + case "nycHasOnboarded": + case "nycDontShareState": + case "memories_year_end_story_badge": + case "developerOptionsNycTrackActivity": + case "BITMASK_AVATAR_ID": + case "has_seen_new_user_onboard_ui": + case "birthday_in_millis": + case "birthday_this_year_in_millis": + case "has_seen_snap_onboarding_message": + case "memories_year_end_story": + case "perf_bandwidthsampler_version": + case "perf_downloadmanager_hyperrequest_enabled": + case "perf_downloadmanager_hyperrequest_disabled": + case "user_id": + case "BITMOJI_SELFIE_ID": + case "daily_client_id_timestamp": + case "daily_client_id": + case "last_lenses_enabled_date": + case "auth_token": + case "perf_preferred_network_interface": + case "email": + case "perf_enable_detailed_timing_metrics": + case "story_count": + case "enable_featured_official_stories": + case "ad_track_user_data": + case "ad_preferences": + case "registered_in_cheetah": + case "developerOptionMockLocation": + case "developerOptionMockLocationNYCOffice": + case "enable_resumable_download": + case "delta_fetch_one_on_one_conversations": + case "perf_hyper_callback_feature_executor": + case "cheetah_selfie_id": + case "web_attachments_overwrite": + case "nycSharingNotificationLastSeenTimestamp": + case "nycLastOpenMapTimestamp": + case "nycSharingNotificationCount": + case "nycHasSeenSharingNotification": + case "nycSettingsShowDevToolTipOnce": + case "search_seeen_p2s_tooltip_count": + case "account_creation_millis": + case "has_seen_cheetah_camera_onboarding_my_story_management_tooltip": + case "has_seen_cheetah_onboarding_my_story_view_tooltip": + case "has_given_access_to_contacts": + case "is_popular_user": + case "profile_v3_phone_number_verification_prompt": + case "friendmoji_blocked_keys": + case "phone_number": + case "last_profile_fragment_exit_timestamp": + case "last_seen_added_me_timestamp": + case "bitmojiFetchInWebp": + case "FRIEND_FEED_AST_GROUP": + case "developerOptionsSnapAdsEnablePromotedStories": + case "developerOptionsCheetahFreeformRankingTreatments": + case "developer_option_lens_on_preview_tweak": + case "has_seen_swipe_filters_onboarding_message": + case "transcoding_overwrite_state": + case "transcodingState": + case "video_filters_overwrite_state": + case "video_filters": + case "usps_geofencing_v2": + case "usps_multi_location": + case "sticker_picker_hometab_overwrite": + case "context_filter_metadata_timestamp": + case "captureRotation": + case "show_geofilter_tools_enabled": + case "checksum_friends": + case "checksum_updates": + case "checksum_conversations": + case "checksums_study_settings": + case "last_time_low_sensitivity_unlockables_request": + case "backup_fastlane": + case "last_scan_unlocked_lenses_check_time": + case "next_force_scheduled_lenses_check_time": + case "should_force_low_sensitivity_request": + case "is_first_all_updates_on_app_upgrade": + case "geofilter_allow_concurrent_requests": + case "friends_sync_token": + case "FRIEND_FEED_LAST_FULL_SYNC_TIMESTAMP": + case "developerOptionsShouldShowLocationToast": + case "enable_usps_geolens": + case "has_clicked_to_turn_off_lens": + case "usps_gtq_migration_plan": + case "developer_option_force_camera_30_fps": + case "gles3_allowed": + case "is_using_multiple_frame_buffer_recording": + case "reg_user_complete_timestamp": + case "reg_last_transition_action": + case "reg_last_page": + case "developerOptionsShowFrameDispatcherBufferUsage": + case "snapads_ad_session_id": + case "snap_tag_image": + case "has_profile_images": + case "address_book_version": + case "is_device_whitelisted_for_lenses_on_backend": + case "PENDING_CLIENT_PROPERTIES_V1": + case "square_tos_accepted": + case "has_seen_send_to_quick_add_dialog": + case "has_seen_send_to_sms_snap_alert_v2": + case "has_seen_auto_friend_invite_alert": + case "tos_version_6_accepted": + case "spectacles_tos_accepted": + case "has_seen_camera_module_lens": + case "has_seen_camera_module_scan": + case "clipboard_detection_enabled": + case "search_our_story_attribution_tos_accepted": + case "has_used_memories_search": + case "number_of_enter_memories": + case "image_player_reset_timestamp": + case "transcoding_reset_timestamp": + case "cheetah_partial_story_response": + case "cash_customer_allowed": + case "last_seen_new_friends_tooltip_timestamp": + case "gcm_registration_id": + case "notificationsEnabled": + case "-1572590044has_given_access_to_contacts": + case "story_privacy_setting": + case "last_checked_trophycase_timestamp": + case "display_name_pop_up_count": + case "last_identity_action_timestamp": + case "developerOptionsDiscoverCustomRegion": + case "developerOptionsDiscoverCustomCountry": + case "suggested_friend_sync_version_v2": + case "use_unsigned_receipt": + case "dirty_video_rendering_overwrite_state": + case "dirty_video_rendering": + case "video_decoder_texcoord_transformation": + case "developerOptionsSetDecoderOperatingRate": + case "snapchatInfiniteVideoPreference": + case "should_override_track_url": + case "FRIEND_FEED_WARM_START_BACKGROUND_TIME_THRESHOLD_SECONDS": + case "FRIEND_FEED_WARM_START_LAST_FULL_RANKING_THRESHOLD_SECONDS": + case "has_seen_sound_tools_tooltip": + case "has_seen_auto_sticker_generation_tooltip": + case "developerOptionsCustomEndpoint": + case "app_application_open_client_ts": + case "last_permission_report_timestamp": + case "is_snapchat_contact_permission_synced": + case "reg_started": + break; + default: + Timber.d( + "New preference check: " + key + " | " + defaultValue + " | " + + result + (result != null ? "(" + result.getClass() + ")" : "") + ); + } + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookClassDef.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookClassDef.java index 4bedd71..798b5d0 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookClassDef.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookClassDef.java @@ -1,104 +1,103 @@ -package com.ljmu.andre.snaptools.ModulePack.HookDefinitions; - -import androidx.annotation.NonNull; -import com.ljmu.andre.ConstantDefiner.Constant; -import com.ljmu.andre.ConstantDefiner.ConstantDefiner; - -public class HookClassDef extends ConstantDefiner { - public static final HookClass AB_TEST_MANAGER = new HookClass("AB_TEST_MANAGER", "aoqr");// - public static final HookClass CAMERA_FRAGMENT = new HookClass("CAMERA_FRAGMENT", "com.snapchat.android.app.feature.camera.CameraFragmentV2"); - public static final HookClass CAPTION_MANAGER_CLASS = new HookClass("CAPTION_MANAGER_CLASS", "urb");// - public static final HookClass CHAT_BODY_METADATA = new HookClass("CHAT_BODY_METADATA", "atjc");// - public static final HookClass CHAT_DIRECT_VIEW_MARKER = new HookClass("CHAT_DIRECT_VIEW_MARKER", "ahtv");// - public static final HookClass CHAT_GROUP_VIEW_MARKER = new HookClass("CHAT_GROUP_VIEW_MARKER", "ahuy");// - public static final HookClass CHAT_HEADER_METADATA = new HookClass("CHAT_HEADER_METADATA", "atep");// - public static final HookClass CHAT_IMAGE_METADATA = new HookClass("CHAT_IMAGE_METADATA", "aida");// - public static final HookClass CHAT_MESSAGE_VIEW_HOLDER = new HookClass("CHAT_MESSAGE_VIEW_HOLDER", "aimn");// - public static final HookClass CHAT_METADATA = new HookClass("CHAT_METADATA", "aswo");// - public static final HookClass CHAT_METADATA_JSON_PARSER = new HookClass("CHAT_METADATA_JSON_PARSER", "aswp");// - public static final HookClass CHAT_METADATA_JSON_PARSER_SECOND = new HookClass("CHAT_METADATA_JSON_PARSER_SECOND", "atjs");// - public static final HookClass CHAT_NOTIFICATION_CREATOR = new HookClass("CHAT_NOTIFICATION_CREATOR", "ahqm");// UNSURE ON THIS ONE - public static final HookClass CHAT_V10_BUILDER = new HookClass("CHAT_V10_BUILDER", "ahxy");// - public static final HookClass CHAT_VIDEO = new HookClass("CHAT_VIDEO", "aipq");// - public static final HookClass CHAT_VIDEO_METADATA = new HookClass("CHAT_VIDEO_METADATA", "aivs");// - public static final HookClass CHEETAH_PROFILE_SETTINGS_CREATOR = new HookClass("CHEETAH_PROFILE_SETTINGS_CREATOR", "afdb");// - public static final HookClass COUNTDOWNTIMER_VIEW = new HookClass("COUNTDOWNTIMER_VIEW", "com.snap.opera.view.CountdownTimerView"); - public static final HookClass DOWNLOADER_RUNNABLE = new HookClass("DOWNLOADER_RUNNABLE", "aoew$2"); - public static final HookClass ENCRYPTED_STREAM_BUILDER = new HookClass("ENCRYPTED_STREAM_BUILDER", "anxy");// - public static final HookClass ENCRYPTION_ALGORITHM = new HookClass("ENCRYPTION_ALGORITHM", "com.snapchat.android.framework.crypto.CbcEncryptionAlgorithm"); - public static final HookClass ENUM_BATCHED_SNAP_POSITION = new HookClass("ENUM_BATCHED_SNAP_POSITION", "aaiw");// - public static final HookClass ENUM_LENS_ACTIVATOR_TYPE = new HookClass("ENUM_LENS_ACTIVATOR_TYPE", "com.looksery.sdk.domain.Category.ActivatorType"); - public static final HookClass ENUM_LENS_TYPE = new HookClass("ENUM_LENS_TYPE", "afwd$c");// - public static final HookClass ENUM_SNAP_ADVANCE_MODES = new HookClass("ENUM_SNAP_ADVANCE_MODES", "tez"); - public static final HookClass EXPERIMENT_BASE = new HookClass("EXPERIMENT_BASE", "aoqo"); - public static final HookClass FONT_CLASS = new HookClass("FONT_CLASS", "android.graphics.Typeface"); - public static final HookClass FRIEND_PROFILE_POPUP_FRAGMENT = new HookClass("FRIEND_PROFILE_POPUP_FRAGMENT", "com.snapchat.android.app.feature.miniprofile.internal.friend.FriendMiniProfilePopupFragment"); - public static final HookClass GROUP_SNAP_METADATA = new HookClass("GROUP_SNAP_METADATA", "aidr");//Unsure on this, seems to be pretty different but contains similar stuff to aotd - public static final HookClass GROUP_SNAP_WRAPPER = new HookClass("GROUP_SNAP_WRAPPER", "tpp");// - public static final HookClassDef INST = new HookClassDef(); - public static final HookClass LENS = new HookClass("LENS", "afwd");// - public static final HookClass LENS_APPLICATION_CONTEXT_ENUM = new HookClass("LENS_APPLICATION_CONTEXT_ENUM", "afwe");// - public static final HookClass LENS_ASSET_BUILT = new HookClass("LENS_ASSET_BUILT", "afwf");// - public static final HookClass LENS_ASSET_LOAD_MODE = new HookClass("LENS_ASSET_LOAD_MODE", "afwz");// Unsure on this, seems similar but also some different code - public static final HookClass LENS_ASSET_TYPE = new HookClass("LENS_ASSET_TYPE", "afwf$a");// - public static final HookClass LENS_AUTHENTICATION = new HookClass("LENS_AUTHENTICATION", "com.snapchat.android.app.feature.lenses.internal.security.LensesSecurityManager"); - public static final HookClass LENS_CAMERA_CONTEXT_ENUM = new HookClass("LENS_CAMERA_CONTEXT_ENUM", "afwh");// - public static final HookClass LENS_CATEGORY = new HookClass("LENS_CATEGORY", "afwm");// - public static final HookClass LENS_CATEGORY_RESOLVER = new HookClass("LENS_CATEGORY_RESOLVER", "afwk");// - public static final HookClass LENS_CONTEXT_HOLDER = new HookClass("LENS_CONTEXT_HOLDER", "afwq"); - public static final HookClass LENS_LOADER = new HookClass("LENS_LOADER", "afss");// - public static final HookClass LENS_SLUG = new HookClass("LENS_SLUG", "atqf");// - public static final HookClass LENS_TRACK = new HookClass("LENS_TRACK", "atup");// - public static final HookClass META_DATA_BUILDER = new HookClass("META_DATA_BUILDER", "ajfg");// - public static final HookClass NETWORK_DISPATCHER = new HookClass("NETWORK_DISPATCHER", "ahtc");// - public static final HookClass NETWORK_MANAGER = new HookClass("NETWORK_MANAGER", "aoci");// - public static final HookClass NEW_CONCENTRIC_TIMERVIEW = new HookClass("NEW_CONCENTRIC_TIMERVIEW", "com.snap.opera.view.NewConcentricTimerView"); - public static final HookClass OPERA_PAGE_VIEW = new HookClass("OPERA_PAGE_VIEW", "com.snap.opera.view.OperaPageView"); - public static final HookClass RECEIVED_SNAP = new HookClass("RECEIVED_SNAP", "amuu");// - public static final HookClass RECEIVED_SNAP_ENCRYPTION = new HookClass("RECEIVED_SNAP_ENCRYPTION", "aiue");// - public static final HookClass RECEIVED_SNAP_PAYLOAD_BUILDER = new HookClass("RECEIVED_SNAP_PAYLOAD_BUILDER", "abiz");// - public static final HookClass SCREENSHOT_DETECTOR = new HookClass("SCREENSHOT_DETECTOR", "aolm");// - public static final HookClass SENT_BATCHED_VIDEO = new HookClass("SENT_BATCHED_VIDEO", "acpk");// - public static final HookClass SENT_IMAGE = new HookClass("SENT_IMAGE", "amux");// - public static final HookClass SENT_SNAP_BASE = new HookClass("SENT_SNAP_BASE", "amtz");// - public static final HookClass SENT_VIDEO = new HookClass("SENT_VIDEO", "amvk");// - public static final HookClass SHARE_IMAGE = new HookClass("SHARE_IMAGE", "acmu");// - public static final HookClass SHARE_VIDEO = new HookClass("SHARE_VIDEO", "acmv");// - public static final HookClass SNAPCHAT_CAPTION_VIEW_CLASS = new HookClass("SNAPCHAT_CAPTION_VIEW_CLASS", "com.snapchat.android.app.feature.creativetools.caption.SnapCaptionView"); - public static final HookClass SNAP_BASE = new HookClass("SNAP_BASE", "aisv"); // Checked - public static final HookClass SNAP_COUNTDOWN_CONTROLLER = new HookClass("SNAP_COUNTDOWN_CONTROLLER", "amdd"); - public static final HookClass SNAP_STATUS = new HookClass("SNAP_STATUS", "aisv$a");// Unsure if correct - public static final HookClass STORY_ADVANCER = new HookClass("STORY_ADVANCER", "sxh");// - public static final HookClass STORY_DATA_DISCOVER = new HookClass("STORY_DATA_DISCOVER", "iqw");// - public static final HookClass STORY_DATA_DYNAMIC = new HookClass("STORY_DATA_DYNAMIC", "iqx"); - public static final HookClass STORY_DATA_MAP = new HookClass("STORY_DATA_MAP", "iqy"); - public static final HookClass STORY_DATA_MOMENT = new HookClass("STORY_DATA_MOMENT", "irk"); - public static final HookClass STORY_DATA_PROMOTED = new HookClass("STORY_DATA_PROMOTED", "iqz"); - public static final HookClass STORY_FRIEND_RECENT = new HookClass("STORY_FRIEND_RECENT", "ell"); - public static final HookClass STORY_FRIEND_VIEWED = new HookClass("STORY_FRIEND_VIEWED", "acad"); - public static final HookClass STORY_LOADER = new HookClass("STORY_LOADER", "abqh"); - public static final HookClass STORY_MANAGER = new HookClass("STORY_MANAGER", "abxz"); - public static final HookClass STORY_METADATA = new HookClass("STORY_METADATA", "tpp"); - public static final HookClass STORY_METADATA_LOADER = new HookClass("STORY_METADATA_LOADER", "acbz"); - public static final HookClass STORY_SNAP = new HookClass("STORY_SNAP", "ammt"); - //public static final HookClass STORY_SNAP_AD_LOADER = new HookClass("STORY_SNAP_AD_LOADER", "abrz"); - public static final HookClass STORY_SNAP_PAYLOAD_BUILDER = new HookClass("STORY_SNAP_PAYLOAD_BUILDER", "abtf"); - public static final HookClass STORY_SPONSORED = new HookClass("STORY_ADVERT", "eng"); - public static final HookClass STORY_STATUS_UPDATER = new HookClass("STORY_STATUS_UPDATER", "abts"); - public static final HookClass ADVANCE_FIX = new HookClass("ADVANCE_FIX", "aay$1"); - public static final HookClass TEXTURE_VIDEO_VIEW = new HookClass("TEXTURE_VIDEO_VIEW", "com.snap.opera.shared.view.TextureVideoView"); - public static final HookClass USER_PREFS = new HookClass("USER_PREFS", "com.snapchat.android.core.user.UserPrefsImpl"); - - public static class HookClass extends Constant { - private String strClass; - - public HookClass(String name, @NonNull String value) { - super(name); - this.strClass = value; - } - - public String getStrClass() { - return this.strClass; - } - } -} +package com.ljmu.andre.snaptools.ModulePack.HookDefinitions; + +import androidx.annotation.NonNull; +import com.ljmu.andre.ConstantDefiner.Constant; +import com.ljmu.andre.ConstantDefiner.ConstantDefiner; + +public class HookClassDef extends ConstantDefiner { + public static final HookClass AB_TEST_MANAGER = new HookClass("AB_TEST_MANAGER", "awho"); + public static final HookClass CAMERA_FRAGMENT = new HookClass("CAMERA_FRAGMENT", "com.snapchat.android.app.feature.camera.CameraFragmentV2"); + public static final HookClass CAPTION_MANAGER_CLASS = new HookClass("CAPTION_MANAGER_CLASS", "yvu"); + public static final HookClass CHAT_BODY_METADATA = new HookClass("CHAT_BODY_METADATA", "bbyg"); + public static final HookClass CHAT_DIRECT_VIEW_MARKER = new HookClass("CHAT_DIRECT_VIEW_MARKER", "aogv"); + public static final HookClass CHAT_GROUP_VIEW_MARKER = new HookClass("CHAT_GROUP_VIEW_MARKER", "aohx"); + public static final HookClass CHAT_HEADER_METADATA = new HookClass("CHAT_HEADER_METADATA", "bbtq"); + public static final HookClass CHAT_IMAGE_METADATA = new HookClass("CHAT_IMAGE_METADATA", "aosk"); + public static final HookClass CHAT_MESSAGE_VIEW_HOLDER = new HookClass("CHAT_MESSAGE_VIEW_HOLDER", "apcm"); + public static final HookClass CHAT_METADATA = new HookClass("CHAT_METADATA", "bblp"); + public static final HookClass CHAT_METADATA_JSON_PARSER = new HookClass("CHAT_METADATA_JSON_PARSER", "bblq"); + public static final HookClass CHAT_METADATA_JSON_PARSER_SECOND = new HookClass("CHAT_METADATA_JSON_PARSER_SECOND", "bbyw"); + public static final HookClass CHAT_NOTIFICATION_CREATOR = new HookClass("CHAT_NOTIFICATION_CREATOR", "aodb"); + public static final HookClass CHAT_V10_BUILDER = new HookClass("CHAT_V10_BUILDER", "aolg"); + public static final HookClass CHAT_VIDEO = new HookClass("CHAT_VIDEO", "apfw"); + public static final HookClass CHAT_VIDEO_METADATA = new HookClass("CHAT_VIDEO_METADATA", "apma"); + public static final HookClass CHEETAH_PROFILE_SETTINGS_CREATOR = new HookClass("CHEETAH_PROFILE_SETTINGS_CREATOR", "akvx"); + public static final HookClass COUNTDOWNTIMER_VIEW = new HookClass("COUNTDOWNTIMER_VIEW", "com.snap.opera.view.CountdownTimerView"); + public static final HookClass DOWNLOADER_RUNNABLE = new HookClass("DOWNLOADER_RUNNABLE", "avun$2"); + public static final HookClass ENCRYPTED_STREAM_BUILDER = new HookClass("ENCRYPTED_STREAM_BUILDER", "avnn"); + public static final HookClass ENCRYPTION_ALGORITHM = new HookClass("ENCRYPTION_ALGORITHM", "com.snapchat.android.framework.crypto.CbcEncryptionAlgorithm"); + public static final HookClass ENUM_BATCHED_SNAP_POSITION = new HookClass("ENUM_BATCHED_SNAP_POSITION", "aexf"); + public static final HookClass ENUM_LENS_ACTIVATOR_TYPE = new HookClass("ENUM_LENS_ACTIVATOR_TYPE", "com.looksery.sdk.domain.Category.ActivatorType"); + public static final HookClass ENUM_LENS_TYPE = new HookClass("ENUM_LENS_TYPE", "amaw$c"); + public static final HookClass ENUM_SNAP_ADVANCE_MODES = new HookClass("ENUM_SNAP_ADVANCE_MODES", "xcr"); + public static final HookClass EXPERIMENT_BASE = new HookClass("EXPERIMENT_BASE", "awhl"); + public static final HookClass FONT_CLASS = new HookClass("FONT_CLASS", "android.graphics.Typeface"); + public static final HookClass FRIEND_PROFILE_POPUP_FRAGMENT = new HookClass("FRIEND_PROFILE_POPUP_FRAGMENT", "com.snapchat.android.app.feature.miniprofile.internal.friend.FriendMiniProfilePopupFragment"); + public static final HookClass GROUP_SNAP_METADATA = new HookClass("GROUP_SNAP_METADATA", "aotd"); + public static final HookClass GROUP_SNAP_WRAPPER = new HookClass("GROUP_SNAP_WRAPPER", "xop"); + public static final HookClassDef INST = new HookClassDef(); + public static final HookClass LENS = new HookClass("LENS", "amaw"); + public static final HookClass LENS_APPLICATION_CONTEXT_ENUM = new HookClass("LENS_APPLICATION_CONTEXT_ENUM", "amax"); + public static final HookClass LENS_ASSET_BUILT = new HookClass("LENS_ASSET_BUILT", "amay"); + public static final HookClass LENS_ASSET_LOAD_MODE = new HookClass("LENS_ASSET_LOAD_MODE", "ambv"); + public static final HookClass LENS_ASSET_TYPE = new HookClass("LENS_ASSET_TYPE", "amay$a"); + public static final HookClass LENS_AUTHENTICATION = new HookClass("LENS_AUTHENTICATION", "com.snapchat.android.app.feature.lenses.internal.security.LensesSecurityManager"); + public static final HookClass LENS_CAMERA_CONTEXT_ENUM = new HookClass("LENS_CAMERA_CONTEXT_ENUM", "amba"); + public static final HookClass LENS_CATEGORY = new HookClass("LENS_CATEGORY", "ambf"); + public static final HookClass LENS_CATEGORY_RESOLVER = new HookClass("LENS_CATEGORY_RESOLVER", "ambd"); + public static final HookClass LENS_CONTEXT_HOLDER = new HookClass("LENS_CONTEXT_HOLDER", "ambj"); + public static final HookClass LENS_LOADER = new HookClass("LENS_LOADER", "alur"); + public static final HookClass LENS_SLUG = new HookClass("LENS_SLUG", "bcfj"); + public static final HookClass LENS_TRACK = new HookClass("LENS_TRACK", "bcjt"); + public static final HookClass META_DATA_BUILDER = new HookClass("META_DATA_BUILDER", "apyt"); + public static final HookClass NETWORK_DISPATCHER = new HookClass("NETWORK_DISPATCHER", "aogb"); + public static final HookClass NETWORK_MANAGER = new HookClass("NETWORK_MANAGER", "avrz"); + public static final HookClass NEW_CONCENTRIC_TIMERVIEW = new HookClass("NEW_CONCENTRIC_TIMERVIEW", "com.snap.opera.view.NewConcentricTimerView"); + public static final HookClass OPERA_PAGE_VIEW = new HookClass("OPERA_PAGE_VIEW", "com.snap.opera.view.OperaPageView"); + public static final HookClass RECEIVED_SNAP = new HookClass("RECEIVED_SNAP", "auhw"); + public static final HookClass RECEIVED_SNAP_ENCRYPTION = new HookClass("RECEIVED_SNAP_ENCRYPTION", "apkj"); + public static final HookClass RECEIVED_SNAP_PAYLOAD_BUILDER = new HookClass("RECEIVED_SNAP_PAYLOAD_BUILDER", "afxv"); + public static final HookClass SCREENSHOT_DETECTOR = new HookClass("SCREENSHOT_DETECTOR", "awbn"); + public static final HookClass SENT_BATCHED_VIDEO = new HookClass("SENT_BATCHED_VIDEO", "ahnk"); + public static final HookClass SENT_IMAGE = new HookClass("SENT_IMAGE", "auhz"); + public static final HookClass SENT_SNAP_BASE = new HookClass("SENT_SNAP_BASE", "auhb"); + public static final HookClass SENT_VIDEO = new HookClass("SENT_VIDEO", "auin"); + public static final HookClass SHARE_IMAGE = new HookClass("SHARE_IMAGE", "ahhy"); + public static final HookClass SHARE_VIDEO = new HookClass("SHARE_VIDEO", "ahid"); + public static final HookClass SNAPCHAT_CAPTION_VIEW_CLASS = new HookClass("SNAPCHAT_CAPTION_VIEW_CLASS", "com.snapchat.android.app.feature.creativetools.caption.SnapCaptionView"); + public static final HookClass SNAP_BASE = new HookClass("SNAP_BASE", "apja"); + public static final HookClass SNAP_COUNTDOWN_CONTROLLER = new HookClass("SNAP_COUNTDOWN_CONTROLLER", "atpd"); + public static final HookClass SNAP_STATUS = new HookClass("SNAP_STATUS", "apja$a"); + public static final HookClass STORY_ADVANCER = new HookClass("STORY_ADVANCER", "wtp"); + public static final HookClass STORY_DATA_DISCOVER = new HookClass("STORY_DATA_DISCOVER", "kml"); + public static final HookClass STORY_DATA_DYNAMIC = new HookClass("STORY_DATA_DYNAMIC", "kmm"); + public static final HookClass STORY_DATA_MAP = new HookClass("STORY_DATA_MAP", "kmn"); + public static final HookClass STORY_DATA_MOMENT = new HookClass("STORY_DATA_MOMENT", "kmz"); + public static final HookClass STORY_DATA_PROMOTED = new HookClass("STORY_DATA_PROMOTED", "kmo"); + public static final HookClass STORY_FRIEND_RECENT = new HookClass("STORY_FRIEND_RECENT", "eut"); + public static final HookClass STORY_FRIEND_VIEWED = new HookClass("STORY_FRIEND_VIEWED", "agru"); + public static final HookClass STORY_LOADER = new HookClass("STORY_LOADER", "agfs"); + public static final HookClass STORY_MANAGER = new HookClass("STORY_MANAGER", "agos"); + public static final HookClass STORY_METADATA = new HookClass("STORY_METADATA", "xop"); + public static final HookClass STORY_METADATA_LOADER = new HookClass("STORY_METADATA_LOADER", "agty"); + public static final HookClass STORY_SNAP = new HookClass("STORY_SNAP", "atza"); + public static final HookClass STORY_SNAP_AD_LOADER = new HookClass("STORY_SNAP_AD_LOADER", "agho"); + public static final HookClass STORY_SNAP_PAYLOAD_BUILDER = new HookClass("STORY_SNAP_PAYLOAD_BUILDER", "agiv"); + public static final HookClass STORY_SPONSORED = new HookClass("STORY_ADVERT", "ewu"); + public static final HookClass STORY_STATUS_UPDATER = new HookClass("STORY_STATUS_UPDATER", "agjl"); + public static final HookClass TEXTURE_VIDEO_VIEW = new HookClass("TEXTURE_VIDEO_VIEW", "com.snap.opera.shared.view.TextureVideoView"); + public static final HookClass USER_PREFS = new HookClass("USER_PREFS", "com.snapchat.android.core.user.UserPrefsImpl"); + + public static class HookClass extends Constant { + private String strClass; + + public HookClass(String name, @NonNull String value) { + super(name); + this.strClass = value; + } + + public String getStrClass() { + return this.strClass; + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookDef.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookDef.java index 88702d2..58c66cf 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookDef.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookDef.java @@ -1,120 +1,119 @@ -package com.ljmu.andre.snaptools.ModulePack.HookDefinitions; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.net.Uri; -import android.os.Bundle; -import androidx.annotation.Nullable; -import android.view.ActionMode; -import android.view.Menu; -import android.view.View; -import com.ljmu.andre.ConstantDefiner.Constant; -import com.ljmu.andre.ConstantDefiner.ConstantDefiner; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef; -import java.io.InputStream; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; - -public class HookDef extends ConstantDefiner { - public static final Hook AB_TEST_CHECK_BOOLEAN = new Hook("AB_TEST_CHECK_BOOLEAN", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Boolean.TYPE); - public static final Hook AB_TEST_CHECK_FLOAT = new Hook("AB_TEST_CHECK_FLOAT", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Float.TYPE); - public static final Hook AB_TEST_CHECK_INT = new Hook("AB_TEST_CHECK_INT", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Integer.TYPE); - public static final Hook AB_TEST_CHECK_LONG = new Hook("AB_TEST_CHECK_LONG", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Long.TYPE); - public static final Hook AB_TEST_CHECK_STRING = new Hook("AB_TEST_CHECK_VALUE", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, String.class); - public static final Hook BATCHED_MEDIA_LIMITER = new Hook("BATCHED_MEDIA_LIMITER", HookClassDef.SENT_VIDEO, "bi", new Object[0]); - public static final Hook CAMERA_IS_VISIBLE = new Hook("CAMERA_IS_VISIBLE", HookClassDef.CAMERA_FRAGMENT, "a", "aqst"); - public static final Hook CAPTION_CREATE_HOOK = new Hook("CAPTION_CREATE_HOOK", HookClassDef.CAPTION_MANAGER_CLASS, "onCreateActionMode", ActionMode.class, Menu.class); - public static final Hook CHAT_IMAGE_GET_ALGORITHM = new Hook("CHAT_IMAGE_GET_ALGORITHM", HookClassDef.CHAT_IMAGE_METADATA, "b", HookClassDef.CHAT_VIDEO.getStrClass()); - public static final Hook CHAT_ISSAVED_INAPP = new Hook("CHAT_ISSAVED_INAPP", (HookClassDef.HookClass) null, "eG_", new Object[0]); - public static final Hook CHAT_MESSAGE_VIEW_MEASURE = new Hook("CHAT_MESSAGE_VIEW_MEASURE", HookClassDef.CHAT_MESSAGE_VIEW_HOLDER, "F", new Object[0]); - public static final Hook CHAT_METADATA_READ = new Hook("CHAT_METADATA_READ", HookClassDef.CHAT_METADATA_JSON_PARSER, "a", "com.google.gson.stream.JsonReader"); - public static final Hook CHAT_METADATA_READ_SECOND = new Hook("CHAT_METADATA_READ_SECOND", HookClassDef.CHAT_METADATA_JSON_PARSER_SECOND, "a", "com.google.gson.stream.JsonReader"); - public static final Hook CHAT_METADATA_WRITE = new Hook("CHAT_METADATA_WRITE", HookClassDef.CHAT_METADATA_JSON_PARSER, "a", "com.google.gson.stream.JsonWriter", HookClassDef.CHAT_METADATA.getStrClass()); - public static final Hook CHAT_METADATA_WRITE_SECOND = new Hook("CHAT_METADATA_WRITE_SECOND", HookClassDef.CHAT_METADATA_JSON_PARSER_SECOND, "a", "com.google.gson.stream.JsonWriter", "atjr"); - public static final Hook CHAT_NOTIFICATION = new Hook("CHAT_NOTIFICATION", HookClassDef.CHAT_NOTIFICATION_CREATOR, "a", "aohl", "com.snapchat.android.core.notification.model.NotificationProcessorCallback"); - public static final Hook CHAT_SAVE_INAPP = new Hook("CHAT_SAVE_INAPP", HookClassDef.CHAT_MESSAGE_VIEW_HOLDER, "x", new Object[0]); - public static final Hook CHAT_VIDEO_GET_ALGORITHM = new Hook("CHAT_VIDEO_GET_ALGORITHM", HookClassDef.CHAT_VIDEO_METADATA, "e", new Object[0]); - public static final Hook CHAT_VIDEO_PATH = new Hook("CHAT_VIDEO_PATH", HookClassDef.CHAT_VIDEO, "eX_", new Object[0]); - public static final Hook CHECK_LENS_ASSET_AUTH = new Hook("CHECK_LENS_ASSET_AUTH", HookClassDef.LENS_AUTHENTICATION, "a", "afwj", String.class); - public static final Hook CHECK_LENS_AUTH = new Hook("CHECK_LENS_AUTH", HookClassDef.LENS_AUTHENTICATION, "a", HookClassDef.LENS.getStrClass(), String.class); - public static final Hook CHECK_LENS_CATEGORY_AUTH = new Hook("CHECK_LENS_CATEGORY_AUTH", HookClassDef.LENS_AUTHENTICATION, "a", "afwd", String.class); - public static final Hook CONSTRUCTOR_OPERA_PAGE_VIEW = new Hook("CONSTRUCTOR_OPERA_PAGE_VIEW", HookClassDef.OPERA_PAGE_VIEW, (String) null, Context.class); - public static final Hook COUNTDOWNTIMER_VIEW_ONDRAW = new Hook("COUNTDOWNTIMER_VIEW_ONDRAW", HookClassDef.COUNTDOWNTIMER_VIEW, "onDraw", Canvas.class); - public static final Hook CREATE_CHEETAH_PROFILE_SETTINGS_VIEW = new Hook("CREATE_CHEETAH_PROFILE_SETTINGS_VIEW", HookClassDef.CHEETAH_PROFILE_SETTINGS_CREATOR, (String) null, View.class); - public static final Hook DIRECT_GET_ALGORITHM = new Hook("DIRECT_GET_ALGORITHM", HookClassDef.RECEIVED_SNAP_ENCRYPTION, "a", HookClassDef.RECEIVED_SNAP.getStrClass(), String.class); - public static final Hook DISPATCH_CHAT_UPDATE = new Hook("DISPATCH_CHAT_UPDATE", HookClassDef.NETWORK_DISPATCHER, "a", "aiqx", "asyk"); - public static final Hook ENCRYPTION_ALGORITHM_STREAM = new Hook("ENCRYPTION_ALGORITHM_STREAM", HookClassDef.ENCRYPTION_ALGORITHM, "b", InputStream.class); - public static final Hook ERROR_SUPPRESS_DOWNLOADER_RUNNABLE = new Hook("ERROR_SUPPRESS_DOWNLOADER_RUNNABLE", HookClassDef.DOWNLOADER_RUNNABLE, "run", new Object[0]); - public static final Hook EXPERIMENT_PUSH_STATE = new Hook("EXPERIMENT_PUSH_STATE", HookClassDef.EXPERIMENT_BASE, "j", new Object[0]); - public static final Hook FONT_HOOK = new Hook("FONT_HOOK", HookClassDef.FONT_CLASS, "createFromFile", String.class); - public static final Hook FRIEND_PROFILE_POPUP_CREATED = new Hook("FRIEND_PROFILE_POPUP_CREATED", HookClassDef.FRIEND_PROFILE_POPUP_FRAGMENT, "onViewCreated", View.class, Bundle.class); - public static final Hook FRIEND_STORY_TILE_USERNAME = new Hook("FRIEND_STORY_TILE_USERNAME", HookClassDef.STORY_FRIEND_VIEWED, "a", new Object[0]); - public static final Hook GET_RECEIVED_SNAP_PAYLOAD = new Hook("GET_RECEIVED_SNAP_PAYLOAD", HookClassDef.RECEIVED_SNAP_PAYLOAD_BUILDER, "getRequestPayload", new Object[0]); - public static final Hook GET_SNAP_ID = new Hook("GET_SNAP_ID", HookClassDef.SNAP_BASE, "h", new Object[0]); // Checked - public static final Hook GET_STORY_SNAP_PAYLOAD = new Hook("GET_STORY_SNAP_PAYLOAD", HookClassDef.STORY_SNAP_PAYLOAD_BUILDER, "getRequestPayload", new Object[0]); - public static final Hook GET_USERNAME = new Hook("GET_USERNAME", HookClassDef.USER_PREFS, "w"); - public static final Hook GROUP_ALGORITHM_UNWRAPPED = new Hook("GROUP_ALGORITHM_UNWRAPPED", HookClassDef.GROUP_SNAP_WRAPPER, "a", String.class); - public static final Hook GROUP_GET_ALGORITHM = new Hook("GROUP_GET_ALGORITHM", HookClassDef.GROUP_SNAP_METADATA, "a", "aisw"); - public static final HookDef INST = new HookDef(); - public static final Hook LENS_LOADING = new Hook("LENS_LOADING", HookClassDef.LENS_LOADER, "a", List.class); - public static final Hook LOAD_INITIAL_STORIES = new Hook("LOAD_INITIAL_STORIES", HookClassDef.STORY_MANAGER, "a", Integer.TYPE, Integer.TYPE, Integer.TYPE, HashMap.class, HashMap.class, Integer.TYPE, Integer.TYPE, Integer.TYPE, List.class, Long.TYPE); - public static final Hook LOAD_NEW_STORY = new Hook("LOAD_NEW_STORY", HookClassDef.STORY_MANAGER, "a", "amur"); - public static final Hook LOAD_STORIES = new Hook("LOAD_STORIES", HookClassDef.STORY_LOADER, "a", List.class); - //public static final Hook LOAD_STORY_SNAP_ADVERT = new Hook("LOAD_STORY_SNAP_ADVERT", HookClassDef.STORY_SNAP_AD_LOADER, "a", "euc", "euc"); // This doesn't work for some reason - public static final Hook MARK_DIRECT_CHAT_VIEWED_PRESENT = new Hook("MARK_DIRECT_CHAT_VIEWED_PRESENT", HookClassDef.CHAT_DIRECT_VIEW_MARKER, "a", "aipc", "aoyr"); - public static final Hook MARK_DIRECT_CHAT_VIEWED_UNPRESENT = new Hook("MARK_DIRECT_CHAT_VIEWED_UNPRESENT", HookClassDef.CHAT_DIRECT_VIEW_MARKER, "a", "aoyr", "aipc"); - public static final Hook MARK_GROUP_CHAT_VIEWED = new Hook("MARK_GROUP_CHAT_VIEWED", HookClassDef.CHAT_GROUP_VIEW_MARKER, "a", "aiqx", String.class); - public static final Hook MARK_STORY_VIEWED = new Hook("MARK_STORY_VIEWED", HookClassDef.STORY_STATUS_UPDATER, "a", "ammc", HookClassDef.STORY_SNAP.getStrClass(), Boolean.TYPE); - public static final Hook NETWORK_EXECUTE_SYNC = new Hook("NETWORK_EXECUTE_SYNC", HookClassDef.NETWORK_MANAGER, "executeSynchronously", new Object[0]); - public static final Hook NEW_CONCENTRIC_TIMERVIEW_ONDRAW = new Hook("NEW_CONCENTRIC_TIMERVIEW_ONDRAW", HookClassDef.NEW_CONCENTRIC_TIMERVIEW, "onDraw", Canvas.class); - public static final Hook OPENED_SNAP = new Hook("OPENED_SNAP", HookClassDef.RECEIVED_SNAP, "e", Boolean.TYPE); - public static final Hook REPLACE_SHARED_IMAGE = new Hook("REPLACE_SHARED_IMAGE", HookClassDef.SHARE_IMAGE, "a", "fjw", Integer.class, String.class, long.class, boolean.class, int.class, "fjv$b"); - public static final Hook REPLACE_SHARED_VIDEO = new Hook("REPLACE_SHARED_VIDEO", HookClassDef.SHARE_VIDEO, "a", Uri.class, Integer.TYPE, Boolean.TYPE, "apmf", Long.TYPE, Long.TYPE); - public static final Hook RESOLVE_LENS_CATEGORY = new Hook("RESOLVE_LENS_CATEGORY", HookClassDef.LENS_CATEGORY_RESOLVER, "a", String.class); - public static final Hook SCREENSHOT_BYPASS = new Hook("SCREENSHOT_BYPASS", HookClassDef.SCREENSHOT_DETECTOR, "a", LinkedHashMap.class); - public static final Hook SENT_BATCHED_SNAP = new Hook("SENT_BATCHED_SNAP", HookClassDef.SENT_BATCHED_VIDEO, "c", new Object[0]); - public static final Hook SENT_SNAP = new Hook("SENT_SNAP", HookClassDef.META_DATA_BUILDER, "a", HookClassDef.SENT_SNAP_BASE.getStrClass()); - public static final Hook SET_SNAP_STATUS = new Hook("SET_SNAP_STATUS", HookClassDef.SNAP_BASE, "a", HookClassDef.SNAP_STATUS.getStrClass()); - public static final Hook SNAP_COUNTDOWN_POSTER = new Hook("SNAP_COUNTDOWN_POSTER", HookClassDef.SNAP_COUNTDOWN_CONTROLLER, "a", Long.TYPE); - public static final Hook SNAP_GET_MEDIA_TYPE = new Hook("SNAP_GET_MEDIA_TYPE", HookClassDef.SNAP_BASE, "aU_", new Object[0]); // Checked - public static final Hook SNAP_GET_TIMESTAMP = new Hook("SNAP_GET_TIMESTAMP", HookClassDef.STORY_SNAP, "az_", new Object[0]); // Checked - public static final Hook SNAP_GET_USERNAME = new Hook("SNAP_GET_USERNAME", HookClassDef.RECEIVED_SNAP, "s", new Object[0]); // Checked - public static final Hook STORY_DISPLAYED = new Hook("STORY_DISPLAYED", HookClassDef.STORY_ADVANCER, "E", new Object[0]); - public static final Hook STORY_GET_ALGORITHM = new Hook("STORY_GET_ALGORITHM", HookClassDef.STORY_SNAP, "aq", new Object[0]); - public static final Hook STORY_METADATA_BUILDER = new Hook("STORY_METADATA_BUILDER", HookClassDef.STORY_METADATA_LOADER, "a", HookClassDef.STORY_SNAP.getStrClass(), "aola", "aokz", "amms"); - public static final Hook STORY_METADATA_GET_OBJECT = new Hook("STORY_METADATA_GET_OBJECT", HookClassDef.STORY_METADATA, "a", String.class); - public static final Hook STORY_METADATA_INSERT_OBJECT = new Hook("STORY_METADATA_INSERT_OBJECT", HookClassDef.STORY_METADATA, "b", String.class, Object.class); - public static final Hook STREAM_TYPE_CHECK_BYPASS = new Hook("STREAM_TYPE_CHECK_BYPASS", HookClassDef.ENCRYPTED_STREAM_BUILDER, "a", "zq", Integer.TYPE, Integer.TYPE); - public static final Hook TEXTURE_VIDVIEW_SETLOOPING = new Hook("TEXTURE_VIDVIEW_SETLOOPING", HookClassDef.TEXTURE_VIDEO_VIEW, "setLooping", Boolean.TYPE); - public static final Hook TEXTURE_VIDVIEW_START = new Hook("TEXTURE_VIDVIEW_START", HookClassDef.TEXTURE_VIDEO_VIEW, "start", new Object[0]); - public static final Hook DIVISION_FIX = new Hook("DIVISION_FIX", HookClassDef.ADVANCE_FIX, "a",Integer.TYPE, Integer.TYPE ,Integer.TYPE ,Integer.TYPE); - - public static class Hook extends Constant { - private final HookClassDef.HookClass hookClass; - @Nullable - private final String hookMethod; - private final Object[] hookParams; - - Hook(String name, HookClassDef.HookClass hookClass2, @Nullable String hookMethod2, Object... hookParams2) { - super(name); - this.hookClass = hookClass2; - this.hookMethod = hookMethod2; - this.hookParams = hookParams2; - } - - public HookClassDef.HookClass getHookClass() { - return this.hookClass; - } - - @Nullable - public String getHookMethod() { - return this.hookMethod; - } - - public Object[] getHookParams() { - return this.hookParams; - } - } -} +package com.ljmu.andre.snaptools.ModulePack.HookDefinitions; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.net.Uri; +import android.os.Bundle; +import androidx.annotation.Nullable; +import android.view.ActionMode; +import android.view.Menu; +import android.view.View; +import com.ljmu.andre.ConstantDefiner.Constant; +import com.ljmu.andre.ConstantDefiner.ConstantDefiner; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef; +import java.io.InputStream; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +public class HookDef extends ConstantDefiner { + public static final Hook AB_TEST_CHECK_BOOLEAN = new Hook("AB_TEST_CHECK_BOOLEAN", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Boolean.TYPE); + public static final Hook AB_TEST_CHECK_FLOAT = new Hook("AB_TEST_CHECK_FLOAT", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Float.TYPE); + public static final Hook AB_TEST_CHECK_INT = new Hook("AB_TEST_CHECK_INT", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Integer.TYPE); + public static final Hook AB_TEST_CHECK_LONG = new Hook("AB_TEST_CHECK_LONG", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, Long.TYPE); + public static final Hook AB_TEST_CHECK_STRING = new Hook("AB_TEST_CHECK_VALUE", HookClassDef.AB_TEST_MANAGER, "a", String.class, String.class, String.class); + public static final Hook BATCHED_MEDIA_LIMITER = new Hook("BATCHED_MEDIA_LIMITER", HookClassDef.SENT_VIDEO, "aV", new Object[0]); + public static final Hook CAMERA_IS_VISIBLE = new Hook("CAMERA_IS_VISIBLE", HookClassDef.CAMERA_FRAGMENT, "a", "azdk"); + public static final Hook CAPTION_CREATE_HOOK = new Hook("CAPTION_CREATE_HOOK", HookClassDef.CAPTION_MANAGER_CLASS, "onCreateActionMode", ActionMode.class, Menu.class); + public static final Hook CHAT_IMAGE_GET_ALGORITHM = new Hook("CHAT_IMAGE_GET_ALGORITHM", HookClassDef.CHAT_IMAGE_METADATA, "b", HookClassDef.CHAT_VIDEO.getStrClass()); + public static final Hook CHAT_ISSAVED_INAPP = new Hook("CHAT_ISSAVED_INAPP", (HookClassDef.HookClass) null, "eC_", new Object[0]); + public static final Hook CHAT_MESSAGE_VIEW_MEASURE = new Hook("CHAT_MESSAGE_VIEW_MEASURE", HookClassDef.CHAT_MESSAGE_VIEW_HOLDER, "F", new Object[0]); + public static final Hook CHAT_METADATA_READ = new Hook("CHAT_METADATA_READ", HookClassDef.CHAT_METADATA_JSON_PARSER, "a", "com.google.gson.stream.JsonReader"); + public static final Hook CHAT_METADATA_READ_SECOND = new Hook("CHAT_METADATA_READ_SECOND", HookClassDef.CHAT_METADATA_JSON_PARSER_SECOND, "a", "com.google.gson.stream.JsonReader"); + public static final Hook CHAT_METADATA_WRITE = new Hook("CHAT_METADATA_WRITE", HookClassDef.CHAT_METADATA_JSON_PARSER, "a", "com.google.gson.stream.JsonWriter", HookClassDef.CHAT_METADATA.getStrClass()); + public static final Hook CHAT_METADATA_WRITE_SECOND = new Hook("CHAT_METADATA_WRITE_SECOND", HookClassDef.CHAT_METADATA_JSON_PARSER_SECOND, "a", "com.google.gson.stream.JsonWriter", "bbyv"); + public static final Hook CHAT_NOTIFICATION = new Hook("CHAT_NOTIFICATION", HookClassDef.CHAT_NOTIFICATION_CREATOR, "a", "avxe", "avwy"); + public static final Hook CHAT_SAVE_INAPP = new Hook("CHAT_SAVE_INAPP", HookClassDef.CHAT_MESSAGE_VIEW_HOLDER, "x", new Object[0]); + public static final Hook CHAT_VIDEO_GET_ALGORITHM = new Hook("CHAT_VIDEO_GET_ALGORITHM", HookClassDef.CHAT_VIDEO_METADATA, "e", new Object[0]); + public static final Hook CHAT_VIDEO_PATH = new Hook("CHAT_VIDEO_PATH", HookClassDef.CHAT_VIDEO, "eT_", new Object[0]); + public static final Hook CHECK_LENS_ASSET_AUTH = new Hook("CHECK_LENS_ASSET_AUTH", HookClassDef.LENS_AUTHENTICATION, "a", "ambc", String.class); + public static final Hook CHECK_LENS_AUTH = new Hook("CHECK_LENS_AUTH", HookClassDef.LENS_AUTHENTICATION, "a", HookClassDef.LENS.getStrClass(), String.class); + public static final Hook CHECK_LENS_CATEGORY_AUTH = new Hook("CHECK_LENS_CATEGORY_AUTH", HookClassDef.LENS_AUTHENTICATION, "a", "amaw", String.class); + public static final Hook CONSTRUCTOR_OPERA_PAGE_VIEW = new Hook("CONSTRUCTOR_OPERA_PAGE_VIEW", HookClassDef.OPERA_PAGE_VIEW, (String) null, Context.class); + public static final Hook COUNTDOWNTIMER_VIEW_ONDRAW = new Hook("COUNTDOWNTIMER_VIEW_ONDRAW", HookClassDef.COUNTDOWNTIMER_VIEW, "onDraw", Canvas.class); + public static final Hook CREATE_CHEETAH_PROFILE_SETTINGS_VIEW = new Hook("CREATE_CHEETAH_PROFILE_SETTINGS_VIEW", HookClassDef.CHEETAH_PROFILE_SETTINGS_CREATOR, (String) null, View.class); + public static final Hook DIRECT_GET_ALGORITHM = new Hook("DIRECT_GET_ALGORITHM", HookClassDef.RECEIVED_SNAP_ENCRYPTION, "a", HookClassDef.RECEIVED_SNAP.getStrClass(), String.class); + public static final Hook DISPATCH_CHAT_UPDATE = new Hook("DISPATCH_CHAT_UPDATE", HookClassDef.NETWORK_DISPATCHER, "a", "aphd", "bbnl"); + public static final Hook ENCRYPTION_ALGORITHM_STREAM = new Hook("ENCRYPTION_ALGORITHM_STREAM", HookClassDef.ENCRYPTION_ALGORITHM, "b", InputStream.class); + public static final Hook ERROR_SUPPRESS_DOWNLOADER_RUNNABLE = new Hook("ERROR_SUPPRESS_DOWNLOADER_RUNNABLE", HookClassDef.DOWNLOADER_RUNNABLE, "run", new Object[0]); + public static final Hook EXPERIMENT_PUSH_STATE = new Hook("EXPERIMENT_PUSH_STATE", HookClassDef.EXPERIMENT_BASE, "j", new Object[0]); + public static final Hook FONT_HOOK = new Hook("FONT_HOOK", HookClassDef.FONT_CLASS, "createFromFile", String.class); + public static final Hook FRIEND_PROFILE_POPUP_CREATED = new Hook("FRIEND_PROFILE_POPUP_CREATED", HookClassDef.FRIEND_PROFILE_POPUP_FRAGMENT, "onViewCreated", View.class, Bundle.class); + public static final Hook FRIEND_STORY_TILE_USERNAME = new Hook("FRIEND_STORY_TILE_USERNAME", HookClassDef.STORY_FRIEND_VIEWED, "a", new Object[0]); + public static final Hook GET_RECEIVED_SNAP_PAYLOAD = new Hook("GET_RECEIVED_SNAP_PAYLOAD", HookClassDef.RECEIVED_SNAP_PAYLOAD_BUILDER, "getRequestPayload", new Object[0]); + public static final Hook GET_SNAP_ID = new Hook("GET_SNAP_ID", HookClassDef.SNAP_BASE, "h", new Object[0]); + public static final Hook GET_STORY_SNAP_PAYLOAD = new Hook("GET_STORY_SNAP_PAYLOAD", HookClassDef.STORY_SNAP_PAYLOAD_BUILDER, "getRequestPayload", new Object[0]); + public static final Hook GET_USERNAME = new Hook("GET_USERNAME", HookClassDef.USER_PREFS, "N"); + public static final Hook GROUP_ALGORITHM_UNWRAPPED = new Hook("GROUP_ALGORITHM_UNWRAPPED", HookClassDef.GROUP_SNAP_WRAPPER, "a", String.class); + public static final Hook GROUP_GET_ALGORITHM = new Hook("GROUP_GET_ALGORITHM", HookClassDef.GROUP_SNAP_METADATA, "a", "apjb"); + public static final HookDef INST = new HookDef(); + public static final Hook LENS_LOADING = new Hook("LENS_LOADING", HookClassDef.LENS_LOADER, "a", List.class); + public static final Hook LOAD_INITIAL_STORIES = new Hook("LOAD_INITIAL_STORIES", HookClassDef.STORY_MANAGER, "a", Integer.TYPE, Integer.TYPE, Integer.TYPE, HashMap.class, HashMap.class, Integer.TYPE, Integer.TYPE, Integer.TYPE, List.class, Long.TYPE); + public static final Hook LOAD_NEW_STORY = new Hook("LOAD_NEW_STORY", HookClassDef.STORY_MANAGER, "a", "auht"); + public static final Hook LOAD_STORIES = new Hook("LOAD_STORIES", HookClassDef.STORY_LOADER, "a", List.class); + public static final Hook LOAD_STORY_SNAP_ADVERT = new Hook("LOAD_STORY_SNAP_ADVERT", HookClassDef.STORY_SNAP_AD_LOADER, "a", HookClassDef.STORY_SNAP_AD_LOADER.getStrClass(), "ffh"); + public static final Hook MARK_DIRECT_CHAT_VIEWED_PRESENT = new Hook("MARK_DIRECT_CHAT_VIEWED_PRESENT", HookClassDef.CHAT_DIRECT_VIEW_MARKER, "a", "apfi", "awqi"); + public static final Hook MARK_DIRECT_CHAT_VIEWED_UNPRESENT = new Hook("MARK_DIRECT_CHAT_VIEWED_UNPRESENT", HookClassDef.CHAT_DIRECT_VIEW_MARKER, "b", "awqi", "apfi"); + public static final Hook MARK_GROUP_CHAT_VIEWED = new Hook("MARK_GROUP_CHAT_VIEWED", HookClassDef.CHAT_GROUP_VIEW_MARKER, "a", "aphd", String.class); + public static final Hook MARK_STORY_VIEWED = new Hook("MARK_STORY_VIEWED", HookClassDef.STORY_STATUS_UPDATER, "a", "atyj", HookClassDef.STORY_SNAP.getStrClass(), Boolean.TYPE); + public static final Hook NETWORK_EXECUTE_SYNC = new Hook("NETWORK_EXECUTE_SYNC", HookClassDef.NETWORK_MANAGER, "executeSynchronously", new Object[0]); + public static final Hook NEW_CONCENTRIC_TIMERVIEW_ONDRAW = new Hook("NEW_CONCENTRIC_TIMERVIEW_ONDRAW", HookClassDef.NEW_CONCENTRIC_TIMERVIEW, "onDraw", Canvas.class); + public static final Hook OPENED_SNAP = new Hook("OPENED_SNAP", HookClassDef.RECEIVED_SNAP, "e", Boolean.TYPE); + public static final Hook REPLACE_SHARED_IMAGE = new Hook("REPLACE_SHARED_IMAGE", HookClassDef.SHARE_IMAGE, "a", Bitmap.class, Integer.class, String.class, Long.TYPE, Boolean.TYPE, Integer.TYPE, "fye$b"); + public static final Hook REPLACE_SHARED_VIDEO = new Hook("REPLACE_SHARED_VIDEO", HookClassDef.SHARE_VIDEO, "a", Uri.class, Integer.TYPE, Boolean.TYPE, "axec", Long.TYPE, Long.TYPE); + public static final Hook RESOLVE_LENS_CATEGORY = new Hook("RESOLVE_LENS_CATEGORY", HookClassDef.LENS_CATEGORY_RESOLVER, "a", String.class); + public static final Hook SCREENSHOT_BYPASS = new Hook("SCREENSHOT_BYPASS", HookClassDef.SCREENSHOT_DETECTOR, "a", LinkedHashMap.class); + public static final Hook SENT_BATCHED_SNAP = new Hook("SENT_BATCHED_SNAP", HookClassDef.SENT_BATCHED_VIDEO, "d", new Object[0]); + public static final Hook SENT_SNAP = new Hook("SENT_SNAP", HookClassDef.META_DATA_BUILDER, "a", HookClassDef.SENT_SNAP_BASE.getStrClass()); + public static final Hook SET_SNAP_STATUS = new Hook("SET_SNAP_STATUS", HookClassDef.SNAP_BASE, "a", HookClassDef.SNAP_STATUS.getStrClass()); + public static final Hook SNAP_COUNTDOWN_POSTER = new Hook("SNAP_COUNTDOWN_POSTER", HookClassDef.SNAP_COUNTDOWN_CONTROLLER, "a", Long.TYPE); + public static final Hook SNAP_GET_MEDIA_TYPE = new Hook("SNAP_GET_MEDIA_TYPE", HookClassDef.SNAP_BASE, "bc_", new Object[0]); + public static final Hook SNAP_GET_TIMESTAMP = new Hook("SNAP_GET_TIMESTAMP", HookClassDef.STORY_SNAP, "aH_", new Object[0]); + public static final Hook SNAP_GET_USERNAME = new Hook("SNAP_GET_USERNAME", HookClassDef.RECEIVED_SNAP, "s", new Object[0]); + public static final Hook STORY_DISPLAYED = new Hook("STORY_DISPLAYED", HookClassDef.STORY_ADVANCER, "F", new Object[0]); + public static final Hook STORY_GET_ALGORITHM = new Hook("STORY_GET_ALGORITHM", HookClassDef.STORY_SNAP, "ar", new Object[0]); + public static final Hook STORY_METADATA_BUILDER = new Hook("STORY_METADATA_BUILDER", HookClassDef.STORY_METADATA_LOADER, "a", HookClassDef.STORY_SNAP.getStrClass(), "awbb", "awba", "atyz"); + public static final Hook STORY_METADATA_GET_OBJECT = new Hook("STORY_METADATA_GET_OBJECT", HookClassDef.STORY_METADATA, "a", String.class); + public static final Hook STORY_METADATA_INSERT_OBJECT = new Hook("STORY_METADATA_INSERT_OBJECT", HookClassDef.STORY_METADATA, "b", String.class, Object.class); + public static final Hook STREAM_TYPE_CHECK_BYPASS = new Hook("STREAM_TYPE_CHECK_BYPASS", HookClassDef.ENCRYPTED_STREAM_BUILDER, "a", "ye", Integer.TYPE, Integer.TYPE); + public static final Hook TEXTURE_VIDVIEW_SETLOOPING = new Hook("TEXTURE_VIDVIEW_SETLOOPING", HookClassDef.TEXTURE_VIDEO_VIEW, "setLooping", Boolean.TYPE); + public static final Hook TEXTURE_VIDVIEW_START = new Hook("TEXTURE_VIDVIEW_START", HookClassDef.TEXTURE_VIDEO_VIEW, "start", new Object[0]); + + public static class Hook extends Constant { + private final HookClassDef.HookClass hookClass; + @Nullable + private final String hookMethod; + private final Object[] hookParams; + + Hook(String name, HookClassDef.HookClass hookClass2, @Nullable String hookMethod2, Object... hookParams2) { + super(name); + this.hookClass = hookClass2; + this.hookMethod = hookMethod2; + this.hookParams = hookParams2; + } + + public HookClassDef.HookClass getHookClass() { + return this.hookClass; + } + + @Nullable + public String getHookMethod() { + return this.hookMethod; + } + + public Object[] getHookParams() { + return this.hookParams; + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookVariableDef.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookVariableDef.java index c06124c..4798e8c 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookVariableDef.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookDefinitions/HookVariableDef.java @@ -1,53 +1,52 @@ -package com.ljmu.andre.snaptools.ModulePack.HookDefinitions; - -import com.ljmu.andre.ConstantDefiner.Constant; -import com.ljmu.andre.ConstantDefiner.ConstantDefiner; - -public class HookVariableDef extends ConstantDefiner { - public static final HookVariable BATCHED_MEDIA_ITEM_BOOLEAN = new HookVariable("BATCHED_MEDIA_ITEM_BOOLEAN", "f"); // Done - public static final HookVariable BATCHED_MEDIA_LIST = new HookVariable("BATCHED_MEDIA_LIST", "aJ"); // Done - public static final HookVariable CHAT_METADATA_MEDIA = new HookVariable("CHAT_METADATA_MEDIA", "c"); // Done - public static final HookVariable CHAT_SAVING_LINKER = new HookVariable("CHAT_SAVING_LINKER", "H"); // Done (unsure) - public static final HookVariable CHAT_SAVING_LINKER_CHAT_REF = new HookVariable("CHAT_SAVING_LINKER_CHAT_REF", "d"); // Done - public static final HookVariable CHAT_TOP_PANEL_VIEW = new HookVariable("CHAT_TOP_PANEL_VIEW", "n");// Done - public static final HookVariable FILTER_METADATA_CACHE = new HookVariable("FILTER_METADATA_CACHE", "a"); - public static final HookVariable FILTER_SERIALIZABLE_METADATA = new HookVariable("FILTER_SERIALIZABLE_METADATA", "a"); // Done - public static final HookVariable GEOFILTER_VIEW_CREATION_ARG3 = new HookVariable("GEOFILTER_VIEW_CREATION_ARG3", "a"); - public static final HookVariable GROUP_ALGORITHM_WRAPPER_FIELD = new HookVariable("GROUP_ALGORITHM_WRAPPER_FIELD", "b"); // Done - public static final HookVariable LENS_ACTIVATOR = new HookVariable("LENS_ACTIVATOR", "b"); // Done - public static final HookVariable LENS_CATEGORY = new HookVariable("LENS_CATEGORY", "a"); // Done - public static final HookVariable LENS_CATEGORY_MAP = new HookVariable("LENS_CATEGORY_MAP", "b"); // Done - public static final HookVariable MCANONICALDISPLAYNAME = new HookVariable("MCANONICALDISPLAYNAME", "aM"); // Done - public static final HookVariable NOTIFICATION_TYPE = new HookVariable("NOTIFICATION_TYPE", "a"); // Done - public static final HookVariable NO_AUTO_ADVANCE = new HookVariable("NO_AUTO_ADVANCE", "NO_AUTO_ADVANCE"); - public static final HookVariable RECEIVED_SNAP_PAYLOAD_HOLDER = new HookVariable("RECEIVED_SNAP_PAYLOAD_HOLDER", "b"); // Done - public static final HookVariable RECEIVED_SNAP_PAYLOAD_MAP = new HookVariable("RECEIVED_SNAP_PAYLOAD_MAP", "a"); // Done - public static final HookVariable SENT_BATCHED_VIDEO_MEDIAHOLDER = new HookVariable("SENT_BATCHED_VIDEO_MEDIAHOLDER", "c"); // Done - public static final HookVariable SENT_MEDIA_BATCH_DATA = new HookVariable("SENT_MEDIA_BATCH_DATA", "cx"); // Done - public static final HookVariable SENT_MEDIA_BITMAP = new HookVariable("SENT_MEDIA_BITMAP", "P"); // Done - public static final HookVariable SENT_MEDIA_TIMESTAMP = new HookVariable("SENT_MEDIA_TIMESTAMP", "bT"); // Done - public static final HookVariable SENT_MEDIA_VIDEO_URI = new HookVariable("SENT_MEDIA_VIDEO_URI", "ba"); - public static final HookVariable SNAPCAPTIONVIEW_CONTEXT = new HookVariable("SNAPCAPTIONVIEW_CONTEXT", "b"); - public static final HookVariable SNAP_IS_ZIPPED = new HookVariable("SNAP_IS_ZIPPED", "aK"); // Done - public static final HookVariable STORY_ADVANCER_DISPLAY_STATE = new HookVariable("STORY_ADVANCER_DISPLAY_STATE", "i"); // Done - public static final HookVariable STORY_ADVANCER_METADATA = new HookVariable("STORY_ADVANCER_METADATA", "f"); // Done - public static final HookVariable STORY_COLLECTION_MAP = new HookVariable("STORY_COLLECTION_MAP", "e"); // Done - public static final HookVariable STORY_UPDATE_METADATA = new HookVariable("STORY_UPDATE_METADATA", "b"); // Done - public static final HookVariable STORY_UPDATE_METADATA_ID = new HookVariable("STORY_UPDATE_METADATA_ID", "a"); // Done - public static final HookVariable STORY_UPDATE_METADATA_LIST = new HookVariable("STORY_UPDATE_METADATA_LIST", "b"); - public static final HookVariable STREAM_TYPE_CHECK_BOOLEAN = new HookVariable("STREAM_TYPE_CHECK_BOOLEAN", "d"); - - public static class HookVariable extends Constant { - private final String varName; - - HookVariable(String name, String varName2) { - super(name); - this.varName = varName2; - } - - public String getVarName() { - return this.varName; - } - } -} - +package com.ljmu.andre.snaptools.ModulePack.HookDefinitions; + +import com.ljmu.andre.ConstantDefiner.Constant; +import com.ljmu.andre.ConstantDefiner.ConstantDefiner; + +public class HookVariableDef extends ConstantDefiner { + public static final HookVariable BATCHED_MEDIA_ITEM_BOOLEAN = new HookVariable("BATCHED_MEDIA_ITEM_BOOLEAN", "e"); + public static final HookVariable BATCHED_MEDIA_LIST = new HookVariable("BATCHED_MEDIA_LIST", "aK"); + public static final HookVariable CHAT_METADATA_MEDIA = new HookVariable("CHAT_METADATA_MEDIA", "c"); + public static final HookVariable CHAT_SAVING_LINKER = new HookVariable("CHAT_SAVING_LINKER", "B"); + public static final HookVariable CHAT_SAVING_LINKER_CHAT_REF = new HookVariable("CHAT_SAVING_LINKER_CHAT_REF", "d"); + public static final HookVariable CHAT_TOP_PANEL_VIEW = new HookVariable("CHAT_TOP_PANEL_VIEW", "o"); + public static final HookVariable FILTER_METADATA_CACHE = new HookVariable("FILTER_METADATA_CACHE", "a"); + public static final HookVariable FILTER_SERIALIZABLE_METADATA = new HookVariable("FILTER_SERIALIZABLE_METADATA", "a"); + public static final HookVariable GEOFILTER_VIEW_CREATION_ARG3 = new HookVariable("GEOFILTER_VIEW_CREATION_ARG3", "a"); + public static final HookVariable GROUP_ALGORITHM_WRAPPER_FIELD = new HookVariable("GROUP_ALGORITHM_WRAPPER_FIELD", "b"); + public static final HookVariable LENS_ACTIVATOR = new HookVariable("LENS_ACTIVATOR", "b"); + public static final HookVariable LENS_CATEGORY = new HookVariable("LENS_CATEGORY", "a"); + public static final HookVariable LENS_CATEGORY_MAP = new HookVariable("LENS_CATEGORY_MAP", "a"); + public static final HookVariable MCANONICALDISPLAYNAME = new HookVariable("MCANONICALDISPLAYNAME", "aK"); + public static final HookVariable NOTIFICATION_TYPE = new HookVariable("NOTIFICATION_TYPE", "a"); + public static final HookVariable NO_AUTO_ADVANCE = new HookVariable("NO_AUTO_ADVANCE", "NO_AUTO_ADVANCE"); + public static final HookVariable RECEIVED_SNAP_PAYLOAD_HOLDER = new HookVariable("RECEIVED_SNAP_PAYLOAD_HOLDER", "b"); + public static final HookVariable RECEIVED_SNAP_PAYLOAD_MAP = new HookVariable("RECEIVED_SNAP_PAYLOAD_MAP", "a"); + public static final HookVariable SENT_BATCHED_VIDEO_MEDIAHOLDER = new HookVariable("SENT_BATCHED_VIDEO_MEDIAHOLDER", "c"); + public static final HookVariable SENT_MEDIA_BATCH_DATA = new HookVariable("SENT_MEDIA_BATCH_DATA", "cf"); + public static final HookVariable SENT_MEDIA_BITMAP = new HookVariable("SENT_MEDIA_BITMAP", "aF"); + public static final HookVariable SENT_MEDIA_TIMESTAMP = new HookVariable("SENT_MEDIA_TIMESTAMP", "bE"); + public static final HookVariable SENT_MEDIA_VIDEO_URI = new HookVariable("SENT_MEDIA_VIDEO_URI", "aO"); + public static final HookVariable SNAPCAPTIONVIEW_CONTEXT = new HookVariable("SNAPCAPTIONVIEW_CONTEXT", "b"); + public static final HookVariable SNAP_IS_ZIPPED = new HookVariable("SNAP_IS_ZIPPED", "aJ"); + public static final HookVariable STORY_ADVANCER_DISPLAY_STATE = new HookVariable("STORY_ADVANCER_DISPLAY_STATE", "f"); + public static final HookVariable STORY_ADVANCER_METADATA = new HookVariable("STORY_ADVANCER_METADATA", "c"); + public static final HookVariable STORY_COLLECTION_MAP = new HookVariable("STORY_COLLECTION_MAP", "c"); + public static final HookVariable STORY_UPDATE_METADATA = new HookVariable("STORY_UPDATE_METADATA", "b"); + public static final HookVariable STORY_UPDATE_METADATA_ID = new HookVariable("STORY_UPDATE_METADATA_ID", "a"); + public static final HookVariable STORY_UPDATE_METADATA_LIST = new HookVariable("STORY_UPDATE_METADATA_LIST", "b"); + public static final HookVariable STREAM_TYPE_CHECK_BOOLEAN = new HookVariable("STREAM_TYPE_CHECK_BOOLEAN", "d"); + + public static class HookVariable extends Constant { + private final String varName; + + HookVariable(String name, String varName2) { + super(name); + this.varName = varName2; + } + + public String getVarName() { + return this.varName; + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookResolver.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookResolver.java index 46dff9e..602dac1 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookResolver.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/HookResolver.java @@ -1,240 +1,240 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.Context; -import androidx.annotation.NonNull; - -import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.Framework.Utils.LoadState.State; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.HookClass; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.Hook; -import com.ljmu.andre.snaptools.Utils.Constants; -import com.ljmu.andre.snaptools.Utils.StringUtils; - -import java.lang.reflect.Member; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import de.robv.android.xposed.XposedHelpers; -import timber.log.Timber; - -import static de.robv.android.xposed.XposedHelpers.findClass; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -@SuppressWarnings({"WeakerAccess"}) -public class HookResolver extends ModuleHelper { - private static final Map hookReferenceMap = new HashMap<>(); - private static final Map> hookClassMap = new HashMap<>(); - - // =========================================================================== - - public HookResolver(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - /** - * =========================================================================== - * Attempt to find the HookReference associated with the input Hook - * =========================================================================== - * - * @param hook - The Hook to find the linked HookReference for - * @return The HookReference linked to Hook - * @throws HookNotFoundException - When no HookReference is found - */ - @NonNull - public static HookReference resolveHook(@NonNull Hook hook) throws HookNotFoundException { - HookReference hookReference = hookReferenceMap.get(hook.getName()); - - if (hookReference == null) { - throw new HookNotFoundException( - String.format("Could not find hook [Name:%s][Class:%s][Method:%s]", - hook.getName(), hook.getHookClass().getStrClass(), hook.getHookMethod())); - } - - return hookReference; - } - - // =========================================================================== - - /** - * =========================================================================== - * Attempt to find the Class associated with the input HookClass - * =========================================================================== - * - * @param hookClass - The HookClass to find the linked Class for - * @return The Class linked to HookClass - * @throws HookNotFoundException - When no Class is found - */ - @NonNull - public static Class resolveHookClass(@NonNull HookClass hookClass) throws HookNotFoundException { - Class resolvedClass = hookClassMap.get(hookClass.getName()); - - if (resolvedClass == null) - throw new HookNotFoundException( - String.format( - "Could not find HookClass [Class:%s]", - hookClass.getStrClass())); - - return resolvedClass; - } - - @Override - public FragmentHelper[] getUIFragments() { - return null; - } - - /** - * =========================================================================== - * Begins Hook loading and Load State Updating - * =========================================================================== - */ - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - if (hookReferenceMap.size() > 0) { - Timber.w("Tried to resolve hooks more than once!"); - return; - } - - int failedClasses = buildClassMap(snapClassLoader); - int failedHooks = buildHookMap(snapClassLoader); - - if (failedClasses > 0 || failedHooks > 0) { - Timber.e("Failed to load [Classes: %s/%s][Hooks: %s/%s]", - failedClasses, HookClassDef.INST.size(), - failedHooks, HookDef.INST.size()); - moduleLoadState.setState(State.ISSUES); - } - } - - /** - * =========================================================================== - * Iterate through the HookClass values and build their appropriate - * =========================================================================== - * - * @return the number of failed Classes - */ - private int buildClassMap(ClassLoader classLoader) { - int failedClasses = 0; - for (HookClass hookClass : HookClassDef.INST.values()) { - try { - Class resolvedClass = findClass(hookClass.getStrClass(), classLoader); - - hookClassMap.put(hookClass.getName(), resolvedClass); - } catch (Throwable t) { - if (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) { - Timber.e("Error building class [Class:%s][Reason:%s]", - hookClass, t.getMessage()); - } else { - Timber.e("Error building class: %s", StringUtils.obfus(hookClass.getStrClass())); - } - - failedClasses++; - } - } - - return failedClasses; - } - - /** - * =========================================================================== - * Attempt to build as many HookReferences using Hook.values() - * =========================================================================== - * - * @return the number of failed HookReferences - */ - private int buildHookMap(ClassLoader classLoader) { - int failedHooks = 0; - for (Hook hook : HookDef.INST.values()) { - try { - if (hook.getHookClass() == null) - continue; - - HookReference hookReference = new HookReference(hook, classLoader); - hookReferenceMap.put(hook.getName(), hookReference); - } catch (Throwable t) { - Timber.e("Error building hook [Hook:%s][Reason:%s]", - hook, t.getMessage()); - - if (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) { - Timber.e("Error building hook [Hook:%s][Reason:%s]", - hook, t.getMessage()); - } else { - Timber.e("Error building hook: %s", StringUtils.obfus(hook.getHookMethod())); - } - failedHooks++; - } - } - - return failedHooks; - } - - // =========================================================================== - - // =========================================================================== - - /** - * =========================================================================== - * Hook reference: Builds and stores the Member for hooking - * =========================================================================== - */ - public static class HookReference { - private final Member hookMember; - - HookReference(Hook hook, ClassLoader classLoader) throws Throwable { - Class hookClass = resolveHookClass(hook.getHookClass()); - String hookMethod = hook.getHookMethod(); - Class[] hookParams = resolveParams(hook.getHookParams(), classLoader); - - if (hookMethod != null) - this.hookMember = XposedHelpers.findMethodExact(hookClass, hookMethod, hookParams); - else - this.hookMember = XposedHelpers.findConstructorExact(hookClass, hookParams); - - Timber.d(this.toString()); - } - - // =========================================================================== - - private Class[] resolveParams(Object[] unresolvedParams, ClassLoader classLoader) throws Throwable { - ArrayList resolvedParamList = new ArrayList<>(); - - for (Object param : unresolvedParams) { - Class resolvedParam; - - if (param instanceof String) - resolvedParam = findClass((String) param, classLoader); - else if (param instanceof Class) - resolvedParam = (Class) param; - else - continue; - - resolvedParamList.add(resolvedParam); - } - - return resolvedParamList.toArray(new Class[0]); - } - - @Override - public String toString() { - if (getHookMember() == null) - return "HookReference[Undefined]"; - - return String.format("HookReference[Class:%s, Method:%s]", - getHookMember().getDeclaringClass(), - getHookMember().getName()); - } - - public Member getHookMember() { - return hookMember; - } - } +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.Context; +import androidx.annotation.NonNull; + +import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.Framework.Utils.LoadState.State; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.HookClass; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.Hook; +import com.ljmu.andre.snaptools.Utils.Constants; +import com.ljmu.andre.snaptools.Utils.StringUtils; + +import java.lang.reflect.Member; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import de.robv.android.xposed.XposedHelpers; +import timber.log.Timber; + +import static de.robv.android.xposed.XposedHelpers.findClass; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +@SuppressWarnings({"WeakerAccess"}) +public class HookResolver extends ModuleHelper { + private static final Map hookReferenceMap = new HashMap<>(); + private static final Map> hookClassMap = new HashMap<>(); + + // =========================================================================== + + public HookResolver(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + /** + * =========================================================================== + * Attempt to find the HookReference associated with the input Hook + * =========================================================================== + * + * @param hook - The Hook to find the linked HookReference for + * @return The HookReference linked to Hook + * @throws HookNotFoundException - When no HookReference is found + */ + @NonNull + public static HookReference resolveHook(@NonNull Hook hook) throws HookNotFoundException { + HookReference hookReference = hookReferenceMap.get(hook.getName()); + + if (hookReference == null) { + throw new HookNotFoundException( + String.format("Could not find hook [Name:%s][Class:%s][Method:%s]", + hook.getName(), hook.getHookClass().getStrClass(), hook.getHookMethod())); + } + + return hookReference; + } + + // =========================================================================== + + /** + * =========================================================================== + * Attempt to find the Class associated with the input HookClass + * =========================================================================== + * + * @param hookClass - The HookClass to find the linked Class for + * @return The Class linked to HookClass + * @throws HookNotFoundException - When no Class is found + */ + @NonNull + public static Class resolveHookClass(@NonNull HookClass hookClass) throws HookNotFoundException { + Class resolvedClass = hookClassMap.get(hookClass.getName()); + + if (resolvedClass == null) + throw new HookNotFoundException( + String.format( + "Could not find HookClass [Class:%s]", + hookClass.getStrClass())); + + return resolvedClass; + } + + @Override + public FragmentHelper[] getUIFragments() { + return null; + } + + /** + * =========================================================================== + * Begins Hook loading and Load State Updating + * =========================================================================== + */ + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + if (hookReferenceMap.size() > 0) { + Timber.w("Tried to resolve hooks more than once!"); + return; + } + + int failedClasses = buildClassMap(snapClassLoader); + int failedHooks = buildHookMap(snapClassLoader); + + if (failedClasses > 0 || failedHooks > 0) { + Timber.e("Failed to load [Classes: %s/%s][Hooks: %s/%s]", + failedClasses, HookClassDef.INST.size(), + failedHooks, HookDef.INST.size()); + moduleLoadState.setState(State.ISSUES); + } + } + + /** + * =========================================================================== + * Iterate through the HookClass values and build their appropriate + * =========================================================================== + * + * @return the number of failed Classes + */ + private int buildClassMap(ClassLoader classLoader) { + int failedClasses = 0; + for (HookClass hookClass : HookClassDef.INST.values()) { + try { + Class resolvedClass = findClass(hookClass.getStrClass(), classLoader); + + hookClassMap.put(hookClass.getName(), resolvedClass); + } catch (Throwable t) { + if (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) { + Timber.e("Error building class [Class:%s][Reason:%s]", + hookClass, t.getMessage()); + } else { + Timber.e("Error building class: %s", StringUtils.obfus(hookClass.getStrClass())); + } + + failedClasses++; + } + } + + return failedClasses; + } + + /** + * =========================================================================== + * Attempt to build as many HookReferences using Hook.values() + * =========================================================================== + * + * @return the number of failed HookReferences + */ + private int buildHookMap(ClassLoader classLoader) { + int failedHooks = 0; + for (Hook hook : HookDef.INST.values()) { + try { + if (hook.getHookClass() == null) + continue; + + HookReference hookReference = new HookReference(hook, classLoader); + hookReferenceMap.put(hook.getName(), hookReference); + } catch (Throwable t) { + Timber.e("Error building hook [Hook:%s][Reason:%s]", + hook, t.getMessage()); + + if (Constants.getApkVersionCode() >= 73 && Constants.isApkDebug()) { + Timber.e("Error building hook [Hook:%s][Reason:%s]", + hook, t.getMessage()); + } else { + Timber.e("Error building hook: %s", StringUtils.obfus(hook.getHookMethod())); + } + failedHooks++; + } + } + + return failedHooks; + } + + // =========================================================================== + + // =========================================================================== + + /** + * =========================================================================== + * Hook reference: Builds and stores the Member for hooking + * =========================================================================== + */ + public static class HookReference { + private final Member hookMember; + + HookReference(Hook hook, ClassLoader classLoader) throws Throwable { + Class hookClass = resolveHookClass(hook.getHookClass()); + String hookMethod = hook.getHookMethod(); + Class[] hookParams = resolveParams(hook.getHookParams(), classLoader); + + if (hookMethod != null) + this.hookMember = XposedHelpers.findMethodExact(hookClass, hookMethod, hookParams); + else + this.hookMember = XposedHelpers.findConstructorExact(hookClass, hookParams); + + Timber.d(this.toString()); + } + + // =========================================================================== + + private Class[] resolveParams(Object[] unresolvedParams, ClassLoader classLoader) throws Throwable { + ArrayList resolvedParamList = new ArrayList<>(); + + for (Object param : unresolvedParams) { + Class resolvedParam; + + if (param instanceof String) + resolvedParam = findClass((String) param, classLoader); + else if (param instanceof Class) + resolvedParam = (Class) param; + else + continue; + + resolvedParamList.add(resolvedParam); + } + + return resolvedParamList.toArray(new Class[0]); + } + + @Override + public String toString() { + if (getHookMember() == null) + return "HookReference[Undefined]"; + + return String.format("HookReference[Class:%s, Method:%s]", + getHookMember().getDeclaringClass(), + getHookMember().getName()); + } + + public Member getHookMember() { + return hookMember; + } + } } \ No newline at end of file diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/LensCollector.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/LensCollector.java index 11fcc1a..b904c1c 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/LensCollector.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/LensCollector.java @@ -1,370 +1,370 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.Context; - -import com.ljmu.andre.CBIDatabase.CBITable; -import com.ljmu.andre.CBIDatabase.Utils.QueryBuilder; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.ModulePack.Databases.LensDatabase; -import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.LensObject; -import com.ljmu.andre.snaptools.ModulePack.Fragments.LensSettingsFragment; -import com.ljmu.andre.snaptools.ModulePack.Utils.FieldMapper; -import com.ljmu.andre.snaptools.Utils.MapUtils; -import com.ljmu.andre.snaptools.Utils.MapUtils.KeyBinder; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import de.robv.android.xposed.XC_MethodReplacement; -import de.robv.android.xposed.XposedHelpers; -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS_CONTEXT_HOLDER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS_SLUG; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS_TRACK; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHECK_LENS_ASSET_AUTH; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHECK_LENS_AUTH; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHECK_LENS_CATEGORY_AUTH; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LENS_LOADING; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.RESOLVE_LENS_CATEGORY; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.LENS_CATEGORY_MAP; -import static com.ljmu.andre.snaptools.ModulePack.HookResolver.resolveHookClass; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.LENS_AUTO_ENABLE; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -@SuppressWarnings("WeakerAccess") -public class LensCollector extends ModuleHelper { - private static final Object BUILD_LOCK = new Object(); - - public LensCollector(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - @Override - public FragmentHelper[] getUIFragments() { - return new FragmentHelper[]{new LensSettingsFragment()}; - } - - // =========================================================================== - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - LensDatabase.init(snapContext); - - /** - * =========================================================================== - * Bypass signature checks on the lenses - * =========================================================================== - */ - hookMethod( - CHECK_LENS_AUTH, - XC_MethodReplacement.returnConstant(true) - ); - - hookMethod( - CHECK_LENS_CATEGORY_AUTH, - XC_MethodReplacement.returnConstant(true) - ); - - hookMethod( - CHECK_LENS_ASSET_AUTH, - XC_MethodReplacement.returnConstant(true) - ); - - hookMethod( - RESOLVE_LENS_CATEGORY, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - if (param.getResult() == null || param.getThrowable() != null) { - try { - Map categorymap = getObjectField(LENS_CATEGORY_MAP, param.thisObject); - - Object category = categorymap.get("LENS_CATEGORY_GROUND"); - - if (category == null) - category = categorymap.get("LENS_CATEGORY_SKY"); - - if (category == null) - category = categorymap.get("LENS_CATEGORY_SELFIE"); - - param.setResult(category); - - } catch (Throwable e) { - Timber.e(e); - } - } - } - } - ); - - /*try { - Class assetClass = resolveHookClass(LENS_ASSET); - FieldMapper.initMapper("Asset", assetClass); - } catch (HookNotFoundException e) { - Timber.e(e); - moduleLoadState.fail(); - }*/ - - - /**Image Quality Improvements*/ - /*XposedHelpers.findAndHookMethod( - "com.snapchat.android.app.shared.util.SnapMediaUtils", snapClassLoader, - "a", Bitmap.class, int.class, new ST_MethodHook() { - @Override protected void before(MethodHookParam param) throws Throwable { - Timber.d("Default Compression: " + param.args[1]); - Bitmap bitmap = (Bitmap) param.args[0]; - param.args[1] = 100; - - Timber.d("Bitmap: [Bytes: %s][Config: %s][Density: %s][Height: %s][Width: %s]", bitmap.getByteCount(), bitmap.getConfig(), bitmap.getDensity(), - bitmap.getHeight(), bitmap.getWidth()); - - logStackTrace(); - } - }); - - XposedHelpers.findAndHookMethod( - "mlc", snapClassLoader, - "a", Bitmap.class, - new ST_MethodHook() { - @Override protected void before(MethodHookParam param) throws Throwable { - Timber.i("SNap Image Set"); - Bitmap bitmap = (Bitmap) param.args[0]; - if (bitmap != null) { - Timber.d("Bitmap: [Bytes: %s][Config: %s][Density: %s][Height: %s][Width: %s]", bitmap.getByteCount(), bitmap.getConfig(), bitmap.getDensity(), - bitmap.getHeight(), bitmap.getWidth()); - - param.args[0] = getResizedBitmap(bitmap, 1960, 4032); - } - - logStackTrace(); - } - });*/ - - try { - Class lensClass = resolveHookClass(LENS); - Class slugClass = resolveHookClass(LENS_SLUG); - Class trackClass = resolveHookClass(LENS_TRACK); - Class contextClass = resolveHookClass(LENS_CONTEXT_HOLDER); - CBITable lensTable = LensDatabase.getTable(LensObject.class); - - hookMethod( - LENS_LOADING, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - synchronized (BUILD_LOCK) { - try { - @SuppressWarnings("unchecked") - List lensList = (List) param.args[0]; - - if (lensList == null || lensList.isEmpty()) - return; - - Collection storedLensList = lensTable.getAll( - new QueryBuilder() - .addSelection("isActive", "1") - ); - - @SuppressWarnings("Convert2Lambda") - Map lensDbMap = MapUtils.convertList( - storedLensList, - new KeyBinder() { - @Override - public String getKey(LensObject mapEntry) { - return mapEntry.id; - } - } - ); - - FieldMapper lensMapper = FieldMapper.initMapper("Lens", lensClass); - - /** - * =========================================================================== - * Build field maps for static classes with unchanged content - * =========================================================================== - */ - /** ========================================================================== **/ - FieldMapper.initMapper("Slug", slugClass); - // =========================================================================== - FieldMapper.initMapper("Track", trackClass); - // =========================================================================== - FieldMapper.initMapper("Context", contextClass); - /** ========================================================================== **/ - - boolean enableNewLenses = getPref(LENS_AUTO_ENABLE); - - for (Object lens : lensList) { - try { - Timber.d("Working on lens: %s", lens); - - String idFieldName = lensMapper.get("id"); - String lensId = (String) XposedHelpers.getObjectField(lens, idFieldName); - - /*if (lensTable.contains(lensId)) { - Timber.i("Lens %s already exists", lensId); - lensDbMap.remove(lensId); - continue; - }*/ - lensDbMap.remove(lensId); - - LensObject newDbLens = new LensObject(); - newDbLens.buildFromFieldMap(lensMapper, lens); - newDbLens.isActive = enableNewLenses; - - if (!newDbLens.isReady()) { - Timber.w("Lens not ready to save: %s", newDbLens); - lensDbMap.remove(lensId); - continue; - } - - if (!lensTable.insert(newDbLens)) - Timber.w("Failed to insert lens into database: %s", newDbLens); - } catch (Throwable t) { - Timber.w(t, "Failed to build lens: %s", lens); - } - } - - Timber.d("Inserting table into list"); - - List convertedLenses = convertLensObjects( - snapClassLoader, - lensMapper, - lensDbMap.values() - ); - - if (convertedLenses != null && convertedLenses.size() > 0) { - lensList.addAll(convertedLenses); - Timber.d("Inserted %s lenses", convertedLenses.size()); - } - - cleanEmptyLenses(lensMapper, lensList); - - FieldMapper.removeMapper("Lens"); - FieldMapper.removeMapper("Slug"); - FieldMapper.removeMapper("Track"); - Timber.d("Cleared %s cached objects", LensObject.destroyDataCache()); - } catch (Throwable t) { - Timber.e(t, "Unknown error handling lens system"); - } - } - } - } - ); - } catch (Throwable e) { - Timber.e(e); - moduleLoadState.fail(); - } - } - - private List convertLensObjects(ClassLoader snapClassLoader, FieldMapper lensMapper, - Collection lensObjects) { - try { - FieldMapper slugMapper = FieldMapper.getMapper("Slug"); - Object slugPos = XposedHelpers.newInstance(slugMapper.getLinkClass()); - slugMapper.setField(slugPos, "alignment", "right"); - slugMapper.setField(slugPos, "position", "BOTTOM_RIGHT"); - slugMapper.setField(slugPos, "text", "SPONSORED"); - slugMapper.setField(slugPos, "time_before_fadeout", 3500); - - FieldMapper trackMapper = FieldMapper.getMapper("Track"); - Object trackInfo = XposedHelpers.newInstance(trackMapper.getLinkClass()); - trackMapper.setField(trackInfo, "skip_track", false); - - - List convertedLenses = new ArrayList<>(lensObjects.size()); - Map lensMapDowncast = lensMapper; - - for (LensObject lensObject : lensObjects) { - if (!lensObject.isReady()) - continue; - - //Timber.d("Processing: " + lensObject); - Object convertedLens; - - try { - convertedLens = XposedHelpers.newInstance(lensMapper.getLinkClass()); - } catch (Throwable t) { - Timber.e(t); - continue; - } - - for (String fieldTag : lensMapDowncast.keySet()) { - Object storedVal = lensObject.getFieldByTag(fieldTag); - - if (storedVal != null) - lensMapper.setField(convertedLens, fieldTag, storedVal); - /*else { - if (fieldTag.equals("id") || fieldTag.equals("code") - || fieldTag.equals("icon_link") || fieldTag.equals("mLensLink")) { - Answers.safeLogEvent( - new CustomEvent("NullLensEntry") - .putCustomAttribute("Field", fieldTag) - ); - } - }*/ - } - - lensMapper.setField(convertedLens, "mSponsoredSlugPosAndText", slugPos); - lensMapper.setField(convertedLens, "unlockable_track_info", trackInfo); - - if (isConvertedLensCompleted(lensMapper, convertedLens)) - convertedLenses.add(convertedLens); - /*else { - Answers.safeLogEvent( - new CustomEvent("LensCrashMitigated") - ); - }*/ - } - - Timber.d("Converted %s lenses", convertedLenses.size()); - return convertedLenses; - } catch (Throwable t) { - Timber.e(t); - } - - return Collections.emptyList(); - } - - private void cleanEmptyLenses(FieldMapper lensMapper, List convertedLensList) { - Iterator convertedLensIterator = convertedLensList.iterator(); - - int emptyLensCount = 0; - - while (convertedLensIterator.hasNext()) { - Object convertedLens = convertedLensIterator.next(); - - if (convertedLens == null || !isConvertedLensCompleted(lensMapper, convertedLens)) { - convertedLensIterator.remove(); - emptyLensCount++; - } - } - -// if (emptyLensCount > 0) { -// Answers.safeLogEvent( -// new CustomEvent("EmptyLensReport") -// .putCustomAttribute("Count", emptyLensCount) -// ); -// } - } - - public boolean isConvertedLensCompleted(FieldMapper lensMapper, Object convertedLens) { - return lensMapper.getFieldVal(convertedLens, "id") != null - && lensMapper.getFieldVal(convertedLens, "code") != null - && lensMapper.getFieldVal(convertedLens, "icon_link") != null - && lensMapper.getFieldVal(convertedLens, "mLensLink") != null; - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.Context; + +import com.ljmu.andre.CBIDatabase.CBITable; +import com.ljmu.andre.CBIDatabase.Utils.QueryBuilder; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.ModulePack.Databases.LensDatabase; +import com.ljmu.andre.snaptools.ModulePack.Databases.Tables.LensObject; +import com.ljmu.andre.snaptools.ModulePack.Fragments.LensSettingsFragment; +import com.ljmu.andre.snaptools.ModulePack.Utils.FieldMapper; +import com.ljmu.andre.snaptools.Utils.MapUtils; +import com.ljmu.andre.snaptools.Utils.MapUtils.KeyBinder; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import de.robv.android.xposed.XC_MethodReplacement; +import de.robv.android.xposed.XposedHelpers; +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS_CONTEXT_HOLDER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS_SLUG; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.LENS_TRACK; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHECK_LENS_ASSET_AUTH; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHECK_LENS_AUTH; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHECK_LENS_CATEGORY_AUTH; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LENS_LOADING; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.RESOLVE_LENS_CATEGORY; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.LENS_CATEGORY_MAP; +import static com.ljmu.andre.snaptools.ModulePack.HookResolver.resolveHookClass; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.LENS_AUTO_ENABLE; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +@SuppressWarnings("WeakerAccess") +public class LensCollector extends ModuleHelper { + private static final Object BUILD_LOCK = new Object(); + + public LensCollector(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + @Override + public FragmentHelper[] getUIFragments() { + return new FragmentHelper[]{new LensSettingsFragment()}; + } + + // =========================================================================== + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + LensDatabase.init(snapContext); + + /** + * =========================================================================== + * Bypass signature checks on the lenses + * =========================================================================== + */ + hookMethod( + CHECK_LENS_AUTH, + XC_MethodReplacement.returnConstant(true) + ); + + hookMethod( + CHECK_LENS_CATEGORY_AUTH, + XC_MethodReplacement.returnConstant(true) + ); + + hookMethod( + CHECK_LENS_ASSET_AUTH, + XC_MethodReplacement.returnConstant(true) + ); + + hookMethod( + RESOLVE_LENS_CATEGORY, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + if (param.getResult() == null || param.getThrowable() != null) { + try { + Map categorymap = getObjectField(LENS_CATEGORY_MAP, param.thisObject); + + Object category = categorymap.get("LENS_CATEGORY_GROUND"); + + if (category == null) + category = categorymap.get("LENS_CATEGORY_SKY"); + + if (category == null) + category = categorymap.get("LENS_CATEGORY_SELFIE"); + + param.setResult(category); + + } catch (Throwable e) { + Timber.e(e); + } + } + } + } + ); + + /*try { + Class assetClass = resolveHookClass(LENS_ASSET); + FieldMapper.initMapper("Asset", assetClass); + } catch (HookNotFoundException e) { + Timber.e(e); + moduleLoadState.fail(); + }*/ + + + /**Image Quality Improvements*/ + /*XposedHelpers.findAndHookMethod( + "com.snapchat.android.app.shared.util.SnapMediaUtils", snapClassLoader, + "a", Bitmap.class, int.class, new ST_MethodHook() { + @Override protected void before(MethodHookParam param) throws Throwable { + Timber.d("Default Compression: " + param.args[1]); + Bitmap bitmap = (Bitmap) param.args[0]; + param.args[1] = 100; + + Timber.d("Bitmap: [Bytes: %s][Config: %s][Density: %s][Height: %s][Width: %s]", bitmap.getByteCount(), bitmap.getConfig(), bitmap.getDensity(), + bitmap.getHeight(), bitmap.getWidth()); + + logStackTrace(); + } + }); + + XposedHelpers.findAndHookMethod( + "mlc", snapClassLoader, + "a", Bitmap.class, + new ST_MethodHook() { + @Override protected void before(MethodHookParam param) throws Throwable { + Timber.i("SNap Image Set"); + Bitmap bitmap = (Bitmap) param.args[0]; + if (bitmap != null) { + Timber.d("Bitmap: [Bytes: %s][Config: %s][Density: %s][Height: %s][Width: %s]", bitmap.getByteCount(), bitmap.getConfig(), bitmap.getDensity(), + bitmap.getHeight(), bitmap.getWidth()); + + param.args[0] = getResizedBitmap(bitmap, 1960, 4032); + } + + logStackTrace(); + } + });*/ + + try { + Class lensClass = resolveHookClass(LENS); + Class slugClass = resolveHookClass(LENS_SLUG); + Class trackClass = resolveHookClass(LENS_TRACK); + Class contextClass = resolveHookClass(LENS_CONTEXT_HOLDER); + CBITable lensTable = LensDatabase.getTable(LensObject.class); + + hookMethod( + LENS_LOADING, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + synchronized (BUILD_LOCK) { + try { + @SuppressWarnings("unchecked") + List lensList = (List) param.args[0]; + + if (lensList == null || lensList.isEmpty()) + return; + + Collection storedLensList = lensTable.getAll( + new QueryBuilder() + .addSelection("isActive", "1") + ); + + @SuppressWarnings("Convert2Lambda") + Map lensDbMap = MapUtils.convertList( + storedLensList, + new KeyBinder() { + @Override + public String getKey(LensObject mapEntry) { + return mapEntry.id; + } + } + ); + + FieldMapper lensMapper = FieldMapper.initMapper("Lens", lensClass); + + /** + * =========================================================================== + * Build field maps for static classes with unchanged content + * =========================================================================== + */ + /** ========================================================================== **/ + FieldMapper.initMapper("Slug", slugClass); + // =========================================================================== + FieldMapper.initMapper("Track", trackClass); + // =========================================================================== + FieldMapper.initMapper("Context", contextClass); + /** ========================================================================== **/ + + boolean enableNewLenses = getPref(LENS_AUTO_ENABLE); + + for (Object lens : lensList) { + try { + Timber.d("Working on lens: %s", lens); + + String idFieldName = lensMapper.get("id"); + String lensId = (String) XposedHelpers.getObjectField(lens, idFieldName); + + /*if (lensTable.contains(lensId)) { + Timber.i("Lens %s already exists", lensId); + lensDbMap.remove(lensId); + continue; + }*/ + lensDbMap.remove(lensId); + + LensObject newDbLens = new LensObject(); + newDbLens.buildFromFieldMap(lensMapper, lens); + newDbLens.isActive = enableNewLenses; + + if (!newDbLens.isReady()) { + Timber.w("Lens not ready to save: %s", newDbLens); + lensDbMap.remove(lensId); + continue; + } + + if (!lensTable.insert(newDbLens)) + Timber.w("Failed to insert lens into database: %s", newDbLens); + } catch (Throwable t) { + Timber.w(t, "Failed to build lens: %s", lens); + } + } + + Timber.d("Inserting table into list"); + + List convertedLenses = convertLensObjects( + snapClassLoader, + lensMapper, + lensDbMap.values() + ); + + if (convertedLenses != null && convertedLenses.size() > 0) { + lensList.addAll(convertedLenses); + Timber.d("Inserted %s lenses", convertedLenses.size()); + } + + cleanEmptyLenses(lensMapper, lensList); + + FieldMapper.removeMapper("Lens"); + FieldMapper.removeMapper("Slug"); + FieldMapper.removeMapper("Track"); + Timber.d("Cleared %s cached objects", LensObject.destroyDataCache()); + } catch (Throwable t) { + Timber.e(t, "Unknown error handling lens system"); + } + } + } + } + ); + } catch (Throwable e) { + Timber.e(e); + moduleLoadState.fail(); + } + } + + private List convertLensObjects(ClassLoader snapClassLoader, FieldMapper lensMapper, + Collection lensObjects) { + try { + FieldMapper slugMapper = FieldMapper.getMapper("Slug"); + Object slugPos = XposedHelpers.newInstance(slugMapper.getLinkClass()); + slugMapper.setField(slugPos, "alignment", "right"); + slugMapper.setField(slugPos, "position", "BOTTOM_RIGHT"); + slugMapper.setField(slugPos, "text", "SPONSORED"); + slugMapper.setField(slugPos, "time_before_fadeout", 3500); + + FieldMapper trackMapper = FieldMapper.getMapper("Track"); + Object trackInfo = XposedHelpers.newInstance(trackMapper.getLinkClass()); + trackMapper.setField(trackInfo, "skip_track", false); + + + List convertedLenses = new ArrayList<>(lensObjects.size()); + Map lensMapDowncast = lensMapper; + + for (LensObject lensObject : lensObjects) { + if (!lensObject.isReady()) + continue; + + //Timber.d("Processing: " + lensObject); + Object convertedLens; + + try { + convertedLens = XposedHelpers.newInstance(lensMapper.getLinkClass()); + } catch (Throwable t) { + Timber.e(t); + continue; + } + + for (String fieldTag : lensMapDowncast.keySet()) { + Object storedVal = lensObject.getFieldByTag(fieldTag); + + if (storedVal != null) + lensMapper.setField(convertedLens, fieldTag, storedVal); + /*else { + if (fieldTag.equals("id") || fieldTag.equals("code") + || fieldTag.equals("icon_link") || fieldTag.equals("mLensLink")) { + Answers.safeLogEvent( + new CustomEvent("NullLensEntry") + .putCustomAttribute("Field", fieldTag) + ); + } + }*/ + } + + lensMapper.setField(convertedLens, "mSponsoredSlugPosAndText", slugPos); + lensMapper.setField(convertedLens, "unlockable_track_info", trackInfo); + + if (isConvertedLensCompleted(lensMapper, convertedLens)) + convertedLenses.add(convertedLens); + /*else { + Answers.safeLogEvent( + new CustomEvent("LensCrashMitigated") + ); + }*/ + } + + Timber.d("Converted %s lenses", convertedLenses.size()); + return convertedLenses; + } catch (Throwable t) { + Timber.e(t); + } + + return Collections.emptyList(); + } + + private void cleanEmptyLenses(FieldMapper lensMapper, List convertedLensList) { + Iterator convertedLensIterator = convertedLensList.iterator(); + + int emptyLensCount = 0; + + while (convertedLensIterator.hasNext()) { + Object convertedLens = convertedLensIterator.next(); + + if (convertedLens == null || !isConvertedLensCompleted(lensMapper, convertedLens)) { + convertedLensIterator.remove(); + emptyLensCount++; + } + } + +// if (emptyLensCount > 0) { +// Answers.safeLogEvent( +// new CustomEvent("EmptyLensReport") +// .putCustomAttribute("Count", emptyLensCount) +// ); +// } + } + + public boolean isConvertedLensCompleted(FieldMapper lensMapper, Object convertedLens) { + return lensMapper.getFieldVal(convertedLens, "id") != null + && lensMapper.getFieldVal(convertedLens, "code") != null + && lensMapper.getFieldVal(convertedLens, "icon_link") != null + && lensMapper.getFieldVal(convertedLens, "mLensLink") != null; + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/MiscChanges.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/MiscChanges.java index 1e44cd5..d9fecf0 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/MiscChanges.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/MiscChanges.java @@ -1,304 +1,258 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.ClipboardManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.ComponentInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.graphics.Typeface; -import android.text.InputFilter; -import android.text.InputType; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; -import android.view.inputmethod.EditorInfo; -import android.widget.EditText; - -import com.ljmu.andre.snaptools.Dialogs.Content.TextInputBasic; -import com.ljmu.andre.snaptools.Dialogs.DialogFactory; -import com.ljmu.andre.snaptools.Dialogs.ThemedDialog; -import com.ljmu.andre.snaptools.Dialogs.ThemedDialog.ThemedClickWrapper; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.ColorPickerDialogExtension; -import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.FontPickerDialogExtension; -import com.ljmu.andre.snaptools.ModulePack.Fragments.MiscChangesFragment; -import com.ljmu.andre.snaptools.Utils.ContextHelper; -import com.ljmu.andre.snaptools.Utils.ResourceMapper; -import com.ljmu.andre.snaptools.Utils.ResourceUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getCreateDir; -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.GsonPreferences.Preferences.putPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SNAPCHAT_CAPTION_VIEW_CLASS; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CAPTION_CREATE_HOOK; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FONT_HOOK; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SNAPCAPTIONVIEW_CONTEXT; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.COPY_BUTTON; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.CURRENT_FONT; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.CUT_BUTTON; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FONTS_PATH; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_MULTILINE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.PASTE_BUTTON; - -/*import com.flask.colorpicker.ColorPickerView.WHEEL_TYPE; -import com.flask.colorpicker.OnColorChangedListener; -import com.flask.colorpicker.OnColorSelectedListener; -import com.flask.colorpicker.builder.ColorPickerClickListener; -import com.flask.colorpicker.builder.ColorPickerDialogBuilder;*/ - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class MiscChanges extends ModuleHelper { - public static boolean isInternalFontCall; - - public MiscChanges(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - public static Typeface createTypefaceSafe(File fontFile) { - isInternalFontCall = true; - return Typeface.createFromFile(fontFile); - } - - @Override - public FragmentHelper[] getUIFragments() { - return new FragmentHelper[]{new MiscChangesFragment()}; - } - - public static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) { - ComponentName componentName = new ComponentName(pkgName, clsName); - int componentEnabledSetting = pm.getComponentEnabledSetting(componentName); - - switch (componentEnabledSetting) { - case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: - return false; - case PackageManager.COMPONENT_ENABLED_STATE_ENABLED: - return true; - case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT: - default: - // We need to get the application info to get the component's default state - try { - PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES - | PackageManager.GET_RECEIVERS - | PackageManager.GET_SERVICES - | PackageManager.GET_PROVIDERS - | PackageManager.GET_DISABLED_COMPONENTS); - - List components = new ArrayList<>(); - if (packageInfo.activities != null) Collections.addAll(components, packageInfo.activities); - if (packageInfo.services != null) Collections.addAll(components, packageInfo.services); - if (packageInfo.providers != null) Collections.addAll(components, packageInfo.providers); - - for (ComponentInfo componentInfo : components) { - if (componentInfo.name.equals(clsName)) { - return componentInfo.isEnabled(); - } - } - - // the component is not declared in the AndroidManifest - return false; - } catch (PackageManager.NameNotFoundException e) { - // the package isn't installed on the device - return false; - } - } - } - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - String currentFontFile = getPref(CURRENT_FONT); - - if (!currentFontFile.equals("Default")) { - hookMethod( - FONT_HOOK, - new HookWrapper((HookBefore) param -> { - if (isInternalFontCall) { - isInternalFontCall = false; - return; - } - - String fontFilename = (String) param.args[0]; - - File fontDir = getCreateDir(FONTS_PATH); - File replacementFontFile = new File(fontDir, currentFontFile); - - if (replacementFontFile.exists()) { - fontFilename = replacementFontFile.getAbsolutePath(); - Timber.d("[FONT] Font handled by hook"); - } - - param.args[0] = fontFilename; - }) - ); - } - - hookMethod( - CAPTION_CREATE_HOOK, - new HookWrapper((HookBefore) param -> { - Timber.d("Attempting to load custom menu"); - - /** - * =========================================================================== - * Recreate Snapchat's context menu - * =========================================================================== - */ - ActionMode actionMode = (ActionMode) param.args[0]; - Menu menu = (Menu) param.args[1]; - menu.clear(); - actionMode.getMenuInflater().inflate(ResourceMapper.getResId(ContextHelper.getActivity(), "caption_context_menu", "menu"), menu); - - EditText captionEditText = getObjectField(SNAPCAPTIONVIEW_CONTEXT, param.thisObject); - - int paste = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_paste"); - int cut = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_cut"); - int copy = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_copy"); - - if (getPref(COPY_BUTTON)) { - menu.findItem(copy).setVisible(true); - } - if (getPref(CUT_BUTTON)) { - menu.findItem(cut).setVisible(true); - } - //TODO: check if something is on the clipboard - ClipboardManager clipboardManager = (ClipboardManager) ContextHelper.getActivity().getSystemService(Context.CLIPBOARD_SERVICE); - if (getPref(PASTE_BUTTON)) { - if (clipboardManager != null) { - if (!clipboardManager.hasPrimaryClip()) { - menu.findItem(paste).setVisible(false); - - } else { - menu.findItem(paste).setVisible(true); - } - } else { - menu.findItem(paste).setVisible(true); - } - } - int bold = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_bold"); - int italic = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_italic"); - int underline = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_underline"); - menu.findItem(bold).setVisible(true); - menu.findItem(italic).setVisible(true); - menu.findItem(underline).setVisible(true); - - // =========================================================================== - - /** - * =========================================================================== - * Add a BG Color Picker - * =========================================================================== - */ - MenuItem bgColor = menu.add("BG Color"); - bgColor.setOnMenuItemClickListener(item -> { - Timber.d("Changing BG color"); - new ThemedDialog(ContextHelper.getActivity()) - .setTitle("Color Picker") - .setExtension( - new ColorPickerDialogExtension( - ContextHelper.getActivity(), - "primary", - captionEditText::setBackgroundColor - ) - ).show(); - - return true; - }); - bgColor.setVisible(true); - - // =========================================================================== - - /** - * =========================================================================== - * Add a Font Picker - * =========================================================================== - */ - MenuItem font = menu.add("Font"); - font.setOnMenuItemClickListener(item -> { - new ThemedDialog(ContextHelper.getActivity()) - .setTitle("Choose Font") - .setExtension( - new FontPickerDialogExtension( - ContextHelper.getActivity(), - MiscChangesFragment.getInstalledFonts(), - s -> { - Timber.d("Selected font: " + s); - putPref(CURRENT_FONT, s); - captionEditText.setTypeface(MiscChangesFragment.getTypefaceSafe(s)); - } - ) - ).show(); - - return true; - }); - font.setVisible(true); - // =========================================================================== - - /** - * =========================================================================== - * Add a Size Picker - * =========================================================================== - */ - MenuItem size = menu.add("Size"); - size.setOnMenuItemClickListener(item -> { - DialogFactory.createBasicTextInputDialog( - ContextHelper.getActivity(), - "Font Size", - "What size should the text be set to?", - null, - null, - InputType.TYPE_CLASS_NUMBER, - new ThemedClickWrapper(themedDialog -> { - String input = themedDialog.getExtension() - .getInputMessage(); - - try { - Float f = Float.parseFloat(input); - captionEditText.setTextSize(f); - themedDialog.dismiss(); - return; - } catch (Exception ignored) { - - } - - themedDialog.dismiss(); - })).show(); - - return true; - }); - size.setVisible(true); - // =========================================================================== - - param.setResult(true); - }) - ); - - - hookAllConstructors( - SNAPCHAT_CAPTION_VIEW_CLASS, - new HookWrapper((HookAfter) param -> { - EditText text = (EditText) param.thisObject; - if (getPref(FORCE_MULTILINE)) { - text.setSingleLine(false); - text.setMaxLines(500); - text.setImeOptions(EditorInfo.IME_ACTION_NONE); - text.setFilters(new InputFilter[0]); - - text.setOnLongClickListener(v -> { - text.showContextMenu(); - return false; - }); - } - }) - ); - } +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.ClipboardManager; +import android.content.Context; +import android.graphics.Typeface; +import android.text.InputFilter; +import android.text.InputType; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; + +import com.ljmu.andre.snaptools.Dialogs.Content.TextInputBasic; +import com.ljmu.andre.snaptools.Dialogs.DialogFactory; +import com.ljmu.andre.snaptools.Dialogs.ThemedDialog; +import com.ljmu.andre.snaptools.Dialogs.ThemedDialog.ThemedClickWrapper; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.ColorPickerDialogExtension; +import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.FontPickerDialogExtension; +import com.ljmu.andre.snaptools.ModulePack.Fragments.MiscChangesFragment; +import com.ljmu.andre.snaptools.Utils.ContextHelper; +import com.ljmu.andre.snaptools.Utils.ResourceMapper; +import com.ljmu.andre.snaptools.Utils.ResourceUtils; + +import java.io.File; + +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getCreateDir; +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.GsonPreferences.Preferences.putPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SNAPCHAT_CAPTION_VIEW_CLASS; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CAPTION_CREATE_HOOK; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FONT_HOOK; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SNAPCAPTIONVIEW_CONTEXT; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.COPY_BUTTON; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.CURRENT_FONT; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.CUT_BUTTON; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FONTS_PATH; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FORCE_MULTILINE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.PASTE_BUTTON; + +/*import com.flask.colorpicker.ColorPickerView.WHEEL_TYPE; +import com.flask.colorpicker.OnColorChangedListener; +import com.flask.colorpicker.OnColorSelectedListener; +import com.flask.colorpicker.builder.ColorPickerClickListener; +import com.flask.colorpicker.builder.ColorPickerDialogBuilder;*/ + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class MiscChanges extends ModuleHelper { + public static boolean isInternalFontCall; + + public MiscChanges(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + public static Typeface createTypefaceSafe(File fontFile) { + isInternalFontCall = true; + return Typeface.createFromFile(fontFile); + } + + @Override + public FragmentHelper[] getUIFragments() { + return new FragmentHelper[]{new MiscChangesFragment()}; + } + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + String currentFontFile = getPref(CURRENT_FONT); + + if (!currentFontFile.equals("Default")) { + hookMethod( + FONT_HOOK, + new HookWrapper((HookBefore) param -> { + if (isInternalFontCall) { + isInternalFontCall = false; + return; + } + + String fontFilename = (String) param.args[0]; + + File fontDir = getCreateDir(FONTS_PATH); + File replacementFontFile = new File(fontDir, currentFontFile); + + if (replacementFontFile.exists()) { + fontFilename = replacementFontFile.getAbsolutePath(); + Timber.d("[FONT] Font handled by hook"); + } + + param.args[0] = fontFilename; + }) + ); + } + + hookMethod( + CAPTION_CREATE_HOOK, + new HookWrapper((HookBefore) param -> { + Timber.d("Attempting to load custom menu"); + + /** + * =========================================================================== + * Recreate Snapchat's context menu + * =========================================================================== + */ + ActionMode actionMode = (ActionMode) param.args[0]; + Menu menu = (Menu) param.args[1]; + menu.clear(); + actionMode.getMenuInflater().inflate(ResourceMapper.getResId(ContextHelper.getActivity(), "caption_context_menu", "menu"), menu); + + EditText captionEditText = getObjectField(SNAPCAPTIONVIEW_CONTEXT, param.thisObject); + + int paste = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_paste"); + int cut = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_cut"); + int copy = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_copy"); + + if (getPref(COPY_BUTTON)) { + menu.findItem(copy).setVisible(true); + } + if (getPref(CUT_BUTTON)) { + menu.findItem(cut).setVisible(true); + } + //TODO: check if something is on the clipboard + ClipboardManager clipboardManager = (ClipboardManager) ContextHelper.getActivity().getSystemService(Context.CLIPBOARD_SERVICE); + if (getPref(PASTE_BUTTON)) { + if (clipboardManager != null) { + if (!clipboardManager.hasPrimaryClip()) { + menu.findItem(paste).setVisible(false); + + } else { + menu.findItem(paste).setVisible(true); + } + } else { + menu.findItem(paste).setVisible(true); + } + } + int bold = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_bold"); + int italic = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_italic"); + int underline = ResourceUtils.getId(ContextHelper.getActivity(), "menu_item_underline"); + menu.findItem(bold).setVisible(true); + menu.findItem(italic).setVisible(true); + menu.findItem(underline).setVisible(true); + + // =========================================================================== + + /** + * =========================================================================== + * Add a BG Color Picker + * =========================================================================== + */ + MenuItem bgColor = menu.add("BG Color"); + bgColor.setOnMenuItemClickListener(item -> { + Timber.d("Changing BG color"); + new ThemedDialog(ContextHelper.getActivity()) + .setTitle("Color Picker") + .setExtension( + new ColorPickerDialogExtension( + ContextHelper.getActivity(), + "primary", + captionEditText::setBackgroundColor + ) + ).show(); + + return true; + }); + bgColor.setVisible(true); + + // =========================================================================== + + /** + * =========================================================================== + * Add a Font Picker + * =========================================================================== + */ + MenuItem font = menu.add("Font"); + font.setOnMenuItemClickListener(item -> { + new ThemedDialog(ContextHelper.getActivity()) + .setTitle("Choose Font") + .setExtension( + new FontPickerDialogExtension( + ContextHelper.getActivity(), + MiscChangesFragment.getInstalledFonts(), + s -> { + Timber.d("Selected font: " + s); + putPref(CURRENT_FONT, s); + captionEditText.setTypeface(MiscChangesFragment.getTypefaceSafe(s)); + } + ) + ).show(); + + return true; + }); + font.setVisible(true); + // =========================================================================== + + /** + * =========================================================================== + * Add a Size Picker + * =========================================================================== + */ + MenuItem size = menu.add("Size"); + size.setOnMenuItemClickListener(item -> { + DialogFactory.createBasicTextInputDialog( + ContextHelper.getActivity(), + "Font Size", + "What size should the text be set to?", + null, + null, + InputType.TYPE_CLASS_NUMBER, + new ThemedClickWrapper(themedDialog -> { + String input = themedDialog.getExtension() + .getInputMessage(); + + try { + Float f = Float.parseFloat(input); + captionEditText.setTextSize(f); + themedDialog.dismiss(); + return; + } catch (Exception ignored) { + + } + + themedDialog.dismiss(); + })).show(); + + return true; + }); + size.setVisible(true); + // =========================================================================== + + param.setResult(true); + }) + ); + + + hookAllConstructors( + SNAPCHAT_CAPTION_VIEW_CLASS, + new HookWrapper((HookAfter) param -> { + EditText text = (EditText) param.thisObject; + if (getPref(FORCE_MULTILINE)) { + text.setSingleLine(false); + text.setMaxLines(500); + text.setImeOptions(EditorInfo.IME_ACTION_NONE); + text.setFilters(new InputFilter[0]); + + text.setOnLongClickListener(v -> { + text.showContextMenu(); + return false; + }); + } + }) + ); + } } \ No newline at end of file diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModuleHelper.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModuleHelper.java index 2cb8d3f..9dca811 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModuleHelper.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModuleHelper.java @@ -1,199 +1,199 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import androidx.annotation.Nullable; - -import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; -import com.ljmu.andre.snaptools.Framework.Module; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.HookClass; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.Hook; -import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.HookVariable; -import com.ljmu.andre.snaptools.ModulePack.HookResolver.HookReference; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import de.robv.android.xposed.XC_MethodHook; -import de.robv.android.xposed.XC_MethodHook.MethodHookParam; -import de.robv.android.xposed.XposedBridge; -import de.robv.android.xposed.XposedHelpers; -import timber.log.Timber; - -import static com.ljmu.andre.snaptools.Utils.UnhookManager.addUnhook; -import static de.robv.android.xposed.XposedHelpers.callMethod; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -@SuppressWarnings({"WeakerAccess", "unused"}) -public abstract class ModuleHelper extends Module { - public ModuleHelper(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - /** - * =========================================================================== - * A function to help distinguish between Constructors and Methods - * =========================================================================== - */ - void hookConstructor(Hook hook, XC_MethodHook xc_methodHook) { - hookMethod(hook, xc_methodHook); - } - - /** - * =========================================================================== - * Attempt to perform the requested hook... Mark a failure on error - * =========================================================================== - */ - void hookMethod(Hook hook, XC_MethodHook xc_methodHook) { - try { - HookReference hookReference = HookResolver.resolveHook(hook); - addUnhook( - name(), - XposedBridge.hookMethod( - hookReference.getHookMember(), - xc_methodHook - ) - ); - - moduleLoadState.success(); - } catch (HookNotFoundException e) { - Timber.e("Hook Failed: %s", e.getMessage()); - moduleLoadState.fail(); - } - } - - - /** - * =========================================================================== - * Attempt to hook all of the Constructors of a given HookClass - * =========================================================================== - */ - void hookAllConstructors(HookClass hookClass, XC_MethodHook xc_methodHook) { - try { - Class resolvedClass = HookResolver.resolveHookClass(hookClass); - addUnhook( - name(), - XposedBridge.hookAllConstructors( - resolvedClass, - xc_methodHook) - ); - - moduleLoadState.success(); - } catch (HookNotFoundException e) { - Timber.e("Hook Failed: %s", e.getMessage()); - moduleLoadState.fail(); - } - } - - /** - * =========================================================================== - * Utility method to get an object field with a generic type - * =========================================================================== - */ - @SuppressWarnings("unchecked") - T getObjectField(HookVariable hookVariable, Object obj) { - return (T) XposedHelpers.getObjectField(obj, hookVariable.getVarName()); - } - - /** - * =========================================================================== - * Utility method to get an object field with a generic type - * =========================================================================== - */ - @SuppressWarnings("unchecked") - T getObjectFieldWithType(HookVariable hookVariable, Object obj, Class type) { - Field objectField = XposedHelpers.findFirstFieldByExactType(obj.getClass(), type); - - try { - return (T) objectField.get(obj); - } catch (IllegalAccessException e) { - // should not happen - XposedBridge.log(e); - throw new IllegalAccessError(e.getMessage()); - } - } - - - /** - * =========================================================================== - * Utility method to set an object field with a hook variable - * =========================================================================== - */ - void setObjectField(HookVariable hookVariable, Object obj, Object value) { - XposedHelpers.setObjectField(obj, hookVariable.getVarName(), value); - } - - /** - * =========================================================================== - * Call a Hook's Method from the linked Obj with the supplied Params - * =========================================================================== - */ - @SuppressWarnings("unchecked") - T callHook(Hook hook, Object obj, Object... params) throws HookNotFoundException { - try { - return (T) callMethod(obj, hook.getHookMethod(), params); - } catch (Throwable e) { - throw new HookNotFoundException(e); - } - } - - /** - * =========================================================================== - * Call a Hook's Static Method with the supplied Params - * =========================================================================== - */ - @SuppressWarnings("unchecked") - T callStaticHook(Hook hook, Object... params) throws HookNotFoundException { - try { - return (T) ((Method) HookResolver.resolveHook(hook).getHookMember()).invoke(null, params); - } catch (Throwable e) { - if (e instanceof HookNotFoundException) - throw (HookNotFoundException) e; - - throw new HookNotFoundException(e); - } - } - - public interface HookBefore { - void before(MethodHookParam param) throws Throwable; - } - - public interface HookAfter { - void after(MethodHookParam param) throws Throwable; - } - - public static class HookWrapper extends ST_MethodHook { - private HookBefore hookBefore; - private HookAfter hookAfter; - - public HookWrapper(@Nullable HookBefore hookBefore, @Nullable HookAfter hookAfter) { - this.hookBefore = hookBefore; - this.hookAfter = hookAfter; - } - - public HookWrapper(HookBefore hookBefore) { - this.hookBefore = hookBefore; - } - - public HookWrapper(HookAfter hookAfter) { - this.hookAfter = hookAfter; - } - - @Override - protected void before(MethodHookParam param) throws Throwable { - if (hookBefore != null) - hookBefore.before(param); - } - - @Override - protected void after(MethodHookParam param) throws Throwable { - if (hookAfter != null) - hookAfter.after(param); - } - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import androidx.annotation.Nullable; + +import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; +import com.ljmu.andre.snaptools.Framework.Module; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.HookClass; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.Hook; +import com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.HookVariable; +import com.ljmu.andre.snaptools.ModulePack.HookResolver.HookReference; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XC_MethodHook.MethodHookParam; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; +import timber.log.Timber; + +import static com.ljmu.andre.snaptools.Utils.UnhookManager.addUnhook; +import static de.robv.android.xposed.XposedHelpers.callMethod; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +@SuppressWarnings({"WeakerAccess", "unused"}) +public abstract class ModuleHelper extends Module { + public ModuleHelper(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + /** + * =========================================================================== + * A function to help distinguish between Constructors and Methods + * =========================================================================== + */ + void hookConstructor(Hook hook, XC_MethodHook xc_methodHook) { + hookMethod(hook, xc_methodHook); + } + + /** + * =========================================================================== + * Attempt to perform the requested hook... Mark a failure on error + * =========================================================================== + */ + void hookMethod(Hook hook, XC_MethodHook xc_methodHook) { + try { + HookReference hookReference = HookResolver.resolveHook(hook); + addUnhook( + name(), + XposedBridge.hookMethod( + hookReference.getHookMember(), + xc_methodHook + ) + ); + + moduleLoadState.success(); + } catch (HookNotFoundException e) { + Timber.e("Hook Failed: %s", e.getMessage()); + moduleLoadState.fail(); + } + } + + + /** + * =========================================================================== + * Attempt to hook all of the Constructors of a given HookClass + * =========================================================================== + */ + void hookAllConstructors(HookClass hookClass, XC_MethodHook xc_methodHook) { + try { + Class resolvedClass = HookResolver.resolveHookClass(hookClass); + addUnhook( + name(), + XposedBridge.hookAllConstructors( + resolvedClass, + xc_methodHook) + ); + + moduleLoadState.success(); + } catch (HookNotFoundException e) { + Timber.e("Hook Failed: %s", e.getMessage()); + moduleLoadState.fail(); + } + } + + /** + * =========================================================================== + * Utility method to get an object field with a generic type + * =========================================================================== + */ + @SuppressWarnings("unchecked") + T getObjectField(HookVariable hookVariable, Object obj) { + return (T) XposedHelpers.getObjectField(obj, hookVariable.getVarName()); + } + + /** + * =========================================================================== + * Utility method to get an object field with a generic type + * =========================================================================== + */ + @SuppressWarnings("unchecked") + T getObjectFieldWithType(HookVariable hookVariable, Object obj, Class type) { + Field objectField = XposedHelpers.findFirstFieldByExactType(obj.getClass(), type); + + try { + return (T) objectField.get(obj); + } catch (IllegalAccessException e) { + // should not happen + XposedBridge.log(e); + throw new IllegalAccessError(e.getMessage()); + } + } + + + /** + * =========================================================================== + * Utility method to set an object field with a hook variable + * =========================================================================== + */ + void setObjectField(HookVariable hookVariable, Object obj, Object value) { + XposedHelpers.setObjectField(obj, hookVariable.getVarName(), value); + } + + /** + * =========================================================================== + * Call a Hook's Method from the linked Obj with the supplied Params + * =========================================================================== + */ + @SuppressWarnings("unchecked") + T callHook(Hook hook, Object obj, Object... params) throws HookNotFoundException { + try { + return (T) callMethod(obj, hook.getHookMethod(), params); + } catch (Throwable e) { + throw new HookNotFoundException(e); + } + } + + /** + * =========================================================================== + * Call a Hook's Static Method with the supplied Params + * =========================================================================== + */ + @SuppressWarnings("unchecked") + T callStaticHook(Hook hook, Object... params) throws HookNotFoundException { + try { + return (T) ((Method) HookResolver.resolveHook(hook).getHookMember()).invoke(null, params); + } catch (Throwable e) { + if (e instanceof HookNotFoundException) + throw (HookNotFoundException) e; + + throw new HookNotFoundException(e); + } + } + + public interface HookBefore { + void before(MethodHookParam param) throws Throwable; + } + + public interface HookAfter { + void after(MethodHookParam param) throws Throwable; + } + + public static class HookWrapper extends ST_MethodHook { + private HookBefore hookBefore; + private HookAfter hookAfter; + + public HookWrapper(@Nullable HookBefore hookBefore, @Nullable HookAfter hookAfter) { + this.hookBefore = hookBefore; + this.hookAfter = hookAfter; + } + + public HookWrapper(HookBefore hookBefore) { + this.hookBefore = hookBefore; + } + + public HookWrapper(HookAfter hookAfter) { + this.hookAfter = hookAfter; + } + + @Override + protected void before(MethodHookParam param) throws Throwable { + if (hookBefore != null) + hookBefore.before(param); + } + + @Override + protected void after(MethodHookParam param) throws Throwable { + if (hookAfter != null) + hookAfter.after(param); + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulePackImpl.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulePackImpl.java index 926c134..5e6aa5f 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulePackImpl.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulePackImpl.java @@ -1,204 +1,204 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.app.Activity; -import android.content.Context; -import android.util.Pair; - -import com.ljmu.andre.snaptools.Exceptions.ModulePackLoadAborted; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.Framework.MetaData.LocalPackMetaData; -import com.ljmu.andre.snaptools.Framework.Module; -import com.ljmu.andre.snaptools.Framework.ModulePack; -import com.ljmu.andre.snaptools.Framework.Utils.LoadState.State; -import com.ljmu.andre.snaptools.Framework.Utils.ModuleLoadState; -import com.ljmu.andre.snaptools.Framework.Utils.PackLoadState; -import com.ljmu.andre.snaptools.ModulePack.Caching.SnapDiskCache; -import com.ljmu.andre.snaptools.ModulePack.Fragments.GeneralSettingsFragment; -import com.ljmu.andre.snaptools.ModulePack.Fragments.KnownBugsFragment; -import com.ljmu.andre.snaptools.ModulePack.ModulesDef.Modules; -import com.ljmu.andre.snaptools.Utils.Constants; - -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getCreateDir; -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FILTERS_PATH; -import static com.ljmu.andre.snaptools.Utils.FrameworkPreferencesDef.DISABLED_MODULES; -import static com.ljmu.andre.snaptools.Utils.PreferenceHelpers.collectionContains; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -@SuppressWarnings({"unused", "WeakerAccess"}) -public class ModulePackImpl extends ModulePack { - private static final int MINIMUM_FRAMEWORK_VERSION = 110; - - public ModulePackImpl(LocalPackMetaData packMetaData, PackLoadState loadState) throws ModulePackLoadAborted { - super(packMetaData, loadState); - - checkFrameworkVersion(); - } - - private void checkFrameworkVersion() throws ModulePackLoadAborted { - if (Constants.getApkVersionCode() < MINIMUM_FRAMEWORK_VERSION) - throw new ModulePackLoadAborted("Pack requires newer APK version"); - } - - /** - * =========================================================================== - * Helper method to quickly determine if a settings UI can be expected - * =========================================================================== - */ - @Override - public boolean hasGeneralSettingsUI() { - return true; - } - - /** - * =========================================================================== - * Pull the Settings UI for this Pack - * =========================================================================== - */ - @Override - public FragmentHelper[] getStaticFragments() { - GeneralSettingsFragment settingsFragment = new GeneralSettingsFragment(); - settingsFragment.setPackName(getPackName()); - - for (Modules moduleData : ModulesDef.INST.values()) { - if (moduleData.canBeDisabled()) { - settingsFragment.addDisplayHolder( - Pair.create( - moduleData.getModuleName(), - moduleData.getDescription() - ) - ); - } - } - - return new FragmentHelper[]{settingsFragment, new KnownBugsFragment().buildMetaData(this)}; } - - /** - * =========================================================================== - * Attempt to load the contained Modules - * =========================================================================== - */ - @Override - public Map loadModules() { - getCreateDir(FILTERS_PATH); - - Map moduleLoadStates = new LinkedHashMap<>(); - HashSet disabledModules = getPref(DISABLED_MODULES); - - for (Modules moduleData : ModulesDef.INST.values()) { - ModuleLoadState loadState = new ModuleLoadState(moduleData.getModuleName()); - moduleLoadStates.put(loadState.getName(), loadState); - - // Check if module should be skipped \\ - if (moduleData.canBeDisabled() && - collectionContains( - DISABLED_MODULES, - moduleData.getModuleName() - )) { - loadState.setState(State.SKIPPED); - continue; - } - - // =========================================================================== - - try { - Class moduleClass = moduleData.getModuleClass(); - Constructor constructor = moduleClass.getConstructor(String.class, boolean.class); - Module module = constructor.newInstance(moduleData.getModuleName(), moduleData.canBeDisabled()); - modules.add(module); - - loadState.setState(State.SUCCESS); - } catch (Throwable e) { - Timber.e(e, "Failed loading module: " - + moduleData.getClassName()); - loadState.setState(State.FAILED); - } - } - - hasLoaded = true; - - return moduleLoadStates; - } - - @Override - public List injectAllHooks(ClassLoader snapClassLoader, Context snapContext) { - if (!hasLoaded) - throw new IllegalStateException("Module Pack not loaded!"); - - if (hasInjected) { - Timber.d("Tried to re-inject all hooks"); - return null; - } - - SnapDiskCache.getInstance().destroyTempDir(); - - List hookResults = new ArrayList<>(); - Map moduleLoadStateMap = getPackLoadState().getModuleLoadStates(); - - for (ModuleLoadState moduleLoadState : moduleLoadStateMap.values()) { - hookResults.add(moduleLoadState); - - if (moduleLoadState.getState() != State.SUCCESS) - continue; - - Module module = getModule(moduleLoadState.getName()); - if (module == null) { - moduleLoadState.setState(State.FAILED); - continue; - } - - try { - module.injectHooks(snapClassLoader, snapContext, moduleLoadState); - } catch (Throwable t) { - Timber.e(t); - moduleLoadState.fail(); - } - } - - packLoadState.refreshPackLoadState(); - - hasInjected = true; - return hookResults; - } - - @Override - public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { - Map moduleLoadStateMap = getPackLoadState().getModuleLoadStates(); - - for (ModuleLoadState state : moduleLoadStateMap.values()) { - if (state.getState() != State.SUCCESS) - continue; - - Module module = getModule(state.getName()); - if (module == null) { - state.setState(State.FAILED); - continue; - } - - try { - module.prepareActivity(snapClassLoader, snapActivity); - } catch (Throwable t) { - Timber.e(t); - state.fail(); - } - } - } - - @Override - public String isPremiumCheck() { - return "A SnapTools Pack"; - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.app.Activity; +import android.content.Context; +import android.util.Pair; + +import com.ljmu.andre.snaptools.Exceptions.ModulePackLoadAborted; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.Framework.MetaData.LocalPackMetaData; +import com.ljmu.andre.snaptools.Framework.Module; +import com.ljmu.andre.snaptools.Framework.ModulePack; +import com.ljmu.andre.snaptools.Framework.Utils.LoadState.State; +import com.ljmu.andre.snaptools.Framework.Utils.ModuleLoadState; +import com.ljmu.andre.snaptools.Framework.Utils.PackLoadState; +import com.ljmu.andre.snaptools.ModulePack.Caching.SnapDiskCache; +import com.ljmu.andre.snaptools.ModulePack.Fragments.GeneralSettingsFragment; +import com.ljmu.andre.snaptools.ModulePack.Fragments.KnownBugsFragment; +import com.ljmu.andre.snaptools.ModulePack.ModulesDef.Modules; +import com.ljmu.andre.snaptools.Utils.Constants; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getCreateDir; +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.FILTERS_PATH; +import static com.ljmu.andre.snaptools.Utils.FrameworkPreferencesDef.DISABLED_MODULES; +import static com.ljmu.andre.snaptools.Utils.PreferenceHelpers.collectionContains; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +@SuppressWarnings({"unused", "WeakerAccess"}) +public class ModulePackImpl extends ModulePack { + private static final int MINIMUM_FRAMEWORK_VERSION = 110; + + public ModulePackImpl(LocalPackMetaData packMetaData, PackLoadState loadState) throws ModulePackLoadAborted { + super(packMetaData, loadState); + + checkFrameworkVersion(); + } + + private void checkFrameworkVersion() throws ModulePackLoadAborted { + if (Constants.getApkVersionCode() < MINIMUM_FRAMEWORK_VERSION) + throw new ModulePackLoadAborted("Pack requires newer APK version"); + } + + /** + * =========================================================================== + * Helper method to quickly determine if a settings UI can be expected + * =========================================================================== + */ + @Override + public boolean hasGeneralSettingsUI() { + return true; + } + + /** + * =========================================================================== + * Pull the Settings UI for this Pack + * =========================================================================== + */ + @Override + public FragmentHelper[] getStaticFragments() { + GeneralSettingsFragment settingsFragment = new GeneralSettingsFragment(); + settingsFragment.setPackName(getPackName()); + + for (Modules moduleData : ModulesDef.INST.values()) { + if (moduleData.canBeDisabled()) { + settingsFragment.addDisplayHolder( + Pair.create( + moduleData.getModuleName(), + moduleData.getDescription() + ) + ); + } + } + + return new FragmentHelper[]{settingsFragment, new KnownBugsFragment().buildMetaData(this)}; } + + /** + * =========================================================================== + * Attempt to load the contained Modules + * =========================================================================== + */ + @Override + public Map loadModules() { + getCreateDir(FILTERS_PATH); + + Map moduleLoadStates = new LinkedHashMap<>(); + HashSet disabledModules = getPref(DISABLED_MODULES); + + for (Modules moduleData : ModulesDef.INST.values()) { + ModuleLoadState loadState = new ModuleLoadState(moduleData.getModuleName()); + moduleLoadStates.put(loadState.getName(), loadState); + + // Check if module should be skipped \\ + if (moduleData.canBeDisabled() && + collectionContains( + DISABLED_MODULES, + moduleData.getModuleName() + )) { + loadState.setState(State.SKIPPED); + continue; + } + + // =========================================================================== + + try { + Class moduleClass = moduleData.getModuleClass(); + Constructor constructor = moduleClass.getConstructor(String.class, boolean.class); + Module module = constructor.newInstance(moduleData.getModuleName(), moduleData.canBeDisabled()); + modules.add(module); + + loadState.setState(State.SUCCESS); + } catch (Throwable e) { + Timber.e(e, "Failed loading module: " + + moduleData.getClassName()); + loadState.setState(State.FAILED); + } + } + + hasLoaded = true; + + return moduleLoadStates; + } + + @Override + public List injectAllHooks(ClassLoader snapClassLoader, Context snapContext) { + if (!hasLoaded) + throw new IllegalStateException("Module Pack not loaded!"); + + if (hasInjected) { + Timber.d("Tried to re-inject all hooks"); + return null; + } + + SnapDiskCache.getInstance().destroyTempDir(); + + List hookResults = new ArrayList<>(); + Map moduleLoadStateMap = getPackLoadState().getModuleLoadStates(); + + for (ModuleLoadState moduleLoadState : moduleLoadStateMap.values()) { + hookResults.add(moduleLoadState); + + if (moduleLoadState.getState() != State.SUCCESS) + continue; + + Module module = getModule(moduleLoadState.getName()); + if (module == null) { + moduleLoadState.setState(State.FAILED); + continue; + } + + try { + module.injectHooks(snapClassLoader, snapContext, moduleLoadState); + } catch (Throwable t) { + Timber.e(t); + moduleLoadState.fail(); + } + } + + packLoadState.refreshPackLoadState(); + + hasInjected = true; + return hookResults; + } + + @Override + public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { + Map moduleLoadStateMap = getPackLoadState().getModuleLoadStates(); + + for (ModuleLoadState state : moduleLoadStateMap.values()) { + if (state.getState() != State.SUCCESS) + continue; + + Module module = getModule(state.getName()); + if (module == null) { + state.setState(State.FAILED); + continue; + } + + try { + module.prepareActivity(snapClassLoader, snapActivity); + } catch (Throwable t) { + Timber.e(t); + state.fail(); + } + } + } + + @Override + public String isPremiumCheck() { + return "A SnapTools Pack"; + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulesDef.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulesDef.java index 5b1dd1e..d7f4559 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulesDef.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ModulesDef.java @@ -1,177 +1,172 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import com.ljmu.andre.ConstantDefiner.Constant; -import com.ljmu.andre.ConstantDefiner.ConstantDefiner; -import com.ljmu.andre.snaptools.Framework.Module; -import com.ljmu.andre.snaptools.ModulePack.ModulesDef.Modules; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -@SuppressWarnings("unused") -class ModulesDef extends ConstantDefiner { - public static final ModulesDef INST = new ModulesDef(); - - public static final Modules HOOK_RESOLVER = new Modules( - 1, - "Hook Resolver", - HookResolver.class, - false - ); - - // =========================================================================== - - public static final Modules SAVING = new Modules( - 2, - "Saving", - Saving.class, - "Responsible for handling the saving of media\nSupported Media:\n\tStory/Received Videos and Images\n\tChat Images and Videos" - ); - - // =========================================================================== - - public static final Modules LENS_COLLECTOR = new Modules( - 3, - "Lens Collector", - LensCollector.class, - "Collects your daily Lenses so that you can choose to enable/disable whichever lenses you desire" - ); - - // =========================================================================== - - public static final Modules CHAT_MANAGER = new Modules( - 4, - "Chat Manager", - ChatSaving.class, - "Collects all chat messages and allows you to re-read them within the SnapTools app. Also provides the option to automatically save messages within Snapchat" - ); - - // =========================================================================== - - public static final Modules MISC_CHANGES = new Modules( - 5, - "Misc Changes", - MiscChanges.class, - "A collection of small changes that provide small benefits:\n\t- Enable torch/flash on front facing camera\n\t- Multiline Captions" - ); - - // =========================================================================== - - public static final Modules REMOVE_SNAP_TIMER = new Modules( - 6, - "Remove Snap Timer", - RemoveSnapTimer.class, - "Removes the timer on all snaps. Used mainly when unlimited viewing is enabled" - ); - - // =========================================================================== - - public static final Modules SCREENSHOT_BYPASS = new Modules( - 7, - "Screenshot Bypass", - ScreenshotBypass.class, - "Block screenshot notifications being sent to the recipient" - ); - - // =========================================================================== - - public static final Modules STORY_BLOCKER = new Modules( - 8, - "Story Blocker", - StoryBlocker.class, - "Blocks advertising within Snapchat, and allows the ability to block specific users' stories" - ); - - // =========================================================================== - - public static final Modules UNLIMITED_VIEWING = new Modules( - 9, - "Unlimited Viewing", - UnlimitedViewing.class, - "Removes the time limit when viewing snaps" - ); - - // =========================================================================== - - public static final Modules SHARING = new Modules( - 10, - "Sharing", - Sharing.class, - "Convert images shared to Snapchat into snaps that can be added to your story as opposed to chat media" - ); - - // =========================================================================== - -// public static final Modules CUSTOM_FILTERS = new Modules( -// 11, -// "Custom Filters", -// CustomFilters.class, -// "Allows for custom images and a Now Playing filter to be loaded alongside Snapchat filters" -// ); - - // =========================================================================== - - public static final Modules STEALTH_VIEWING = new Modules( - 12, - "Stealth Viewing", - StealthViewing.class, - "Allows you to view chats and snaps without marking them as viewed" - ); - - public static final Modules ACCOUNT_MANAGER = new Modules( - 13, - "Account Manager", - AccountManager.class, - "Allows for safely and securely swapping between Snapchat accounts, along with other account management features" - ); - - // =========================================================================== - - public static final Modules FORCED_HOOKS = new Modules( - 14, - "Forced Hooks", - ForcedHooks.class, - false - ); - - // =========================================================================== - - public static class Modules extends Constant { - private final Class moduleClass; - private final boolean canBeDisabled; - private String description; - - Modules(int index, String moduleName, Class moduleClass, String description) { - this(index, moduleName, moduleClass, true); - this.description = description; - } - - Modules(int index, String moduleName, Class moduleClass, boolean canBeDisabled) { - super(index, moduleName); - this.moduleClass = moduleClass; - this.canBeDisabled = canBeDisabled; - } - - public Class getModuleClass() { - return moduleClass; - } - - public String getClassName() { - return moduleClass.getSimpleName(); - } - - public String getModuleName() { - return getName(); - } - - public String getDescription() { - return description; - } - - public boolean canBeDisabled() { - return canBeDisabled; - } - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import com.ljmu.andre.ConstantDefiner.Constant; +import com.ljmu.andre.ConstantDefiner.ConstantDefiner; +import com.ljmu.andre.snaptools.Framework.Module; +import com.ljmu.andre.snaptools.ModulePack.ModulesDef.Modules; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +@SuppressWarnings("unused") +class ModulesDef extends ConstantDefiner { + public static final ModulesDef INST = new ModulesDef(); + + public static final Modules HOOK_RESOLVER = new Modules( + 1, + "Hook Resolver", + HookResolver.class, + false + ); + + // =========================================================================== + + public static final Modules SAVING = new Modules( + 2, + "Saving", + Saving.class, + "Responsible for handling the saving of media\nSupported Media:\n\tStory/Received Videos and Images\n\tChat Images and Videos" + ); + + // =========================================================================== + + public static final Modules LENS_COLLECTOR = new Modules( + 3, + "Lens Collector", + LensCollector.class, + "Collects your daily Lenses so that you can choose to enable/disable whichever lenses you desire" + ); + + // =========================================================================== + + public static final Modules CHAT_MANAGER = new Modules( + 4, + "Chat Manager", + ChatSaving.class, + "Collects all chat messages and allows you to re-read them within the SnapTools app. Also provides the option to automatically save messages within Snapchat" + ); + + // =========================================================================== + + public static final Modules MISC_CHANGES = new Modules( + 5, + "Misc Changes", + MiscChanges.class, + "A collection of small changes that provide small benefits:\n\t- Enable torch/flash on front facing camera\n\t- Multiline Captions" + ); + + // =========================================================================== + + public static final Modules REMOVE_SNAP_TIMER = new Modules( + 6, + "Remove Snap Timer", + RemoveSnapTimer.class, + "Removes the timer on all snaps. Used mainly when unlimited viewing is enabled" + ); + + // =========================================================================== + + public static final Modules SCREENSHOT_BYPASS = new Modules( + 7, + "Screenshot Bypass", + ScreenshotBypass.class, + "Block screenshot notifications being sent to the recipient" + ); + + // =========================================================================== + + public static final Modules STORY_BLOCKER = new Modules( + 8, + "Story Blocker", + StoryBlocker.class, + "Blocks advertising within Snapchat, and allows the ability to block specific users' stories" + ); + + // =========================================================================== + + public static final Modules UNLIMITED_VIEWING = new Modules( + 9, + "Unlimited Viewing", + UnlimitedViewing.class, + "Removes the time limit when viewing snaps" + ); + + // =========================================================================== + + public static final Modules SHARING = new Modules( + 10, + "Sharing", + Sharing.class, + "Convert images shared to Snapchat into snaps that can be added to your story as opposed to chat media" + ); + + // =========================================================================== + +// public static final Modules CUSTOM_FILTERS = new Modules( +// 11, +// "Custom Filters", +// CustomFilters.class, +// "Allows for custom images and a Now Playing filter to be loaded alongside Snapchat filters" +// ); + + // =========================================================================== + + public static final Modules STEALTH_VIEWING = new Modules( + 12, + "Stealth Viewing", + StealthViewing.class, + "Allows you to view chats and snaps without marking them as viewed" + ); + + // =========================================================================== + + // =========================================================================== + + public static final Modules FORCED_HOOKS = new Modules( + 13, + "Forced Hooks", + ForcedHooks.class, + false + ); + + // =========================================================================== + + public static class Modules extends Constant { + private final Class moduleClass; + private final boolean canBeDisabled; + private String description; + + Modules(int index, String moduleName, Class moduleClass, String description) { + this(index, moduleName, moduleClass, true); + this.description = description; + } + + Modules(int index, String moduleName, Class moduleClass, boolean canBeDisabled) { + super(index, moduleName); + this.moduleClass = moduleClass; + this.canBeDisabled = canBeDisabled; + } + + public Class getModuleClass() { + return moduleClass; + } + + public String getClassName() { + return moduleClass.getSimpleName(); + } + + public String getModuleName() { + return getName(); + } + + public String getDescription() { + return description; + } + + public boolean canBeDisabled() { + return canBeDisabled; + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/RemoveSnapTimer.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/RemoveSnapTimer.java index b4b6edc..7b06275 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/RemoveSnapTimer.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/RemoveSnapTimer.java @@ -1,54 +1,54 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.Context; - -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; - -import de.robv.android.xposed.XC_MethodReplacement; - -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.COUNTDOWNTIMER_VIEW_ONDRAW; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.NEW_CONCENTRIC_TIMERVIEW_ONDRAW; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class RemoveSnapTimer extends ModuleHelper { - public RemoveSnapTimer(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - @Override - public FragmentHelper[] getUIFragments() { - return null; - } - - // =========================================================================== - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - // Removed within snapchat =================================================== -// hookMethod( -// SNAPTIMERVIEW_ONDRAW, -// XC_MethodReplacement.DO_NOTHING -// ); -// -// hookMethod( -// CONCENTRIC_TIMERVIEW_ONDRAW, -// XC_MethodReplacement.DO_NOTHING -// ); - - hookMethod( - NEW_CONCENTRIC_TIMERVIEW_ONDRAW, - XC_MethodReplacement.DO_NOTHING - ); - - hookMethod( - COUNTDOWNTIMER_VIEW_ONDRAW, - XC_MethodReplacement.DO_NOTHING - ); - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.Context; + +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; + +import de.robv.android.xposed.XC_MethodReplacement; + +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.COUNTDOWNTIMER_VIEW_ONDRAW; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.NEW_CONCENTRIC_TIMERVIEW_ONDRAW; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class RemoveSnapTimer extends ModuleHelper { + public RemoveSnapTimer(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + @Override + public FragmentHelper[] getUIFragments() { + return null; + } + + // =========================================================================== + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + // Removed within snapchat =================================================== +// hookMethod( +// SNAPTIMERVIEW_ONDRAW, +// XC_MethodReplacement.DO_NOTHING +// ); +// +// hookMethod( +// CONCENTRIC_TIMERVIEW_ONDRAW, +// XC_MethodReplacement.DO_NOTHING +// ); + + hookMethod( + NEW_CONCENTRIC_TIMERVIEW_ONDRAW, + XC_MethodReplacement.DO_NOTHING + ); + + hookMethod( + COUNTDOWNTIMER_VIEW_ONDRAW, + XC_MethodReplacement.DO_NOTHING + ); + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Saving.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Saving.java index 457cb9b..126038f 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Saving.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Saving.java @@ -1,888 +1,866 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.app.Activity; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.net.Uri; -import androidx.annotation.Nullable; -import android.widget.FrameLayout; -import android.widget.Toast; - -import com.google.common.io.ByteStreams; -import com.google.common.io.Closer; -import com.google.common.io.Files; -import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.ModulePack.Fragments.SavingSettingsFragment; -import com.ljmu.andre.snaptools.ModulePack.Notifications.SaveNotification; -import com.ljmu.andre.snaptools.ModulePack.Notifications.SaveNotification.ToastType; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.SaveTriggerManager; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.ChatImageSnap; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.ChatVideoSnap; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.GroupSnap; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.ReceivedSnap; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.SentSnap; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap.Builder; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap.SaveState; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap.SnapTypeDef; -import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.StorySnap; -import com.ljmu.andre.snaptools.ModulePack.Utils.SavingLayout; -import com.ljmu.andre.snaptools.ModulePack.Utils.SavingViewPool; -import com.ljmu.andre.snaptools.Utils.ContextHelper; -import com.ljmu.andre.snaptools.Utils.CustomObservers.ErrorObserver; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.UUID; -import java.util.concurrent.Callable; - -import de.robv.android.xposed.XposedHelpers; -import io.reactivex.Observable; -import io.reactivex.schedulers.Schedulers; -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getCreateDir; -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.RECEIVED_SNAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SENT_IMAGE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SENT_VIDEO; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_IMAGE_GET_ALGORITHM; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_VIDEO_GET_ALGORITHM; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_VIDEO_PATH; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CONSTRUCTOR_OPERA_PAGE_VIEW; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.DIRECT_GET_ALGORITHM; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.ENCRYPTION_ALGORITHM_STREAM; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_USERNAME; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GROUP_ALGORITHM_UNWRAPPED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GROUP_GET_ALGORITHM; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.OPENED_SNAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SENT_BATCHED_SNAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SENT_SNAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_MEDIA_TYPE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_TIMESTAMP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_USERNAME; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_DISPLAYED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_GET_ALGORITHM; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_BUILDER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_GET_OBJECT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_INSERT_OBJECT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STREAM_TYPE_CHECK_BYPASS; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.DIVISION_FIX; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_METADATA_MEDIA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.GROUP_ALGORITHM_WRAPPER_FIELD; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_BATCHED_VIDEO_MEDIAHOLDER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_BATCH_DATA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_BITMAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_TIMESTAMP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_VIDEO_URI; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SNAP_IS_ZIPPED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_DISPLAY_STATE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_METADATA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STREAM_TYPE_CHECK_BOOLEAN; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.SAVE_SENT_SNAPS; -import static com.ljmu.andre.snaptools.Utils.FrameworkPreferencesDef.TEMP_PATH; -import static com.ljmu.andre.snaptools.Utils.XposedUtils.logEntireClass; -import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField; -import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -@SuppressWarnings("WeakerAccess") -public class Saving extends ModuleHelper { - private static final String KEY_HEADER = "SNAP_KEY"; - public static boolean hasLoadedHooks; - private static String yourUsername = ""; - - public Saving(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - @Override - public FragmentHelper[] getUIFragments() { - return new FragmentHelper[]{ - new SavingSettingsFragment() - }; - } - - // =========================================================================== - - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - SaveTriggerManager.init(); - - /** - * =========================================================================== - * Hook to inject the manual saving layout - * =========================================================================== - */ - hookConstructor( - CONSTRUCTOR_OPERA_PAGE_VIEW, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - try { - FrameLayout operaLayout = (FrameLayout) param.thisObject; - SavingLayout savingLayout = SavingViewPool.requestLayout(ContextHelper.getActivity()); - - operaLayout.addView(savingLayout); - } catch (Throwable t) { - Timber.e(t); - } - } - } - ); - - /** - * =========================================================================== - * Bypasses type checking on encrypted streams - * This would cause a duplicate stream to become corrupt so cannot happen - * =========================================================================== - */ - hookMethod( - STREAM_TYPE_CHECK_BYPASS, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - setObjectField(STREAM_TYPE_CHECK_BOOLEAN, param.thisObject, false); - } - } - ); - - /** - * =========================================================================== - * New Sent Snap Detected -> Determine the type and extract the media - * =========================================================================== - */ - if (getPref(SAVE_SENT_SNAPS)) { - hookMethod( - SENT_BATCHED_SNAP, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Timber.i("Splitting process"); - - Object mediaHolder = getObjectField(SENT_BATCHED_VIDEO_MEDIAHOLDER, param.thisObject); - - Observable.fromCallable(new Callable() { - @Override - public Object call() throws Exception { - Uri videoUri = getObjectField(SENT_MEDIA_VIDEO_URI, mediaHolder); - Timber.i("Handling splitting media"); - String videoPath = videoUri.getPath().replaceFirst("file:", ""); - File sourceMedia = new File(videoPath); - - if (!sourceMedia.exists() && !sourceMedia.createNewFile()) - Timber.w("Source tracked video doesn't exist and couldn't be created"); - - File tempDir = getCreateDir(TEMP_PATH); - File targetMedia = new File(tempDir, "Batched_Sent_Snap.mp4"); - - Files.copy(sourceMedia, targetMedia); - Timber.d("Copied batch file successfully"); - return new Object(); - } - }).subscribeOn(Schedulers.computation()) - .subscribe(new ErrorObserver<>("Error duplicating sent batch")); - - //Files.copy(file); - } - } - ); - - hookMethod( - SENT_SNAP, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Object media = param.args[0]; - - SentSnap sentSnap = null; - - try { - Class sentImage = HookResolver.resolveHookClass(SENT_IMAGE); - Class sentVideo = HookResolver.resolveHookClass(SENT_VIDEO); - - if (media.getClass().equals(sentImage)) - sentSnap = handleSentSnap(ContextHelper.getActivity(), media, false); - else if (media.getClass().equals(sentVideo)) - sentSnap = handleSentSnap(ContextHelper.getActivity(), media, true); - else { - Timber.e("Unhandled Sent Snap Type: %s", media.getClass()); - } - } catch (Throwable t) { - Timber.e(t); - } - - SaveNotification.show( - ContextHelper.getActivity(), - sentSnap != null ? SaveNotification.ToastType.GOOD : SaveNotification.ToastType.BAD, - Toast.LENGTH_LONG, - sentSnap - ); - } - } - ); - } - - - /** - * =========================================================================== - * Story Snap Algorithm Injection - * =========================================================================== - */ - hookMethod( - STORY_GET_ALGORITHM, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - if (param.getResult() == null) { - Timber.w("Null Story Snap Algorithm"); - return; - } - - //Timber.d("Got original algorithm: " + param.thisObject.toString()); - //logStackTrace(); - - String key = getOrCreateKey(param.thisObject); - boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, param.thisObject); - boolean isZipped = getObjectField(SNAP_IS_ZIPPED, param.thisObject); - String fileExtension = isVideo ? ".mp4" : ".jpg"; - - Long timestamp = callHook(SNAP_GET_TIMESTAMP, param.thisObject); - String username = callHook(SNAP_GET_USERNAME, param.thisObject); - - if (username != null && username.equals(yourUsername)) { - Timber.i("Viewing your own story"); - return; - } - - // Build the snap ============================================================ - new Builder() - .setContext(ContextHelper.getActivity()) - .setKey(key) - .setUsername(username) - .setDateTime(timestamp) - .setSnapType(SnapTypeDef.STORY) - .setFileExtension(fileExtension) - .setIsZipped(isZipped) - .build(StorySnap.class) - // Signal the snap that it's retrieving the algorithm ======================== - .providingAlgorithm(); - - setAdditionalInstanceField(param.getResult(), KEY_HEADER, key); - } - } - ); - - /** - * =========================================================================== - * Direct Snap Algorithm Injection - * =========================================================================== - */ - hookMethod( - DIRECT_GET_ALGORITHM, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - if (param.getResult() == null) { - Timber.w("Null Direct Snap Algorithm"); - return; - } - - Timber.d("EncryptionHolder: " + param.getResult()); - - Object snapMetaData = param.args[0]; - String key = getOrCreateKey(snapMetaData); - boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, snapMetaData); - boolean isZipped = getObjectField(SNAP_IS_ZIPPED, snapMetaData); - String fileExtension = isVideo ? ".mp4" : ".jpg"; - - Long timestamp = callHook(SNAP_GET_TIMESTAMP, snapMetaData); - String username = callHook(SNAP_GET_USERNAME, snapMetaData); - - new Builder() - .setContext(ContextHelper.getActivity()) - .setKey(key) - .setUsername(username) - .setDateTime(timestamp) - .setSnapType(SnapTypeDef.RECEIVED) - .setFileExtension(fileExtension) - .setIsZipped(isZipped) - .build(ReceivedSnap.class); - - Object encryptionHolder = param.getResult(); - Object encryptor = XposedHelpers.getObjectField(encryptionHolder, "c"); - setAdditionalInstanceField(encryptor, KEY_HEADER, key); - } - } - ); - - /** - * =========================================================================== - * Chat Image Algorithm Injection - * =========================================================================== - */ - hookMethod( - CHAT_IMAGE_GET_ALGORITHM, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - if (param.getResult() == null) { - Timber.w("Null Chat Video Algorithm"); - return; - } - - Object chatMetaData = param.args[0]; - - if (chatMetaData == null) { - Timber.w("Null ChatMetaData"); - return; - } - - String key = getOrCreateKey(chatMetaData); - boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, chatMetaData); - - setAdditionalInstanceField(param.getResult(), KEY_HEADER, key); - - // If it's a video, don't overwrite the existing stored data ================= - if (isVideo) - return; - - Long timestamp = callHook(SNAP_GET_TIMESTAMP, chatMetaData); - String username = callHook(SNAP_GET_USERNAME, chatMetaData); - - new Snap.Builder() - .setContext(ContextHelper.getActivity()) - .setKey(key) - .setUsername(username) - .setDateTime(timestamp) - .setSnapType(SnapTypeDef.CHAT) - .setFileExtension(".jpg") - .build(ChatImageSnap.class); - } - } - ); - - /** - * =========================================================================== - * Chat Video Algorithm Injection - * =========================================================================== - */ - hookMethod( - CHAT_VIDEO_GET_ALGORITHM, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Object chatMetaData = getObjectField(CHAT_METADATA_MEDIA, param.thisObject); - - if (chatMetaData == null) { - Timber.w("Null ChatMetaData"); - return; - } - - Uri videoPath = callHook(CHAT_VIDEO_PATH, chatMetaData); - - String key = getOrCreateKey(chatMetaData); - - Long timestamp = callHook(SNAP_GET_TIMESTAMP, chatMetaData); - String username = callHook(SNAP_GET_USERNAME, chatMetaData); - - ChatVideoSnap snap = new ChatVideoSnap.Builder() - .setVideoPath(videoPath) - .setContext(ContextHelper.getActivity()) - .setKey(key) - .setUsername(username) - .setDateTime(timestamp) - .setSnapType(SnapTypeDef.CHAT) - .setFileExtension(".mp4") - .build(); - - snap.providingAlgorithm(); - } - } - ); - - /** - * =========================================================================== - * Group Snap Algorithm Injection - * =========================================================================== - */ - hookMethod( - GROUP_GET_ALGORITHM, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - if (param.getResult() == null) { - Timber.w("Null Group Snap Algorithm"); - return; - } - - Object groupMetaData = param.args[0]; - - if (groupMetaData == null) { - Timber.w("Null GroupMetaData"); - return; - } - - Class receivedSnapClass = HookResolver.resolveHookClass(RECEIVED_SNAP); - - if (groupMetaData.getClass() == receivedSnapClass) { - Timber.i("Tried to process received snap in group handler"); - return; - } - - String key = getOrCreateKey(groupMetaData); - boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, groupMetaData); - String fileExtension = isVideo ? ".mp4" : ".jpg"; - - Object encryptionWrapper; - if (isVideo) - encryptionWrapper = callHook(GROUP_ALGORITHM_UNWRAPPED, param.getResult(), "video_first_frame_media_info"); - else - encryptionWrapper = callHook(GROUP_ALGORITHM_UNWRAPPED, param.getResult(), "image_media_info"); - - Long timestamp = callHook(SNAP_GET_TIMESTAMP, groupMetaData); - String username = callHook(SNAP_GET_USERNAME, groupMetaData); - - new Builder() - .setContext(ContextHelper.getActivity()) - .setKey(key) - .setUsername(username) - .setDateTime(timestamp) - .setSnapType(SnapTypeDef.GROUP) - .setFileExtension(fileExtension) - .build(GroupSnap.class); - - setAdditionalInstanceField(getObjectField(GROUP_ALGORITHM_WRAPPER_FIELD, encryptionWrapper), KEY_HEADER, key); - } - } - ); - - /** - * =========================================================================== - * Encryption Algorithm Stream Wrapper - * =========================================================================== - */ - hookMethod( - ENCRYPTION_ALGORITHM_STREAM, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - String key = (String) getAdditionalInstanceField(param.thisObject, KEY_HEADER); - - if (key == null) { - return; - } - - Snap snap = Snap.getSnapFromCache(key); - - if (snap != null) { - InputStream inputStream = (InputStream) param.getResult(); - Timber.d("InputStream: " + inputStream); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try { - ByteStreams.copy(inputStream, outputStream); - } catch (IOException e) { - Timber.e(e); - } - - ByteArrayInputStream copiedInputStream = new ByteArrayInputStream(outputStream.toByteArray()); - param.setResult(copiedInputStream); - - SaveState saveState = snap.copyStream(outputStream); - - Timber.d("Stream Save State: " + saveState); - - if (saveState == null) { - Timber.d("Null savestate... Ignoring"); - return; - } - - ToastType toastType; - - switch (saveState) { - case NOT_READY: - Timber.i("Snap not ready"); - return; - case EXISTING: - toastType = SaveNotification.ToastType.WARNING; - break; - case FAILED: - toastType = SaveNotification.ToastType.BAD; - break; - case SUCCESS: - toastType = SaveNotification.ToastType.GOOD; - break; - default: - Timber.e("Unhandled Save State: " + saveState); - return; - } - - SaveNotification.show( - ContextHelper.getActivity(), - toastType, - Toast.LENGTH_LONG, - snap - ); - } - } - } - ); - - /** - * =========================================================================== - * Force Stories to contain their matched MetaData - * =========================================================================== - */ - hookMethod( - STORY_METADATA_BUILDER, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Object storyMetadata = param.getResult(); - callHook(STORY_METADATA_INSERT_OBJECT, storyMetadata, "STORY_REPLY_SNAP", param.args[0]); - } - } - ); - - /** - * =========================================================================== - * Story Viewed -> Grab MetaData and draw the frame to a bitmap to save - * =========================================================================== - */ - hookMethod( - STORY_DISPLAYED, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Object displayState = getObjectField(STORY_ADVANCER_DISPLAY_STATE, param.thisObject); - - if (displayState == null || !displayState.toString().equals("FULLY_DISPLAYED")) - return; - - Object snapMetaData = getObjectField(STORY_ADVANCER_METADATA, param.thisObject); - - Object storySnap = callHook(STORY_METADATA_GET_OBJECT, snapMetaData, "STORY_REPLY_SNAP"); - - if (storySnap == null) { - Timber.d("StorySnap null: Probably not a normal story"); - return; - } - - String username = callHook(SNAP_GET_USERNAME, storySnap); - - if (username != null && username.equals(yourUsername)) { - Timber.i("Your story displayed"); - return; - } - - String key = getOrCreateKey(storySnap); - Snap snap = Snap.getSnapFromCache(key); - - if (snap == null) { - Timber.e("Null Snap from Cache"); - SaveNotification.show( - ContextHelper.getActivity(), - SaveNotification.ToastType.BAD, - Toast.LENGTH_LONG - ); - return; - } - - SaveState saveState = snap.finalDisplayEvent(); - - Timber.d("Stream Save State: " + saveState); - - if (saveState == null) { - Timber.d("Null savestate... Ignoring"); - return; - } - - ToastType toastType; - - switch (saveState) { - case NOT_READY: - Timber.i("Snap not ready"); - return; - case EXISTING: - toastType = SaveNotification.ToastType.WARNING; - break; - case FAILED: - toastType = SaveNotification.ToastType.BAD; - break; - case SUCCESS: - toastType = SaveNotification.ToastType.GOOD; - break; - default: - Timber.e("Unhandled Save State: " + saveState); - return; - } - - SaveNotification.show( - ContextHelper.getActivity(), - toastType, - Toast.LENGTH_LONG, - snap - ); - } - } - ); - - /** - * =========================================================================== - * Received Snap Opened -> Grab MetaData and wait - * =========================================================================== - */ - hookMethod( - OPENED_SNAP, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Timber.d("Snap Type: " + param.thisObject.getClass()); - - if (!((boolean) param.args[0])) { - Timber.d("Direct Snap Input False"); - - return; - } - - String key = getOrCreateKey(param.thisObject); - Snap snap = Snap.getSnapFromCache(key); - - if (snap == null) { - Timber.e("Null Snap from Cache"); - SaveNotification.show(ContextHelper.getActivity(), SaveNotification.ToastType.BAD, Toast.LENGTH_LONG); - return; - } - - if (!(snap instanceof ReceivedSnap)) { - Timber.w("Tried to handle an incorrect type in the received snap handler: " + snap.getClass()); - return; - } - - SaveState saveState = snap.finalDisplayEvent(); - - Timber.d("Stream Save State: " + saveState); - - if (saveState == null) { - Timber.d("Null savestate... Ignoring"); - return; - } - - ToastType toastType; - - switch (saveState) { - case NOT_READY: - Timber.i("Snap not ready"); - return; - case EXISTING: - toastType = SaveNotification.ToastType.WARNING; - break; - case FAILED: - toastType = SaveNotification.ToastType.BAD; - break; - case SUCCESS: - toastType = SaveNotification.ToastType.GOOD; - break; - default: - Timber.e("Unhandled Save State: " + saveState); - return; - } - - SaveNotification.show( - ContextHelper.getActivity(), - toastType, - Toast.LENGTH_LONG, - snap - ); - - /*TransferState transferState = ((ReceivedSnap) snap).markReady(); - - if (transferState == null) { - Timber.w("Direct snap not ready to save"); - return; - } - - StackingDotNotification.showStatus(snapActivity, transferState.getToastType(), Toast.LENGTH_LONG);*/ - } - } - ); - - hookMethod( - DIVISION_FIX, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - try { - if ((Integer) param.args[0] == 0) - param.args[0] = 1; - if ((Integer) param.args[1] == 0) - param.args[1] = 1; - if ((Integer) param.args[2] == 0) - param.args[2] = 1; - if ((Integer) param.args[3] == 0) - param.args[3] = 1; - } catch (Exception e) { - Timber.e("DIVISION_FIX HOOK -> "+ e); - } - } - } - ); - - - hasLoadedHooks = true; - } - - @Override - public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { - // Safer to call User Prefs later, after Snapchat has been initialized - try { - yourUsername = callStaticHook(GET_USERNAME); - } catch (HookNotFoundException e) { - Timber.e(e); - moduleLoadState.fail(); - } - } - - // =========================================================================== - - /** - * =========================================================================== - * Attempt to extract and save the appropriate media from the holder - * =========================================================================== - * - * @return NULL if unsuccessful - */ - private SentSnap handleSentSnap(Activity snapActivity, Object mediaHolder, boolean isVideo) { - String username; - - try { - username = callStaticHook(GET_USERNAME); - } catch (HookNotFoundException e) { - Timber.e(e); - return null; - } - - long timestamp = getObjectField(SENT_MEDIA_TIMESTAMP, mediaHolder); - // Create Snap to get OutputFile ============================================= - SentSnap snap = new SentSnap() - .setContext(snapActivity) - .setKey(UUID.randomUUID().toString()) - .setUsername(username) - .setDateTime(timestamp) - .setFileExtension(isVideo ? ".mp4" : ".jpg") - .setSnapType(SnapTypeDef.SENT); - - File outputFile = snap.getOutputFile(); - - try { - if (!outputFile.exists() && !outputFile.createNewFile()) - throw new IOException("Couldn't Create New File: " + outputFile.getAbsolutePath()); - } catch (IOException e) { - Timber.e(e); - return null; - } - - // Create a closer to register all streams =================================== - Closer closer = Closer.create(); - - try { - FileOutputStream outputStream = closer.register(new FileOutputStream(outputFile)); - - if (!isVideo) { - - // Compress the image into the stream ======================================== - Bitmap sentImage = getObjectField(SENT_MEDIA_BITMAP, mediaHolder); - if (sentImage == null) { - Timber.w("No sendable image?"); - return null; - } - - sentImage.compress(CompressFormat.JPEG, 100, outputStream); - - } else { - - Timber.d("MediaHolder: " + mediaHolder.getClass()); - // Copy the video file into our output stream ================================ - Object batchHolder = getObjectField(SENT_MEDIA_BATCH_DATA, mediaHolder); - - Timber.d("BatchHolder: " + batchHolder); - logEntireClass(batchHolder, 2); - - File videoFile; - if (batchHolder != null) { - videoFile = new File(getCreateDir(TEMP_PATH), "Batched_Sent_Snap.mp4"); - } else { - Uri videoUri = getObjectField(SENT_MEDIA_VIDEO_URI, mediaHolder); - - if (videoUri == null) - return null; - - String videoPath = videoUri.getPath().replaceFirst("file:", ""); - videoFile = new File(videoPath); - - if (!videoFile.exists()) - return null; - } - - Files.copy( - videoFile, - outputFile - ); - } - - snap.runMediaScanner(outputFile.getAbsolutePath()); - return snap; - } catch (FileNotFoundException e) { - Timber.e(e, "Input or Output files not found!"); - } catch (IOException e) { - Timber.e(e, "Error copying the video to the output location"); - } finally { - try { - closer.close(); - } catch (IOException ignore) { - // We tried our best to close the streams... - // Nothing more we can do - } - } - - return null; - } - - // =========================================================================== - - /** - * =========================================================================== - * Get an existing or create a unique key for a specific object - * =========================================================================== - */ - @Nullable - private synchronized String getOrCreateKey(Object obj) { - if (obj == null) - return null; - - String key = (String) getAdditionalInstanceField(obj, KEY_HEADER); - - if (key == null) { - key = UUID.randomUUID().toString(); - setAdditionalInstanceField(obj, KEY_HEADER, key); - Timber.d("Applying [Key: %s] to [Obj: %s]", key, obj.toString()); - } - - return key; - } -} \ No newline at end of file +package com.ljmu.andre.snaptools.ModulePack; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; +import android.net.Uri; +import androidx.annotation.Nullable; +import android.widget.FrameLayout; +import android.widget.Toast; + +import com.google.common.io.ByteStreams; +import com.google.common.io.Closer; +import com.google.common.io.Files; +import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.ModulePack.Fragments.SavingSettingsFragment; +import com.ljmu.andre.snaptools.ModulePack.Notifications.SaveNotification; +import com.ljmu.andre.snaptools.ModulePack.Notifications.SaveNotification.ToastType; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.SaveTriggerManager; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.ChatImageSnap; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.ChatVideoSnap; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.GroupSnap; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.ReceivedSnap; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.SentSnap; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap.Builder; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap.SaveState; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.Snap.SnapTypeDef; +import com.ljmu.andre.snaptools.ModulePack.SavingUtils.Snaps.StorySnap; +import com.ljmu.andre.snaptools.ModulePack.Utils.SavingLayout; +import com.ljmu.andre.snaptools.ModulePack.Utils.SavingViewPool; +import com.ljmu.andre.snaptools.Utils.ContextHelper; +import com.ljmu.andre.snaptools.Utils.CustomObservers.ErrorObserver; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; +import java.util.concurrent.Callable; + +import de.robv.android.xposed.XposedHelpers; +import io.reactivex.Observable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getCreateDir; +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.RECEIVED_SNAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SENT_IMAGE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SENT_VIDEO; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_IMAGE_GET_ALGORITHM; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_VIDEO_GET_ALGORITHM; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CHAT_VIDEO_PATH; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CONSTRUCTOR_OPERA_PAGE_VIEW; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.DIRECT_GET_ALGORITHM; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.ENCRYPTION_ALGORITHM_STREAM; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_USERNAME; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GROUP_ALGORITHM_UNWRAPPED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GROUP_GET_ALGORITHM; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.OPENED_SNAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SENT_BATCHED_SNAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SENT_SNAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_MEDIA_TYPE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_TIMESTAMP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_USERNAME; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_DISPLAYED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_GET_ALGORITHM; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_BUILDER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_GET_OBJECT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_INSERT_OBJECT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STREAM_TYPE_CHECK_BYPASS; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_METADATA_MEDIA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.GROUP_ALGORITHM_WRAPPER_FIELD; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_BATCHED_VIDEO_MEDIAHOLDER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_BATCH_DATA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_BITMAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_TIMESTAMP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SENT_MEDIA_VIDEO_URI; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.SNAP_IS_ZIPPED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_DISPLAY_STATE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_METADATA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STREAM_TYPE_CHECK_BOOLEAN; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.SAVE_SENT_SNAPS; +import static com.ljmu.andre.snaptools.Utils.FrameworkPreferencesDef.TEMP_PATH; +import static com.ljmu.andre.snaptools.Utils.XposedUtils.logEntireClass; +import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField; +import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +@SuppressWarnings("WeakerAccess") +public class Saving extends ModuleHelper { + private static final String KEY_HEADER = "SNAP_KEY"; + public static boolean hasLoadedHooks; + private static String yourUsername = ""; + + public Saving(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + @Override + public FragmentHelper[] getUIFragments() { + return new FragmentHelper[]{ + new SavingSettingsFragment() + }; + } + + // =========================================================================== + + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + SaveTriggerManager.init(); + + /** + * =========================================================================== + * Hook to inject the manual saving layout + * =========================================================================== + */ + hookConstructor( + CONSTRUCTOR_OPERA_PAGE_VIEW, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + try { + FrameLayout operaLayout = (FrameLayout) param.thisObject; + SavingLayout savingLayout = SavingViewPool.requestLayout(ContextHelper.getActivity()); + + operaLayout.addView(savingLayout); + } catch (Throwable t) { + Timber.e(t); + } + } + } + ); + + /** + * =========================================================================== + * Bypasses type checking on encrypted streams + * This would cause a duplicate stream to become corrupt so cannot happen + * =========================================================================== + */ + hookMethod( + STREAM_TYPE_CHECK_BYPASS, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + setObjectField(STREAM_TYPE_CHECK_BOOLEAN, param.thisObject, false); + } + } + ); + + /** + * =========================================================================== + * New Sent Snap Detected -> Determine the type and extract the media + * =========================================================================== + */ + if (getPref(SAVE_SENT_SNAPS)) { + hookMethod( + SENT_BATCHED_SNAP, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Timber.i("Splitting process"); + + Object mediaHolder = getObjectField(SENT_BATCHED_VIDEO_MEDIAHOLDER, param.thisObject); + + Observable.fromCallable(new Callable() { + @Override + public Object call() throws Exception { + Uri videoUri = getObjectField(SENT_MEDIA_VIDEO_URI, mediaHolder); + Timber.i("Handling splitting media"); + String videoPath = videoUri.getPath().replaceFirst("file:", ""); + File sourceMedia = new File(videoPath); + + if (!sourceMedia.exists() && !sourceMedia.createNewFile()) + Timber.w("Source tracked video doesn't exist and couldn't be created"); + + File tempDir = getCreateDir(TEMP_PATH); + File targetMedia = new File(tempDir, "Batched_Sent_Snap.mp4"); + + Files.copy(sourceMedia, targetMedia); + Timber.d("Copied batch file successfully"); + return new Object(); + } + }).subscribeOn(Schedulers.computation()) + .subscribe(new ErrorObserver<>("Error duplicating sent batch")); + + //Files.copy(file); + } + } + ); + + hookMethod( + SENT_SNAP, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Object media = param.args[0]; + + SentSnap sentSnap = null; + + try { + Class sentImage = HookResolver.resolveHookClass(SENT_IMAGE); + Class sentVideo = HookResolver.resolveHookClass(SENT_VIDEO); + + if (media.getClass().equals(sentImage)) + sentSnap = handleSentSnap(ContextHelper.getActivity(), media, false); + else if (media.getClass().equals(sentVideo)) + sentSnap = handleSentSnap(ContextHelper.getActivity(), media, true); + else { + Timber.e("Unhandled Sent Snap Type: %s", media.getClass()); + } + } catch (Throwable t) { + Timber.e(t); + } + + SaveNotification.show( + ContextHelper.getActivity(), + sentSnap != null ? SaveNotification.ToastType.GOOD : SaveNotification.ToastType.BAD, + Toast.LENGTH_LONG, + sentSnap + ); + } + } + ); + } + + + /** + * =========================================================================== + * Story Snap Algorithm Injection + * =========================================================================== + */ + hookMethod( + STORY_GET_ALGORITHM, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + if (param.getResult() == null) { + Timber.w("Null Story Snap Algorithm"); + return; + } + + //Timber.d("Got original algorithm: " + param.thisObject.toString()); + //logStackTrace(); + + String key = getOrCreateKey(param.thisObject); + boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, param.thisObject); + boolean isZipped = getObjectField(SNAP_IS_ZIPPED, param.thisObject); + String fileExtension = isVideo ? ".mp4" : ".jpg"; + + Long timestamp = callHook(SNAP_GET_TIMESTAMP, param.thisObject); + String username = callHook(SNAP_GET_USERNAME, param.thisObject); + + if (username != null && username.equals(yourUsername)) { + Timber.i("Viewing your own story"); + return; + } + + // Build the snap ============================================================ + new Builder() + .setContext(ContextHelper.getActivity()) + .setKey(key) + .setUsername(username) + .setDateTime(timestamp) + .setSnapType(SnapTypeDef.STORY) + .setFileExtension(fileExtension) + .setIsZipped(isZipped) + .build(StorySnap.class) + // Signal the snap that it's retrieving the algorithm ======================== + .providingAlgorithm(); + + setAdditionalInstanceField(param.getResult(), KEY_HEADER, key); + } + } + ); + + /** + * =========================================================================== + * Direct Snap Algorithm Injection + * =========================================================================== + */ + hookMethod( + DIRECT_GET_ALGORITHM, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + if (param.getResult() == null) { + Timber.w("Null Direct Snap Algorithm"); + return; + } + + Timber.d("EncryptionHolder: " + param.getResult()); + + Object snapMetaData = param.args[0]; + String key = getOrCreateKey(snapMetaData); + boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, snapMetaData); + boolean isZipped = getObjectField(SNAP_IS_ZIPPED, snapMetaData); + String fileExtension = isVideo ? ".mp4" : ".jpg"; + + Long timestamp = callHook(SNAP_GET_TIMESTAMP, snapMetaData); + String username = callHook(SNAP_GET_USERNAME, snapMetaData); + + new Builder() + .setContext(ContextHelper.getActivity()) + .setKey(key) + .setUsername(username) + .setDateTime(timestamp) + .setSnapType(SnapTypeDef.RECEIVED) + .setFileExtension(fileExtension) + .setIsZipped(isZipped) + .build(ReceivedSnap.class); + + Object encryptionHolder = param.getResult(); + Object encryptor = XposedHelpers.getObjectField(encryptionHolder, "c"); + setAdditionalInstanceField(encryptor, KEY_HEADER, key); + } + } + ); + + /** + * =========================================================================== + * Chat Image Algorithm Injection + * =========================================================================== + */ + hookMethod( + CHAT_IMAGE_GET_ALGORITHM, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + if (param.getResult() == null) { + Timber.w("Null Chat Video Algorithm"); + return; + } + + Object chatMetaData = param.args[0]; + + if (chatMetaData == null) { + Timber.w("Null ChatMetaData"); + return; + } + + String key = getOrCreateKey(chatMetaData); + boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, chatMetaData); + + setAdditionalInstanceField(param.getResult(), KEY_HEADER, key); + + // If it's a video, don't overwrite the existing stored data ================= + if (isVideo) + return; + + Long timestamp = callHook(SNAP_GET_TIMESTAMP, chatMetaData); + String username = callHook(SNAP_GET_USERNAME, chatMetaData); + + new Snap.Builder() + .setContext(ContextHelper.getActivity()) + .setKey(key) + .setUsername(username) + .setDateTime(timestamp) + .setSnapType(SnapTypeDef.CHAT) + .setFileExtension(".jpg") + .build(ChatImageSnap.class); + } + } + ); + + /** + * =========================================================================== + * Chat Video Algorithm Injection + * =========================================================================== + */ + hookMethod( + CHAT_VIDEO_GET_ALGORITHM, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Object chatMetaData = getObjectField(CHAT_METADATA_MEDIA, param.thisObject); + + if (chatMetaData == null) { + Timber.w("Null ChatMetaData"); + return; + } + + Uri videoPath = callHook(CHAT_VIDEO_PATH, chatMetaData); + + String key = getOrCreateKey(chatMetaData); + + Long timestamp = callHook(SNAP_GET_TIMESTAMP, chatMetaData); + String username = callHook(SNAP_GET_USERNAME, chatMetaData); + + ChatVideoSnap snap = new ChatVideoSnap.Builder() + .setVideoPath(videoPath) + .setContext(ContextHelper.getActivity()) + .setKey(key) + .setUsername(username) + .setDateTime(timestamp) + .setSnapType(SnapTypeDef.CHAT) + .setFileExtension(".mp4") + .build(); + + snap.providingAlgorithm(); + } + } + ); + + /** + * =========================================================================== + * Group Snap Algorithm Injection + * =========================================================================== + */ + hookMethod( + GROUP_GET_ALGORITHM, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + if (param.getResult() == null) { + Timber.w("Null Group Snap Algorithm"); + return; + } + + Object groupMetaData = param.args[0]; + + if (groupMetaData == null) { + Timber.w("Null GroupMetaData"); + return; + } + + Class receivedSnapClass = HookResolver.resolveHookClass(RECEIVED_SNAP); + + if (groupMetaData.getClass() == receivedSnapClass) { + Timber.i("Tried to process received snap in group handler"); + return; + } + + String key = getOrCreateKey(groupMetaData); + boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, groupMetaData); + String fileExtension = isVideo ? ".mp4" : ".jpg"; + + Object encryptionWrapper; + if (isVideo) + encryptionWrapper = callHook(GROUP_ALGORITHM_UNWRAPPED, param.getResult(), "video_first_frame_media_info"); + else + encryptionWrapper = callHook(GROUP_ALGORITHM_UNWRAPPED, param.getResult(), "image_media_info"); + + Long timestamp = callHook(SNAP_GET_TIMESTAMP, groupMetaData); + String username = callHook(SNAP_GET_USERNAME, groupMetaData); + + new Builder() + .setContext(ContextHelper.getActivity()) + .setKey(key) + .setUsername(username) + .setDateTime(timestamp) + .setSnapType(SnapTypeDef.GROUP) + .setFileExtension(fileExtension) + .build(GroupSnap.class); + + setAdditionalInstanceField(getObjectField(GROUP_ALGORITHM_WRAPPER_FIELD, encryptionWrapper), KEY_HEADER, key); + } + } + ); + + /** + * =========================================================================== + * Encryption Algorithm Stream Wrapper + * =========================================================================== + */ + hookMethod( + ENCRYPTION_ALGORITHM_STREAM, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + String key = (String) getAdditionalInstanceField(param.thisObject, KEY_HEADER); + + if (key == null) { + return; + } + + Snap snap = Snap.getSnapFromCache(key); + + if (snap != null) { + InputStream inputStream = (InputStream) param.getResult(); + Timber.d("InputStream: " + inputStream); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + ByteStreams.copy(inputStream, outputStream); + } catch (IOException e) { + Timber.e(e); + } + + ByteArrayInputStream copiedInputStream = new ByteArrayInputStream(outputStream.toByteArray()); + param.setResult(copiedInputStream); + + SaveState saveState = snap.copyStream(outputStream); + + Timber.d("Stream Save State: " + saveState); + + if (saveState == null) { + Timber.d("Null savestate... Ignoring"); + return; + } + + ToastType toastType; + + switch (saveState) { + case NOT_READY: + Timber.i("Snap not ready"); + return; + case EXISTING: + toastType = SaveNotification.ToastType.WARNING; + break; + case FAILED: + toastType = SaveNotification.ToastType.BAD; + break; + case SUCCESS: + toastType = SaveNotification.ToastType.GOOD; + break; + default: + Timber.e("Unhandled Save State: " + saveState); + return; + } + + SaveNotification.show( + ContextHelper.getActivity(), + toastType, + Toast.LENGTH_LONG, + snap + ); + } + } + } + ); + + /** + * =========================================================================== + * Force Stories to contain their matched MetaData + * =========================================================================== + */ + hookMethod( + STORY_METADATA_BUILDER, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Object storyMetadata = param.getResult(); + callHook(STORY_METADATA_INSERT_OBJECT, storyMetadata, "STORY_REPLY_SNAP", param.args[0]); + } + } + ); + + /** + * =========================================================================== + * Story Viewed -> Grab MetaData and draw the frame to a bitmap to save + * =========================================================================== + */ + hookMethod( + STORY_DISPLAYED, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Object displayState = getObjectField(STORY_ADVANCER_DISPLAY_STATE, param.thisObject); + + if (displayState == null || !displayState.toString().equals("FULLY_DISPLAYED")) + return; + + Object snapMetaData = getObjectField(STORY_ADVANCER_METADATA, param.thisObject); + + Object storySnap = callHook(STORY_METADATA_GET_OBJECT, snapMetaData, "STORY_REPLY_SNAP"); + + if (storySnap == null) { + Timber.d("StorySnap null: Probably not a normal story"); + return; + } + + String username = callHook(SNAP_GET_USERNAME, storySnap); + + if (username != null && username.equals(yourUsername)) { + Timber.i("Your story displayed"); + return; + } + + String key = getOrCreateKey(storySnap); + Snap snap = Snap.getSnapFromCache(key); + + if (snap == null) { + Timber.e("Null Snap from Cache"); + SaveNotification.show( + ContextHelper.getActivity(), + SaveNotification.ToastType.BAD, + Toast.LENGTH_LONG + ); + return; + } + + SaveState saveState = snap.finalDisplayEvent(); + + Timber.d("Stream Save State: " + saveState); + + if (saveState == null) { + Timber.d("Null savestate... Ignoring"); + return; + } + + ToastType toastType; + + switch (saveState) { + case NOT_READY: + Timber.i("Snap not ready"); + return; + case EXISTING: + toastType = SaveNotification.ToastType.WARNING; + break; + case FAILED: + toastType = SaveNotification.ToastType.BAD; + break; + case SUCCESS: + toastType = SaveNotification.ToastType.GOOD; + break; + default: + Timber.e("Unhandled Save State: " + saveState); + return; + } + + SaveNotification.show( + ContextHelper.getActivity(), + toastType, + Toast.LENGTH_LONG, + snap + ); + } + } + ); + + /** + * =========================================================================== + * Received Snap Opened -> Grab MetaData and wait + * =========================================================================== + */ + hookMethod( + OPENED_SNAP, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Timber.d("Snap Type: " + param.thisObject.getClass()); + + if (!((boolean) param.args[0])) { + Timber.d("Direct Snap Input False"); + + return; + } + + String key = getOrCreateKey(param.thisObject); + Snap snap = Snap.getSnapFromCache(key); + + if (snap == null) { + Timber.e("Null Snap from Cache"); + SaveNotification.show(ContextHelper.getActivity(), SaveNotification.ToastType.BAD, Toast.LENGTH_LONG); + return; + } + + if (!(snap instanceof ReceivedSnap)) { + Timber.w("Tried to handle an incorrect type in the received snap handler: " + snap.getClass()); + return; + } + + SaveState saveState = snap.finalDisplayEvent(); + + Timber.d("Stream Save State: " + saveState); + + if (saveState == null) { + Timber.d("Null savestate... Ignoring"); + return; + } + + ToastType toastType; + + switch (saveState) { + case NOT_READY: + Timber.i("Snap not ready"); + return; + case EXISTING: + toastType = SaveNotification.ToastType.WARNING; + break; + case FAILED: + toastType = SaveNotification.ToastType.BAD; + break; + case SUCCESS: + toastType = SaveNotification.ToastType.GOOD; + break; + default: + Timber.e("Unhandled Save State: " + saveState); + return; + } + + SaveNotification.show( + ContextHelper.getActivity(), + toastType, + Toast.LENGTH_LONG, + snap + ); + + /*TransferState transferState = ((ReceivedSnap) snap).markReady(); + + if (transferState == null) { + Timber.w("Direct snap not ready to save"); + return; + } + + StackingDotNotification.showStatus(snapActivity, transferState.getToastType(), Toast.LENGTH_LONG);*/ + } + } + ); + + hasLoadedHooks = true; + } + + @Override + public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { + // Safer to call User Prefs later, after Snapchat has been initialized + try { + yourUsername = callStaticHook(GET_USERNAME); + } catch (HookNotFoundException e) { + Timber.e(e); + moduleLoadState.fail(); + } + } + + // =========================================================================== + + /** + * =========================================================================== + * Attempt to extract and save the appropriate media from the holder + * =========================================================================== + * + * @return NULL if unsuccessful + */ + private SentSnap handleSentSnap(Activity snapActivity, Object mediaHolder, boolean isVideo) { + String username; + + try { + username = callStaticHook(GET_USERNAME); + } catch (HookNotFoundException e) { + Timber.e(e); + return null; + } + + long timestamp = getObjectField(SENT_MEDIA_TIMESTAMP, mediaHolder); + + // Create Snap to get OutputFile ============================================= + SentSnap snap = new SentSnap() + .setContext(snapActivity) + .setKey(UUID.randomUUID().toString()) + .setUsername(username) + .setDateTime(timestamp) + .setFileExtension(isVideo ? ".mp4" : ".jpg") + .setSnapType(SnapTypeDef.SENT); + + File outputFile = snap.getOutputFile(); + + try { + if (!outputFile.exists() && !outputFile.createNewFile()) + throw new IOException("Couldn't Create New File: " + outputFile.getAbsolutePath()); + } catch (IOException e) { + Timber.e(e); + return null; + } + + // Create a closer to register all streams =================================== + Closer closer = Closer.create(); + + try { + FileOutputStream outputStream = closer.register(new FileOutputStream(outputFile)); + + if (!isVideo) { + + // Compress the image into the stream ======================================== + Bitmap sentImage = getObjectField(SENT_MEDIA_BITMAP, mediaHolder); + if (sentImage == null) { + Timber.w("No sendable image?"); + return null; + } + + sentImage.compress(CompressFormat.JPEG, 100, outputStream); + + } else { + + Timber.d("MediaHolder: " + mediaHolder.getClass()); + // Copy the video file into our output stream ================================ + Object batchHolder = getObjectField(SENT_MEDIA_BATCH_DATA, mediaHolder); + + Timber.d("BatchHolder: " + batchHolder); + logEntireClass(batchHolder, 2); + + File videoFile; + if (batchHolder != null) { + videoFile = new File(getCreateDir(TEMP_PATH), "Batched_Sent_Snap.mp4"); + } else { + Uri videoUri = getObjectField(SENT_MEDIA_VIDEO_URI, mediaHolder); + + if (videoUri == null) + return null; + + String videoPath = videoUri.getPath().replaceFirst("file:", ""); + videoFile = new File(videoPath); + + if (!videoFile.exists()) + return null; + } + + Files.copy( + videoFile, + outputFile + ); + } + + snap.runMediaScanner(outputFile.getAbsolutePath()); + return snap; + } catch (FileNotFoundException e) { + Timber.e(e, "Input or Output files not found!"); + } catch (IOException e) { + Timber.e(e, "Error copying the video to the output location"); + } finally { + try { + closer.close(); + } catch (IOException ignore) { + // We tried our best to close the streams... + // Nothing more we can do + } + } + + return null; + } + + // =========================================================================== + + /** + * =========================================================================== + * Get an existing or create a unique key for a specific object + * =========================================================================== + */ + @Nullable + private synchronized String getOrCreateKey(Object obj) { + if (obj == null) + return null; + + String key = (String) getAdditionalInstanceField(obj, KEY_HEADER); + + if (key == null) { + key = UUID.randomUUID().toString(); + setAdditionalInstanceField(obj, KEY_HEADER, key); + Timber.d("Applying [Key: %s] to [Obj: %s]", key, obj.toString()); + } + + return key; + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ScreenshotBypass.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ScreenshotBypass.java index 238f943..3ce906f 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ScreenshotBypass.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/ScreenshotBypass.java @@ -1,58 +1,58 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.Context; - -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; - -import de.robv.android.xposed.XC_MethodReplacement; - -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SCREENSHOT_BYPASS; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class ScreenshotBypass extends ModuleHelper { - public ScreenshotBypass(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - @Override - public FragmentHelper[] getUIFragments() { - return null; - } - - // =========================================================================== - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - hookMethod( - SCREENSHOT_BYPASS, - XC_MethodReplacement.DO_NOTHING); - - /*hookMethod( - SET_SCREENSHOT_COUNT, - new ST_MethodHook() { - @Override protected void before(MethodHookParam param) { - param.args[0] = 0L; - } - }); - hookMethod( - SET_VIDEO_SCREENSHOT_COUNT, - new ST_MethodHook() { - @Override protected void before(MethodHookParam param) { - param.args[0] = 0L; - } - }); - hookMethod( - SET_SCREENSHOT_COUNT3, - new ST_MethodHook() { - @Override protected void before(MethodHookParam param) { - param.args[0] = 0L; - } - });*/ - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.Context; + +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; + +import de.robv.android.xposed.XC_MethodReplacement; + +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SCREENSHOT_BYPASS; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class ScreenshotBypass extends ModuleHelper { + public ScreenshotBypass(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + @Override + public FragmentHelper[] getUIFragments() { + return null; + } + + // =========================================================================== + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + hookMethod( + SCREENSHOT_BYPASS, + XC_MethodReplacement.DO_NOTHING); + + /*hookMethod( + SET_SCREENSHOT_COUNT, + new ST_MethodHook() { + @Override protected void before(MethodHookParam param) { + param.args[0] = 0L; + } + }); + hookMethod( + SET_VIDEO_SCREENSHOT_COUNT, + new ST_MethodHook() { + @Override protected void before(MethodHookParam param) { + param.args[0] = 0L; + } + }); + hookMethod( + SET_SCREENSHOT_COUNT3, + new ST_MethodHook() { + @Override protected void before(MethodHookParam param) { + param.args[0] = 0L; + } + });*/ + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Sharing.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Sharing.java index caac2e3..1cc6523 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Sharing.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/Sharing.java @@ -1,393 +1,388 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.net.Uri; - -import com.google.common.io.Files; -import com.ljmu.andre.snaptools.Dialogs.DialogFactory; -import com.ljmu.andre.snaptools.Dialogs.ThemedDialog; -import com.ljmu.andre.snaptools.Dialogs.ThemedDialog.ThemedClickListener; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.ModulePack.Fragments.SharingFragment; -import com.ljmu.andre.snaptools.ModulePack.Notifications.SafeToastAdapter; -import com.ljmu.andre.snaptools.Utils.ContextHelper; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.io.File; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.List; - -import de.robv.android.xposed.XC_MethodReplacement; -import de.robv.android.xposed.XposedHelpers; -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.ENUM_BATCHED_SNAP_POSITION; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.BATCHED_MEDIA_LIMITER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CAMERA_IS_VISIBLE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.REPLACE_SHARED_IMAGE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.REPLACE_SHARED_VIDEO; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.BATCHED_MEDIA_ITEM_BOOLEAN; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.BATCHED_MEDIA_LIST; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.BATCHED_MEDIA_CAP; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.SHARING_AUTO_ROTATE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.SHOW_SHARING_TUTORIAL; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class Sharing extends ModuleHelper { - public static final int FAILSAFE_BATCHED_MEDIA_CAP = 36; - - public Sharing(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - @Override - public FragmentHelper[] getUIFragments() { - return new FragmentHelper[]{ - new SharingFragment() - }; - } - - // =========================================================================== - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - if (getPref(SHOW_SHARING_TUTORIAL)) { - hookMethod( - CAMERA_IS_VISIBLE, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Intent intent = ContextHelper.getActivity().getIntent(); - - if (intent == null || ContextHelper.getActivity().isFinishing()) { - Timber.d("Null Intent"); - return; - } - - if (intent.getBooleanExtra("IS_SHARE", false)) { - if (intent.getStringExtra("image_url") != null) { - DialogFactory.createConfirmation( - ContextHelper.getActivity(), - "Shared Image Detected", - "Found an image that has been shared to Snapchat" - + "\nPress No to cancel the share" + - "\nPress Yes to continue, then take a regular snap for the media to be replaced" + - "\n\nImportant: If you experience Image Rotation while sharing, activate the \"Prevent Image Rotation Bug\" in Sharing Settings" - , - new ThemedClickListener() { - @Override - public void clicked(ThemedDialog themedDialog) { - themedDialog.dismiss(); - } - }, - new ThemedClickListener() { - @Override - public void clicked(ThemedDialog themedDialog) { - intent.removeExtra("image_url"); - intent.removeExtra("IS_SHARE"); - themedDialog.dismiss(); - } - } - ).show(); - } else if (intent.getStringExtra("video_url") != null) { - DialogFactory.createConfirmation( - ContextHelper.getActivity(), - "Shared Video Detected", - "Found a video that has been shared to Snapchat" - + "\nPress No to cancel the share" + - "\nPress Yes to continue, then take a regular video for the media to be replaced" - , - new ThemedClickListener() { - @Override - public void clicked(ThemedDialog themedDialog) { - themedDialog.dismiss(); - } - }, - new ThemedClickListener() { - @Override - public void clicked(ThemedDialog themedDialog) { - intent.removeExtra("image_url"); - intent.removeExtra("IS_SHARE"); - themedDialog.dismiss(); - } - } - ).show(); - - } else - intent.removeExtra("IS_SHARE"); - } - } - } - ); - } - - hookMethod( - REPLACE_SHARED_IMAGE, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Intent intent = ContextHelper.getActivity().getIntent(); - - Timber.d("Called camera REPLACE_SHARED_IMAGE"); - - if (intent == null) { - Timber.d("Null Intent"); - return; - } - - if (intent.getBooleanExtra("IS_SHARE", false)) { - intent.removeExtra("IS_SHARE"); - Timber.d("It's a shared item"); - String imgPath = intent.getStringExtra("image_url"); - intent.removeExtra("image_url"); - Timber.d("ImgPath: %s", imgPath); - - if (imgPath == null) { - SafeToastAdapter.showErrorToast( - ContextHelper.getActivity(), - "Shared image path not found" - ); - return; - } - - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options); - - - if (bitmap == null) { - SafeToastAdapter.showErrorToast( - ContextHelper.getActivity(), - "Failed to load shared media" - ); - return; - } - - // Weird Image Rotation Fix (The infamous 270 degrees bug) - if (getPref(SHARING_AUTO_ROTATE)) { - Matrix matrix = new Matrix(); - matrix.postRotate(-90); - bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), - bitmap.getHeight(), matrix, true - ); - } - - if (bitmap == null) { - SafeToastAdapter.showErrorToast( - ContextHelper.getActivity(), - "Failed to manipulate shared media to prevent Image Rotation" - ); - return; - } - - // TODO: Put fjx$a in a variable somewhere. - param.args[0] = XposedHelpers - .findConstructorExact("fjw$a", snapClassLoader, Bitmap.class) - .newInstance(bitmap); - } - } - } - ); - - hookMethod( - REPLACE_SHARED_VIDEO, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - if (ContextHelper.getActivity() == null || ContextHelper.getActivity().isDestroyed() || ContextHelper.getActivity().isFinishing()) { - Timber.w("SnapActivity not valid for shared video"); - return; - } - - Intent intent = ContextHelper.getActivity().getIntent(); - - if (intent == null) { - Timber.d("Null Intent"); - return; - } - - try { - if (intent.getBooleanExtra("IS_SHARE", false)) { - intent.removeExtra("IS_SHARE"); - Timber.d("It's a shared item"); - String videoPath = intent.getStringExtra("video_url"); - intent.removeExtra("video_url"); - Timber.d("VidPath: " + videoPath); - - if (videoPath == null) { - SafeToastAdapter.showErrorToast( - ContextHelper.getActivity(), - "Shared video path not found" - ); - return; - } - File sourceFile = new File(videoPath); - - if (!sourceFile.exists()) { - SafeToastAdapter.showErrorToast( - ContextHelper.getActivity(), - "Shared video doesn't exist" - ); - return; - } - - Uri snapPath = (Uri) param.args[0]; - - if (snapPath == null) { - SafeToastAdapter.showErrorToast( - ContextHelper.getActivity(), - "Recorded video path not found" - ); - return; - } - - File sharedVideoFile = new File(videoPath); - File snapFile = new File(snapPath.getPath()); - Files.copy(sharedVideoFile, snapFile); - - } - } catch (Throwable t) { - Timber.e(t, "Error with shared video"); - } - } - } - ); - -// findAndHookMethod( -// "frj", snapClassLoader, -// "onVideoRecordingSuccess", -// new HookWrapper((HookBefore) param -> { -// Timber.d("Video File: " + XposedHelpers.getObjectField(param.thisObject, "k")); -// if (snapActivity == null || snapActivity.isDestroyed() || snapActivity.isFinishing()) { -// Timber.w("SnapActivity not valid for shared video"); -// return; -// } -// -// Intent intent = snapActivity.getIntent(); -// -// if (intent == null) { -// Timber.d("Null Intent"); -// return; -// } -// -// try { -// if (intent.getBooleanExtra("IS_SHARE", false)) { -// intent.removeExtra("IS_SHARE"); -// Timber.d("It's a shared item"); -// String videoPath = intent.getStringExtra("video_url"); -// intent.removeExtra("video_url"); -// Timber.d("VidPath: " + videoPath); -// -// if (videoPath == null) { -// SafeToastAdapter.showErrorToast( -// snapActivity, -// "Shared video path not found" -// ); -// -// Answers.safeLogEvent( -// new CustomEvent("SharedMedia") -// .putCustomAttribute("Type", "Video") -// .putCustomAttribute("Success", "FALSE") -// ); -// return; -// } -// File sourceFile = new File(videoPath); -// -// if (!sourceFile.exists()) { -// SafeToastAdapter.showErrorToast( -// snapActivity, -// "Shared video doesn't exist" -// ); -// -// Answers.safeLogEvent( -// new CustomEvent("SharedMedia") -// .putCustomAttribute("Type", "Video") -// .putCustomAttribute("Success", "FALSE") -// ); -// return; -// } -// -// File sharedVideoFile = new File(videoPath); -// File snapFile = (File) XposedHelpers.getObjectField(param.thisObject, "k"); -// Files.copy(sharedVideoFile, snapFile); -// -// Answers.safeLogEvent( -// new CustomEvent("SharedMedia") -// .putCustomAttribute("Type", "Video") -// .putCustomAttribute("Success", "TRUE") -// ); -// } -// } catch (Throwable t) { -// Timber.e(t, "Error with shared video"); -// } -// }) -// ); - - hookMethod( - BATCHED_MEDIA_LIMITER, - new XC_MethodReplacement() { - @Override - protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { - try { - List batchedMediaList = getObjectField(BATCHED_MEDIA_LIST, param.thisObject); - Class typeEnum = HookResolver.resolveHookClass(ENUM_BATCHED_SNAP_POSITION); - - if (batchedMediaList == null) { - Timber.i("Null batched media list"); - return Enum.valueOf(typeEnum, "NONE"); - } - - int batchCap = Math.min(getPref(BATCHED_MEDIA_CAP), FAILSAFE_BATCHED_MEDIA_CAP); - - List arrayList = new ArrayList<>(batchCap); - int i = 0; - - while (i < batchedMediaList.size() && i < batchCap) { - Object batchedMediaItem = batchedMediaList.get(i); - setObjectField(BATCHED_MEDIA_ITEM_BOOLEAN, batchedMediaItem, false); - arrayList.add(i); - i++; - } - - - if (arrayList.isEmpty()) { - Timber.i("Empty batch list"); - return Enum.valueOf(typeEnum, "NONE"); - } - - Timber.i("Batched size: " + arrayList.size()); - - if (arrayList.size() == 1) { - int intValue = arrayList.get(0); - if (intValue == 0) - return Enum.valueOf(typeEnum, "BEGIN"); - - if (intValue == i - 1) - return Enum.valueOf(typeEnum, "END"); - - return Enum.valueOf(typeEnum, "MIDDLE"); - } else if (arrayList.size() == 2 && arrayList.get(0) == 0 && arrayList.get(1) == i - 1) - return Enum.valueOf(typeEnum, "BEGIN_AND_END"); - else - return Enum.valueOf(typeEnum, "OTHER"); - } catch (Throwable t) { - Timber.e(t); - throw t; - } - } - } - ); - } -} \ No newline at end of file +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.net.Uri; + +import com.google.common.io.Files; +import com.ljmu.andre.snaptools.Dialogs.DialogFactory; +import com.ljmu.andre.snaptools.Dialogs.ThemedDialog; +import com.ljmu.andre.snaptools.Dialogs.ThemedDialog.ThemedClickListener; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.ModulePack.Fragments.SharingFragment; +import com.ljmu.andre.snaptools.ModulePack.Notifications.SafeToastAdapter; +import com.ljmu.andre.snaptools.Utils.ContextHelper; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import de.robv.android.xposed.XC_MethodReplacement; +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.ENUM_BATCHED_SNAP_POSITION; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.BATCHED_MEDIA_LIMITER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CAMERA_IS_VISIBLE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.REPLACE_SHARED_IMAGE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.REPLACE_SHARED_VIDEO; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.BATCHED_MEDIA_ITEM_BOOLEAN; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.BATCHED_MEDIA_LIST; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.BATCHED_MEDIA_CAP; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.SHARING_AUTO_ROTATE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.SHOW_SHARING_TUTORIAL; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class Sharing extends ModuleHelper { + public static final int FAILSAFE_BATCHED_MEDIA_CAP = 36; + + public Sharing(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + @Override + public FragmentHelper[] getUIFragments() { + return new FragmentHelper[]{ + new SharingFragment() + }; + } + + // =========================================================================== + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + if (getPref(SHOW_SHARING_TUTORIAL)) { + hookMethod( + CAMERA_IS_VISIBLE, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Intent intent = ContextHelper.getActivity().getIntent(); + + if (intent == null || ContextHelper.getActivity().isFinishing()) { + Timber.d("Null Intent"); + return; + } + + if (intent.getBooleanExtra("IS_SHARE", false)) { + if (intent.getStringExtra("image_url") != null) { + DialogFactory.createConfirmation( + ContextHelper.getActivity(), + "Shared Image Detected", + "Found an image that has been shared to Snapchat" + + "\nPress No to cancel the share" + + "\nPress Yes to continue, then take a regular snap for the media to be replaced" + + "\n\nImportant: If you experience Image Rotation while sharing, activate the \"Prevent Image Rotation Bug\" in Sharing Settings" + , + new ThemedClickListener() { + @Override + public void clicked(ThemedDialog themedDialog) { + themedDialog.dismiss(); + } + }, + new ThemedClickListener() { + @Override + public void clicked(ThemedDialog themedDialog) { + intent.removeExtra("image_url"); + intent.removeExtra("IS_SHARE"); + themedDialog.dismiss(); + } + } + ).show(); + } else if (intent.getStringExtra("video_url") != null) { + DialogFactory.createConfirmation( + ContextHelper.getActivity(), + "Shared Video Detected", + "Found a video that has been shared to Snapchat" + + "\nPress No to cancel the share" + + "\nPress Yes to continue, then take a regular video for the media to be replaced" + , + new ThemedClickListener() { + @Override + public void clicked(ThemedDialog themedDialog) { + themedDialog.dismiss(); + } + }, + new ThemedClickListener() { + @Override + public void clicked(ThemedDialog themedDialog) { + intent.removeExtra("image_url"); + intent.removeExtra("IS_SHARE"); + themedDialog.dismiss(); + } + } + ).show(); + + } else + intent.removeExtra("IS_SHARE"); + } + } + } + ); + } + + hookMethod( + REPLACE_SHARED_IMAGE, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Intent intent = ContextHelper.getActivity().getIntent(); + + Timber.d("Called camera REPLACE_SHARED_IMAGE"); + + if (intent == null) { + Timber.d("Null Intent"); + return; + } + + if (intent.getBooleanExtra("IS_SHARE", false)) { + intent.removeExtra("IS_SHARE"); + Timber.d("It's a shared item"); + String imgPath = intent.getStringExtra("image_url"); + intent.removeExtra("image_url"); + Timber.d("ImgPath: %s", imgPath); + + if (imgPath == null) { + SafeToastAdapter.showErrorToast( + ContextHelper.getActivity(), + "Shared image path not found" + ); + return; + } + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options); + + + if (bitmap == null) { + SafeToastAdapter.showErrorToast( + ContextHelper.getActivity(), + "Failed to load shared media" + ); + return; + } + + // Weird Image Rotation Fix (The infamous 270 degrees bug) + if (getPref(SHARING_AUTO_ROTATE)) { + Matrix matrix = new Matrix(); + matrix.postRotate(-90); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), + bitmap.getHeight(), matrix, true + ); + } + + if (bitmap == null) { + SafeToastAdapter.showErrorToast( + ContextHelper.getActivity(), + "Failed to manipulate shared media to prevent Image Rotation" + ); + return; + } + + param.args[0] = bitmap; + } + } + } + ); + + hookMethod( + REPLACE_SHARED_VIDEO, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + if (ContextHelper.getActivity() == null || ContextHelper.getActivity().isDestroyed() || ContextHelper.getActivity().isFinishing()) { + Timber.w("SnapActivity not valid for shared video"); + return; + } + + Intent intent = ContextHelper.getActivity().getIntent(); + + if (intent == null) { + Timber.d("Null Intent"); + return; + } + + try { + if (intent.getBooleanExtra("IS_SHARE", false)) { + intent.removeExtra("IS_SHARE"); + Timber.d("It's a shared item"); + String videoPath = intent.getStringExtra("video_url"); + intent.removeExtra("video_url"); + Timber.d("VidPath: " + videoPath); + + if (videoPath == null) { + SafeToastAdapter.showErrorToast( + ContextHelper.getActivity(), + "Shared video path not found" + ); + return; + } + File sourceFile = new File(videoPath); + + if (!sourceFile.exists()) { + SafeToastAdapter.showErrorToast( + ContextHelper.getActivity(), + "Shared video doesn't exist" + ); + return; + } + + Uri snapPath = (Uri) param.args[0]; + + if (snapPath == null) { + SafeToastAdapter.showErrorToast( + ContextHelper.getActivity(), + "Recorded video path not found" + ); + return; + } + + File sharedVideoFile = new File(videoPath); + File snapFile = new File(snapPath.getPath()); + Files.copy(sharedVideoFile, snapFile); + + } + } catch (Throwable t) { + Timber.e(t, "Error with shared video"); + } + } + } + ); + +// findAndHookMethod( +// "frj", snapClassLoader, +// "onVideoRecordingSuccess", +// new HookWrapper((HookBefore) param -> { +// Timber.d("Video File: " + XposedHelpers.getObjectField(param.thisObject, "k")); +// if (snapActivity == null || snapActivity.isDestroyed() || snapActivity.isFinishing()) { +// Timber.w("SnapActivity not valid for shared video"); +// return; +// } +// +// Intent intent = snapActivity.getIntent(); +// +// if (intent == null) { +// Timber.d("Null Intent"); +// return; +// } +// +// try { +// if (intent.getBooleanExtra("IS_SHARE", false)) { +// intent.removeExtra("IS_SHARE"); +// Timber.d("It's a shared item"); +// String videoPath = intent.getStringExtra("video_url"); +// intent.removeExtra("video_url"); +// Timber.d("VidPath: " + videoPath); +// +// if (videoPath == null) { +// SafeToastAdapter.showErrorToast( +// snapActivity, +// "Shared video path not found" +// ); +// +// Answers.safeLogEvent( +// new CustomEvent("SharedMedia") +// .putCustomAttribute("Type", "Video") +// .putCustomAttribute("Success", "FALSE") +// ); +// return; +// } +// File sourceFile = new File(videoPath); +// +// if (!sourceFile.exists()) { +// SafeToastAdapter.showErrorToast( +// snapActivity, +// "Shared video doesn't exist" +// ); +// +// Answers.safeLogEvent( +// new CustomEvent("SharedMedia") +// .putCustomAttribute("Type", "Video") +// .putCustomAttribute("Success", "FALSE") +// ); +// return; +// } +// +// File sharedVideoFile = new File(videoPath); +// File snapFile = (File) XposedHelpers.getObjectField(param.thisObject, "k"); +// Files.copy(sharedVideoFile, snapFile); +// +// Answers.safeLogEvent( +// new CustomEvent("SharedMedia") +// .putCustomAttribute("Type", "Video") +// .putCustomAttribute("Success", "TRUE") +// ); +// } +// } catch (Throwable t) { +// Timber.e(t, "Error with shared video"); +// } +// }) +// ); + + hookMethod( + BATCHED_MEDIA_LIMITER, + new XC_MethodReplacement() { + @Override + protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { + try { + List batchedMediaList = getObjectField(BATCHED_MEDIA_LIST, param.thisObject); + Class typeEnum = HookResolver.resolveHookClass(ENUM_BATCHED_SNAP_POSITION); + + if (batchedMediaList == null) { + Timber.i("Null batched media list"); + return Enum.valueOf(typeEnum, "NONE"); + } + + int batchCap = Math.min(getPref(BATCHED_MEDIA_CAP), FAILSAFE_BATCHED_MEDIA_CAP); + + List arrayList = new ArrayList<>(batchCap); + int i = 0; + + while (i < batchedMediaList.size() && i < batchCap) { + Object batchedMediaItem = batchedMediaList.get(i); + setObjectField(BATCHED_MEDIA_ITEM_BOOLEAN, batchedMediaItem, false); + arrayList.add(i); + i++; + } + + + if (arrayList.isEmpty()) { + Timber.i("Empty batch list"); + return Enum.valueOf(typeEnum, "NONE"); + } + + Timber.i("Batched size: " + arrayList.size()); + + if (arrayList.size() == 1) { + int intValue = arrayList.get(0); + if (intValue == 0) + return Enum.valueOf(typeEnum, "BEGIN"); + + if (intValue == i - 1) + return Enum.valueOf(typeEnum, "END"); + + return Enum.valueOf(typeEnum, "MIDDLE"); + } else if (arrayList.size() == 2 && arrayList.get(0) == 0 && arrayList.get(1) == i - 1) + return Enum.valueOf(typeEnum, "BEGIN_AND_END"); + else + return Enum.valueOf(typeEnum, "OTHER"); + } catch (Throwable t) { + Timber.e(t); + throw t; + } + } + } + ); + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StealthViewing.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StealthViewing.java index 282c10f..045b6d9 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StealthViewing.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StealthViewing.java @@ -1,482 +1,482 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.app.Activity; -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; - -import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.StealthViewProvider; -import com.ljmu.andre.snaptools.ModulePack.Fragments.StealthViewingFragment; -import com.ljmu.andre.snaptools.Utils.ContextHelper; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_V10_BUILDER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SNAP_STATUS; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_SNAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CONSTRUCTOR_OPERA_PAGE_VIEW; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CREATE_CHEETAH_PROFILE_SETTINGS_VIEW; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.DISPATCH_CHAT_UPDATE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_RECEIVED_SNAP_PAYLOAD; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_SNAP_ID; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_STORY_SNAP_PAYLOAD; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_DIRECT_CHAT_VIEWED_PRESENT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_DIRECT_CHAT_VIEWED_UNPRESENT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_GROUP_CHAT_VIEWED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_STORY_VIEWED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.NETWORK_EXECUTE_SYNC; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.OPENED_SNAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SET_SNAP_STATUS; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_DISPLAYED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_BUILDER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_GET_OBJECT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_INSERT_OBJECT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_TOP_PANEL_VIEW; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.RECEIVED_SNAP_PAYLOAD_HOLDER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.RECEIVED_SNAP_PAYLOAD_MAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_DISPLAY_STATE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_METADATA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_UPDATE_METADATA; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_UPDATE_METADATA_ID; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_UPDATE_METADATA_LIST; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.*; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ViewFactory.detach; -import static com.ljmu.andre.snaptools.Utils.ContextHelper.getModuleContext; -import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getId; -import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getIdFromString; -import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getView; -import static de.robv.android.xposed.XposedHelpers.callMethod; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class StealthViewing extends ModuleHelper { - private static final int MAX_STEALTH_BYPASS_SIZE = 50; - public static boolean bypassNextStealthView; - private List activeLayouts = new ArrayList<>(); - private Set stealthySnapSet = Collections.newSetFromMap(new LinkedHashMap() { - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > 100; - } - }); - - private Set stealthBypassSet = Collections.newSetFromMap(new LinkedHashMap() { - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > MAX_STEALTH_BYPASS_SIZE; - } - }); - private StealthViewProvider viewProvider = new StealthViewProvider(); - - private int cheetahHeaderId, oldHeaderId; - private ViewGroup cheetahStealthContainer, stealthContainer; - - public StealthViewing(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - @Override - public FragmentHelper[] getUIFragments() { - return new FragmentHelper[]{ - new StealthViewingFragment() - }; - } - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - bypassNextStealthView = !(boolean) getPref(DEFAULT_SNAP_STEALTH); - - if (getPref(SHOW_SNAP_STEALTH_BUTTON)) { - hookConstructor( - CONSTRUCTOR_OPERA_PAGE_VIEW, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Timber.d("Opera view created.... Assigning active view"); - activeLayouts.add((FrameLayout) param.thisObject); - } - } - ); - - hookMethod( - OPENED_SNAP, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Timber.d("Direct snap displayed... Binding stealth to active layout"); - - if (Looper.myLooper() != Looper.getMainLooper()) { - new Handler(Looper.getMainLooper()).post(() -> assignStealthToActiveLayout(ContextHelper.getActivity())); - } else - assignStealthToActiveLayout(ContextHelper.getActivity()); - } - } - ); - - if (!Saving.hasLoadedHooks) { - /** - * =========================================================================== - * Force Stories to contain their matched MetaData - * =========================================================================== - */ - hookMethod( - STORY_METADATA_BUILDER, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Object storyMetadata = param.getResult(); - callHook(STORY_METADATA_INSERT_OBJECT, storyMetadata, "STORY_REPLY_SNAP", param.args[0]); - } - } - ); - } - - /** - * =========================================================================== - * Story displayed hook - * =========================================================================== - */ - hookMethod( - STORY_DISPLAYED, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Object displayState = getObjectField(STORY_ADVANCER_DISPLAY_STATE, param.thisObject); - if (displayState == null || !displayState.toString().equals("FULLY_DISPLAYED")) - return; - - Object snapMetaData = getObjectField(STORY_ADVANCER_METADATA, param.thisObject); - - Object storySnap = callHook(STORY_METADATA_GET_OBJECT, snapMetaData, "STORY_REPLY_SNAP"); - - if (storySnap == null) { - Timber.d("StorySnap null: Probably not a normal story"); - return; - } - - Timber.d("Story snap displayed... Binding stealth to active layout"); - - if (Looper.myLooper() != Looper.getMainLooper()) { - new Handler(Looper.getMainLooper()).post(() -> assignStealthToActiveLayout(ContextHelper.getActivity())); - } else - assignStealthToActiveLayout(ContextHelper.getActivity()); - } - } - ); - } - - /** - * =========================================================================== - * Snap status updater - * =========================================================================== - */ - try { - Class statusEnumClass = HookResolver.resolveHookClass(SNAP_STATUS); - Class storySnapClass = HookResolver.resolveHookClass(STORY_SNAP); - - Object unviewedEnum = Enum.valueOf(statusEnumClass, "UNVIEWED_AND_LOADED"); - Object receievedEnum = Enum.valueOf(statusEnumClass, "RECEIVED_AND_VIEWED"); - Object viewedEnum = Enum.valueOf(statusEnumClass, "VIEWED_AND_REPLAY_AVAILABLE"); - - hookMethod( - SET_SNAP_STATUS, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - if (param.thisObject.getClass().equals(storySnapClass)) { - return; - } - - String snapId = callHook(GET_SNAP_ID, param.thisObject); - - Enum statusEnum = (Enum) param.args[0]; - - if (statusEnum.equals(receievedEnum) || statusEnum.equals(viewedEnum)) { - if (handleStealthCheck(snapId)) { - param.args[0] = unviewedEnum; - } - } - } - } - ); - } catch (HookNotFoundException | IllegalArgumentException e) { - Timber.e("Hook not found" + e); - moduleLoadState.fail(); - } - - /** - * =========================================================================== - * Story status updater - * =========================================================================== - */ - hookMethod( - MARK_STORY_VIEWED, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - String snapId = callHook(GET_SNAP_ID, param.args[1]); - - if (handleStealthCheck(snapId) && !(boolean) getPref(STEALTH_MARK_STORY_VIEWED)) - param.setResult(null); - } - } - ); - - /** - * =========================================================================== - * Received snap update payload provider - * =========================================================================== - */ - hookMethod( - GET_RECEIVED_SNAP_PAYLOAD, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Object snapPayloadHolder = getObjectField(RECEIVED_SNAP_PAYLOAD_HOLDER, param.thisObject); - Map snapMap = getObjectField(RECEIVED_SNAP_PAYLOAD_MAP, snapPayloadHolder); - - for (String shouldntStealth : stealthySnapSet) { - snapMap.remove(shouldntStealth); - } - - Timber.d("EntrySize: " + snapMap.size()); - } - } - ); - - /** - * =========================================================================== - * Story snap update payload provider - * =========================================================================== - */ - hookMethod( - GET_STORY_SNAP_PAYLOAD, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - List storyUpdateList = getObjectField(STORY_UPDATE_METADATA_LIST, param.thisObject); - - int storyUpdateListSize = storyUpdateList.size(); - - Iterator storyIterator = storyUpdateList.iterator(); - - while (storyIterator.hasNext()) { - Object storyMetaHolder = storyIterator.next(); - Object storyMetaData = getObjectField(STORY_UPDATE_METADATA, storyMetaHolder); - String storyId = getObjectField(STORY_UPDATE_METADATA_ID, storyMetaData); - - if (stealthySnapSet.contains(storyId)) - storyIterator.remove(); - } - - stealthySnapSet.clear(); - - Timber.d("Stripped %s stories from update list", (storyUpdateListSize - storyUpdateList.size())); - } - } - ); - - /** - * =========================================================================== - * General network manager hook - * =========================================================================== - */ - hookMethod( - NETWORK_EXECUTE_SYNC, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - String url = (String) callMethod(param.thisObject, "getUrl"); - Timber.d("ExecAsyncUrl: " + url); - - if (!((boolean) getPref(DEFAULT_CHAT_STEALTH) || (boolean) getPref(BLOCK_OUTGOING_TYPING_NOTIFICATION))) - return; - - if (url.endsWith("chat_typing")) { - param.setResult(null); - } - } - } - ); - - /** - * =========================================================================== - * Chat network update hook - * =========================================================================== - */ - hookMethod( - DISPATCH_CHAT_UPDATE, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Timber.d("Chat event"); - Timber.d("Param1: " + param.args[0]); - Timber.d("Param2: " + param.args[1]); - - if (getPref(DEFAULT_CHAT_STEALTH)) { - Timber.d("Bypassed"); - param.setResult(null); - } - } - } - ); - - /** - * =========================================================================== - * Chat Message hasBeenRead hook - * =========================================================================== - */ - hookMethod( - MARK_DIRECT_CHAT_VIEWED_PRESENT, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Timber.d("Marking as read (Present while received): " + param.args[0]); - - if (getPref(DEFAULT_CHAT_STEALTH)) - param.setResult(false); - } - } - ); - - hookMethod( - MARK_DIRECT_CHAT_VIEWED_UNPRESENT, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Timber.d("Marking as read (Not present while received): " + param.args[1]); - - if (getPref(DEFAULT_CHAT_STEALTH)) - param.setResult(false); - } - } - ); - - hookMethod( - MARK_GROUP_CHAT_VIEWED, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Timber.d("KAL Dun got called: " + param.args[0]); - - if (getPref(DEFAULT_CHAT_STEALTH)) - param.setResult(null); - } - } - ); - - /** - * =========================================================================== - * Chat Header Button hook - * =========================================================================== - */ - - if (getPref(SHOW_CHAT_STEALTH_BUTTON)) { - hookAllConstructors( - CHAT_V10_BUILDER, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - View topPanel = getObjectField(CHAT_TOP_PANEL_VIEW, param.thisObject); - RelativeLayout headerTitle = getView(topPanel, getId(ContextHelper.getActivity(), "chat_header_title")); - - boolean isCheetah = getView(headerTitle, cheetahHeaderId) != null; - - int headerId = isCheetah ? - cheetahHeaderId : oldHeaderId; - - headerTitle.addView(viewProvider.getStealthChatButton(ContextHelper.getActivity(), headerId, getModuleContext(ContextHelper.getActivity()), isCheetah)); - } - } - ); - } - - /** - * =========================================================================== - * Profile Navigation Buttons hook - * =========================================================================== - */ - hookConstructor( - CREATE_CHEETAH_PROFILE_SETTINGS_VIEW, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - LinearLayout snapcodeContainer = (LinearLayout) param.args[0]; - - snapcodeContainer.addView(detach(cheetahStealthContainer)); - } - } - ); - } - - - @Override - public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { - cheetahHeaderId = getId(snapActivity, "chat_name_and_story_container"); - oldHeaderId = getId(snapActivity, "chat_friends_name"); - cheetahStealthContainer = viewProvider.getProfileContainer(snapActivity, getModuleContext(snapActivity)); - stealthContainer = viewProvider.getProfileContainer(snapActivity, getModuleContext(snapActivity)); - } - - private void assignStealthToActiveLayout(Activity snapActivity) { - Iterator activeLayoutIterator = activeLayouts.iterator(); - - while (activeLayoutIterator.hasNext()) { - FrameLayout activeLayout = activeLayoutIterator.next(); - - Timber.d("Testing layout: " + activeLayout); - Timber.d("WindowVis: %s", activeLayout.getWindowVisibility()); - - if (activeLayout.getWindowVisibility() == View.GONE) { - Timber.d("Active layout is dead... removing from list"); - activeLayoutIterator.remove(); - } else { - if (activeLayout.findViewById(getIdFromString("stealth_layout")) != null) { - Timber.i("Active layout already has stealth layout... skipping"); - continue; - } - - activeLayout.addView(viewProvider.getStealthSnapLayout(snapActivity, getModuleContext(snapActivity))); - } - } - } - - private boolean handleStealthCheck(String snapId) { - Timber.d("BypassActive: %s", bypassNextStealthView); - - if (bypassNextStealthView) { - Timber.d("Not using stealth for snap"); - bypassNextStealthView = !(boolean) getPref(DEFAULT_SNAP_STEALTH); - stealthBypassSet.add(snapId); - stealthySnapSet.remove(snapId); - return false; - } - - if (stealthBypassSet.contains(snapId)) { - Timber.d("Snap has already been marked as NON STEALTH"); - return false; - } - - stealthySnapSet.add(snapId); - bypassNextStealthView = !(boolean) getPref(DEFAULT_SNAP_STEALTH); - return true; - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.app.Activity; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.ModulePack.Fragments.KotlinViews.StealthViewProvider; +import com.ljmu.andre.snaptools.ModulePack.Fragments.StealthViewingFragment; +import com.ljmu.andre.snaptools.Utils.ContextHelper; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.CHAT_V10_BUILDER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.SNAP_STATUS; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_SNAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CONSTRUCTOR_OPERA_PAGE_VIEW; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.CREATE_CHEETAH_PROFILE_SETTINGS_VIEW; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.DISPATCH_CHAT_UPDATE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_RECEIVED_SNAP_PAYLOAD; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_SNAP_ID; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.GET_STORY_SNAP_PAYLOAD; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_DIRECT_CHAT_VIEWED_PRESENT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_DIRECT_CHAT_VIEWED_UNPRESENT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_GROUP_CHAT_VIEWED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.MARK_STORY_VIEWED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.NETWORK_EXECUTE_SYNC; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.OPENED_SNAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SET_SNAP_STATUS; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_DISPLAYED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_BUILDER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_GET_OBJECT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_INSERT_OBJECT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.CHAT_TOP_PANEL_VIEW; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.RECEIVED_SNAP_PAYLOAD_HOLDER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.RECEIVED_SNAP_PAYLOAD_MAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_DISPLAY_STATE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_ADVANCER_METADATA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_UPDATE_METADATA; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_UPDATE_METADATA_ID; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_UPDATE_METADATA_LIST; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.*; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ViewFactory.detach; +import static com.ljmu.andre.snaptools.Utils.ContextHelper.getModuleContext; +import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getId; +import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getIdFromString; +import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getView; +import static de.robv.android.xposed.XposedHelpers.callMethod; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class StealthViewing extends ModuleHelper { + private static final int MAX_STEALTH_BYPASS_SIZE = 50; + public static boolean bypassNextStealthView; + private List activeLayouts = new ArrayList<>(); + private Set stealthySnapSet = Collections.newSetFromMap(new LinkedHashMap() { + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > 100; + } + }); + + private Set stealthBypassSet = Collections.newSetFromMap(new LinkedHashMap() { + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_STEALTH_BYPASS_SIZE; + } + }); + private StealthViewProvider viewProvider = new StealthViewProvider(); + + private int cheetahHeaderId, oldHeaderId; + private ViewGroup cheetahStealthContainer, stealthContainer; + + public StealthViewing(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + @Override + public FragmentHelper[] getUIFragments() { + return new FragmentHelper[]{ + new StealthViewingFragment() + }; + } + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + bypassNextStealthView = !(boolean) getPref(DEFAULT_SNAP_STEALTH); + + if (getPref(SHOW_SNAP_STEALTH_BUTTON)) { + hookConstructor( + CONSTRUCTOR_OPERA_PAGE_VIEW, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Timber.d("Opera view created.... Assigning active view"); + activeLayouts.add((FrameLayout) param.thisObject); + } + } + ); + + hookMethod( + OPENED_SNAP, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Timber.d("Direct snap displayed... Binding stealth to active layout"); + + if (Looper.myLooper() != Looper.getMainLooper()) { + new Handler(Looper.getMainLooper()).post(() -> assignStealthToActiveLayout(ContextHelper.getActivity())); + } else + assignStealthToActiveLayout(ContextHelper.getActivity()); + } + } + ); + + if (!Saving.hasLoadedHooks) { + /** + * =========================================================================== + * Force Stories to contain their matched MetaData + * =========================================================================== + */ + hookMethod( + STORY_METADATA_BUILDER, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Object storyMetadata = param.getResult(); + callHook(STORY_METADATA_INSERT_OBJECT, storyMetadata, "STORY_REPLY_SNAP", param.args[0]); + } + } + ); + } + + /** + * =========================================================================== + * Story displayed hook + * =========================================================================== + */ + hookMethod( + STORY_DISPLAYED, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Object displayState = getObjectField(STORY_ADVANCER_DISPLAY_STATE, param.thisObject); + if (displayState == null || !displayState.toString().equals("FULLY_DISPLAYED")) + return; + + Object snapMetaData = getObjectField(STORY_ADVANCER_METADATA, param.thisObject); + + Object storySnap = callHook(STORY_METADATA_GET_OBJECT, snapMetaData, "STORY_REPLY_SNAP"); + + if (storySnap == null) { + Timber.d("StorySnap null: Probably not a normal story"); + return; + } + + Timber.d("Story snap displayed... Binding stealth to active layout"); + + if (Looper.myLooper() != Looper.getMainLooper()) { + new Handler(Looper.getMainLooper()).post(() -> assignStealthToActiveLayout(ContextHelper.getActivity())); + } else + assignStealthToActiveLayout(ContextHelper.getActivity()); + } + } + ); + } + + /** + * =========================================================================== + * Snap status updater + * =========================================================================== + */ + try { + Class statusEnumClass = HookResolver.resolveHookClass(SNAP_STATUS); + Class storySnapClass = HookResolver.resolveHookClass(STORY_SNAP); + + Object unviewedEnum = Enum.valueOf(statusEnumClass, "UNVIEWED_AND_LOADED"); + Object receievedEnum = Enum.valueOf(statusEnumClass, "RECEIVED_AND_VIEWED"); + Object viewedEnum = Enum.valueOf(statusEnumClass, "VIEWED_AND_REPLAY_AVAILABLE"); + + hookMethod( + SET_SNAP_STATUS, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + if (param.thisObject.getClass().equals(storySnapClass)) { + return; + } + + String snapId = callHook(GET_SNAP_ID, param.thisObject); + + Enum statusEnum = (Enum) param.args[0]; + + if (statusEnum.equals(receievedEnum) || statusEnum.equals(viewedEnum)) { + if (handleStealthCheck(snapId)) { + param.args[0] = unviewedEnum; + } + } + } + } + ); + } catch (HookNotFoundException | IllegalArgumentException e) { + Timber.e(e); + moduleLoadState.fail(); + } + + /** + * =========================================================================== + * Story status updater + * =========================================================================== + */ + hookMethod( + MARK_STORY_VIEWED, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + String snapId = callHook(GET_SNAP_ID, param.args[1]); + + if (handleStealthCheck(snapId) && !(boolean) getPref(STEALTH_MARK_STORY_VIEWED)) + param.setResult(null); + } + } + ); + + /** + * =========================================================================== + * Received snap update payload provider + * =========================================================================== + */ + hookMethod( + GET_RECEIVED_SNAP_PAYLOAD, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Object snapPayloadHolder = getObjectField(RECEIVED_SNAP_PAYLOAD_HOLDER, param.thisObject); + Map snapMap = getObjectField(RECEIVED_SNAP_PAYLOAD_MAP, snapPayloadHolder); + + for (String shouldntStealth : stealthySnapSet) { + snapMap.remove(shouldntStealth); + } + + Timber.d("EntrySize: " + snapMap.size()); + } + } + ); + + /** + * =========================================================================== + * Story snap update payload provider + * =========================================================================== + */ + hookMethod( + GET_STORY_SNAP_PAYLOAD, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + List storyUpdateList = getObjectField(STORY_UPDATE_METADATA_LIST, param.thisObject); + + int storyUpdateListSize = storyUpdateList.size(); + + Iterator storyIterator = storyUpdateList.iterator(); + + while (storyIterator.hasNext()) { + Object storyMetaHolder = storyIterator.next(); + Object storyMetaData = getObjectField(STORY_UPDATE_METADATA, storyMetaHolder); + String storyId = getObjectField(STORY_UPDATE_METADATA_ID, storyMetaData); + + if (stealthySnapSet.contains(storyId)) + storyIterator.remove(); + } + + stealthySnapSet.clear(); + + Timber.d("Stripped %s stories from update list", (storyUpdateListSize - storyUpdateList.size())); + } + } + ); + + /** + * =========================================================================== + * General network manager hook + * =========================================================================== + */ + hookMethod( + NETWORK_EXECUTE_SYNC, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + String url = (String) callMethod(param.thisObject, "getUrl"); + Timber.d("ExecAsyncUrl: " + url); + + if (!((boolean) getPref(DEFAULT_CHAT_STEALTH) || (boolean) getPref(BLOCK_OUTGOING_TYPING_NOTIFICATION))) + return; + + if (url.endsWith("chat_typing")) { + param.setResult(null); + } + } + } + ); + + /** + * =========================================================================== + * Chat network update hook + * =========================================================================== + */ + hookMethod( + DISPATCH_CHAT_UPDATE, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Timber.d("Chat event"); + Timber.d("Param1: " + param.args[0]); + Timber.d("Param2: " + param.args[1]); + + if (getPref(DEFAULT_CHAT_STEALTH)) { + Timber.d("Bypassed"); + param.setResult(null); + } + } + } + ); + + /** + * =========================================================================== + * Chat Message hasBeenRead hook + * =========================================================================== + */ + hookMethod( + MARK_DIRECT_CHAT_VIEWED_PRESENT, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Timber.d("Marking as read (Present while received): " + param.args[0]); + + if (getPref(DEFAULT_CHAT_STEALTH)) + param.setResult(false); + } + } + ); + + hookMethod( + MARK_DIRECT_CHAT_VIEWED_UNPRESENT, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Timber.d("Marking as read (Not present while received): " + param.args[1]); + + if (getPref(DEFAULT_CHAT_STEALTH)) + param.setResult(false); + } + } + ); + + hookMethod( + MARK_GROUP_CHAT_VIEWED, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Timber.d("KAL Dun got called: " + param.args[0]); + + if (getPref(DEFAULT_CHAT_STEALTH)) + param.setResult(null); + } + } + ); + + /** + * =========================================================================== + * Chat Header Button hook + * =========================================================================== + */ + + if (getPref(SHOW_CHAT_STEALTH_BUTTON)) { + hookAllConstructors( + CHAT_V10_BUILDER, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + View topPanel = getObjectField(CHAT_TOP_PANEL_VIEW, param.thisObject); + RelativeLayout headerTitle = getView(topPanel, getId(ContextHelper.getActivity(), "chat_header_title")); + + boolean isCheetah = getView(headerTitle, cheetahHeaderId) != null; + + int headerId = isCheetah ? + cheetahHeaderId : oldHeaderId; + + headerTitle.addView(viewProvider.getStealthChatButton(ContextHelper.getActivity(), headerId, getModuleContext(ContextHelper.getActivity()), isCheetah)); + } + } + ); + } + + /** + * =========================================================================== + * Profile Navigation Buttons hook + * =========================================================================== + */ + hookConstructor( + CREATE_CHEETAH_PROFILE_SETTINGS_VIEW, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + LinearLayout snapcodeContainer = (LinearLayout) param.args[0]; + + snapcodeContainer.addView(detach(cheetahStealthContainer)); + } + } + ); + } + + + @Override + public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { + cheetahHeaderId = getId(snapActivity, "chat_name_and_story_container"); + oldHeaderId = getId(snapActivity, "chat_friends_name"); + cheetahStealthContainer = viewProvider.getProfileContainer(snapActivity, getModuleContext(snapActivity)); + stealthContainer = viewProvider.getProfileContainer(snapActivity, getModuleContext(snapActivity)); + } + + private void assignStealthToActiveLayout(Activity snapActivity) { + Iterator activeLayoutIterator = activeLayouts.iterator(); + + while (activeLayoutIterator.hasNext()) { + FrameLayout activeLayout = activeLayoutIterator.next(); + + Timber.d("Testing layout: " + activeLayout); + Timber.d("WindowVis: %s", activeLayout.getWindowVisibility()); + + if (activeLayout.getWindowVisibility() == View.GONE) { + Timber.d("Active layout is dead... removing from list"); + activeLayoutIterator.remove(); + } else { + if (activeLayout.findViewById(getIdFromString("stealth_layout")) != null) { + Timber.i("Active layout already has stealth layout... skipping"); + continue; + } + + activeLayout.addView(viewProvider.getStealthSnapLayout(snapActivity, getModuleContext(snapActivity))); + } + } + } + + private boolean handleStealthCheck(String snapId) { + Timber.d("BypassActive: %s", bypassNextStealthView); + + if (bypassNextStealthView) { + Timber.d("Not using stealth for snap"); + bypassNextStealthView = !(boolean) getPref(DEFAULT_SNAP_STEALTH); + stealthBypassSet.add(snapId); + stealthySnapSet.remove(snapId); + return false; + } + + if (stealthBypassSet.contains(snapId)) { + Timber.d("Snap has already been marked as NON STEALTH"); + return false; + } + + stealthySnapSet.add(snapId); + bypassNextStealthView = !(boolean) getPref(DEFAULT_SNAP_STEALTH); + return true; + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StoryBlocker.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StoryBlocker.java index 6dceaf2..d8cc5ce 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StoryBlocker.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/StoryBlocker.java @@ -1,277 +1,277 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import androidx.core.content.ContextCompat; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.RelativeLayout; -import android.widget.RelativeLayout.LayoutParams; - -import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.ModulePack.Fragments.StoryBlockingSettingsFragment; -import com.ljmu.andre.snaptools.ModulePack.Notifications.SafeToastAdapter; -import com.ljmu.andre.snaptools.Utils.AnimationUtils; -import com.ljmu.andre.snaptools.Utils.ContextHelper; -import com.ljmu.andre.snaptools.Utils.PreferenceHelpers; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -import de.robv.android.xposed.XC_MethodReplacement; -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_FRIEND_RECENT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_FRIEND_VIEWED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_SPONSORED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FRIEND_PROFILE_POPUP_CREATED; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FRIEND_STORY_TILE_USERNAME; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_INITIAL_STORIES; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_NEW_STORY; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_STORIES; -//import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_STORY_SNAP_ADVERT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_COLLECTION_MAP; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.BLOCKED_STORIES; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_ADVERTS_BLOCKED; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_DISCOVER_BLOCKED; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_SHOW_BUTTON; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ViewFactory.detach; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ViewFactory.dp; -import static com.ljmu.andre.snaptools.Utils.ContextHelper.getModuleContext; -import static com.ljmu.andre.snaptools.Utils.PreferenceHelpers.collectionContains; -import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getColor; -import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getDrawable; -import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getView; -import static de.robv.android.xposed.XposedHelpers.callMethod; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class StoryBlocker extends ModuleHelper { - - private Button blockerButton; - - public StoryBlocker(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - @Override - public FragmentHelper[] getUIFragments() { - return new FragmentHelper[]{new StoryBlockingSettingsFragment()}; - } - - // =========================================================================== - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - boolean blockDiscovery = getPref(STORY_BLOCKER_DISCOVER_BLOCKED); -/* - if (getPref(STORY_BLOCKER_ADVERTS_BLOCKED)) { - hookMethod( - LOAD_STORY_SNAP_ADVERT, - XC_MethodReplacement.DO_NOTHING - ); - } -*/ - if (getPref(STORY_BLOCKER_SHOW_BUTTON)) { - - hookMethod( - FRIEND_PROFILE_POPUP_CREATED, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Bundle arguments = (Bundle) callMethod(param.thisObject, "getArguments"); - String username = arguments.getString("FRIEND_MINI_PROFILE_USERNAME"); - - if (username == null) { - Timber.e(new Throwable("Empty StoryTile Username")); - return; - } - - username = username.toLowerCase(); - - boolean isUserBlocked = collectionContains(BLOCKED_STORIES, username); - - RelativeLayout relativeView = getView((View) param.args[0], "mini_profile_view"); - - relativeView.addView(detach(blockerButton)); - - updateBlockerButtonState(ContextHelper.getModuleContext(snapContext), blockerButton, isUserBlocked); - - String finalUsername = username; - blockerButton.setOnClickListener(v -> { - boolean isUserBlocked1 = collectionContains(BLOCKED_STORIES, finalUsername); - - if (isUserBlocked1) { - PreferenceHelpers.removeFromCollection(BLOCKED_STORIES, finalUsername); - } else { - PreferenceHelpers.addToCollection(BLOCKED_STORIES, finalUsername); - } - - updateBlockerButtonState(ContextHelper.getModuleContext(snapContext), blockerButton, !isUserBlocked1); - - SafeToastAdapter.showDefaultToast( - ContextHelper.getActivity(), - "Restart Snapchat for the changes to take affect" - ); - }); - - AnimationUtils.expand(blockerButton, 4); - } - } - ); - } - -// if (blockDiscovery) { -// ST_MethodHook hooker = new ST_MethodHook() { -// @Override protected void after(MethodHookParam param) throws Throwable { -// if (!(boolean) XposedHelpers.callMethod(param.thisObject, STORY_DATA_IS_SUBSCRIBED.getVarName())) -// param.setThrowable(new NullPointerException()); -// } -// }; -// -// hookAllConstructors( -// STORY_DATA_DISCOVER, -// hooker -// ); -// hookAllConstructors( -// STORY_DATA_DYNAMIC, -// hooker -// ); -// hookAllConstructors( -// STORY_DATA_MAP, -// hooker -// ); -// hookAllConstructors( -// STORY_DATA_PROMOTED, -// hooker -// ); -// hookAllConstructors( -// STORY_DATA_MOMENT, -// hooker -// ); -// } - - hookMethod( - LOAD_INITIAL_STORIES, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - Map map = (Map) param.args[3]; - Map map2 = (Map) param.args[4]; - - HashSet blockedStories = getPref(BLOCKED_STORIES); - - for (String blockedUser : blockedStories) { - map.remove(blockedUser); - map2.remove(blockedUser); - } - } - } - ); - - hookMethod( - LOAD_NEW_STORY, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - Map map = getObjectField(STORY_COLLECTION_MAP, param.thisObject); - - if (map.isEmpty()) - return; - - HashSet blockedStories = getPref(BLOCKED_STORIES); - - for (String blockedUser : blockedStories) { - map.remove(blockedUser); - } - } - } - ); - - try { - Class sponsoredStoryClass = HookResolver.resolveHookClass(STORY_SPONSORED); - Class recentStoryClass = HookResolver.resolveHookClass(STORY_FRIEND_RECENT); - Class friendStoryClass = HookResolver.resolveHookClass(STORY_FRIEND_VIEWED); - - hookMethod( - LOAD_STORIES, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - try { - List originalList = (List) param.args[0]; - List iterativeList = new ArrayList<>(originalList); - - for (Object storyItemObject : iterativeList) { - Class storyItemClass = storyItemObject.getClass(); - - Timber.d("StoryItem: " + storyItemClass); - - if (blockDiscovery && storyItemClass.equals(sponsoredStoryClass)) { - originalList.remove(storyItemObject); - } else if (storyItemClass.equals(friendStoryClass) || storyItemClass.equals(recentStoryClass)) { - try { - String username = callHook(FRIEND_STORY_TILE_USERNAME, storyItemObject); - if (username != null && !username.isEmpty() && collectionContains(BLOCKED_STORIES, username.toLowerCase())) - originalList.remove(storyItemObject); - } catch (Throwable t) { - Timber.e(t); - } - } - } - } catch (UnsupportedOperationException ignored) { - - } - } - } - ); - } catch (HookNotFoundException e) { - Timber.e(e, "Failed loading story blockers"); - moduleLoadState.fail(); - } - } - - private void updateBlockerButtonState(Context modContext, Button button, boolean isUserBlocked) { - if (isUserBlocked) { - button.setBackgroundResource(getDrawable(modContext, "neutral_button")); - button.setTextColor(ContextCompat.getColor(modContext, getColor(modContext, "primaryLight"))); - button.setText("Unblock Stories"); - } else { - button.setBackgroundResource(getDrawable(modContext, "error_button")); - button.setTextColor(ContextCompat.getColor(modContext, getColor(modContext, "errorLight"))); - button.setText("Block Stories"); - } - - AnimationUtils.scaleUp(button); - } - - @Override - public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { - if (getPref(STORY_BLOCKER_SHOW_BUTTON)) { - Context modContext = getModuleContext(snapActivity); - int horizontalPadding = dp(20, snapActivity); - int verticalPadding = dp(7, snapActivity); - - blockerButton = new Button(modContext); - blockerButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); - - RelativeLayout.LayoutParams buttonparams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - buttonparams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); - buttonparams.addRule(RelativeLayout.CENTER_HORIZONTAL); - buttonparams.bottomMargin = dp(20, snapActivity); - blockerButton.setLayoutParams(buttonparams); - } - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.RelativeLayout; +import android.widget.RelativeLayout.LayoutParams; + +import com.ljmu.andre.snaptools.Exceptions.HookNotFoundException; +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.ModulePack.Fragments.StoryBlockingSettingsFragment; +import com.ljmu.andre.snaptools.ModulePack.Notifications.SafeToastAdapter; +import com.ljmu.andre.snaptools.Utils.AnimationUtils; +import com.ljmu.andre.snaptools.Utils.ContextHelper; +import com.ljmu.andre.snaptools.Utils.PreferenceHelpers; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import de.robv.android.xposed.XC_MethodReplacement; +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_FRIEND_RECENT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_FRIEND_VIEWED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.STORY_SPONSORED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FRIEND_PROFILE_POPUP_CREATED; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.FRIEND_STORY_TILE_USERNAME; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_INITIAL_STORIES; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_NEW_STORY; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_STORIES; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.LOAD_STORY_SNAP_ADVERT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.STORY_COLLECTION_MAP; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.BLOCKED_STORIES; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_ADVERTS_BLOCKED; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_DISCOVER_BLOCKED; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.STORY_BLOCKER_SHOW_BUTTON; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ViewFactory.detach; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ViewFactory.dp; +import static com.ljmu.andre.snaptools.Utils.ContextHelper.getModuleContext; +import static com.ljmu.andre.snaptools.Utils.PreferenceHelpers.collectionContains; +import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getColor; +import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getDrawable; +import static com.ljmu.andre.snaptools.Utils.ResourceUtils.getView; +import static de.robv.android.xposed.XposedHelpers.callMethod; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class StoryBlocker extends ModuleHelper { + + private Button blockerButton; + + public StoryBlocker(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + @Override + public FragmentHelper[] getUIFragments() { + return new FragmentHelper[]{new StoryBlockingSettingsFragment()}; + } + + // =========================================================================== + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + boolean blockDiscovery = getPref(STORY_BLOCKER_DISCOVER_BLOCKED); + + if (getPref(STORY_BLOCKER_ADVERTS_BLOCKED)) { + hookMethod( + LOAD_STORY_SNAP_ADVERT, + XC_MethodReplacement.DO_NOTHING + ); + } + + if (getPref(STORY_BLOCKER_SHOW_BUTTON)) { + + hookMethod( + FRIEND_PROFILE_POPUP_CREATED, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Bundle arguments = (Bundle) callMethod(param.thisObject, "getArguments"); + String username = arguments.getString("FRIEND_MINI_PROFILE_USERNAME"); + + if (username == null) { + Timber.e(new Throwable("Empty StoryTile Username")); + return; + } + + username = username.toLowerCase(); + + boolean isUserBlocked = collectionContains(BLOCKED_STORIES, username); + + RelativeLayout relativeView = getView((View) param.args[0], "mini_profile_view"); + + relativeView.addView(detach(blockerButton)); + + updateBlockerButtonState(ContextHelper.getModuleContext(snapContext), blockerButton, isUserBlocked); + + String finalUsername = username; + blockerButton.setOnClickListener(v -> { + boolean isUserBlocked1 = collectionContains(BLOCKED_STORIES, finalUsername); + + if (isUserBlocked1) { + PreferenceHelpers.removeFromCollection(BLOCKED_STORIES, finalUsername); + } else { + PreferenceHelpers.addToCollection(BLOCKED_STORIES, finalUsername); + } + + updateBlockerButtonState(ContextHelper.getModuleContext(snapContext), blockerButton, !isUserBlocked1); + + SafeToastAdapter.showDefaultToast( + ContextHelper.getActivity(), + "Restart Snapchat for the changes to take affect" + ); + }); + + AnimationUtils.expand(blockerButton, 4); + } + } + ); + } + +// if (blockDiscovery) { +// ST_MethodHook hooker = new ST_MethodHook() { +// @Override protected void after(MethodHookParam param) throws Throwable { +// if (!(boolean) XposedHelpers.callMethod(param.thisObject, STORY_DATA_IS_SUBSCRIBED.getVarName())) +// param.setThrowable(new NullPointerException()); +// } +// }; +// +// hookAllConstructors( +// STORY_DATA_DISCOVER, +// hooker +// ); +// hookAllConstructors( +// STORY_DATA_DYNAMIC, +// hooker +// ); +// hookAllConstructors( +// STORY_DATA_MAP, +// hooker +// ); +// hookAllConstructors( +// STORY_DATA_PROMOTED, +// hooker +// ); +// hookAllConstructors( +// STORY_DATA_MOMENT, +// hooker +// ); +// } + + hookMethod( + LOAD_INITIAL_STORIES, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + Map map = (Map) param.args[3]; + Map map2 = (Map) param.args[4]; + + HashSet blockedStories = getPref(BLOCKED_STORIES); + + for (String blockedUser : blockedStories) { + map.remove(blockedUser); + map2.remove(blockedUser); + } + } + } + ); + + hookMethod( + LOAD_NEW_STORY, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + Map map = getObjectField(STORY_COLLECTION_MAP, param.thisObject); + + if (map.isEmpty()) + return; + + HashSet blockedStories = getPref(BLOCKED_STORIES); + + for (String blockedUser : blockedStories) { + map.remove(blockedUser); + } + } + } + ); + + try { + Class sponsoredStoryClass = HookResolver.resolveHookClass(STORY_SPONSORED); + Class recentStoryClass = HookResolver.resolveHookClass(STORY_FRIEND_RECENT); + Class friendStoryClass = HookResolver.resolveHookClass(STORY_FRIEND_VIEWED); + + hookMethod( + LOAD_STORIES, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + try { + List originalList = (List) param.args[0]; + List iterativeList = new ArrayList<>(originalList); + + for (Object storyItemObject : iterativeList) { + Class storyItemClass = storyItemObject.getClass(); + + Timber.d("StoryItem: " + storyItemClass); + + if (blockDiscovery && storyItemClass.equals(sponsoredStoryClass)) { + originalList.remove(storyItemObject); + } else if (storyItemClass.equals(friendStoryClass) || storyItemClass.equals(recentStoryClass)) { + try { + String username = callHook(FRIEND_STORY_TILE_USERNAME, storyItemObject); + if (username != null && !username.isEmpty() && collectionContains(BLOCKED_STORIES, username.toLowerCase())) + originalList.remove(storyItemObject); + } catch (Throwable t) { + Timber.e(t); + } + } + } + } catch (UnsupportedOperationException ignored) { + + } + } + } + ); + } catch (HookNotFoundException e) { + Timber.e(e, "Failed loading story blockers"); + moduleLoadState.fail(); + } + } + + private void updateBlockerButtonState(Context modContext, Button button, boolean isUserBlocked) { + if (isUserBlocked) { + button.setBackgroundResource(getDrawable(modContext, "neutral_button")); + button.setTextColor(ContextCompat.getColor(modContext, getColor(modContext, "primaryLight"))); + button.setText("Unblock Stories"); + } else { + button.setBackgroundResource(getDrawable(modContext, "error_button")); + button.setTextColor(ContextCompat.getColor(modContext, getColor(modContext, "errorLight"))); + button.setText("Block Stories"); + } + + AnimationUtils.scaleUp(button); + } + + @Override + public void prepareActivity(ClassLoader snapClassLoader, Activity snapActivity) { + if (getPref(STORY_BLOCKER_SHOW_BUTTON)) { + Context modContext = getModuleContext(snapActivity); + int horizontalPadding = dp(20, snapActivity); + int verticalPadding = dp(7, snapActivity); + + blockerButton = new Button(modContext); + blockerButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); + + RelativeLayout.LayoutParams buttonparams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + buttonparams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + buttonparams.addRule(RelativeLayout.CENTER_HORIZONTAL); + buttonparams.bottomMargin = dp(20, snapActivity); + blockerButton.setLayoutParams(buttonparams); + } + } +} diff --git a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/UnlimitedViewing.java b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/UnlimitedViewing.java index 46bea9d..bb25bd6 100644 --- a/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/UnlimitedViewing.java +++ b/app/src/pack/java/com/ljmu/andre/snaptools/ModulePack/UnlimitedViewing.java @@ -1,134 +1,134 @@ -package com.ljmu.andre.snaptools.ModulePack; - -import android.content.Context; - -import com.ljmu.andre.snaptools.Fragments.FragmentHelper; -import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; - -import java.util.concurrent.TimeUnit; - -import timber.log.Timber; - -import static com.ljmu.andre.GsonPreferences.Preferences.getPref; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.ENUM_SNAP_ADVANCE_MODES; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.RECEIVED_SNAP; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_COUNTDOWN_POSTER; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_MEDIA_TYPE; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_INSERT_OBJECT; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.TEXTURE_VIDVIEW_SETLOOPING; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.TEXTURE_VIDVIEW_START; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.MCANONICALDISPLAYNAME; -import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.NO_AUTO_ADVANCE; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.UNLIMITED_VIEWING_IMAGES; -import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.UNLIMITED_VIEWING_VIDEOS; -import static de.robv.android.xposed.XposedHelpers.getStaticObjectField; - -/** - * This class was created by Andre R M (SID: 701439) - * It and its contents are free to use by all - */ - -public class UnlimitedViewing extends ModuleHelper { - public UnlimitedViewing(String name, boolean canBeDisabled) { - super(name, canBeDisabled); - } - - // =========================================================================== - - @Override - public FragmentHelper[] getUIFragments() { - return null; - } - - // =========================================================================== - - @Override - public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { - boolean unlimitedViewingImages = getPref(UNLIMITED_VIEWING_IMAGES); - boolean unlimitedViewingVideos = getPref(UNLIMITED_VIEWING_VIDEOS); - - hookMethod( - TEXTURE_VIDVIEW_START, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - callHook(TEXTURE_VIDVIEW_SETLOOPING, param.thisObject, true); - } - }); - - - hookMethod( - SNAP_COUNTDOWN_POSTER, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - param.args[0] = TimeUnit.DAYS.toMillis(1); - } - }); - - try { - Class enumClass = HookResolver.resolveHookClass(ENUM_SNAP_ADVANCE_MODES); - Object NO_AUTO_ADVANCE_ENUM = getStaticObjectField(enumClass, NO_AUTO_ADVANCE.getVarName()); - String durationKey = "total_duration_sec"; - String autoAdvanceKey = "auto_advance_mode"; - - hookMethod( - STORY_METADATA_INSERT_OBJECT, - new ST_MethodHook() { - @Override - protected void before(MethodHookParam param) throws Throwable { - String key = (String) param.args[0]; - - if (key.equals(durationKey)) - param.args[1] = TimeUnit.DAYS.toSeconds(1); - else if (key.equals(autoAdvanceKey)) - param.args[1] = NO_AUTO_ADVANCE_ENUM; - } - }); - - } catch (Throwable e) { - Timber.e(e, "Couldn't find AdvanceMode Enum"); - moduleLoadState.fail(); - } - - hookAllConstructors( - RECEIVED_SNAP, - new ST_MethodHook() { - @Override - protected void after(MethodHookParam param) throws Throwable { - boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, param.thisObject); - - if (isVideo && !unlimitedViewingVideos) - return; - - if (!isVideo && !unlimitedViewingImages) - return; - - setObjectField( - MCANONICALDISPLAYNAME, - param.thisObject, - TimeUnit.DAYS.toSeconds(1) - ); - } - } - ); - - - /*hookAllConstructors( - GALLERY_SNAP, - new ST_MethodHook() { - @Override protected void after(MethodHookParam param) throws Throwable { - setObjectField( - GALLERY_STORY_DURATION, - param.thisObject, - TimeUnit.DAYS.toSeconds(1) - ); - } - }); - - hookMethod( - GALLERY_SNAP_GET_DURATION, - XC_MethodReplacement.returnConstant(TimeUnit.DAYS.toSeconds(1)) - );*/ - } -} +package com.ljmu.andre.snaptools.ModulePack; + +import android.content.Context; + +import com.ljmu.andre.snaptools.Fragments.FragmentHelper; +import com.ljmu.andre.snaptools.Utils.XposedUtils.ST_MethodHook; + +import java.util.concurrent.TimeUnit; + +import timber.log.Timber; + +import static com.ljmu.andre.GsonPreferences.Preferences.getPref; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.ENUM_SNAP_ADVANCE_MODES; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookClassDef.RECEIVED_SNAP; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_COUNTDOWN_POSTER; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.SNAP_GET_MEDIA_TYPE; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.STORY_METADATA_INSERT_OBJECT; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.TEXTURE_VIDVIEW_SETLOOPING; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookDef.TEXTURE_VIDVIEW_START; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.MCANONICALDISPLAYNAME; +import static com.ljmu.andre.snaptools.ModulePack.HookDefinitions.HookVariableDef.NO_AUTO_ADVANCE; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.UNLIMITED_VIEWING_IMAGES; +import static com.ljmu.andre.snaptools.ModulePack.Utils.ModulePreferenceDef.UNLIMITED_VIEWING_VIDEOS; +import static de.robv.android.xposed.XposedHelpers.getStaticObjectField; + +/** + * This class was created by Andre R M (SID: 701439) + * It and its contents are free to use by all + */ + +public class UnlimitedViewing extends ModuleHelper { + public UnlimitedViewing(String name, boolean canBeDisabled) { + super(name, canBeDisabled); + } + + // =========================================================================== + + @Override + public FragmentHelper[] getUIFragments() { + return null; + } + + // =========================================================================== + + @Override + public void loadHooks(ClassLoader snapClassLoader, Context snapContext) { + boolean unlimitedViewingImages = getPref(UNLIMITED_VIEWING_IMAGES); + boolean unlimitedViewingVideos = getPref(UNLIMITED_VIEWING_VIDEOS); + + hookMethod( + TEXTURE_VIDVIEW_START, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + callHook(TEXTURE_VIDVIEW_SETLOOPING, param.thisObject, true); + } + }); + + + hookMethod( + SNAP_COUNTDOWN_POSTER, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + param.args[0] = TimeUnit.DAYS.toMillis(1); + } + }); + + try { + Class enumClass = HookResolver.resolveHookClass(ENUM_SNAP_ADVANCE_MODES); + Object NO_AUTO_ADVANCE_ENUM = getStaticObjectField(enumClass, NO_AUTO_ADVANCE.getVarName()); + String durationKey = "total_duration_sec"; + String autoAdvanceKey = "auto_advance_mode"; + + hookMethod( + STORY_METADATA_INSERT_OBJECT, + new ST_MethodHook() { + @Override + protected void before(MethodHookParam param) throws Throwable { + String key = (String) param.args[0]; + + if (key.equals(durationKey)) + param.args[1] = TimeUnit.DAYS.toSeconds(1); + else if (key.equals(autoAdvanceKey)) + param.args[1] = NO_AUTO_ADVANCE_ENUM; + } + }); + + } catch (Throwable e) { + Timber.e(e, "Couldn't find AdvanceMode Enum"); + moduleLoadState.fail(); + } + + hookAllConstructors( + RECEIVED_SNAP, + new ST_MethodHook() { + @Override + protected void after(MethodHookParam param) throws Throwable { + boolean isVideo = callHook(SNAP_GET_MEDIA_TYPE, param.thisObject); + + if (isVideo && !unlimitedViewingVideos) + return; + + if (!isVideo && !unlimitedViewingImages) + return; + + setObjectField( + MCANONICALDISPLAYNAME, + param.thisObject, + TimeUnit.DAYS.toSeconds(1) + ); + } + } + ); + + + /*hookAllConstructors( + GALLERY_SNAP, + new ST_MethodHook() { + @Override protected void after(MethodHookParam param) throws Throwable { + setObjectField( + GALLERY_STORY_DURATION, + param.thisObject, + TimeUnit.DAYS.toSeconds(1) + ); + } + }); + + hookMethod( + GALLERY_SNAP_GET_DURATION, + XC_MethodReplacement.returnConstant(TimeUnit.DAYS.toSeconds(1)) + );*/ + } +}