diff --git a/app/libs/classes-dex2jar.jar b/app/libs/classes-dex2jar.jar index 31494cc4f..8170fcb49 100644 Binary files a/app/libs/classes-dex2jar.jar and b/app/libs/classes-dex2jar.jar differ diff --git a/app/libs/classes3-dex2jar.jar b/app/libs/classes3-dex2jar.jar index 837caa19a..a3305f759 100644 Binary files a/app/libs/classes3-dex2jar.jar and b/app/libs/classes3-dex2jar.jar differ diff --git a/app/libs/classes4-dex2jar.jar b/app/libs/classes4-dex2jar.jar index e48b206fd..05e0ae959 100644 Binary files a/app/libs/classes4-dex2jar.jar and b/app/libs/classes4-dex2jar.jar differ diff --git a/app/libs/classes5-dex2jar.jar b/app/libs/classes5-dex2jar.jar index 684e35e96..dec2dbcc0 100644 Binary files a/app/libs/classes5-dex2jar.jar and b/app/libs/classes5-dex2jar.jar differ diff --git a/app/src/main/java/ru/vtosters/lite/encryption/EncryptProvider.java b/app/src/main/java/ru/vtosters/lite/encryption/EncryptProvider.java new file mode 100644 index 000000000..7ccfefb44 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/EncryptProvider.java @@ -0,0 +1,116 @@ +package ru.vtosters.lite.encryption; + +import android.util.Log; +import android.util.Pair; + +import com.vk.im.engine.models.messages.Msg; +import com.vk.im.engine.models.messages.MsgFromUser; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import ru.vtosters.lite.encryption.base.IMProcessor; +import ru.vtosters.lite.encryption.processors.BeeCryptProcessor; +import ru.vtosters.lite.encryption.processors.DefaultCoffeeProcessor; +import ru.vtosters.lite.encryption.processors.DonateCoffeeProcessor; +import ru.vtosters.lite.encryption.processors.MP3InvisibleProcessor; +import ru.vtosters.lite.encryption.processors.VTostersAESProcessor; +import ru.vtosters.lite.encryption.processors.VTostersProcessor; + +public class EncryptProvider { + public static List processors = new ArrayList<>(); + + public static IMProcessor getProcessorFor(int peerId) { + IMProcessor enabled = null; + for (IMProcessor processor: EncryptProvider.processors) { + if (processor.isUsedToEncrypt(peerId)) { + enabled = processor; + } + } + return enabled; + } + + public static String getBody(MsgFromUser msg) { + return msg.E(); + } + + public static int getPeerId(Msg msg) { + return msg.b(); + } + + // This will run through EVERY single processor available. + public static String decryptMessage(MsgFromUser msg) { + return decryptMessage(getBody(msg), getPeerId(msg)); + } + + public static String decryptMessage(String msgBody, int peer) { + try { + for (IMProcessor processor : processors) { + if ((processor.isUsed() || !processor.isPublic()) && processor.isEncrypted(msgBody) && (processor.isPublic() || getKeyForProcessor(processor, peer) != null)) + return "\uD83D\uDD12 " + processor.decode(msgBody, getKeyForProcessor(processor, peer)); + } + } catch (Exception e) { + e.printStackTrace(); + } + return msgBody; + } + + private static byte[] getKeyForProcessor(IMProcessor processor, int peer) { + if (processor.isPublic()) return null; + String keyString = processor.getEncryptionKeyFor(peer); + if (keyString == null) return null; + + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(keyString.getBytes()); + byte[] key = new byte[16]; + System.arraycopy(hash, 0, key, 0, key.length); + return key; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + + return keyString.getBytes(); + } + + // This will use only the processor which was chosen in chat menu [that's why ID is needed, bruh] + public static String encryptMessage(MsgFromUser msg) { + + String msgBody = getBody(msg); + + Log.d("EncryptProvider", "encryptMessage: msg = " + msg.toString()); + Log.d("EncryptProvider", "encryptMessage: body = " + msgBody); + Log.d("EncryptProvider", "encryptMessage: peerId = " + getPeerId(msg)); + + for (IMProcessor processor: processors) { + int peer = getPeerId(msg); + if (processor.isUsedToEncrypt(peer)) { + return processor.encode(msgBody, getKeyForProcessor(processor, peer)); + } + } + + return msgBody; + } + + // For rendering UI + public static List> getUserVisibleEncoders() { + List> theList = new ArrayList<>(); + + for (IMProcessor processor: processors) { + if (processor.isPublic()) theList.add(new Pair<>("VT_IMDecode_"+processor.getPrefKey(), processor.getUIName())); + } + + return theList; + } + + static { + processors.add(new VTostersProcessor()); + processors.add(new VTostersAESProcessor()); + processors.add(new DefaultCoffeeProcessor()); + processors.add(new DonateCoffeeProcessor()); + processors.add(new BeeCryptProcessor()); + processors.add(new MP3InvisibleProcessor()); + } +} diff --git a/app/src/main/java/ru/vtosters/lite/encryption/base/IMProcessor.java b/app/src/main/java/ru/vtosters/lite/encryption/base/IMProcessor.java new file mode 100644 index 000000000..19e90bca3 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/base/IMProcessor.java @@ -0,0 +1,78 @@ +package ru.vtosters.lite.encryption.base; + +import static ru.vtosters.lite.utils.Preferences.BooleanFalse; +import static ru.vtosters.lite.utils.Preferences.BooleanTrue; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import ru.vtosters.lite.utils.Globals; + +public abstract class IMProcessor { + // By default it checks start and end tag. + public boolean isEncrypted(String source) { + //Log.d("IMProcessor", "Checking if encrypted: "+source+" / "+getUIName()); + return source.startsWith(startTag()) && source.endsWith(endTag()); + } + + // Key can be null - if it's a public processor + @NonNull protected abstract String encodeInternal(@NonNull String source, @Nullable byte[] key); + @NonNull protected abstract String decodeInternal(@NonNull String source, @Nullable byte[] key); + + // Automatically removes tags. Magic! + @NonNull + public String encode(@NonNull String source, @Nullable byte[] key) { + //Log.d("IMProcessor", "Encoding "+source); + return startTag() + encodeInternal(source, key) + endTag(); + } + + @NonNull + public String decode(@NonNull String source, @Nullable byte[] key) { + //Log.d("IMProcessor", "Decoding"); + return decodeInternal(source.substring(startTag().length(), source.length() - endTag().length()), key); + } + + // Public - no key required. + public boolean isPublic() { + return true; + } + + // UI thing + @NonNull abstract public String getUIName(); + @NonNull abstract public String getPrefKey(); + + @NonNull public String startTag() { + return ""; + } + @NonNull public String endTag() { + return ""; + } + + public boolean isUsed() { + //Log.d("IMProcessor", "Checking if decoded with "+getUIName()); + return BooleanTrue("VT_IMDecode_"+getPrefKey()); + } + + public boolean isUsedToEncrypt(int id) { + //Log.d("IMProcessor", "Checking if "+id+" is ready to be encrypted with "+getUIName()); + return BooleanFalse("VT_IMEncode_"+getPrefKey()+"_"+id); + } + + public void enableEncryptFor(int id) { + Globals.getPrefs().edit().putBoolean("VT_IMEncode_"+getPrefKey()+"_"+id, true).apply(); + } + + public void disableEncryptFor(int id) { + Globals.getPrefs().edit().putBoolean("VT_IMEncode_"+getPrefKey()+"_"+id, false).apply(); + } + + public void setEncryptionKeyFor(int id, @Nullable String key) { + if (key == null || key.isEmpty()) key = "VTAesDefault"; + Globals.getPrefs().edit().putString("VT_IMEncodeKey_"+getPrefKey()+"_"+id, key).apply(); + } + + @Nullable + public String getEncryptionKeyFor(int id) { + return Globals.getPrefs().getString("VT_IMEncodeKey_"+getPrefKey()+"_"+id, null); + } +} diff --git a/app/src/main/java/ru/vtosters/lite/encryption/processors/BeeCryptProcessor.java b/app/src/main/java/ru/vtosters/lite/encryption/processors/BeeCryptProcessor.java new file mode 100644 index 000000000..379e6b007 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/processors/BeeCryptProcessor.java @@ -0,0 +1,75 @@ +package ru.vtosters.lite.encryption.processors; + +import android.util.Base64; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.nio.charset.StandardCharsets; + +import ru.vtosters.lite.encryption.base.IMProcessor; + +public class BeeCryptProcessor extends IMProcessor { + private static final String BC_CHARSET = "жъЖЪ"; + + @NonNull + @Override + protected String encodeInternal(@NonNull String source, @Nullable byte[] key) { + try { + String a = Base64.encodeToString(source.getBytes(StandardCharsets.UTF_8), 2); + StringBuilder res = new StringBuilder(); + for (int i = 0; i < a.length(); i++) { + StringBuilder bintxt = new StringBuilder(); + int c = a.charAt(i); + for (int j = 0; j < 4; j++) { + bintxt.insert(0, BC_CHARSET.charAt(c % 4)); + c /= 4; + } + res.append(bintxt); + } + return res.toString(); + } catch (java.lang.Exception e) { + e.printStackTrace(); + return source; + } + } + + @Override + public boolean isEncrypted(String source) { + return source.matches("[жъЖЪ]{16,}"); + } + + @NonNull + @Override + protected String decodeInternal(@NonNull String source, @Nullable byte[] key) { + StringBuilder b64 = new StringBuilder(); + int i = 0; + while (i < source.length() - 3) { + try { + String bintxt = source.substring(i, i + 4); + int c = 0; + for (int j = 0; j < 4; j++) { + c = (c * 4) + Math.max(0, BC_CHARSET.indexOf(bintxt.charAt(j))); + } + b64.append((char) c); + i += 4; + } catch (java.lang.Exception e) { + e.printStackTrace(); + return source; + } + } + return new String(Base64.decode(b64.toString(), 2), StandardCharsets.UTF_8); + } + + @NonNull + @Override + public String getUIName() { + return "MP3 [beecrypt]"; + } + + @NonNull + @Override + public String getPrefKey() { + return "mp3_bc"; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/encryption/processors/DefaultCoffeeProcessor.java b/app/src/main/java/ru/vtosters/lite/encryption/processors/DefaultCoffeeProcessor.java new file mode 100644 index 000000000..ce41064e8 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/processors/DefaultCoffeeProcessor.java @@ -0,0 +1,103 @@ +package ru.vtosters.lite.encryption.processors; + +import android.util.Base64; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import ru.vtosters.lite.encryption.base.IMProcessor; + +public class DefaultCoffeeProcessor extends IMProcessor { + private final static String KEY = "stupidUsersMustD"; + private static Cipher dCipher, eCipher; + + static { + try { + SecretKeySpec key = new SecretKeySpec(KEY.getBytes(), "AES"); + + dCipher = Cipher.getInstance("AES"); + dCipher.init(Cipher.DECRYPT_MODE, key); + + eCipher = Cipher.getInstance("AES"); + eCipher.init(Cipher.ENCRYPT_MODE, key); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private String toHex(String text) { + char[] chars = text.toCharArray(); + StringBuilder str = new StringBuilder(); + for (char aChar : chars) { + String s = Integer.toHexString(aChar); + if (s.length() < 2) s = "0" + s; + str.append(s.toUpperCase()).append(" "); + } + + String string = str.toString(); + return string.substring(0, string.length()-1); + } + + private String fromHex(String text) { + text = text.replace(" ", ""); + char[] chars = text.toCharArray(); + char[] chars2 = new char[chars.length/2]; + for (int i = 0; i < chars.length; i+=2) { + String c = String.valueOf(chars[i]) + chars[i + 1]; + chars2[i/2] = (char) Integer.parseInt(c, 16); + } + + return new String(chars2); + } + + @NonNull + @Override + protected String encodeInternal(@NonNull String source, @Nullable byte[] key) { + try { + return toHex(Base64.encodeToString(eCipher.doFinal(source.getBytes()), 0)); + } catch (Exception e) { + e.printStackTrace(); + } + + return source; + } + + @NonNull + @Override + protected String decodeInternal(@NonNull String source, @Nullable byte[] key) { + try { + return new String(dCipher.doFinal(Base64.decode(fromHex(source), 0))); + } catch (Exception e) { + e.printStackTrace(); + } + + return source; + } + + @Override + @NonNull + public String startTag() { + return "VK CO FF EE "; + } + + @Override + @NonNull + public String endTag() { + return " VK CO FF EE"; + } + + @NonNull + @Override + public String getUIName() { + return "VK Coffee [default]"; + } + + @NonNull + @Override + public String getPrefKey() { + return "coffee_default"; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/encryption/processors/DonateCoffeeProcessor.java b/app/src/main/java/ru/vtosters/lite/encryption/processors/DonateCoffeeProcessor.java new file mode 100644 index 000000000..f82647b27 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/processors/DonateCoffeeProcessor.java @@ -0,0 +1,29 @@ +package ru.vtosters.lite.encryption.processors; + +import androidx.annotation.NonNull; + +public class DonateCoffeeProcessor extends DefaultCoffeeProcessor { + @Override + @NonNull + public String startTag() { + return "II "; + } + + @Override + @NonNull + public String endTag() { + return " II"; + } + + @NonNull + @Override + public String getUIName() { + return "VK Coffee [hidden]"; + } + + @NonNull + @Override + public String getPrefKey() { + return "coffee_donate"; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/encryption/processors/MP3InvisibleProcessor.java b/app/src/main/java/ru/vtosters/lite/encryption/processors/MP3InvisibleProcessor.java new file mode 100644 index 000000000..01193f703 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/processors/MP3InvisibleProcessor.java @@ -0,0 +1,79 @@ +package ru.vtosters.lite.encryption.processors; + +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.nio.charset.StandardCharsets; + +import ru.vtosters.lite.encryption.base.IMProcessor; + +public class MP3InvisibleProcessor extends IMProcessor { + @NonNull + @Override + protected String encodeInternal(@NonNull String source, @Nullable byte[] key) { + try { + byte[] bytes = source.getBytes(StandardCharsets.UTF_8); + java.lang.String[] plainText = new String[bytes.length]; + for (int i = 0; i < plainText.length; i++) { + plainText[i] = toStr(bytes[i] & 255, " ​‌‏ "); + } + return "  " + TextUtils.join(" ", plainText); + } catch (java.lang.Exception e) { + e.printStackTrace(); + return source; + } + } + + @NonNull + @Override + protected String decodeInternal(@NonNull String source, @Nullable byte[] key) { + try { + java.lang.String[] t = source.replaceAll("^" + "  ", "").split(" "); + byte[] plainText = new byte[t.length]; + for (int i = 0; i < plainText.length; i++) { + plainText[i] = (byte) toNum(t[i], " ​‌‏ "); + } + return new String(plainText, StandardCharsets.UTF_8); + } catch (java.lang.Exception e) { + e.printStackTrace(); + return source; + } + } + + private static String toStr(int a, String cc) { + StringBuilder s = new StringBuilder(); + while (a > 0) { + s.insert(0, cc.charAt(a % cc.length())); + a /= cc.length(); + } + return s.toString(); + } + + private static int toNum(String a, String cc) { + int n = 0; + for (int i = 0; i < a.length(); i++) { + int start = (a.length() - i) - 1; + n = (int) (((double) n) + (((double) cc.indexOf(a.substring(start, start + 1))) * Math.pow(cc.length(), i))); + } + return n; + } + + @Override + public boolean isEncrypted(String source) { + return source.matches("^" + "  " + "([" + " ​‌‏ " + "\\s]*)"); + } + + @NonNull + @Override + public String getUIName() { + return "MP3 [invisible]"; + } + + @NonNull + @Override + public String getPrefKey() { + return "mp3"; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/encryption/processors/VTostersAESProcessor.java b/app/src/main/java/ru/vtosters/lite/encryption/processors/VTostersAESProcessor.java new file mode 100644 index 000000000..1031de409 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/processors/VTostersAESProcessor.java @@ -0,0 +1,130 @@ +package ru.vtosters.lite.encryption.processors; + +import android.util.Base64; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import ru.vtosters.lite.encryption.base.IMProcessor; +import ru.vtosters.lite.utils.Globals; + +public class VTostersAESProcessor extends IMProcessor { + private final static String CIPHER_INSTANCE = "AES/CBC/PKCS7Padding"; + private final static String KEY_METHOD = "AES"; + + private HashMap decodeMap = new HashMap<>(), encodeMap = new HashMap<>(); + + public void free(byte[] key) { + decodeMap.remove(key); + encodeMap.remove(key); + } + + @NonNull + private Cipher wrapEncoder(byte[] key) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { + if (encodeMap.containsKey(key)) return encodeMap.get(key); + + Triple encodeCipher = getBaseCipher(key); + encodeCipher.first.init(Cipher.ENCRYPT_MODE, encodeCipher.second, encodeCipher.third); + encodeMap.put(key, encodeCipher.first); + + return encodeCipher.first; + } + + @NonNull + private Cipher wrapDecoder(byte[] key) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { + if (decodeMap.containsKey(key)) return decodeMap.get(key); + + Triple decodeCipher = getBaseCipher(key); + decodeCipher.first.init(Cipher.DECRYPT_MODE, decodeCipher.second, decodeCipher.third); + decodeMap.put(key, decodeCipher.first); + + return decodeCipher.first; + } + + @NonNull + private Triple getBaseCipher(byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException { + byte[] off = new byte[16]; + System.arraycopy(key, 0, off, 0, off.length); + return new Triple<>(Cipher.getInstance(CIPHER_INSTANCE), new SecretKeySpec(key, KEY_METHOD), new IvParameterSpec(off)); + } + + @NonNull + @Override + protected String encodeInternal(@NonNull String source, @Nullable byte[] key) { + try { + return Base64.encodeToString(wrapEncoder(key).doFinal(source.getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT); + } catch (Exception e) { + e.printStackTrace(); + return source; + } + } + + @NonNull + @Override + protected String decodeInternal(@NonNull String source, @Nullable byte[] key) { + try { + return new String(wrapDecoder(key).doFinal(Base64.decode(source, Base64.DEFAULT)), StandardCharsets.UTF_8); + } catch (Exception e) { + e.printStackTrace(); + return source; + } + } + + @Override + public boolean isPublic() { + return false; + } + + @NonNull + @Override + public String startTag() { + return "VT0ST3RS [AES] "; + } + + @NonNull + @Override + public String endTag() { + return " VT0ST3RS [AES]"; + } + + @NonNull + @Override + public String getUIName() { + return "VTosters [AES]"; + } + + @NonNull + @Override + public String getPrefKey() { + return "vtaes"; + } + + @Nullable + @Override + public String getEncryptionKeyFor(int id) { + return Globals.getPrefs().getString("VT_IMEncodeKey_"+getPrefKey()+"_"+id, "VTAesDefault"); + } + + public static class Triple { + public final F first; + public final S second; + public final T third; + + public Triple(F first, S second, T third) { + this.first = first; + this.second = second; + this.third = third; + } + } +} diff --git a/app/src/main/java/ru/vtosters/lite/encryption/processors/VTostersProcessor.java b/app/src/main/java/ru/vtosters/lite/encryption/processors/VTostersProcessor.java new file mode 100644 index 000000000..c4dcf1fb2 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/encryption/processors/VTostersProcessor.java @@ -0,0 +1,46 @@ +package ru.vtosters.lite.encryption.processors; + +import android.util.Base64; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import ru.vtosters.lite.encryption.base.IMProcessor; + +public class VTostersProcessor extends IMProcessor { + @NonNull + @Override + protected String encodeInternal(@NonNull String source, @Nullable byte[] key) { + return Base64.encodeToString(source.getBytes(), Base64.DEFAULT); + } + + @NonNull + @Override + protected String decodeInternal(@NonNull String source, @Nullable byte[] key) { + return new String(Base64.decode(source, Base64.DEFAULT)); + } + + @Override + @NonNull + public String startTag() { + return "VT0ST3RS "; + } + + @Override + @NonNull + public String endTag() { + return " VT0ST3RS"; + } + + @NonNull + @Override + public String getUIName() { + return "VTosters Classique [Base64]"; + } + + @NonNull + @Override + public String getPrefKey() { + return "vt64"; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/utils/About.java b/app/src/main/java/ru/vtosters/lite/utils/About.java index 63fdeb201..3fc16b1dc 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/About.java +++ b/app/src/main/java/ru/vtosters/lite/utils/About.java @@ -1,7 +1,9 @@ package ru.vtosters.lite.utils; -import static ru.vtosters.lite.utils.Globals.*; -import static ru.vtosters.lite.utils.Preferences.*; +import static ru.vtosters.lite.utils.Globals.getApplicationName; +import static ru.vtosters.lite.utils.Globals.getContext; +import static ru.vtosters.lite.utils.Preferences.VERSIONNAME; +import static ru.vtosters.lite.utils.Preferences.oldabout; import android.content.Context; diff --git a/app/src/main/java/ru/vtosters/lite/utils/DeletedMessagesHandler.java b/app/src/main/java/ru/vtosters/lite/utils/DeletedMessagesHandler.java new file mode 100644 index 000000000..a3756041c --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/utils/DeletedMessagesHandler.java @@ -0,0 +1,159 @@ +package ru.vtosters.lite.utils; + +import static ru.vtosters.lite.utils.Globals.getContext; +import static ru.vtosters.lite.utils.Preferences.BooleanFalse; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteOpenHelper; +import android.text.TextUtils; + +import com.vk.im.engine.events.OnMsgUpdateEvent; +import com.vk.im.engine.internal.longpoll.a.MsgDeleteLpTask; +import com.vk.im.engine.internal.storage.CacheEnvironment; +import com.vk.im.engine.models.messages.Msg; +import com.vk.im.engine.models.messages.MsgFromUser; +import com.vk.im.engine.models.messages.NestedMsg; +import com.vk.libsqliteext.CustomSqliteExtensions; + +import org.sqlite.database.sqlite.SQLiteDatabase; + +import java.util.ArrayList; +import java.util.List; + +import ru.vtosters.lite.encryption.EncryptProvider; + +public class DeletedMessagesHandler { + + private static List sDeletedMessagesList = new ArrayList<>(); + + private static final String DELETED_PREFIX = "\uD83D\uDDD1 "; + + private static SQLiteDatabase sVKSQLiteDatabase; + private static int sBodyIndex = -1; + private static DeletedMessagesDBHelper sToasterDatabase; + + public static void reloadMessagesList() { + if (sToasterDatabase == null) { + sToasterDatabase = new DeletedMessagesDBHelper(); + } + sDeletedMessagesList = new ArrayList<>(sToasterDatabase.loadAllMessages()); + } + + public static void grabVKDatabase(CacheEnvironment storageEnvironment) { + sVKSQLiteDatabase = storageEnvironment.b(); + } + + public static boolean hook() { + return BooleanFalse("hook_deleted_messages"); + } + + public static void setBodyDBParser(Msg msg) { + if (!(msg instanceof MsgFromUser)) return; + + for (Integer integer : sDeletedMessagesList) { + MsgFromUser msgFromUser = (MsgFromUser) msg; + checkForNestedMsg(msgFromUser.g); + if (integer == msg.d) { + editTextOfMsg((MsgFromUser) msg); + break; + } + } + } + + private static void checkForNestedMsg(List nestedMsgs) { + for (NestedMsg nestedMsg : nestedMsgs) { + if (!nestedMsg.i.isEmpty()) checkForNestedMsg(nestedMsg.i); + + nestedMsg.b(EncryptProvider.decryptMessage(nestedMsg.E(), nestedMsg.h().c)); + + } + } + + private static void editTextOfMsg(MsgFromUser msgFromUser) { + if (!msgFromUser.e.startsWith(DELETED_PREFIX)) { + msgFromUser.e = DELETED_PREFIX + EncryptProvider.decryptMessage(msgFromUser); + } + } + + public static void updateDialog(MsgDeleteLpTask msgDeleteLpTask) { + Cursor c = getMessageFromDatabaseById(msgDeleteLpTask.c); + if (c == null) return; + + int localId = c.getInt(c.getColumnIndex("local_id")); + + String str = DeletedMessagesHandler.class.getSimpleName(); + msgDeleteLpTask.a.a(str, new OnMsgUpdateEvent(str, msgDeleteLpTask.b, localId)); + } + + public static void hookDeletedMessageId(MsgDeleteLpTask msgDeleteLpTask) { + if (!hook()) return; + + int messageId = msgDeleteLpTask.c; + + Cursor c = getMessageFromDatabaseById(messageId); + if (c == null) return; + if (sBodyIndex == -1) sBodyIndex = c.getColumnIndex("body"); + if (TextUtils.isEmpty(c.getString(sBodyIndex))) { + deleteMessageFromDB(messageId); + return; + } + + sDeletedMessagesList.add(messageId); + sToasterDatabase.saveDeletedMessage(messageId); + } + + public static Cursor getMessageFromDatabaseById(int messageId) { + String query = "SELECT * FROM messages WHERE vk_id = " + messageId; + Cursor c = CustomSqliteExtensions.a(sVKSQLiteDatabase, query); + if (!c.moveToFirst()) return null; + return c; + } + + public static void deleteMessageFromDB(int messageId) { + sVKSQLiteDatabase.execSQL("DELETE FROM messages WHERE vk_id = " + messageId); + } + + public static class DeletedMessagesDBHelper extends SQLiteOpenHelper { + + public DeletedMessagesDBHelper() { + super(getContext(), "deleted_msgs", null, 1); + } + + @Override + public void onCreate(android.database.sqlite.SQLiteDatabase db) { + db.execSQL("create table deleted_msgs (id integer primary key, msgId integer)"); + } + + @Override + public void onUpgrade(android.database.sqlite.SQLiteDatabase db, int oldVersion, int newVersion) { + + } + + public void saveDeletedMessage(int msgId) { + android.database.sqlite.SQLiteDatabase sqLiteDatabase = getWritableDatabase(); + + ContentValues contentValues = new ContentValues(); + contentValues.put("msgId", msgId); + + sqLiteDatabase.insert("deleted_msgs", null, contentValues); + sqLiteDatabase.close(); + } + + public List loadAllMessages() { + List msgIdsList = new ArrayList<>(); + + Cursor cursor = getReadableDatabase().query("deleted_msgs", null, null, null, null, null, null); + if (cursor.moveToFirst()) { + int msgIdIndex = cursor.getColumnIndex("msgId"); + + do { + msgIdsList.add(cursor.getInt(msgIdIndex)); + } while (cursor.moveToNext()); + cursor.close(); + } + + return msgIdsList; + } + } +} diff --git a/app/src/main/java/ru/vtosters/lite/utils/ExternalLinkHandler.java b/app/src/main/java/ru/vtosters/lite/utils/ExternalLinkHandler.java index 6aa519ccb..ca2d3129c 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/ExternalLinkHandler.java +++ b/app/src/main/java/ru/vtosters/lite/utils/ExternalLinkHandler.java @@ -2,7 +2,7 @@ package ru.vtosters.lite.utils; import static ru.vtosters.lite.utils.Globals.getContext; import static ru.vtosters.lite.utils.Globals.isEmpty; -import static ru.vtosters.lite.utils.Preferences.*; +import static ru.vtosters.lite.utils.Preferences.isEnableExternalOpening; import android.app.AlertDialog; import android.content.Context; diff --git a/app/src/main/java/ru/vtosters/lite/utils/Globals.java b/app/src/main/java/ru/vtosters/lite/utils/Globals.java index bd201659d..b5cada71b 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/Globals.java +++ b/app/src/main/java/ru/vtosters/lite/utils/Globals.java @@ -34,6 +34,13 @@ import java.util.Locale; public class Globals { private static final List activities = new ArrayList<>(); + static SharedPreferences prefs = getContext().getSharedPreferences("com.vtosters.lite_preferences", Context.MODE_PRIVATE); + + public static SharedPreferences getPrefs() { + if (prefs == null) prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + return prefs; + } + // Current UserId public static int getUserId() { return VKAccountManager.b().a(); @@ -207,6 +214,11 @@ public class Globals { }); } + public static Activity getCurrentActivity() { + if (activities.size() == 0) return null; + else return activities.get(0); + } + public static String getApplicationName() { return getContext().getApplicationInfo().loadLabel(getContext().getPackageManager()).toString(); } diff --git a/app/src/main/java/ru/vtosters/lite/utils/Preferences.java b/app/src/main/java/ru/vtosters/lite/utils/Preferences.java index 51f08817d..90cdf109b 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/Preferences.java +++ b/app/src/main/java/ru/vtosters/lite/utils/Preferences.java @@ -1,9 +1,12 @@ package ru.vtosters.lite.utils; import static ru.vtosters.lite.ui.fragments.multiaccount.MultiAccountManager.migrate; -import static ru.vtosters.lite.utils.Globals.*; -import static ru.vtosters.lite.utils.Newsfeed.*; -import static ru.vtosters.lite.utils.Proxy.*; +import static ru.vtosters.lite.utils.Globals.fixGapps; +import static ru.vtosters.lite.utils.Globals.getContext; +import static ru.vtosters.lite.utils.Globals.getPreferences; +import static ru.vtosters.lite.utils.Globals.registerActivities; +import static ru.vtosters.lite.utils.Newsfeed.setupFilters; +import static ru.vtosters.lite.utils.Proxy.setProxy; import android.app.Application; import android.content.Context; diff --git a/app/src/main/java/ru/vtosters/lite/utils/Proxy.java b/app/src/main/java/ru/vtosters/lite/utils/Proxy.java index ae0bfa600..7baf2c45e 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/Proxy.java +++ b/app/src/main/java/ru/vtosters/lite/utils/Proxy.java @@ -1,7 +1,7 @@ package ru.vtosters.lite.utils; -import static ru.vtosters.lite.utils.Globals.*; -import static ru.vtosters.lite.utils.Preferences.*; +import static ru.vtosters.lite.utils.Globals.getPreferences; +import static ru.vtosters.lite.utils.Preferences.BooleanFalse; import java.util.Properties; diff --git a/app/src/main/java/ru/vtosters/lite/utils/SSFS.java b/app/src/main/java/ru/vtosters/lite/utils/SSFS.java index 3ba705880..cdbcd7d19 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/SSFS.java +++ b/app/src/main/java/ru/vtosters/lite/utils/SSFS.java @@ -1,9 +1,13 @@ package ru.vtosters.lite.utils; -import static ru.vtosters.lite.utils.About.*; -import static ru.vtosters.lite.utils.Globals.*; -import static ru.vtosters.lite.utils.Preferences.*; -import static ru.vtosters.lite.utils.Proxy.*; +import static ru.vtosters.lite.utils.About.getBuildNumber; +import static ru.vtosters.lite.utils.Globals.getPreferences; +import static ru.vtosters.lite.utils.Globals.getUserSecret; +import static ru.vtosters.lite.utils.Globals.getUserToken; +import static ru.vtosters.lite.utils.Preferences.dev; +import static ru.vtosters.lite.utils.Preferences.getLocale; +import static ru.vtosters.lite.utils.Preferences.vksans; +import static ru.vtosters.lite.utils.Proxy.proxy; import com.vk.core.ui.themes.VKThemeHelper; diff --git a/app/src/main/java/ru/vtosters/lite/utils/Themes.java b/app/src/main/java/ru/vtosters/lite/utils/Themes.java index fc940e2d6..1ec4bd30a 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/Themes.java +++ b/app/src/main/java/ru/vtosters/lite/utils/Themes.java @@ -1,7 +1,11 @@ package ru.vtosters.lite.utils; -import static ru.vtosters.lite.utils.Globals.*; -import static ru.vtosters.lite.utils.Preferences.*; +import static ru.vtosters.lite.utils.Globals.getContext; +import static ru.vtosters.lite.utils.Globals.getPreferences; +import static ru.vtosters.lite.utils.Preferences.color_grishka; +import static ru.vtosters.lite.utils.Preferences.isBGStickersEnabled; +import static ru.vtosters.lite.utils.Preferences.navbar; +import static ru.vtosters.lite.utils.Preferences.vksans; import android.content.Context; import android.content.res.ColorStateList; diff --git a/smali/smali/com/vk/im/engine/internal/api_commands/messages/MessagesSendApiCmd.smali b/smali/smali/com/vk/im/engine/internal/api_commands/messages/MessagesSendApiCmd.smali index 986d99b8b..b52da0a46 100644 --- a/smali/smali/com/vk/im/engine/internal/api_commands/messages/MessagesSendApiCmd.smali +++ b/smali/smali/com/vk/im/engine/internal/api_commands/messages/MessagesSendApiCmd.smali @@ -261,6 +261,10 @@ move-result-object v6 + invoke-static {p0}, Lru/vtosters/lite/encryption/EncryptProvider;->encryptMessage(Lcom/vk/im/engine/models/messages/MsgFromUser;)Ljava/lang/String; + + move-result-object v6 + invoke-interface {v2, v5, v6}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; .line 108 diff --git a/smali/smali/com/vk/im/engine/models/messages/Msg.smali b/smali/smali/com/vk/im/engine/models/messages/Msg.smali index 568b31dbc..2d4b7426b 100644 --- a/smali/smali/com/vk/im/engine/models/messages/Msg.smali +++ b/smali/smali/com/vk/im/engine/models/messages/Msg.smali @@ -36,37 +36,37 @@ # instance fields -.field private c:I +.field public c:I -.field private d:I +.field public d:I -.field private e:I +.field public e:I -.field private f:I +.field public f:I -.field private g:I +.field public g:I -.field private h:J +.field public h:J -.field private i:Lcom/vk/im/engine/models/Member; +.field public i:Lcom/vk/im/engine/models/Member; -.field private j:Z +.field public j:Z -.field private k:Z +.field public k:Z -.field private l:Z +.field public l:Z -.field private m:Z +.field public m:Z -.field private n:Lcom/vk/im/engine/models/messages/MsgSyncState; +.field public n:Lcom/vk/im/engine/models/messages/MsgSyncState; -.field private o:Lcom/vk/im/engine/models/Weight; +.field public o:Lcom/vk/im/engine/models/Weight; -.field private p:Z +.field public p:Z -.field private q:Z +.field public q:Z -.field private r:I +.field public r:I # direct methods diff --git a/smali/smali/com/vk/im/engine/models/messages/MsgFromUser.smali b/smali/smali/com/vk/im/engine/models/messages/MsgFromUser.smali index c4adc771d..970ad36bf 100644 --- a/smali/smali/com/vk/im/engine/models/messages/MsgFromUser.smali +++ b/smali/smali/com/vk/im/engine/models/messages/MsgFromUser.smali @@ -29,11 +29,11 @@ # instance fields -.field private d:Ljava/lang/String; +.field public d:Ljava/lang/String; -.field private e:Ljava/lang/String; +.field public e:Ljava/lang/String; -.field private f:Ljava/util/List; +.field public f:Ljava/util/List; .annotation system Ldalvik/annotation/Signature; value = { "Ljava/util/List<", @@ -43,7 +43,7 @@ .end annotation .end field -.field private g:Ljava/util/List; +.field public g:Ljava/util/List; .annotation system Ldalvik/annotation/Signature; value = { "Ljava/util/List<", @@ -53,11 +53,11 @@ .end annotation .end field -.field private h:Ljava/lang/String; +.field public h:Ljava/lang/String; -.field private i:Ljava/lang/String; +.field public i:Ljava/lang/String; -.field private j:Ljava/lang/String; +.field public j:Ljava/lang/String; # direct methods diff --git a/smali/smali_classes3/com/vk/im/engine/internal/longpoll/a/MsgDeleteLpTask.smali b/smali/smali_classes3/com/vk/im/engine/internal/longpoll/a/MsgDeleteLpTask.smali index 5f124a6ae..9bc17546f 100644 --- a/smali/smali_classes3/com/vk/im/engine/internal/longpoll/a/MsgDeleteLpTask.smali +++ b/smali/smali_classes3/com/vk/im/engine/internal/longpoll/a/MsgDeleteLpTask.smali @@ -4,11 +4,11 @@ # instance fields -.field private final a:Lcom/vk/im/engine/ImEnvironment; +.field public final a:Lcom/vk/im/engine/ImEnvironment; -.field private final b:I +.field public final b:I -.field private final c:I +.field public final c:I # direct methods diff --git a/smali/smali_classes3/com/vk/im/engine/internal/storage/delegates/messages/MsgStorageManager.smali b/smali/smali_classes3/com/vk/im/engine/internal/storage/delegates/messages/MsgStorageManager.smali index f5fc10c25..6971e1c98 100644 --- a/smali/smali_classes3/com/vk/im/engine/internal/storage/delegates/messages/MsgStorageManager.smali +++ b/smali/smali_classes3/com/vk/im/engine/internal/storage/delegates/messages/MsgStorageManager.smali @@ -21,6 +21,8 @@ .method public constructor (Lcom/vk/im/engine/internal/storage/CacheEnvironment;)V .locals 7 + invoke-static {p1}, Lru/vtosters/lite/utils/DeletedMessagesHandler;->grabVKDatabase(Lcom/vk/im/engine/internal/storage/CacheEnvironment;)V + const-string v0, "env" invoke-static {p1, v0}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/Object;Ljava/lang/String;)V diff --git a/smali/smali_classes4/com/vk/im/engine/models/Member.smali b/smali/smali_classes4/com/vk/im/engine/models/Member.smali index c995fbfa7..94880c981 100644 --- a/smali/smali_classes4/com/vk/im/engine/models/Member.smali +++ b/smali/smali_classes4/com/vk/im/engine/models/Member.smali @@ -26,9 +26,9 @@ # instance fields -.field private b:Lcom/vk/im/engine/models/MemberType; +.field public b:Lcom/vk/im/engine/models/MemberType; -.field private c:I +.field public c:I # direct methods diff --git a/smali/smali_classes5/com/vk/im/engine/models/messages/NestedMsg.smali b/smali/smali_classes5/com/vk/im/engine/models/messages/NestedMsg.smali index 03c278ecc..0c2c5fa8d 100644 --- a/smali/smali_classes5/com/vk/im/engine/models/messages/NestedMsg.smali +++ b/smali/smali_classes5/com/vk/im/engine/models/messages/NestedMsg.smali @@ -31,19 +31,19 @@ # instance fields -.field private b:Lcom/vk/im/engine/models/messages/NestedMsg$Type; +.field public b:Lcom/vk/im/engine/models/messages/NestedMsg$Type; -.field private c:I +.field public c:I -.field private d:I +.field public d:I -.field private e:Lcom/vk/im/engine/models/Member; +.field public e:Lcom/vk/im/engine/models/Member; -.field private f:Ljava/lang/String; +.field public f:Ljava/lang/String; -.field private g:Ljava/lang/String; +.field public g:Ljava/lang/String; -.field private h:Ljava/util/List; +.field public h:Ljava/util/List; .annotation system Ldalvik/annotation/Signature; value = { "Ljava/util/List<", @@ -53,7 +53,7 @@ .end annotation .end field -.field private i:Ljava/util/List; +.field public i:Ljava/util/List; .annotation system Ldalvik/annotation/Signature; value = { "Ljava/util/List<", @@ -63,7 +63,7 @@ .end annotation .end field -.field private j:J +.field public j:J # direct methods