mirror of
https://github.com/areteruhiro/LIME-beta-hiro.git
synced 2025-02-05 21:11:39 +09:00
Merge branch 'master' into patch-30
This commit is contained in:
commit
5f4287024f
@ -33,7 +33,8 @@ public class LimeOptions {
|
||||
public Option blockTracking = new Option("block_tracking", R.string.switch_block_tracking, false);
|
||||
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 archived = new Option("archived_message", R.string.switch_archived, false);
|
||||
public Option callTone = new Option("call_tone", R.string.call_tone, false);
|
||||
|
||||
public Option[] options = {
|
||||
removeVoom,
|
||||
@ -51,11 +52,12 @@ public class LimeOptions {
|
||||
openInBrowser,
|
||||
preventMarkAsRead,
|
||||
preventUnsendMessage,
|
||||
Archived,
|
||||
archived,
|
||||
sendMuteMessage,
|
||||
removeKeepUnread,
|
||||
blockTracking,
|
||||
stopVersionCheck,
|
||||
outputCommunication
|
||||
outputCommunication,
|
||||
callTone
|
||||
};
|
||||
}
|
||||
|
@ -30,9 +30,11 @@ import io.github.chipppppppppp.lime.hooks.RemoveFlexibleContents;
|
||||
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.SendMuteMessage;
|
||||
import io.github.chipppppppppp.lime.hooks.SpoofAndroidId;
|
||||
import io.github.chipppppppppp.lime.hooks.SpoofUserAgent;
|
||||
import io.github.chipppppppppp.lime.hooks.UnsentCap;
|
||||
|
||||
public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResources, IXposedHookZygoteInit {
|
||||
public static String modulePath;
|
||||
@ -63,7 +65,9 @@ public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResou
|
||||
new BlockTracking(),
|
||||
new ModifyResponse(),
|
||||
new OutputRequest(),
|
||||
new Archived()
|
||||
new Archived(),
|
||||
new Ringtone(),
|
||||
new UnsentCap()
|
||||
};
|
||||
|
||||
public void handleLoadPackage(@NonNull XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||
|
@ -27,7 +27,7 @@ public class Archived implements IHook {
|
||||
|
||||
@Override
|
||||
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||
if (!limeOptions.Archived.checked) return;
|
||||
if (!limeOptions.archived.checked) return;
|
||||
|
||||
XposedBridge.hookAllMethods(Application.class, "onCreate", new XC_MethodHook() {
|
||||
@Override
|
||||
@ -50,7 +50,7 @@ public class Archived implements IHook {
|
||||
}
|
||||
|
||||
private void hookMessageDeletion(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db, Context moduleContext) {
|
||||
if (!limeOptions.Archived.checked) return;
|
||||
if (!limeOptions.archived.checked) return;
|
||||
|
||||
try {
|
||||
XposedBridge.hookAllMethods(
|
||||
|
@ -12,6 +12,8 @@ import io.github.chipppppppppp.lime.LimeOptions;
|
||||
|
||||
public class RemoveFlexibleContents implements IHook {
|
||||
int recommendationResId, serviceNameResId, notificationResId;
|
||||
int serviceRowContainerResId, serviceIconResId, serviceCarouselResId;
|
||||
int serviceTitleBackgroundResId, serviceTitleResId, serviceSeeMoreResId, serviceSeeMoreBadgeResId;
|
||||
|
||||
@Override
|
||||
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||
@ -25,6 +27,13 @@ public class RemoveFlexibleContents implements IHook {
|
||||
recommendationResId = context.getResources().getIdentifier("home_tab_contents_recommendation_placement", "id", context.getPackageName());
|
||||
serviceNameResId = context.getResources().getIdentifier("home_tab_service_name", "id", context.getPackageName());
|
||||
notificationResId = context.getResources().getIdentifier("notification_hub_row_rolling_view_group", "id", context.getPackageName());
|
||||
serviceRowContainerResId = context.getResources().getIdentifier("service_row_container", "id", context.getPackageName());
|
||||
serviceIconResId = context.getResources().getIdentifier("home_tab_service_icon", "id", context.getPackageName());
|
||||
serviceCarouselResId = context.getResources().getIdentifier("home_tab_service_carousel", "id", context.getPackageName());
|
||||
serviceTitleBackgroundResId = context.getResources().getIdentifier("home_tab_service_title_background", "id", context.getPackageName());
|
||||
serviceTitleResId = context.getResources().getIdentifier("home_tab_service_title", "id", context.getPackageName());
|
||||
serviceSeeMoreResId = context.getResources().getIdentifier("home_tab_service_see_more", "id", context.getPackageName());
|
||||
serviceSeeMoreBadgeResId = context.getResources().getIdentifier("home_tab_service_see_more_badge", "id", context.getPackageName());
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -33,22 +42,37 @@ public class RemoveFlexibleContents implements IHook {
|
||||
View.class,
|
||||
"onAttachedToWindow",
|
||||
new XC_MethodHook() {
|
||||
View view;
|
||||
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
view = (View) param.thisObject;
|
||||
if (limeOptions.removeRecommendation.checked && view.getId() == recommendationResId
|
||||
|| limeOptions.removeServiceLabels.checked && view.getId() == serviceNameResId) {
|
||||
View view = (View) param.thisObject;
|
||||
int viewId = view.getId();
|
||||
//String resourceName = getResourceName(view.getContext(), viewId);
|
||||
//XposedBridge.log("View ID: " + viewId + ", Resource Name: " + resourceName);
|
||||
|
||||
if (limeOptions.removeRecommendation.checked && viewId == recommendationResId
|
||||
|| limeOptions.removeServiceLabels.checked && viewId == serviceNameResId
|
||||
|| viewId == serviceRowContainerResId
|
||||
|| viewId == serviceIconResId
|
||||
|| viewId == serviceCarouselResId
|
||||
|| viewId == serviceTitleBackgroundResId
|
||||
|| viewId == serviceTitleResId
|
||||
|| viewId == serviceSeeMoreResId
|
||||
|| viewId == serviceSeeMoreBadgeResId) {
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
layoutParams.height = 0;
|
||||
view.setLayoutParams(layoutParams);
|
||||
view.setVisibility(View.GONE);
|
||||
} else if (view.getId() == notificationResId) {
|
||||
} else if (viewId == notificationResId) {
|
||||
((View) view.getParent()).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
private String getResourceName(Context context, int resourceId) {
|
||||
return context.getResources().getResourceEntryName(resourceId);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
package io.github.chipppppppppp.lime.hooks;
|
||||
|
||||
import android.app.AndroidAppHelper;
|
||||
import android.content.Context;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||
import io.github.chipppppppppp.lime.LimeOptions;
|
||||
|
||||
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;
|
||||
|
||||
XposedBridge.hookAllMethods(
|
||||
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
|
||||
Constants.RESPONSE_HOOK.methodName,
|
||||
new XC_MethodHook() {
|
||||
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
String paramValue = param.args[1].toString();
|
||||
|
||||
if (paramValue.contains("type:NOTIFIED_RECEIVED_CALL,") && !isPlaying) {
|
||||
Context context = AndroidAppHelper.currentApplication().getApplicationContext();
|
||||
if (context != null) {
|
||||
Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
|
||||
ringtone = RingtoneManager.getRingtone(context, ringtoneUri);
|
||||
ringtone.play();
|
||||
isPlaying = true;
|
||||
}
|
||||
|
||||
if (paramValue.contains("RESULT=REJECTED,") ||
|
||||
paramValue.contains("RESULT=REJECTED,")) {
|
||||
if (ringtone != null && ringtone.isPlaying()) {
|
||||
ringtone.stop();
|
||||
isPlaying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Class<?> voIPBaseFragmentClass = loadPackageParam.classLoader.loadClass("com.linecorp.voip2.common.base.VoIPBaseFragment");
|
||||
XposedBridge.hookAllMethods(voIPBaseFragmentClass, "onCreate", new XC_MethodHook() {
|
||||
@Override
|
||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||
if (ringtone != null && ringtone.isPlaying()) {
|
||||
ringtone.stop();
|
||||
isPlaying = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,493 @@
|
||||
package io.github.chipppppppppp.lime.hooks;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AndroidAppHelper;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import de.robv.android.xposed.XC_MethodHook;
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import de.robv.android.xposed.XposedHelpers;
|
||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||
|
||||
import io.github.chipppppppppp.lime.LimeOptions;
|
||||
import io.github.chipppppppppp.lime.R;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class UnsentCap implements IHook {
|
||||
|
||||
public static final String Main_file = "unsent_capture.txt";
|
||||
public static final String Main_backup = "capture_backup.txt";
|
||||
public static final String Unresolved_Ids = "Unresolved_Ids.txt";
|
||||
SQLiteDatabase db1 = null;
|
||||
SQLiteDatabase db2 = null;
|
||||
|
||||
@Override
|
||||
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||
if (!limeOptions.preventUnsendMessage.checked) return;
|
||||
|
||||
XposedBridge.hookAllConstructors(
|
||||
loadPackageParam.classLoader.loadClass("jp.naver.line.android.common.view.listview.PopupListView"),
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
ViewGroup viewGroup = (ViewGroup) param.thisObject;
|
||||
Context appContext = viewGroup.getContext();
|
||||
Context moduleContext = AndroidAppHelper.currentApplication().createPackageContext(
|
||||
"io.github.chipppppppppp.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
|
||||
RelativeLayout container = new RelativeLayout(appContext);
|
||||
container.setLayoutParams(new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
Button openFileButton = new Button(appContext);
|
||||
openFileButton.setText(moduleContext.getResources().getString(R.string.confirm_messages));
|
||||
openFileButton.setTextSize(12);
|
||||
openFileButton.setTextColor(Color.BLACK);
|
||||
openFileButton.setId(View.generateViewId());
|
||||
|
||||
RelativeLayout.LayoutParams buttonParams = new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
buttonParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
|
||||
container.addView(openFileButton, buttonParams);
|
||||
|
||||
Button clearFileButton = new Button(appContext);
|
||||
clearFileButton.setText(moduleContext.getResources().getString(R.string.delete_messages));
|
||||
clearFileButton.setTextSize(12);
|
||||
clearFileButton.setTextColor(Color.RED);
|
||||
clearFileButton.setId(View.generateViewId());
|
||||
|
||||
RelativeLayout.LayoutParams clearButtonParams = new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
|
||||
clearButtonParams.addRule(RelativeLayout.BELOW, openFileButton.getId());
|
||||
clearButtonParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
|
||||
container.addView(clearFileButton, clearButtonParams);
|
||||
|
||||
openFileButton.setOnClickListener(v -> {
|
||||
File backupFile = new File(appContext.getFilesDir(), Main_backup);
|
||||
if (!backupFile.exists()) {
|
||||
try {
|
||||
backupFile.createNewFile();
|
||||
} catch (IOException ignored) {
|
||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_creation_failed), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (backupFile.length() > 0) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(backupFile)))) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
output.append(line).append("\n");
|
||||
}
|
||||
|
||||
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(appContext);
|
||||
ScrollView verticalScrollView = new ScrollView(appContext);
|
||||
TextView textView = new TextView(appContext);
|
||||
textView.setText(output.toString());
|
||||
textView.setMaxLines(Integer.MAX_VALUE);
|
||||
textView.setHorizontallyScrolling(true);
|
||||
textView.setHorizontalScrollBarEnabled(true);
|
||||
|
||||
horizontalScrollView.addView(textView);
|
||||
verticalScrollView.addView(horizontalScrollView);
|
||||
|
||||
new AlertDialog.Builder(appContext)
|
||||
.setTitle(moduleContext.getResources().getString(R.string.backup))
|
||||
.setView(verticalScrollView)
|
||||
.setPositiveButton(moduleContext.getResources().getString(R.string.positive_button), null)
|
||||
.create()
|
||||
.show();
|
||||
} catch (IOException ignored) {
|
||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.failed_read_backup_file, Main_backup), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.no_backup_found), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
clearFileButton.setOnClickListener(v -> new AlertDialog.Builder(appContext)
|
||||
.setTitle(moduleContext.getResources().getString(R.string.confirm))
|
||||
.setMessage(moduleContext.getResources().getString(R.string.confirm_delete))
|
||||
.setPositiveButton(moduleContext.getResources().getString(R.string.yes), (dialog, which) -> {
|
||||
File backupFile = new File(appContext.getFilesDir(), Main_backup);
|
||||
if (backupFile.exists()) {
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(backupFile))) {
|
||||
writer.write("");
|
||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_content_deleted), Toast.LENGTH_SHORT).show();
|
||||
} catch (IOException ignored) {
|
||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_delete_failed), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_not_found), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(moduleContext.getResources().getString(R.string.no), null)
|
||||
.create()
|
||||
.show());
|
||||
|
||||
((ListView) viewGroup.getChildAt(0)).addFooterView(container);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
XposedHelpers.findAndHookMethod(
|
||||
"com.linecorp.line.chatlist.view.fragment.ChatListPageFragment",
|
||||
loadPackageParam.classLoader, "onCreateView",
|
||||
LayoutInflater.class, ViewGroup.class, android.os.Bundle.class,
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
Context moduleContext = AndroidAppHelper.currentApplication().createPackageContext(
|
||||
"io.github.chipppppppppp.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
|
||||
View rootView = (View) param.getResult();
|
||||
Context context = rootView.getContext();
|
||||
|
||||
File originalFile = new File(context.getFilesDir(), Main_file);
|
||||
if (!originalFile.exists()) {
|
||||
try {
|
||||
originalFile.createNewFile();
|
||||
} catch (IOException ignored) {
|
||||
Toast.makeText(context, moduleContext.getResources().getString(R.string.file_creation_failed), Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (originalFile.length() > 0) {
|
||||
int lineCount = countLinesInFile(originalFile);
|
||||
if (lineCount > 0) {
|
||||
Button button = new Button(context);
|
||||
button.setText(Integer.toString(lineCount));
|
||||
button.setId(View.generateViewId());
|
||||
button.setLayoutParams(new RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
|
||||
button.setOnClickListener(v -> {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(originalFile)))) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
boolean showToast = false;
|
||||
SharedPreferences prefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
boolean messageShown = prefs.getBoolean("messageShown", false);
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains("No content") || line.contains("No name")) {
|
||||
if (!messageShown) {
|
||||
showToast = true;
|
||||
prefs.edit().putBoolean("messageShown", true).apply();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
output.append(line).append("\n");
|
||||
}
|
||||
|
||||
if (showToast) {
|
||||
Toast.makeText(context, moduleContext.getResources().getString(R.string.no_get_restart_app), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
HorizontalScrollView horizontalScrollView = new HorizontalScrollView(context);
|
||||
ScrollView verticalScrollView = new ScrollView(context);
|
||||
TextView textView = new TextView(context);
|
||||
textView.setText(output.toString());
|
||||
textView.setMaxLines(Integer.MAX_VALUE);
|
||||
textView.setHorizontallyScrolling(true);
|
||||
textView.setHorizontalScrollBarEnabled(true);
|
||||
|
||||
horizontalScrollView.addView(textView);
|
||||
verticalScrollView.addView(horizontalScrollView);
|
||||
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(moduleContext.getResources().getString(R.string.deleted_messages))
|
||||
.setView(verticalScrollView)
|
||||
.setPositiveButton(moduleContext.getResources().getString(R.string.positive_button), (dialog, which) -> {
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(new File(context.getFilesDir(), Main_backup), true))) {
|
||||
writer.write(output.toString());
|
||||
new BufferedWriter(new FileWriter(originalFile)).close();
|
||||
Toast.makeText(context, moduleContext.getResources().getString(R.string.content_moved_to_backup), Toast.LENGTH_SHORT).show();
|
||||
prefs.edit().putBoolean("messageShown", false).apply();
|
||||
} catch (IOException ignored) {
|
||||
Toast.makeText(context, moduleContext.getResources().getString(R.string.file_move_failed), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
if (button.getParent() instanceof ViewGroup) {
|
||||
((ViewGroup) button.getParent()).removeView(button);
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
|
||||
} catch (IOException ignored) {
|
||||
Toast.makeText(context, moduleContext.getResources().getString(R.string.failed_read_backup_file, Main_backup), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
if (rootView instanceof ViewGroup) {
|
||||
((ViewGroup) rootView).addView(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
XposedBridge.hookAllMethods(Application.class, "onCreate", new XC_MethodHook() {
|
||||
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
Application appContext = (Application) param.thisObject;
|
||||
if (appContext == null) {
|
||||
XposedBridge.log("appContext is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
Context moduleContext;
|
||||
try {
|
||||
moduleContext = appContext.createPackageContext(
|
||||
"io.github.chipppppppppp.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
XposedBridge.log("Failed to create package context: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
File dbFile1 = appContext.getDatabasePath("naver_line");
|
||||
File dbFile2 = appContext.getDatabasePath("contact");
|
||||
if (!dbFile1.exists() || !dbFile2.exists()) return;
|
||||
|
||||
SQLiteDatabase.OpenParams.Builder builder1 = new SQLiteDatabase.OpenParams.Builder().addOpenFlags(SQLiteDatabase.OPEN_READWRITE);
|
||||
SQLiteDatabase db1 = SQLiteDatabase.openDatabase(dbFile1, builder1.build());
|
||||
SQLiteDatabase.OpenParams.Builder builder2 = new SQLiteDatabase.OpenParams.Builder().addOpenFlags(SQLiteDatabase.OPEN_READWRITE);
|
||||
SQLiteDatabase db2 = SQLiteDatabase.openDatabase(dbFile2, builder2.build());
|
||||
|
||||
hookMessageDeletion(loadPackageParam, appContext, db1, db2);
|
||||
resolveUnresolvedIds(loadPackageParam, appContext, db1, db2, moduleContext);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
private String queryDatabase(SQLiteDatabase db, String query, String... selectionArgs) {
|
||||
if (db == null) {
|
||||
XposedBridge.log("Database is not initialized.");
|
||||
return null;
|
||||
}
|
||||
Cursor cursor = db.rawQuery(query, selectionArgs);
|
||||
String result = null;
|
||||
if (cursor.moveToFirst()) result = cursor.getString(0);
|
||||
cursor.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
private int countLinesInFile(File file) {
|
||||
int count = 0;
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (!(line.contains("No content") || line.contains("No name"))) count++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void hookMessageDeletion(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db1, SQLiteDatabase db2) {
|
||||
try {
|
||||
XposedBridge.hookAllMethods(
|
||||
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
|
||||
Constants.RESPONSE_HOOK.methodName,
|
||||
new XC_MethodHook() {
|
||||
@Override
|
||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||
Context appContext = AndroidAppHelper.currentApplication();
|
||||
if (appContext == null) return;
|
||||
|
||||
File dbFile = appContext.getDatabasePath("naver_line");
|
||||
if (!dbFile.exists()) return;
|
||||
|
||||
SQLiteDatabase.OpenParams dbParams = new SQLiteDatabase.OpenParams.Builder()
|
||||
.addOpenFlags(SQLiteDatabase.OPEN_READWRITE)
|
||||
.build();
|
||||
SQLiteDatabase db = SQLiteDatabase.openDatabase(dbFile, dbParams);
|
||||
String paramValue = param.args[1].toString();
|
||||
if (!paramValue.contains("type:NOTIFIED_DESTROY_MESSAGE,")) {
|
||||
|
||||
if (db != null) db.close(); // 条件を満たさない場合はデータベースをクローズ
|
||||
return;
|
||||
}
|
||||
Context moduleContext = appContext.createPackageContext(
|
||||
"io.github.chipppppppppp.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||
processMessage(paramValue, moduleContext, db1, db2, context);
|
||||
|
||||
if (db != null) db.close();
|
||||
}
|
||||
});
|
||||
} catch (ClassNotFoundException ignored ) {
|
||||
}
|
||||
}
|
||||
|
||||
private void processMessage(String paramValue, Context moduleContext, SQLiteDatabase db1, SQLiteDatabase db2, Context context) {
|
||||
String unresolvedFilePath = context.getFilesDir() + "/" + Unresolved_Ids;
|
||||
String[] operations = paramValue.split("Operation\\(");
|
||||
for (String operation : operations) {
|
||||
if (operation.trim().isEmpty()) continue;
|
||||
|
||||
String revision = null, createdTime = null, type = null, from = null, to = null, param12 = null, param22 = null, operationContent = null, serverId = null, talkId = null;
|
||||
String[] parts = operation.split(",");
|
||||
for (String part : parts) {
|
||||
part = part.trim();
|
||||
if (part.startsWith("param1:")) talkId = part.substring("param1:".length()).trim();
|
||||
else if (part.startsWith("param2:")) serverId = part.substring("param2:".length()).trim();
|
||||
else if (part.startsWith("revision:")) revision = part.substring("revision:".length()).trim();
|
||||
else if (part.startsWith("createdTime:")) createdTime = part.substring("createdTime:".length()).trim();
|
||||
else if (part.startsWith("type:")) type = part.substring("type:".length()).trim();
|
||||
else if (part.startsWith("from:")) from = part.substring("from:".length()).trim();
|
||||
else if (part.startsWith("to:")) to = part.substring("to:".length()).trim();
|
||||
else if (part.startsWith("contentMetadata:")) param12 = part.substring("contentMetadata:".length()).trim();
|
||||
else if (part.startsWith("operationContent:")) operationContent = part.substring("operationContent:".length()).trim();
|
||||
}
|
||||
|
||||
if (serverId == null || talkId == null) continue;
|
||||
|
||||
String content = queryDatabase(db1, "SELECT content FROM chat_history WHERE server_id=?", serverId);
|
||||
String timeEpochStr = queryDatabase(db1, "SELECT created_time FROM chat_history WHERE server_id=?", serverId);
|
||||
String timeFormatted = formatMessageTime(timeEpochStr);
|
||||
String groupName = queryDatabase(db1, "SELECT name FROM groups WHERE id=?", talkId);
|
||||
String media = queryDatabase(db1, "SELECT attachement_type FROM chat_history WHERE server_id=?", serverId);
|
||||
String talkName = queryDatabase(db2, "SELECT profile_name FROM contacts WHERE mid=?", talkId);
|
||||
|
||||
String name = (groupName != null ? groupName : (talkName != null ? talkName : "No Name" + ":" + "talkId" + talkId));
|
||||
if (timeEpochStr == null) saveUnresolvedIds(serverId, talkId, unresolvedFilePath);
|
||||
|
||||
String from_mid = null, sender_name = null;
|
||||
if (groupName != null) {
|
||||
from_mid = queryDatabase(db1, "SELECT from_mid FROM chat_history WHERE server_id=?", serverId);
|
||||
if (from_mid != null) sender_name = queryDatabase(db2, "SELECT profile_name FROM contacts WHERE mid=?", from_mid);
|
||||
}
|
||||
|
||||
if (sender_name != null) name = groupName + ": " + sender_name;
|
||||
|
||||
String mediaDescription = "";
|
||||
if (media != null) {
|
||||
switch (media) {
|
||||
case "7": mediaDescription = moduleContext.getResources().getString(R.string.sticker); break;
|
||||
case "1": mediaDescription = moduleContext.getResources().getString(R.string.picture); break;
|
||||
case "2": mediaDescription = moduleContext.getResources().getString(R.string.video); break;
|
||||
}
|
||||
}
|
||||
|
||||
String logEntry = (timeFormatted != null ? timeFormatted : "No Time: ")
|
||||
+ name
|
||||
+ ": " + ((content != null) ? content : "No content:" + serverId)
|
||||
+ mediaDescription;
|
||||
|
||||
File fileToWrite = new File(context.getFilesDir(), Main_file);
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileToWrite, true))) {
|
||||
writer.write(logEntry);
|
||||
writer.newLine();
|
||||
} catch (IOException e) {
|
||||
XposedBridge.log("IOException occurred while writing to file: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveUnresolvedIds(String serverId, String talkId, String filePath) {
|
||||
String newEntry = "serverId:" + serverId + ",talkId:" + talkId;
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.equals(newEntry)) return;
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) {
|
||||
writer.write(newEntry);
|
||||
writer.newLine();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
private void resolveUnresolvedIds(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db1, SQLiteDatabase db2, Context moduleContext) {
|
||||
String unresolvedFilePath = context.getFilesDir() + "/" + Unresolved_Ids;
|
||||
File unresolvedFile = new File(unresolvedFilePath);
|
||||
File testFile = new File(context.getFilesDir(), Main_file);
|
||||
if (!unresolvedFile.exists()) return;
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(unresolvedFile));
|
||||
BufferedWriter testWriter = new BufferedWriter(new FileWriter(testFile, true))) {
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String[] parts = line.split(",");
|
||||
String serverId = parts[0].split(":")[1];
|
||||
String talkId = parts[1].split(":")[1];
|
||||
|
||||
String content = queryDatabase(db1, "SELECT content FROM chat_history WHERE server_id=?", serverId);
|
||||
if (content == null) continue;
|
||||
|
||||
String timeEpochStr = queryDatabase(db1, "SELECT created_time FROM chat_history WHERE server_id=?", serverId);
|
||||
String timeFormatted = formatMessageTime(timeEpochStr);
|
||||
String groupName = queryDatabase(db1, "SELECT name FROM groups WHERE id=?", talkId);
|
||||
String talkName = queryDatabase(db2, "SELECT profile_name FROM contacts WHERE mid=?", talkId);
|
||||
String name = (groupName != null ? groupName : (talkName != null ? talkName : "No Name" + ":" + "talkId" + talkId));
|
||||
|
||||
String media = queryDatabase(db1, "SELECT attachement_type FROM chat_history WHERE server_id=?", serverId);
|
||||
String mediaDescription = "";
|
||||
if (media != null) {
|
||||
switch (media) {
|
||||
case "7": mediaDescription = moduleContext.getResources().getString(R.string.sticker); break;
|
||||
case "1": mediaDescription = moduleContext.getResources().getString(R.string.picture); break;
|
||||
case "2": mediaDescription = moduleContext.getResources().getString(R.string.video); break;
|
||||
}
|
||||
}
|
||||
|
||||
String from_mid = null, sender_name = null;
|
||||
if (groupName != null) {
|
||||
from_mid = queryDatabase(db1, "SELECT from_mid FROM chat_history WHERE server_id=?", serverId);
|
||||
if (from_mid != null) sender_name = queryDatabase(db2, "SELECT profile_name FROM contacts WHERE mid=?", from_mid);
|
||||
}
|
||||
|
||||
if (sender_name != null) name = groupName + ": " + sender_name;
|
||||
|
||||
testWriter.write((timeFormatted != null ? timeFormatted : "No Time: ")
|
||||
+ name + ": " + content + mediaDescription);
|
||||
testWriter.newLine();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
unresolvedFile.delete();
|
||||
}
|
||||
private String formatMessageTime(String timeEpochStr) {
|
||||
if (timeEpochStr == null) return null;
|
||||
long timeEpoch = Long.parseLong(timeEpochStr);
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
return sdf.format(new Date(timeEpoch));
|
||||
}
|
||||
}
|
@ -3,6 +3,14 @@
|
||||
<!-- General -->
|
||||
<string name="positive_button">OK</string>
|
||||
<string name="negative_button">キャンセル</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="yes">はい</string>
|
||||
<string name="no">いいえ</string>
|
||||
<string name="cancel">キャンセル</string>
|
||||
<string name="sticker">スタンプ</string>
|
||||
<string name="video">動画</string>
|
||||
<string name="picture">写真</string>
|
||||
<string name="reacquisition">再取得</string>
|
||||
|
||||
<!-- Xposed API -->
|
||||
<string name="xposed_desc">LINE をクリーンに。</string>
|
||||
@ -43,7 +51,28 @@
|
||||
<string name="modify_response">レスポンスを改変</string>
|
||||
<string name="button_copy">コピー</string>
|
||||
<string name="button_paste">ペースト</string>
|
||||
|
||||
<string name="call_tone">着信音を鳴らす(LSPatch用)</string>
|
||||
<!-- Menu -->
|
||||
<string name="switch_keep_unread">未読のまま閲覧</string>
|
||||
|
||||
<!-- UNSENT CAP -->
|
||||
<string name="backup">バックアップ</string>
|
||||
<string name="backup_creation_failed">バックアップの作成に失敗しました</string>
|
||||
<string name="confirm">確認</string>
|
||||
<string name="class_not_found">クラスが見つかりません</string>
|
||||
<string name="confirm_messages">確認済みのメッセージ</string>
|
||||
<string name="delete_messages">メッセージを削除</string>
|
||||
<string name="deleted_messages">削除されたメッセージ</string>
|
||||
<string name="content_moved_to_backup">内容がバックアップファイルに移動されました</string>
|
||||
<string name="file_content_deleted">ファイルの内容が削除されました</string>
|
||||
<string name="file_creation_failed">ファイルを作成できませんでした</string>
|
||||
<string name="file_delete_failed">ファイルの削除に失敗しました</string>
|
||||
<string name="file_move_failed">ファイルの移動に失敗しました</string>
|
||||
<string name="file_save_failed">ファイルの保存に失敗しました</string>
|
||||
<string name="file_not_found">ファイルが見つかりません</string>
|
||||
<string name="failed_read_backup_file">%s の読み取りに失敗しました</string>
|
||||
<string name="confirm_delete">本当に削除しますか?</string>
|
||||
<string name="no_backup_found">何もバックアップされていません</string>
|
||||
<string name="no_get_restart_app">正しく取得できませんでした。\nアプリを再起動してください</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1,8 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string translatable="false" name="app_name">LIME</string>
|
||||
<!-- General -->
|
||||
<string name="positive_button">OK</string>
|
||||
<string name="negative_button">Cancel</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="no">No</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="sticker">Stickers</string>
|
||||
<string name="video">Videos</string>
|
||||
<string name="picture">Photos</string>
|
||||
<string name="reacquisition">Reacquisition</string>
|
||||
|
||||
<!-- Xposed API -->
|
||||
<string name="xposed_desc">Clean LINE.</string>
|
||||
@ -43,7 +52,28 @@
|
||||
<string name="modify_response">Modify responses</string>
|
||||
<string name="button_copy">Copy</string>
|
||||
<string name="button_paste">Paste</string>
|
||||
|
||||
<string name="call_tone">Notify ringtone of LSPatch</string>
|
||||
<!-- Menu -->
|
||||
<string name="switch_keep_unread">Keep unread</string>
|
||||
|
||||
<!-- UNSENT CAP -->
|
||||
<string name="backup">Backup</string>
|
||||
<string name="backup_creation_failed">Failed to create backup</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="class_not_found">Class not found</string>
|
||||
<string name="confirm_messages">Confirmed messages</string>
|
||||
<string name="delete_messages">Deleted messages</string>
|
||||
<string name="deleted_messages">Deleted messages</string>
|
||||
<string name="content_moved_to_backup">Content moved to backup file</string>
|
||||
<string name="file_content_deleted">File contents deleted</string>
|
||||
<string name="file_creation_failed">Could not create file</string>
|
||||
<string name="file_delete_failed">Failed to delete file</string>
|
||||
<string name="file_move_failed">Failed to move file</string>
|
||||
<string name="file_save_failed">Failed to save file</string>
|
||||
<string name="file_not_found">File not found</string>
|
||||
<string name="failed_read_backup_file">Failed to read %s</string>
|
||||
<string name="confirm_delete">Really want to delete it?</string>
|
||||
<string name="no_backup_found">Nothing backed up</string>
|
||||
<string name="no_get_restart_app">Could not get properly.\nPlease restart the app</string>
|
||||
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user