Skip to content

Commit

Permalink
PhotoAddNotification
Browse files Browse the repository at this point in the history
  • Loading branch information
areteruhiro committed Dec 15, 2024
1 parent 40ad6a7 commit 5d9f042
Show file tree
Hide file tree
Showing 8 changed files with 468 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public Option(String name, int id, boolean checked) {
public Option removeRecommendation = new Option("remove_recommendation", R.string.switch_remove_recommendation, true);
public Option removePremiumRecommendation = new Option("remove_premium_recommendation", R.string.switch_remove_premium_recommendation, true);
public Option removeServiceLabels = new Option("remove_service_labels", R.string.switch_remove_service_labels, false);
public Option removeAllServices = new Option("remove_services", R.string.switch_remove_service, false);
public Option removeAllServices = new Option("remove_services", R.string.RemoveService, false);
public Option removeReplyMute = new Option("remove_reply_mute", R.string.switch_remove_reply_mute, true);
public Option redirectWebView = new Option("redirect_webview", R.string.switch_redirect_webview, true);
public Option openInBrowser = new Option("open_in_browser", R.string.switch_open_in_browser, false);
Expand All @@ -35,7 +35,9 @@ public Option(String name, int id, boolean checked) {
public Option stopVersionCheck = new Option("stop_version_check", R.string.switch_stop_version_check, false);
public Option outputCommunication = new Option("output_communication", R.string.switch_output_communication, false);
public Option archived = new Option("archived_message", R.string.switch_archived, false);
public Option callTone = new Option("call_tone", R.string.call_tone, false);
public Option call_tone = new Option("call_tone", R.string.call_tone, false);
public Option PhotoAddNotification = new Option("PhotoAddNotification", R.string.PhotoAddNotification, false);


public Option[] options = {
removeVoom,
Expand All @@ -60,6 +62,7 @@ public Option(String name, int id, boolean checked) {
blockTracking,
stopVersionCheck,
outputCommunication,
callTone
call_tone,
PhotoAddNotification
};
}
8 changes: 5 additions & 3 deletions app/src/main/java/io/github/chipppppppppp/lime/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.github.chipppppppppp.lime.hooks.ModifyResponse;
import io.github.chipppppppppp.lime.hooks.OutputRequest;
import io.github.chipppppppppp.lime.hooks.OutputResponse;
import io.github.chipppppppppp.lime.hooks.PhotoAddNotification;
import io.github.chipppppppppp.lime.hooks.PreventMarkAsRead;
import io.github.chipppppppppp.lime.hooks.PreventUnsendMessage;
import io.github.chipppppppppp.lime.hooks.RedirectWebView;
Expand All @@ -30,7 +31,7 @@
import io.github.chipppppppppp.lime.hooks.RemoveIconLabels;
import io.github.chipppppppppp.lime.hooks.RemoveIcons;
import io.github.chipppppppppp.lime.hooks.RemoveReplyMute;
import io.github.chipppppppppp.lime.hooks.Ringtone;
import io.github.chipppppppppp.lime.hooks.RingTone;
import io.github.chipppppppppp.lime.hooks.SendMuteMessage;
import io.github.chipppppppppp.lime.hooks.SpoofAndroidId;
import io.github.chipppppppppp.lime.hooks.SpoofUserAgent;
Expand Down Expand Up @@ -66,8 +67,9 @@ public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResou
new ModifyResponse(),
new OutputRequest(),
new Archived(),
new Ringtone(),
new UnsentCap()
new RingTone(),
new UnsentCap(),
new PhotoAddNotification()
};

public void handleLoadPackage(@NonNull XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package io.github.chipppppppppp.lime.hooks;

import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;

import java.io.File;
import java.util.Arrays;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import io.github.chipppppppppp.lime.LimeOptions;

public class PhotoAddNotification implements IHook {
@Override
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
if (!limeOptions.PhotoAddNotification.checked) return;

XposedHelpers.findAndHookMethod(Application.class, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Application appContext = (Application) param.thisObject;
File dbFile1 = appContext.getDatabasePath("naver_line");
File dbFile2 = appContext.getDatabasePath("contact");

if (dbFile1.exists() && dbFile2.exists()) {
SQLiteDatabase.OpenParams.Builder builder1 = new SQLiteDatabase.OpenParams.Builder();
builder1.addOpenFlags(SQLiteDatabase.OPEN_READWRITE);
SQLiteDatabase.OpenParams dbParams1 = builder1.build();

SQLiteDatabase.OpenParams.Builder builder2 = new SQLiteDatabase.OpenParams.Builder();
builder2.addOpenFlags(SQLiteDatabase.OPEN_READWRITE);
SQLiteDatabase.OpenParams dbParams2 = builder2.build();

SQLiteDatabase db1 = SQLiteDatabase.openDatabase(dbFile1, dbParams1);
SQLiteDatabase db2 = SQLiteDatabase.openDatabase(dbFile2, dbParams2);

hookNotificationMethods(loadPackageParam, appContext, db1, db2);
}
}
});
}
private void hookNotificationMethods(XC_LoadPackage.LoadPackageParam loadPackageParam,
Context context, SQLiteDatabase dbGroups, SQLiteDatabase dbContacts) {
XposedHelpers.findAndHookMethod(NotificationManager.class, "notify",
int.class, Notification.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
handleNotificationHook(context, dbGroups, dbContacts, param, false);
}
});

XposedHelpers.findAndHookMethod(NotificationManager.class, "notify",
String.class, int.class, Notification.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
handleNotificationHook(context, dbGroups, dbContacts, param, true);
}
});
}

private void handleNotificationHook(Context context, SQLiteDatabase dbGroups, SQLiteDatabase dbContacts, XC_MethodHook.MethodHookParam param, boolean hasTag) {
Notification notification = hasTag ? (Notification) param.args[2] : (Notification) param.args[1];
String title = getNotificationTitle(notification);

if (title == null) {
// XposedBridge.log("Notification title is null. Skipping.");
return;
}

// 通知の本文を取得
String originalText = getNotificationText(notification);
if (originalText != null && (originalText.contains("写真を送信しました") || originalText.contains("sent a photo"))) { // "写真を送信しました" または "sent a photo" が含まれている場合のみ処理
// XposedBridge.log("Target notification detected: " + originalText);

String chatId = resolveChatId(dbGroups, dbContacts, notification);
if (chatId == null) {
// XposedBridge.log("Chat ID not found for title: " + title);
return;
}

File latestFile = getLatestMessageFile(chatId);
if (latestFile == null) {
// XposedBridge.log("No latest message file found for chat ID: " + chatId);
return;
}

Bitmap bitmap = loadBitmapFromFile(latestFile);
if (bitmap == null) {
// XposedBridge.log("Failed to load bitmap from file: " + latestFile.getAbsolutePath());
return;
}

Notification newNotification = createNotificationWithImageFromFile(context, notification, latestFile, originalText);

if (hasTag) {
param.args[2] = newNotification;
} else {
param.args[1] = newNotification;
}

logNotificationDetails("Modified NotificationManager.notify", hasTag ? (int) param.args[1] : -1, newNotification, hasTag ? (String) param.args[0] : null);
}
}


private String resolveChatId(SQLiteDatabase dbGroups, SQLiteDatabase dbContacts, Notification notification) {
String subText = notification.extras.getString(Notification.EXTRA_SUB_TEXT);
if (subText != null) {
String groupId = queryDatabase(dbGroups, "SELECT id FROM groups WHERE name =?", subText);
if (groupId != null) {
return groupId;
}
String talkId = queryDatabase(dbContacts, "SELECT mid FROM contacts WHERE profile_name =?", subText);
return talkId;
}
return null;
}

private File getLatestMessageFile(String chatId) {
File messagesDir = new File(Environment.getExternalStorageDirectory(),
"/Android/data/jp.naver.line.android/files/chats/" + chatId + "/messages");
if (!messagesDir.exists() || !messagesDir.isDirectory()) {
// XposedBridge.log("Messages directory does not exist: " + messagesDir.getAbsolutePath());
return null;
}

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// XposedBridge.log("Sleep interrupted: " + e.getMessage());
}

File[] files = messagesDir.listFiles((dir, name) -> !name.endsWith(".thumb") && !name.endsWith(".downloading"));
if (files == null || files.length == 0) {
// XposedBridge.log("No files found in messages directory: " + messagesDir.getAbsolutePath());
return null;
}

Arrays.sort(files, (f1, f2) -> Long.compare(f2.lastModified(), f1.lastModified()));
return files[0];
}


private String queryDatabase(SQLiteDatabase db, String query, String... selectionArgs) {
Cursor cursor = db.rawQuery(query, selectionArgs);
String result = null;
if (cursor.moveToFirst()) {
result = cursor.getString(0);
}
cursor.close();
return result;
}

private String getNotificationTitle(Notification notification) {
if (notification.extras != null) {
return notification.extras.getString(Notification.EXTRA_TITLE);
}
return null;
}

private String getNotificationText(Notification notification) {
if (notification.extras != null) {
return notification.extras.getString(Notification.EXTRA_TEXT);
}
return null;
}

private Bitmap loadBitmapFromFile(File file) {
if (!file.exists()) {
return null;
}
return BitmapFactory.decodeFile(file.getAbsolutePath());
}

private Notification createNotificationWithImageFromFile(Context context, Notification original, File imageFile, String originalText) {
Bitmap bitmap = loadBitmapFromFile(imageFile);
if (bitmap == null) {
return original;
}

Notification.Builder builder = Notification.Builder.recoverBuilder(context, original)
.setStyle(new Notification.BigPictureStyle()
.bigPicture(bitmap)
.setSummaryText(originalText));
return builder.build();
}

private void logNotificationDetails(String method, int id, Notification notification, String tag) {
// XposedBridge.log(method + " called. ID: " + id + (tag != null ? ", Tag: " + tag : ""));
if (notification.extras != null) {
String title = notification.extras.getString(Notification.EXTRA_TITLE);
String text = notification.extras.getString(Notification.EXTRA_TEXT);
// XposedBridge.log("Notification Title: " + (title != null ? title : "No Title"));
// XposedBridge.log("Notification Text: " + (text != null ? text : "No Text"));
} else {
// XposedBridge.log("Notification has no extras.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import io.github.chipppppppppp.lime.LimeOptions;

public class Ringtone implements IHook {
public class RingTone implements IHook {
private android.media.Ringtone ringtone = null;
private boolean isPlaying = false;

@Override
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {

if (!limeOptions.callTone.checked) return;
if (!limeOptions.call_tone.checked) return;

XposedBridge.hookAllMethods(
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
Expand Down
Loading

0 comments on commit 5d9f042

Please sign in to comment.