mirror of
https://github.com/areteruhiro/LIME-beta-hiro.git
synced 2025-02-05 21:11:39 +09:00
既読確認機能を割り込み処理をするように
gradleをバージョンアップ 設定の取得をファイルから行うように
This commit is contained in:
parent
a9048992e0
commit
e36587053d
@ -10,7 +10,7 @@ android {
|
|||||||
minSdk 28
|
minSdk 28
|
||||||
targetSdk 35
|
targetSdk 35
|
||||||
versionCode 11501
|
versionCode 11501
|
||||||
versionName "1.15.01"
|
versionName "1.16.10beta"
|
||||||
multiDexEnabled false
|
multiDexEnabled false
|
||||||
proguardFiles += 'proguard-rules.pro'
|
proguardFiles += 'proguard-rules.pro'
|
||||||
buildConfigField 'String', 'HOOK_TARGET_VERSION', '"141910383"'
|
buildConfigField 'String', 'HOOK_TARGET_VERSION', '"141910383"'
|
||||||
@ -71,4 +71,4 @@ dependencies {
|
|||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked"
|
options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked"
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
@ -49,7 +49,7 @@ public class LimeOptions {
|
|||||||
public Option MySendMessage = new Option("MySendMessage", R.string.MySendMessage, false);
|
public Option MySendMessage = new Option("MySendMessage", R.string.MySendMessage, false);
|
||||||
|
|
||||||
public Option AgeCheckSkip = new Option("AgeCheckSkip", R.string.AgeCheckSkip, false);
|
public Option AgeCheckSkip = new Option("AgeCheckSkip", R.string.AgeCheckSkip, false);
|
||||||
|
public Option hide_canceled_message = new Option("hide_canceled_message", R.string.hide_canceled_message, false);
|
||||||
public Option RemoveNotification = new Option("RemoveNotification", R.string.removeNotification, false);
|
public Option RemoveNotification = new Option("RemoveNotification", R.string.removeNotification, false);
|
||||||
public Option DarkColor = new Option("DarkColor", R.string.DarkColor, false);
|
public Option DarkColor = new Option("DarkColor", R.string.DarkColor, false);
|
||||||
public Option NoMuteMessage = new Option("NoMuteMessage", R.string.NoMuteMessage, false);
|
public Option NoMuteMessage = new Option("NoMuteMessage", R.string.NoMuteMessage, false);
|
||||||
@ -75,7 +75,7 @@ public class LimeOptions {
|
|||||||
redirectWebView,
|
redirectWebView,
|
||||||
openInBrowser,
|
openInBrowser,
|
||||||
preventMarkAsRead,
|
preventMarkAsRead,
|
||||||
preventUnsendMessage,
|
preventUnsendMessage,hide_canceled_message,
|
||||||
sendMuteMessage,
|
sendMuteMessage,
|
||||||
Archived,
|
Archived,
|
||||||
ReadChecker,MySendMessage,ReadCheckerChatdataDelete,
|
ReadChecker,MySendMessage,ReadCheckerChatdataDelete,
|
||||||
|
@ -1,59 +1,24 @@
|
|||||||
package io.github.hiro.lime;
|
package io.github.hiro.lime;
|
||||||
|
|
||||||
import android.content.res.XModuleResources;
|
import android.content.res.XModuleResources;
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import de.robv.android.xposed.IXposedHookInitPackageResources;
|
import de.robv.android.xposed.IXposedHookInitPackageResources;
|
||||||
import de.robv.android.xposed.IXposedHookLoadPackage;
|
import de.robv.android.xposed.IXposedHookLoadPackage;
|
||||||
import de.robv.android.xposed.IXposedHookZygoteInit;
|
import de.robv.android.xposed.IXposedHookZygoteInit;
|
||||||
import de.robv.android.xposed.XSharedPreferences;
|
import de.robv.android.xposed.XSharedPreferences;
|
||||||
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
|
||||||
import de.robv.android.xposed.callbacks.XC_LayoutInflated;
|
import de.robv.android.xposed.callbacks.XC_LayoutInflated;
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
import io.github.hiro.lime.hooks.AddRegistrationOptions;
|
import io.github.hiro.lime.hooks.*;
|
||||||
import io.github.hiro.lime.hooks.AgeCheckSkip;
|
|
||||||
import io.github.hiro.lime.hooks.AutomaticBackup;
|
|
||||||
import io.github.hiro.lime.hooks.CheckHookTargetVersion;
|
|
||||||
import io.github.hiro.lime.hooks.Constants;
|
|
||||||
import io.github.hiro.lime.hooks.Disabled_Group_notification;
|
|
||||||
import io.github.hiro.lime.hooks.EmbedOptions;
|
|
||||||
import io.github.hiro.lime.hooks.IHook;
|
|
||||||
import io.github.hiro.lime.hooks.KeepUnread;
|
|
||||||
import io.github.hiro.lime.hooks.KeepUnreadLSpatch;
|
|
||||||
import io.github.hiro.lime.hooks.ModifyRequest;
|
|
||||||
import io.github.hiro.lime.hooks.ModifyResponse;
|
|
||||||
import io.github.hiro.lime.hooks.PhotoAddNotification;
|
|
||||||
import io.github.hiro.lime.hooks.OutputRequest;
|
|
||||||
import io.github.hiro.lime.hooks.OutputResponse;
|
|
||||||
import io.github.hiro.lime.hooks.PreventMarkAsRead;
|
|
||||||
import io.github.hiro.lime.hooks.PreventUnsendMessage;
|
|
||||||
import io.github.hiro.lime.hooks.RedirectWebView;
|
|
||||||
import io.github.hiro.lime.hooks.RemoveAds;
|
|
||||||
import io.github.hiro.lime.hooks.RemoveFlexibleContents;
|
|
||||||
import io.github.hiro.lime.hooks.RemoveIconLabels;
|
|
||||||
import io.github.hiro.lime.hooks.RemoveIcons;
|
|
||||||
import io.github.hiro.lime.hooks.RemoveNotification;
|
|
||||||
import io.github.hiro.lime.hooks.RemoveReplyMute;
|
|
||||||
import io.github.hiro.lime.hooks.RemoveVoiceRecord;
|
|
||||||
|
|
||||||
|
|
||||||
import io.github.hiro.lime.hooks.RingTone;
|
|
||||||
import io.github.hiro.lime.hooks.SendMuteMessage;
|
|
||||||
import io.github.hiro.lime.hooks.SpoofAndroidId;
|
|
||||||
import io.github.hiro.lime.hooks.SpoofUserAgent;
|
|
||||||
|
|
||||||
import io.github.hiro.lime.hooks.UnsentRec;
|
|
||||||
import io.github.hiro.lime.hooks.Archived;
|
|
||||||
import io.github.hiro.lime.hooks.ReadChecker;
|
|
||||||
import io.github.hiro.lime.hooks.DarkColor;
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResources, IXposedHookZygoteInit {
|
public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResources, IXposedHookZygoteInit {
|
||||||
|
|
||||||
public static String modulePath;
|
public static String modulePath;
|
||||||
|
public static CustomPreferences customPreferences;
|
||||||
public static XSharedPreferences xModulePrefs;
|
public static XSharedPreferences xModulePrefs;
|
||||||
public static XSharedPreferences xPackagePrefs;
|
public static XSharedPreferences xPackagePrefs;
|
||||||
public static XSharedPreferences xPrefs;
|
public static XSharedPreferences xPrefs;
|
||||||
@ -91,23 +56,48 @@ public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResou
|
|||||||
new PhotoAddNotification(),
|
new PhotoAddNotification(),
|
||||||
new RemoveVoiceRecord(),
|
new RemoveVoiceRecord(),
|
||||||
new AgeCheckSkip()
|
new AgeCheckSkip()
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initZygote(@NonNull StartupParam startupParam) throws Throwable {
|
||||||
|
modulePath = startupParam.modulePath;
|
||||||
|
customPreferences = new CustomPreferences(); // CustomPreferences を初期化
|
||||||
|
}
|
||||||
|
|
||||||
public void handleLoadPackage(@NonNull XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
public void handleLoadPackage(@NonNull XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||||
if (!loadPackageParam.packageName.equals(Constants.PACKAGE_NAME)) return;
|
if (!loadPackageParam.packageName.equals(Constants.PACKAGE_NAME)) return;
|
||||||
Constants.initializeHooks(loadPackageParam);
|
Constants.initializeHooks(loadPackageParam);
|
||||||
|
|
||||||
xModulePrefs = new XSharedPreferences(Constants.MODULE_NAME, "options");
|
xModulePrefs = new XSharedPreferences(Constants.MODULE_NAME, "options");
|
||||||
xPackagePrefs = new XSharedPreferences(Constants.PACKAGE_NAME, Constants.MODULE_NAME + "-options");
|
xPackagePrefs = new XSharedPreferences(Constants.PACKAGE_NAME, Constants.MODULE_NAME + "-options");
|
||||||
if (xModulePrefs.getBoolean("unembed_options", false)) {
|
|
||||||
|
// 設定ファイルを再読み込み
|
||||||
|
xModulePrefs.reload();
|
||||||
|
xPackagePrefs.reload();
|
||||||
|
|
||||||
|
// unembed_optionsの値をログに出力
|
||||||
|
boolean unembedOptions = xModulePrefs.getBoolean("unembed_options", false);
|
||||||
|
XposedBridge.log("unembed_options: " + unembedOptions);
|
||||||
|
|
||||||
|
if (unembedOptions) {
|
||||||
xPrefs = xModulePrefs;
|
xPrefs = xModulePrefs;
|
||||||
|
XposedBridge.log("Using module preferences");
|
||||||
|
|
||||||
|
// xModulePrefsから設定を読み込む
|
||||||
|
for (LimeOptions.Option option : limeOptions.options) {
|
||||||
|
option.checked = xModulePrefs.getBoolean(option.name, option.checked);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
xPrefs = xPackagePrefs;
|
xPrefs = xPackagePrefs;
|
||||||
}
|
XposedBridge.log("Using package preferences");
|
||||||
for (LimeOptions.Option option : limeOptions.options) {
|
|
||||||
option.checked = xPrefs.getBoolean(option.name, option.checked);
|
// customPreferencesから設定を読み込む
|
||||||
|
for (LimeOptions.Option option : limeOptions.options) {
|
||||||
|
option.checked = Boolean.parseBoolean(customPreferences.getSetting(option.name, String.valueOf(option.checked)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 各フックを適用
|
||||||
for (IHook hook : hooks) {
|
for (IHook hook : hooks) {
|
||||||
hook.hook(limeOptions, loadPackageParam);
|
hook.hook(limeOptions, loadPackageParam);
|
||||||
}
|
}
|
||||||
@ -120,8 +110,7 @@ public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResou
|
|||||||
|
|
||||||
XModuleResources xModuleResources = XModuleResources.createInstance(modulePath, resparam.res);
|
XModuleResources xModuleResources = XModuleResources.createInstance(modulePath, resparam.res);
|
||||||
|
|
||||||
|
// 既存のリソースフック
|
||||||
|
|
||||||
if (limeOptions.removeIconLabels.checked) {
|
if (limeOptions.removeIconLabels.checked) {
|
||||||
resparam.res.setReplacement(Constants.PACKAGE_NAME, "dimen", "main_bnb_button_height", xModuleResources.fwd(R.dimen.main_bnb_button_height));
|
resparam.res.setReplacement(Constants.PACKAGE_NAME, "dimen", "main_bnb_button_height", xModuleResources.fwd(R.dimen.main_bnb_button_height));
|
||||||
resparam.res.setReplacement(Constants.PACKAGE_NAME, "dimen", "main_bnb_button_width", xModuleResources.fwd(R.dimen.main_bnb_button_width));
|
resparam.res.setReplacement(Constants.PACKAGE_NAME, "dimen", "main_bnb_button_width", xModuleResources.fwd(R.dimen.main_bnb_button_width));
|
||||||
@ -137,11 +126,6 @@ public class Main implements IXposedHookLoadPackage, IXposedHookInitPackageResou
|
|||||||
resparam.res.setReplacement(Constants.PACKAGE_NAME, "dimen", "home_tab_v3_service_icon_size", xModuleResources.fwd(R.dimen.home_tab_v3_service_icon_size));
|
resparam.res.setReplacement(Constants.PACKAGE_NAME, "dimen", "home_tab_v3_service_icon_size", xModuleResources.fwd(R.dimen.home_tab_v3_service_icon_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initZygote(@NonNull StartupParam startupParam) throws Throwable {
|
|
||||||
modulePath = startupParam.modulePath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -353,4 +353,4 @@ public class MainActivity extends Activity {
|
|||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package io.github.hiro.lime.hooks;
|
||||||
|
|
||||||
|
import android.os.Environment;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class CustomPreferences {
|
||||||
|
|
||||||
|
private static final String SETTINGS_DIR = "LimeBackup/Setting";
|
||||||
|
private static final String SETTINGS_FILE = "settings.properties";
|
||||||
|
|
||||||
|
private final File settingsFile;
|
||||||
|
|
||||||
|
public CustomPreferences() {
|
||||||
|
File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), SETTINGS_DIR);
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
settingsFile = new File(dir, SETTINGS_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveSetting(String key, String value) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try (FileInputStream fis = new FileInputStream(settingsFile)) {
|
||||||
|
properties.load(fis);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ファイルが存在しない場合、新規作成する
|
||||||
|
}
|
||||||
|
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(settingsFile)) {
|
||||||
|
properties.setProperty(key, value);
|
||||||
|
properties.store(fos, null);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSetting(String key, String defaultValue) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
try (FileInputStream fis = new FileInputStream(settingsFile)) {
|
||||||
|
properties.load(fis);
|
||||||
|
return properties.getProperty(key, defaultValue);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
@ -80,14 +80,16 @@ public class EmbedOptions implements IHook {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomPreferences customPreferences = new CustomPreferences();
|
||||||
|
|
||||||
|
for (LimeOptions.Option option : limeOptions.options) {
|
||||||
|
option.checked = Boolean.parseBoolean(customPreferences.getSetting(option.name, String.valueOf(option.checked)));
|
||||||
|
}
|
||||||
Context moduleContext = AndroidAppHelper.currentApplication().createPackageContext(
|
Context moduleContext = AndroidAppHelper.currentApplication().createPackageContext(
|
||||||
"io.github.hiro.lime", Context.CONTEXT_IGNORE_SECURITY);
|
"io.github.hiro.lime", Context.CONTEXT_IGNORE_SECURITY);
|
||||||
ViewGroup viewGroup = ((ViewGroup) param.args[0]);
|
ViewGroup viewGroup = ((ViewGroup) param.args[0]);
|
||||||
Context context = viewGroup.getContext();
|
Context context = viewGroup.getContext();
|
||||||
Utils.addModuleAssetPath(context);
|
Utils.addModuleAssetPath(context);
|
||||||
SharedPreferences prefs = context.getSharedPreferences(Constants.MODULE_NAME + "-options", Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(context);
|
LinearLayout layout = new LinearLayout(context);
|
||||||
layout.setLayoutParams(new LinearLayout.LayoutParams(
|
layout.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
@ -128,8 +130,7 @@ public class EmbedOptions implements IHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
final String script = new String(Base64.decode(prefs.getString("encoded_js_modify_request", ""), Base64.NO_WRAP));
|
final String script = new String(Base64.decode(customPreferences.getSetting("encoded_js_modify_request", ""), Base64.NO_WRAP));
|
||||||
|
|
||||||
LinearLayout layoutModifyRequest = new LinearLayout(context);
|
LinearLayout layoutModifyRequest = new LinearLayout(context);
|
||||||
layoutModifyRequest.setLayoutParams(new LinearLayout.LayoutParams(
|
layoutModifyRequest.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
@ -264,13 +265,31 @@ public class EmbedOptions implements IHook {
|
|||||||
});
|
});
|
||||||
layout.addView(KeepUnread_Button);
|
layout.addView(KeepUnread_Button);
|
||||||
|
|
||||||
|
if (limeOptions.preventUnsendMessage.checked) {
|
||||||
|
Button canceled_message_Button = new Button(context);
|
||||||
|
canceled_message_Button.setLayoutParams(buttonParams);
|
||||||
|
canceled_message_Button.setText(moduleContext.getResources().getString(R.string.canceled_message));
|
||||||
|
canceled_message_Button.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Cancel_Message_Button(context, moduleContext);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
layout.addView(canceled_message_Button);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.positive_button, new DialogInterface.OnClickListener() {
|
builder.setPositiveButton(R.string.positive_button, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
String code = editText.getText().toString();
|
String code = editText.getText().toString();
|
||||||
if (!code.equals(script)) {
|
if (!code.equals(script)) {
|
||||||
prefs.edit().putString("encoded_js_modify_request", Base64.encodeToString(code.getBytes(), Base64.NO_WRAP)).commit();
|
// CustomPreferencesのインスタンスを作成
|
||||||
|
CustomPreferences customPreferences = new CustomPreferences();
|
||||||
|
// Base64エンコードして設定を保存
|
||||||
|
String encodedCode = Base64.encodeToString(code.getBytes(), Base64.NO_WRAP);
|
||||||
|
customPreferences.saveSetting("encoded_js_modify_request", encodedCode);
|
||||||
|
|
||||||
Toast.makeText(context.getApplicationContext(), context.getString(R.string.restarting), Toast.LENGTH_SHORT).show();
|
Toast.makeText(context.getApplicationContext(), context.getString(R.string.restarting), Toast.LENGTH_SHORT).show();
|
||||||
Process.killProcess(Process.myPid());
|
Process.killProcess(Process.myPid());
|
||||||
context.startActivity(new Intent().setClassName(Constants.PACKAGE_NAME, "jp.naver.line.android.activity.SplashActivity"));
|
context.startActivity(new Intent().setClassName(Constants.PACKAGE_NAME, "jp.naver.line.android.activity.SplashActivity"));
|
||||||
@ -305,8 +324,7 @@ public class EmbedOptions implements IHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
final String script = new String(Base64.decode(prefs.getString("encoded_js_modify_response", ""), Base64.NO_WRAP));
|
final String script = new String(Base64.decode(customPreferences.getSetting("encoded_js_modify_response", ""), Base64.NO_WRAP));
|
||||||
|
|
||||||
LinearLayout layoutModifyResponse = new LinearLayout(context);
|
LinearLayout layoutModifyResponse = new LinearLayout(context);
|
||||||
layoutModifyResponse.setLayoutParams(new LinearLayout.LayoutParams(
|
layoutModifyResponse.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
@ -379,8 +397,7 @@ public class EmbedOptions implements IHook {
|
|||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
String code = editText.getText().toString();
|
String code = editText.getText().toString();
|
||||||
if (!code.equals(script)) {
|
if (!code.equals(script)) {
|
||||||
prefs.edit().putString("encoded_js_modify_response", Base64.encodeToString(code.getBytes(), Base64.NO_WRAP)).commit();
|
customPreferences.saveSetting("encoded_js_modify_response", Base64.encodeToString(code.getBytes(), Base64.NO_WRAP)); Toast.makeText(context.getApplicationContext(), context.getString(R.string.restarting), Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(context.getApplicationContext(), context.getString(R.string.restarting), Toast.LENGTH_SHORT).show();
|
|
||||||
Process.killProcess(Process.myPid());
|
Process.killProcess(Process.myPid());
|
||||||
context.startActivity(new Intent().setClassName(Constants.PACKAGE_NAME, "jp.naver.line.android.activity.SplashActivity"));
|
context.startActivity(new Intent().setClassName(Constants.PACKAGE_NAME, "jp.naver.line.android.activity.SplashActivity"));
|
||||||
}
|
}
|
||||||
@ -427,7 +444,7 @@ public class EmbedOptions implements IHook {
|
|||||||
if (limeOptions.options[i].checked != switchView.isChecked()) {
|
if (limeOptions.options[i].checked != switchView.isChecked()) {
|
||||||
optionChanged = true;
|
optionChanged = true;
|
||||||
}
|
}
|
||||||
prefs.edit().putBoolean(limeOptions.options[i].name, switchView.isChecked()).commit();
|
customPreferences.saveSetting(limeOptions.options[i].name, String.valueOf(switchView.isChecked()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optionChanged) {
|
if (optionChanged) {
|
||||||
@ -489,427 +506,89 @@ public class EmbedOptions implements IHook {
|
|||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
XposedBridge.hookAllMethods(
|
|
||||||
loadPackageParam.classLoader.loadClass("com.linecorp.line.settings.lab.LineUserLabSettingsFragment"),
|
|
||||||
"onViewCreated",
|
|
||||||
new XC_MethodHook() {
|
|
||||||
@Override
|
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
|
||||||
|
|
||||||
Context moduleContext = AndroidAppHelper.currentApplication().createPackageContext(
|
|
||||||
"io.github.hiro.lime", Context.CONTEXT_IGNORE_SECURITY);
|
|
||||||
ViewGroup viewGroup = ((ViewGroup) param.args[0]);
|
|
||||||
Context context = viewGroup.getContext();
|
|
||||||
Utils.addModuleAssetPath(context);
|
|
||||||
SharedPreferences prefs = context.getSharedPreferences(Constants.MODULE_NAME + "-options", Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
LinearLayout layout = new LinearLayout(context);
|
|
||||||
layout.setLayoutParams(new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT));
|
|
||||||
layout.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
layout.setPadding(Utils.dpToPx(20, context), Utils.dpToPx(20, context), Utils.dpToPx(20, context), Utils.dpToPx(20, context));
|
|
||||||
|
|
||||||
Switch switchRedirectWebView = null;
|
|
||||||
for (LimeOptions.Option option : limeOptions.options) {
|
|
||||||
final String name = option.name;
|
|
||||||
|
|
||||||
Switch switchView = new Switch(context);
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
params.topMargin = Utils.dpToPx(20, context);
|
|
||||||
switchView.setLayoutParams(params);
|
|
||||||
switchView.setText(option.id);
|
|
||||||
switchView.setChecked(option.checked);
|
|
||||||
|
|
||||||
if (name.equals("redirect_webview")) switchRedirectWebView = switchView;
|
|
||||||
else if (name.equals("open_in_browser")) {
|
|
||||||
switchRedirectWebView.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
|
||||||
if (isChecked) switchView.setEnabled(true);
|
|
||||||
else {
|
|
||||||
switchView.setChecked(false);
|
|
||||||
switchView.setEnabled(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
switchView.setEnabled(limeOptions.redirectWebView.checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
layout.addView(switchView);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
final String script = new String(Base64.decode(prefs.getString("encoded_js_modify_request", ""), Base64.NO_WRAP));
|
|
||||||
|
|
||||||
LinearLayout layoutModifyRequest = new LinearLayout(context);
|
|
||||||
layoutModifyRequest.setLayoutParams(new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT));
|
|
||||||
layoutModifyRequest.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
layoutModifyRequest.setPadding(Utils.dpToPx(20, context), Utils.dpToPx(20, context), Utils.dpToPx(20, context), Utils.dpToPx(20, context));
|
|
||||||
|
|
||||||
EditText editText = new EditText(context);
|
|
||||||
editText.setLayoutParams(new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT));
|
|
||||||
editText.setTypeface(Typeface.MONOSPACE);
|
|
||||||
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
|
||||||
InputType.TYPE_TEXT_FLAG_MULTI_LINE |
|
|
||||||
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS |
|
|
||||||
InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE);
|
|
||||||
editText.setMovementMethod(new ScrollingMovementMethod());
|
|
||||||
editText.setTextIsSelectable(true);
|
|
||||||
editText.setHorizontallyScrolling(true);
|
|
||||||
editText.setVerticalScrollBarEnabled(true);
|
|
||||||
editText.setHorizontalScrollBarEnabled(true);
|
|
||||||
editText.setText(script);
|
|
||||||
|
|
||||||
layoutModifyRequest.addView(editText);
|
|
||||||
|
|
||||||
LinearLayout buttonLayout = new LinearLayout(context);
|
|
||||||
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
buttonParams.topMargin = Utils.dpToPx(10, context);
|
|
||||||
buttonLayout.setLayoutParams(buttonParams);
|
|
||||||
buttonLayout.setOrientation(LinearLayout.HORIZONTAL);
|
|
||||||
|
|
||||||
Button copyButton = new Button(context);
|
|
||||||
copyButton.setText(R.string.button_copy);
|
|
||||||
copyButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
ClipData clip = ClipData.newPlainText("", editText.getText().toString());
|
|
||||||
clipboard.setPrimaryClip(clip);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
buttonLayout.addView(copyButton);
|
|
||||||
|
|
||||||
Button pasteButton = new Button(context);
|
|
||||||
pasteButton.setText(R.string.button_paste);
|
|
||||||
pasteButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
if (clipboard != null && clipboard.hasPrimaryClip()) {
|
|
||||||
ClipData clip = clipboard.getPrimaryClip();
|
|
||||||
if (clip != null && clip.getItemCount() > 0) {
|
|
||||||
CharSequence pasteData = clip.getItemAt(0).getText();
|
|
||||||
editText.setText(pasteData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
buttonLayout.addView(pasteButton);
|
|
||||||
layoutModifyRequest.addView(buttonLayout);
|
|
||||||
ScrollView scrollView = new ScrollView(context);
|
|
||||||
scrollView.addView(layoutModifyRequest);
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context)
|
|
||||||
.setTitle(R.string.modify_request);
|
|
||||||
builder.setView(scrollView);
|
|
||||||
Button backupButton = new Button(context);
|
|
||||||
buttonParams.topMargin = Utils.dpToPx(20, context);
|
|
||||||
backupButton.setLayoutParams(buttonParams);
|
|
||||||
backupButton.setText(moduleContext.getResources().getString(R.string.Back_Up));
|
|
||||||
backupButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
backupChatHistory(context,moduleContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(backupButton);
|
|
||||||
Button restoreButton = new Button(context);
|
|
||||||
restoreButton.setLayoutParams(buttonParams);
|
|
||||||
restoreButton.setText(moduleContext.getResources().getString(R.string.Restore));
|
|
||||||
restoreButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
restoreChatHistory(context,moduleContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(restoreButton);
|
|
||||||
Button backupfolderButton = new Button(context);
|
|
||||||
backupfolderButton.setLayoutParams(buttonParams);
|
|
||||||
backupfolderButton.setText(moduleContext.getResources().getString(R.string.Talk_Picture_Back_up));
|
|
||||||
backupfolderButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
backupChatsFolder(context,moduleContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(backupfolderButton);
|
|
||||||
Button restorefolderButton = new Button(context);
|
|
||||||
restorefolderButton.setLayoutParams(buttonParams);
|
|
||||||
restorefolderButton.setText(moduleContext.getResources().getString(R.string.Picure_Restore));
|
|
||||||
restorefolderButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
restoreChatsFolder(context,moduleContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(restorefolderButton);
|
|
||||||
if (limeOptions.MuteGroup.checked) {
|
|
||||||
Button MuteGroups_Button = new Button(context);
|
|
||||||
MuteGroups_Button.setLayoutParams(buttonParams);
|
|
||||||
MuteGroups_Button.setText(moduleContext.getResources().getString(R.string.Mute_Group));
|
|
||||||
MuteGroups_Button.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
MuteGroups_Button(context,moduleContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(MuteGroups_Button);
|
|
||||||
}
|
|
||||||
|
|
||||||
Button KeepUnread_Button = new Button(context);
|
|
||||||
KeepUnread_Button.setLayoutParams(buttonParams);
|
|
||||||
KeepUnread_Button.setText(moduleContext.getResources().getString(R.string.edit_margin_settings));
|
|
||||||
KeepUnread_Button.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
KeepUnread_Button(context,moduleContext);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(KeepUnread_Button);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.positive_button, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
String code = editText.getText().toString();
|
|
||||||
if (!code.equals(script)) {
|
|
||||||
prefs.edit().putString("encoded_js_modify_request", Base64.encodeToString(code.getBytes(), Base64.NO_WRAP)).commit();
|
|
||||||
Toast.makeText(context.getApplicationContext(), context.getString(R.string.restarting), Toast.LENGTH_SHORT).show();
|
|
||||||
Process.killProcess(Process.myPid());
|
|
||||||
context.startActivity(new Intent().setClassName(Constants.PACKAGE_NAME, "jp.naver.line.android.activity.SplashActivity"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton(R.string.negative_button, null);
|
}
|
||||||
|
|
||||||
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
private void Cancel_Message_Button(Context context, Context moduleContext) {
|
||||||
@Override
|
// フォルダのパスを設定
|
||||||
public void onDismiss(DialogInterface dialog) {
|
File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "LimeBackup/Setting");
|
||||||
editText.setText(script);
|
if (!dir.exists()) {
|
||||||
}
|
if (!dir.mkdirs()) {
|
||||||
});
|
Toast.makeText(context, "Failed to create directory", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AlertDialog dialog = builder.create();
|
// ファイルのパスを設定
|
||||||
Button button = new Button(context);
|
File file = new File(dir, "canceled_message.txt");
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
params.topMargin = Utils.dpToPx(20, context);
|
|
||||||
button.setLayoutParams(params);
|
|
||||||
button.setText(R.string.modify_request);
|
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
layout.addView(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
// ファイルが存在しない場合、デフォルトのメッセージを設定
|
||||||
final String script = new String(Base64.decode(prefs.getString("encoded_js_modify_response", ""), Base64.NO_WRAP));
|
if (!file.exists()) {
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
|
||||||
|
String defaultMessage = moduleContext.getResources().getString(R.string.canceled_message_txt);
|
||||||
|
writer.write(defaultMessage);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Toast.makeText(context, "Failed to create file: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LinearLayout layoutModifyResponse = new LinearLayout(context);
|
// ファイルの内容を読み取る
|
||||||
layoutModifyResponse.setLayoutParams(new LinearLayout.LayoutParams(
|
StringBuilder fileContent = new StringBuilder();
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
if (file.exists()) {
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT));
|
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||||
layoutModifyResponse.setOrientation(LinearLayout.VERTICAL);
|
String line;
|
||||||
layoutModifyResponse.setPadding(Utils.dpToPx(20, context), Utils.dpToPx(20, context), Utils.dpToPx(20, context), Utils.dpToPx(20, context));
|
while ((line = reader.readLine()) != null) {
|
||||||
EditText editText = new EditText(context);
|
fileContent.append(line).append("\n");
|
||||||
editText.setLayoutParams(new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT));
|
|
||||||
editText.setTypeface(Typeface.MONOSPACE);
|
|
||||||
editText.setInputType(InputType.TYPE_CLASS_TEXT |
|
|
||||||
InputType.TYPE_TEXT_FLAG_MULTI_LINE |
|
|
||||||
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS |
|
|
||||||
InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE);
|
|
||||||
editText.setMovementMethod(new ScrollingMovementMethod());
|
|
||||||
editText.setTextIsSelectable(true);
|
|
||||||
editText.setHorizontallyScrolling(true);
|
|
||||||
editText.setVerticalScrollBarEnabled(true);
|
|
||||||
editText.setHorizontalScrollBarEnabled(true);
|
|
||||||
editText.setText(script);
|
|
||||||
layoutModifyResponse.addView(editText);
|
|
||||||
LinearLayout buttonLayout = new LinearLayout(context);
|
|
||||||
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
buttonParams.topMargin = Utils.dpToPx(10, context);
|
|
||||||
buttonLayout.setLayoutParams(buttonParams);
|
|
||||||
buttonLayout.setOrientation(LinearLayout.HORIZONTAL);
|
|
||||||
|
|
||||||
Button copyButton = new Button(context);
|
|
||||||
copyButton.setText(R.string.button_copy);
|
|
||||||
copyButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
ClipData clip = ClipData.newPlainText("", editText.getText().toString());
|
|
||||||
clipboard.setPrimaryClip(clip);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
buttonLayout.addView(copyButton);
|
|
||||||
|
|
||||||
Button pasteButton = new Button(context);
|
|
||||||
pasteButton.setText(R.string.button_paste);
|
|
||||||
pasteButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
||||||
if (clipboard != null && clipboard.hasPrimaryClip()) {
|
|
||||||
ClipData clip = clipboard.getPrimaryClip();
|
|
||||||
if (clip != null && clip.getItemCount() > 0) {
|
|
||||||
CharSequence pasteData = clip.getItemAt(0).getText();
|
|
||||||
editText.setText(pasteData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
buttonLayout.addView(pasteButton);
|
|
||||||
layoutModifyResponse.addView(buttonLayout);
|
|
||||||
ScrollView scrollView = new ScrollView(context);
|
|
||||||
scrollView.addView(layoutModifyResponse);
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context)
|
|
||||||
.setTitle(R.string.modify_response);
|
|
||||||
builder.setView(scrollView);
|
|
||||||
builder.setPositiveButton(R.string.positive_button, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
String code = editText.getText().toString();
|
|
||||||
if (!code.equals(script)) {
|
|
||||||
prefs.edit().putString("encoded_js_modify_response", Base64.encodeToString(code.getBytes(), Base64.NO_WRAP)).commit();
|
|
||||||
Toast.makeText(context.getApplicationContext(), context.getString(R.string.restarting), Toast.LENGTH_SHORT).show();
|
|
||||||
Process.killProcess(Process.myPid());
|
|
||||||
context.startActivity(new Intent().setClassName(Constants.PACKAGE_NAME, "jp.naver.line.android.activity.SplashActivity"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton(R.string.negative_button, null);
|
|
||||||
|
|
||||||
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
|
||||||
@Override
|
|
||||||
public void onDismiss(DialogInterface dialog) {
|
|
||||||
editText.setText(script);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
AlertDialog dialog = builder.create();
|
|
||||||
Button button = new Button(context);
|
|
||||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
params.topMargin = Utils.dpToPx(20, context);
|
|
||||||
button.setLayoutParams(params);
|
|
||||||
button.setText(R.string.modify_response);
|
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
layout.addView(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollView scrollView = new ScrollView(context);
|
|
||||||
scrollView.addView(layout);
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context)
|
|
||||||
.setTitle(R.string.options_title);
|
|
||||||
builder.setView(scrollView);
|
|
||||||
builder.setPositiveButton(R.string.positive_button, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
boolean optionChanged = false;
|
|
||||||
for (int i = 0; i < limeOptions.options.length; ++i) {
|
|
||||||
Switch switchView = (Switch) layout.getChildAt(i);
|
|
||||||
if (limeOptions.options[i].checked != switchView.isChecked()) {
|
|
||||||
optionChanged = true;
|
|
||||||
}
|
|
||||||
prefs.edit().putBoolean(limeOptions.options[i].name, switchView.isChecked()).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optionChanged) {
|
|
||||||
Toast.makeText(context.getApplicationContext(), context.getString(R.string.restarting), Toast.LENGTH_SHORT).show();
|
|
||||||
Process.killProcess(Process.myPid());
|
|
||||||
context.startActivity(new Intent().setClassName(Constants.PACKAGE_NAME, "jp.naver.line.android.activity.SplashActivity"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton(R.string.negative_button, null);
|
|
||||||
|
|
||||||
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
|
||||||
@Override
|
|
||||||
public void onDismiss(DialogInterface dialog) {
|
|
||||||
for (int i = 0; i < limeOptions.options.length; ++i) {
|
|
||||||
Switch switchView = (Switch) layout.getChildAt(i);
|
|
||||||
switchView.setChecked(limeOptions.options[i].checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
AlertDialog dialog = builder.create();
|
|
||||||
|
|
||||||
// 新しい項目ボタンを作成
|
|
||||||
android.view.View view = (android.view.View) param.args[0];
|
|
||||||
android.widget.Button newButton = new android.widget.Button(context);
|
|
||||||
newButton.setText(R.string.app_name); // ボタンのテキストを設定
|
|
||||||
|
|
||||||
// ボタンのレイアウトパラメータを設定
|
|
||||||
android.widget.FrameLayout.LayoutParams layoutParams = new android.widget.FrameLayout.LayoutParams(
|
|
||||||
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT, // 幅
|
|
||||||
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT // 高さ
|
|
||||||
);
|
|
||||||
layoutParams.gravity = Gravity.BOTTOM | Gravity.END; // 右下に配置
|
|
||||||
layoutParams.bottomMargin = Utils.dpToPx(16, context); // 下マージンを16dpに設定
|
|
||||||
layoutParams.rightMargin = Utils.dpToPx(16, context); // 右マージンを16dpに設定
|
|
||||||
|
|
||||||
newButton.setLayoutParams(layoutParams);
|
|
||||||
|
|
||||||
// ボタンのクリックリスナーを設定
|
|
||||||
newButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
dialog.show(); // ボタンクリックで AlertDialog を表示
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ボタンをビューに追加
|
|
||||||
if (view instanceof android.view.ViewGroup) {
|
|
||||||
android.view.ViewGroup viewGroup1 = (android.view.ViewGroup) view;
|
|
||||||
|
|
||||||
// FrameLayout を作成してボタンを追加
|
|
||||||
android.widget.FrameLayout frameLayout = new android.widget.FrameLayout(context);
|
|
||||||
frameLayout.setLayoutParams(new android.view.ViewGroup.LayoutParams(
|
|
||||||
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
||||||
));
|
|
||||||
frameLayout.addView(newButton);
|
|
||||||
|
|
||||||
// FrameLayout を親ビューに追加
|
|
||||||
viewGroup1.addView(frameLayout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Toast.makeText(context, "Failed to read file: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
);
|
// EditText の設定
|
||||||
|
final EditText editText = new EditText(context);
|
||||||
|
editText.setText(fileContent.toString().trim()); // 不要な改行を削除
|
||||||
|
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||||
|
editText.setMinLines(10);
|
||||||
|
editText.setGravity(Gravity.TOP);
|
||||||
|
|
||||||
|
// Save ボタンの設定
|
||||||
|
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
buttonParams.setMargins(16, 16, 16, 16);
|
||||||
|
|
||||||
|
Button saveButton = new Button(context);
|
||||||
|
saveButton.setText(moduleContext.getResources().getString(R.string.options_title)); // リソースからテキストを取得
|
||||||
|
saveButton.setLayoutParams(buttonParams);
|
||||||
|
saveButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
|
||||||
|
writer.write(editText.getText().toString());
|
||||||
|
Toast.makeText(context, "Saved successfully", Toast.LENGTH_SHORT).show();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Toast.makeText(context, "Failed to save file: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// レイアウトの設定
|
||||||
|
LinearLayout layout = new LinearLayout(context);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
layout.addView(editText);
|
||||||
|
layout.addView(saveButton);
|
||||||
|
|
||||||
|
// AlertDialog の設定
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||||
|
builder.setTitle(moduleContext.getResources().getString(R.string.canceled_message)); // リソースからタイトルを取得
|
||||||
|
builder.setView(layout);
|
||||||
|
builder.setNegativeButton(moduleContext.getResources().getString(R.string.cancel), null); // リソースからキャンセルボタンのテキストを取得
|
||||||
|
builder.show();
|
||||||
}
|
}
|
||||||
public int getStatusBarHeight(Context context) {
|
public int getStatusBarHeight(Context context) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
@ -21,7 +21,8 @@ public class ModifyRequest implements IHook {
|
|||||||
new XC_MethodHook() {
|
new XC_MethodHook() {
|
||||||
@Override
|
@Override
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
final String script = new String(Base64.decode(Main.xPrefs.getString("encoded_js_modify_request", ""), Base64.NO_WRAP));
|
CustomPreferences customPreferences = new CustomPreferences();
|
||||||
|
final String script = new String(Base64.decode(customPreferences.getSetting("encoded_js_modify_request", ""), Base64.NO_WRAP));
|
||||||
Context ctx = Context.enter();
|
Context ctx = Context.enter();
|
||||||
ctx.setOptimizationLevel(-1);
|
ctx.setOptimizationLevel(-1);
|
||||||
try {
|
try {
|
||||||
|
@ -21,14 +21,20 @@ public class ModifyResponse implements IHook {
|
|||||||
new XC_MethodHook() {
|
new XC_MethodHook() {
|
||||||
@Override
|
@Override
|
||||||
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
final String script = new String(Base64.decode(Main.xPrefs.getString("encoded_js_modify_response", ""), Base64.NO_WRAP));
|
CustomPreferences customPreferences = new CustomPreferences();
|
||||||
|
|
||||||
|
final String script = new String(Base64.decode(customPreferences.getSetting("encoded_js_modify_response", ""), Base64.NO_WRAP));
|
||||||
|
|
||||||
Context ctx = Context.enter();
|
Context ctx = Context.enter();
|
||||||
ctx.setOptimizationLevel(-1);
|
ctx.setOptimizationLevel(-1); // 最適化レベルを無効化
|
||||||
try {
|
try {
|
||||||
Scriptable scope = ctx.initStandardObjects();
|
Scriptable scope = ctx.initStandardObjects();
|
||||||
|
|
||||||
Object jsData = Context.javaToJS(new Communication(Communication.Type.RESPONSE, param.args[0].toString(), param.args[1]), scope);
|
Object jsData = Context.javaToJS(new Communication(Communication.Type.RESPONSE, param.args[0].toString(), param.args[1]), scope);
|
||||||
|
|
||||||
ScriptableObject.putProperty(scope, "data", jsData);
|
ScriptableObject.putProperty(scope, "data", jsData);
|
||||||
ScriptableObject.putProperty(scope, "console", Context.javaToJS(new Console(), scope));
|
ScriptableObject.putProperty(scope, "console", Context.javaToJS(new Console(), scope));
|
||||||
|
|
||||||
ctx.evaluateString(scope, script, "Script", 1, null);
|
ctx.evaluateString(scope, script, "Script", 1, null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
XposedBridge.log(e.toString());
|
XposedBridge.log(e.toString());
|
||||||
|
@ -3,11 +3,11 @@ package io.github.hiro.lime.hooks;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.customtabs.CustomTabsIntent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
import androidx.browser.customtabs.CustomTabsIntent;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XC_MethodHook;
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
|
@ -7,13 +7,21 @@ import de.robv.android.xposed.XC_MethodHook;
|
|||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
import io.github.hiro.lime.LimeOptions;
|
import io.github.hiro.lime.LimeOptions;
|
||||||
import io.github.hiro.lime.Main;
|
|
||||||
|
|
||||||
public class SpoofAndroidId implements IHook {
|
public class SpoofAndroidId implements IHook {
|
||||||
@Override
|
@Override
|
||||||
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||||
if (!Main.xPackagePrefs.getBoolean("spoof_android_id", false)) return;
|
// CustomPreferences を初期化
|
||||||
|
io.github.hiro.lime.hooks.CustomPreferences customPreferences = new io.github.hiro.lime.hooks.CustomPreferences();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 設定を確認
|
||||||
|
if (!Boolean.parseBoolean(customPreferences.getSetting("spoof_android_id", "false"))) {
|
||||||
|
return; // 設定が無効な場合は何もしない
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings.Secure.getString メソッドをフック
|
||||||
XposedHelpers.findAndHookMethod(
|
XposedHelpers.findAndHookMethod(
|
||||||
Settings.Secure.class,
|
Settings.Secure.class,
|
||||||
"getString",
|
"getString",
|
||||||
@ -22,11 +30,13 @@ public class SpoofAndroidId implements IHook {
|
|||||||
new XC_MethodHook() {
|
new XC_MethodHook() {
|
||||||
@Override
|
@Override
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
|
// ANDROID_ID が要求された場合
|
||||||
if (param.args[1].toString().equals(Settings.Secure.ANDROID_ID)) {
|
if (param.args[1].toString().equals(Settings.Secure.ANDROID_ID)) {
|
||||||
|
// 偽の ANDROID_ID を返す
|
||||||
param.setResult("0000000000000000");
|
param.setResult("0000000000000000");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,28 @@
|
|||||||
package io.github.hiro.lime.hooks;
|
package io.github.hiro.lime.hooks;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XC_MethodHook;
|
import de.robv.android.xposed.XC_MethodHook;
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
import de.robv.android.xposed.callbacks.XC_LoadPackage;
|
||||||
import io.github.hiro.lime.LimeOptions;
|
import io.github.hiro.lime.LimeOptions;
|
||||||
import io.github.hiro.lime.Main;
|
|
||||||
|
|
||||||
public class SpoofUserAgent implements IHook {
|
public class SpoofUserAgent implements IHook {
|
||||||
private boolean hasLoggedSpoofedUserAgent = false;
|
private boolean hasLoggedSpoofedUserAgent = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
public void hook(LimeOptions limeOptions, XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
|
||||||
if (!Main.xPackagePrefs.getBoolean("android_secondary", false)) return;
|
// CustomPreferences を初期化
|
||||||
|
io.github.hiro.lime.hooks.CustomPreferences customPreferences = new io.github.hiro.lime.hooks.CustomPreferences();
|
||||||
|
|
||||||
|
|
||||||
|
// 設定を確認
|
||||||
|
if (!Boolean.parseBoolean(customPreferences.getSetting("android_secondary", "false"))) {
|
||||||
|
return; // 設定が無効な場合は何もしない
|
||||||
|
}
|
||||||
|
|
||||||
|
// ターゲットメソッドをフック
|
||||||
XposedHelpers.findAndHookMethod(
|
XposedHelpers.findAndHookMethod(
|
||||||
loadPackageParam.classLoader.loadClass(Constants.USER_AGENT_HOOK.className),
|
loadPackageParam.classLoader.loadClass(Constants.USER_AGENT_HOOK.className),
|
||||||
Constants.USER_AGENT_HOOK.methodName,
|
Constants.USER_AGENT_HOOK.methodName,
|
||||||
@ -24,16 +30,17 @@ public class SpoofUserAgent implements IHook {
|
|||||||
new XC_MethodHook() {
|
new XC_MethodHook() {
|
||||||
@Override
|
@Override
|
||||||
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
SharedPreferences prefs = Main.xPackagePrefs;
|
// 設定から値を取得
|
||||||
|
String device = customPreferences.getSetting("device_name", "ANDROID");
|
||||||
String device = prefs.getString("device_name", "ANDROID");
|
String androidVersion = customPreferences.getSetting("android_version", "14.16.0");
|
||||||
String androidVersion = prefs.getString("android_version", "14.16.0");
|
String osName = customPreferences.getSetting("os_name", "Android OS");
|
||||||
String osName = prefs.getString("os_name", "Android OS");
|
String osVersion = customPreferences.getSetting("os_version", "14");
|
||||||
String osVersion = prefs.getString("os_version", "14");
|
|
||||||
|
|
||||||
|
// 偽の User-Agent を生成
|
||||||
String spoofedUserAgent = device + "\t" + androidVersion + "\t" + osName + "\t" + osVersion;
|
String spoofedUserAgent = device + "\t" + androidVersion + "\t" + osName + "\t" + osVersion;
|
||||||
param.setResult(spoofedUserAgent);
|
param.setResult(spoofedUserAgent);
|
||||||
|
|
||||||
|
// ログに出力(初回のみ)
|
||||||
if (!hasLoggedSpoofedUserAgent) {
|
if (!hasLoggedSpoofedUserAgent) {
|
||||||
XposedBridge.log("Spoofed User-Agent: " + spoofedUserAgent);
|
XposedBridge.log("Spoofed User-Agent: " + spoofedUserAgent);
|
||||||
hasLoggedSpoofedUserAgent = true;
|
hasLoggedSpoofedUserAgent = true;
|
||||||
@ -42,4 +49,4 @@ public class SpoofUserAgent implements IHook {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,18 @@
|
|||||||
package io.github.hiro.lime.hooks;
|
package io.github.hiro.lime.hooks;
|
||||||
|
|
||||||
|
import static io.github.hiro.lime.Main.limeOptions;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.AndroidAppHelper;
|
import android.app.AndroidAppHelper;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.os.Environment;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -24,6 +28,7 @@ import java.io.BufferedReader;
|
|||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -136,7 +141,7 @@ public class UnsentRec implements IHook {
|
|||||||
writer.close();
|
writer.close();
|
||||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_content_deleted), Toast.LENGTH_SHORT).show();
|
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_content_deleted), Toast.LENGTH_SHORT).show();
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
|
|
||||||
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_delete_failed), Toast.LENGTH_SHORT).show();
|
Toast.makeText(appContext, moduleContext.getResources().getString(R.string.file_delete_failed), Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -168,7 +173,7 @@ public class UnsentRec implements IHook {
|
|||||||
try {
|
try {
|
||||||
originalFile.createNewFile();
|
originalFile.createNewFile();
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
|
|
||||||
Toast.makeText(context, moduleContext.getResources().getString(R.string.file_creation_failed), Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, moduleContext.getResources().getString(R.string.file_creation_failed), Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -298,27 +303,40 @@ public class UnsentRec implements IHook {
|
|||||||
SQLiteDatabase db2 = SQLiteDatabase.openDatabase(dbFile2, dbParams2);
|
SQLiteDatabase db2 = SQLiteDatabase.openDatabase(dbFile2, dbParams2);
|
||||||
|
|
||||||
|
|
||||||
hookMessageDeletion(loadPackageParam, appContext, db1, db2);
|
hookMessageDeletion(loadPackageParam, appContext, db1, db2,moduleContext);
|
||||||
resolveUnresolvedIds(loadPackageParam, appContext, db1, db2, moduleContext);
|
resolveUnresolvedIds(loadPackageParam, appContext, db1, db2, moduleContext);
|
||||||
|
|
||||||
|
canceled_message(loadPackageParam, appContext, db1, db2, moduleContext);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String queryDatabase(SQLiteDatabase db, String query, String... selectionArgs) {
|
|
||||||
|
|
||||||
|
private String queryDatabase(SQLiteDatabase db, String query, String... selectionArgs) {
|
||||||
if (db == null) {
|
if (db == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Cursor cursor = db.rawQuery(query, selectionArgs);
|
Cursor cursor = db.rawQuery(query, selectionArgs);
|
||||||
String result = null;
|
String result = null;
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
result = cursor.getString(0);
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
result = cursor.getString(0);
|
||||||
|
XposedBridge.log("Query Result: " + result); // Log each result found
|
||||||
|
} while (cursor.moveToNext()); // In case there are multiple rows
|
||||||
|
} else {
|
||||||
|
XposedBridge.log("No rows found for query: " + query);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.close();
|
cursor.close();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int countLinesInFile(File file) {
|
private int countLinesInFile(File file) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
|
||||||
@ -334,7 +352,7 @@ public class UnsentRec implements IHook {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hookMessageDeletion(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db1, SQLiteDatabase db2) {
|
private void hookMessageDeletion(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db1, SQLiteDatabase db2,Context moduleContext) {
|
||||||
try {
|
try {
|
||||||
XposedBridge.hookAllMethods(
|
XposedBridge.hookAllMethods(
|
||||||
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
|
loadPackageParam.classLoader.loadClass(Constants.RESPONSE_HOOK.className),
|
||||||
@ -355,7 +373,6 @@ public class UnsentRec implements IHook {
|
|||||||
} catch (ClassNotFoundException ignored) {
|
} catch (ClassNotFoundException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMessage(String paramValue, Context moduleContext, SQLiteDatabase db1, SQLiteDatabase db2, Context context) {
|
private void processMessage(String paramValue, Context moduleContext, SQLiteDatabase db1, SQLiteDatabase db2, Context context) {
|
||||||
String unresolvedFilePath = context.getFilesDir() + "/UnresolvedIds.txt";
|
String unresolvedFilePath = context.getFilesDir() + "/UnresolvedIds.txt";
|
||||||
|
|
||||||
@ -398,6 +415,9 @@ public class UnsentRec implements IHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (serverId != null && talkId != null) {
|
if (serverId != null && talkId != null) {
|
||||||
|
// 新しいメソッドを呼び出して、メッセージを取り消し済みとして更新
|
||||||
|
updateMessageAsCanceled(db1, serverId,context,moduleContext);
|
||||||
|
|
||||||
String content = queryDatabase(db1, "SELECT content FROM chat_history WHERE server_id=?", serverId);
|
String content = queryDatabase(db1, "SELECT content FROM chat_history WHERE server_id=?", serverId);
|
||||||
String imageCheck = queryDatabase(db1, "SELECT attachement_image FROM chat_history WHERE server_id=?", serverId);
|
String imageCheck = queryDatabase(db1, "SELECT attachement_image FROM chat_history WHERE server_id=?", serverId);
|
||||||
String timeEpochStr = queryDatabase(db1, "SELECT created_time FROM chat_history WHERE server_id=?", serverId);
|
String timeEpochStr = queryDatabase(db1, "SELECT created_time FROM chat_history WHERE server_id=?", serverId);
|
||||||
@ -460,10 +480,163 @@ public class UnsentRec implements IHook {
|
|||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void canceled_message(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db1, SQLiteDatabase db2, Context moduleContext) {
|
||||||
|
Class<?> chatHistoryRequestClass = XposedHelpers.findClass("com.linecorp.line.chat.request.ChatHistoryRequest", loadPackageParam.classLoader);
|
||||||
|
XposedHelpers.findAndHookMethod(chatHistoryRequestClass, "getChatId", new XC_MethodHook() {
|
||||||
|
@Override
|
||||||
|
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
|
||||||
|
String chatId = (String) param.getResult();
|
||||||
|
|
||||||
|
// チャット ID をログに出力
|
||||||
|
XposedBridge.log("Chat ID: " + chatId);
|
||||||
|
String canceledContent = getCanceledContentFromFile(context, moduleContext);
|
||||||
|
// チェックボックスの状態を確認
|
||||||
|
if (limeOptions.hide_canceled_message.checked) {
|
||||||
|
XposedBridge.log("hide_canceled_message is checked");
|
||||||
|
|
||||||
|
// content と server_id, chat_id を取得 (複数レコードを想定)
|
||||||
|
Cursor cursor = db1.rawQuery("SELECT content, server_id, chat_id FROM chat_history WHERE chat_id=?", new String[]{chatId});
|
||||||
|
if (cursor != null) {
|
||||||
|
try {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String content = cursor.getString(0);
|
||||||
|
String serverId = cursor.getString(1);
|
||||||
|
String currentChatId = cursor.getString(2);
|
||||||
|
|
||||||
|
XposedBridge.log("Content: " + content);
|
||||||
|
XposedBridge.log("Server ID: " + serverId);
|
||||||
|
XposedBridge.log("Current Chat ID: " + currentChatId);
|
||||||
|
|
||||||
|
// content が「削除されたメッセージです」の場合のみ処理
|
||||||
|
if (content != null && content.contains(canceledContent)) {
|
||||||
|
if (currentChatId != null && !currentChatId.startsWith("/")) {
|
||||||
|
// chat_id の先頭に "/" を付ける
|
||||||
|
String updatedChatId = "/" + currentChatId;
|
||||||
|
|
||||||
|
// 更新クエリを実行 (content が「削除されたメッセージです」の場合のみ)
|
||||||
|
db1.execSQL("UPDATE chat_history SET chat_id=? WHERE chat_id=? AND server_id=? AND content=?",
|
||||||
|
new Object[]{updatedChatId, currentChatId, serverId, content});
|
||||||
|
XposedBridge.log("Updated Chat ID: " + updatedChatId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XposedBridge.log("No rows found for chat_id: " + chatId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XposedBridge.log("hide_canceled_message is NOT checked");
|
||||||
|
|
||||||
|
// hide_canceled_message.checked が false の場合
|
||||||
|
String chatId1 = "/"+(String) param.getResult();
|
||||||
|
Cursor cursor = db1.rawQuery("SELECT content, server_id, chat_id FROM chat_history WHERE chat_id=?", new String[]{chatId1});
|
||||||
|
if (cursor != null) {
|
||||||
|
try {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String content = cursor.getString(0);
|
||||||
|
String serverId = cursor.getString(1);
|
||||||
|
String currentChatId = cursor.getString(2);
|
||||||
|
|
||||||
|
XposedBridge.log("Content: " + content);
|
||||||
|
XposedBridge.log("Server ID: " + serverId);
|
||||||
|
XposedBridge.log("Current Chat ID: " + currentChatId);
|
||||||
|
|
||||||
|
// content が「削除されたメッセージです」の場合のみ処理
|
||||||
|
if (content != null && content.contains(canceledContent)) {
|
||||||
|
if (currentChatId != null && currentChatId.startsWith("/")) {
|
||||||
|
// chat_id から "/" を除外する
|
||||||
|
String updatedChatId = currentChatId.substring(1);
|
||||||
|
|
||||||
|
// 更新クエリを実行 (content が「削除されたメッセージです」の場合のみ)
|
||||||
|
db1.execSQL("UPDATE chat_history SET chat_id=? WHERE chat_id=? AND server_id=? AND content=?",
|
||||||
|
new Object[]{updatedChatId, currentChatId, serverId, content});
|
||||||
|
XposedBridge.log("Updated Chat ID: " + updatedChatId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XposedBridge.log("No rows found for chat_id: " + chatId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMessageAsCanceled(SQLiteDatabase db1, String serverId, Context context, Context moduleContext) {
|
||||||
|
// canceledContent をファイルから取得
|
||||||
|
String canceledContent = getCanceledContentFromFile(context, moduleContext);
|
||||||
|
|
||||||
|
// 既存のメッセージを取得
|
||||||
|
Cursor cursor = db1.rawQuery("SELECT * FROM chat_history WHERE server_id=?", new String[]{serverId});
|
||||||
|
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
// カラムの値を取得(null の場合もそのまま代入)
|
||||||
|
String type = cursor.isNull(cursor.getColumnIndex("type")) ? null : cursor.getString(cursor.getColumnIndex("type"));
|
||||||
|
String chatId = cursor.isNull(cursor.getColumnIndex("chat_id")) ? null : cursor.getString(cursor.getColumnIndex("chat_id"));
|
||||||
|
String fromMid = cursor.isNull(cursor.getColumnIndex("from_mid")) ? null : cursor.getString(cursor.getColumnIndex("from_mid"));
|
||||||
|
String createdTime = cursor.isNull(cursor.getColumnIndex("created_time")) ? null : cursor.getString(cursor.getColumnIndex("created_time"));
|
||||||
|
String deliveredTime = cursor.isNull(cursor.getColumnIndex("delivered_time")) ? null : cursor.getString(cursor.getColumnIndex("delivered_time"));
|
||||||
|
String status = cursor.isNull(cursor.getColumnIndex("status")) ? null : cursor.getString(cursor.getColumnIndex("status"));
|
||||||
|
Integer sentCount = cursor.isNull(cursor.getColumnIndex("sent_count")) ? null : cursor.getInt(cursor.getColumnIndex("sent_count"));
|
||||||
|
Integer readCount = cursor.isNull(cursor.getColumnIndex("read_count")) ? null : cursor.getInt(cursor.getColumnIndex("read_count"));
|
||||||
|
String locationName = cursor.isNull(cursor.getColumnIndex("location_name")) ? null : cursor.getString(cursor.getColumnIndex("location_name"));
|
||||||
|
String locationAddress = cursor.isNull(cursor.getColumnIndex("location_address")) ? null : cursor.getString(cursor.getColumnIndex("location_address"));
|
||||||
|
String locationPhone = cursor.isNull(cursor.getColumnIndex("location_phone")) ? null : cursor.getString(cursor.getColumnIndex("location_phone"));
|
||||||
|
Double locationLatitude = cursor.isNull(cursor.getColumnIndex("location_latitude")) ? null : cursor.getDouble(cursor.getColumnIndex("location_latitude"));
|
||||||
|
Double locationLongitude = cursor.isNull(cursor.getColumnIndex("location_longitude")) ? null : cursor.getDouble(cursor.getColumnIndex("location_longitude"));
|
||||||
|
String attachmentImage = cursor.isNull(cursor.getColumnIndex("attachement_image")) ? null : cursor.getString(cursor.getColumnIndex("attachement_image"));
|
||||||
|
Integer attachmentImageHeight = cursor.isNull(cursor.getColumnIndex("attachement_image_height")) ? null : cursor.getInt(cursor.getColumnIndex("attachement_image_height"));
|
||||||
|
Integer attachmentImageWidth = cursor.isNull(cursor.getColumnIndex("attachement_image_width")) ? null : cursor.getInt(cursor.getColumnIndex("attachement_image_width"));
|
||||||
|
Integer attachmentImageSize = cursor.isNull(cursor.getColumnIndex("attachement_image_size")) ? null : cursor.getInt(cursor.getColumnIndex("attachement_image_size"));
|
||||||
|
String attachmentType = cursor.isNull(cursor.getColumnIndex("attachement_type")) ? null : cursor.getString(cursor.getColumnIndex("attachement_type"));
|
||||||
|
String attachmentLocalUri = cursor.isNull(cursor.getColumnIndex("attachement_local_uri")) ? null : cursor.getString(cursor.getColumnIndex("attachement_local_uri"));
|
||||||
|
String parameter = cursor.isNull(cursor.getColumnIndex("parameter")) ? null : cursor.getString(cursor.getColumnIndex("parameter"));
|
||||||
|
String chunks = cursor.isNull(cursor.getColumnIndex("chunks")) ? null : cursor.getString(cursor.getColumnIndex("chunks"));
|
||||||
|
|
||||||
|
// 既存のレコードがあるか確認
|
||||||
|
Cursor existingCursor = db1.rawQuery("SELECT * FROM chat_history WHERE server_id=? AND content=?", new String[]{serverId, chatId});
|
||||||
|
if (!existingCursor.moveToFirst()) {
|
||||||
|
// 新しいレコードを挿入
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("server_id", serverId);
|
||||||
|
values.put("type", type);
|
||||||
|
values.put("chat_id", chatId);
|
||||||
|
values.put("from_mid", fromMid);
|
||||||
|
values.put("content", canceledContent); // ファイルから取得した内容に変更
|
||||||
|
values.put("created_time", createdTime);
|
||||||
|
values.put("delivered_time", deliveredTime);
|
||||||
|
values.put("status", status);
|
||||||
|
if (sentCount != null) values.put("sent_count", sentCount);
|
||||||
|
if (readCount != null) values.put("read_count", readCount);
|
||||||
|
values.put("location_name", locationName);
|
||||||
|
values.put("location_address", locationAddress);
|
||||||
|
values.put("location_phone", locationPhone);
|
||||||
|
if (locationLatitude != null) values.put("location_latitude", locationLatitude);
|
||||||
|
if (locationLongitude != null) values.put("location_longitude", locationLongitude);
|
||||||
|
values.put("attachement_image", attachmentImage);
|
||||||
|
if (attachmentImageHeight != null) values.put("attachement_image_height", attachmentImageHeight);
|
||||||
|
if (attachmentImageWidth != null) values.put("attachement_image_width", attachmentImageWidth);
|
||||||
|
if (attachmentImageSize != null) values.put("attachement_image_size", attachmentImageSize);
|
||||||
|
values.put("attachement_type", attachmentType);
|
||||||
|
values.put("attachement_local_uri", attachmentLocalUri);
|
||||||
|
values.put("parameter", parameter);
|
||||||
|
values.put("chunks", chunks);
|
||||||
|
|
||||||
|
db1.insert("chat_history", null, values);
|
||||||
|
}
|
||||||
|
existingCursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
private void saveUnresolvedIds(String serverId, String talkId, String filePath) {
|
private void saveUnresolvedIds(String serverId, String talkId, String filePath) {
|
||||||
String newEntry = "serverId:" + serverId + ",talkId:" + talkId;
|
String newEntry = "serverId:" + serverId + ",talkId:" + talkId;
|
||||||
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
|
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
|
||||||
@ -483,7 +656,45 @@ public class UnsentRec implements IHook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getCanceledContentFromFile(Context context,Context moduleContext) {
|
||||||
|
// フォルダのパスを設定
|
||||||
|
File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "LimeBackup/Setting");
|
||||||
|
if (!dir.exists()) {
|
||||||
|
if (!dir.mkdirs()) {
|
||||||
|
return moduleContext.getResources().getString(R.string.canceled_message_txt); // フォルダ作成失敗時のデフォルト値
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ファイルのパスを設定
|
||||||
|
File file = new File(dir, "canceled_message.txt");
|
||||||
|
|
||||||
|
// ファイルが存在しない場合、デフォルトの文字列を書き込む
|
||||||
|
if (!file.exists()) {
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
|
|
||||||
|
String defaultContent = moduleContext.getResources().getString(R.string.canceled_message_txt);
|
||||||
|
fos.write(defaultContent.getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
String defaultContent = moduleContext.getResources().getString(R.string.canceled_message_txt);
|
||||||
|
return defaultContent; // ファイル書き込み失敗時のデフォルト値
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ファイルから文字列を読み取る
|
||||||
|
StringBuilder contentBuilder = new StringBuilder();
|
||||||
|
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
contentBuilder.append(line).append("\n");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return moduleContext.getResources().getString(R.string.canceled_message_txt); // ファイル読み込み失敗時のデフォルト値
|
||||||
|
}
|
||||||
|
|
||||||
|
// 読み取った内容を返す(末尾の改行を削除)
|
||||||
|
String content = contentBuilder.toString().trim();
|
||||||
|
return content.isEmpty() ? moduleContext.getResources().getString(R.string.canceled_message_txt) : content; // 空ファイルの場合のデフォルト値
|
||||||
|
}
|
||||||
private void resolveUnresolvedIds(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db1, SQLiteDatabase db2, Context moduleContext) {
|
private void resolveUnresolvedIds(XC_LoadPackage.LoadPackageParam loadPackageParam, Context context, SQLiteDatabase db1, SQLiteDatabase db2, Context moduleContext) {
|
||||||
String unresolvedFilePath = context.getFilesDir() + "/UnresolvedIds.txt";
|
String unresolvedFilePath = context.getFilesDir() + "/UnresolvedIds.txt";
|
||||||
|
|
||||||
|
@ -149,6 +149,9 @@
|
|||||||
<string name="AgeCheckSkip">年齢確認をスキップ</string>
|
<string name="AgeCheckSkip">年齢確認をスキップ</string>
|
||||||
<string name="GroupNotification">GroupNotification</string>
|
<string name="GroupNotification">GroupNotification</string>
|
||||||
<string name="LINELabOnly">設定ボタンをLINEラボ画面のみに追加する</string>
|
<string name="LINELabOnly">設定ボタンをLINEラボ画面のみに追加する</string>
|
||||||
|
<string name="canceled_message">取り消されたメッセージのトークを変更する</string>
|
||||||
|
<string name="canceled_message_txt">取り消しされたメッセージです</string>
|
||||||
|
<string name="hide_canceled_message">取り消されたメッセージのメッセージを非表示にする</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -141,5 +141,8 @@
|
|||||||
<string name="ReadCheckerChatdataDelete">(緊急)在聊天室內已讀確認按鈕右邊設定一個刪除紀錄按鈕</string>
|
<string name="ReadCheckerChatdataDelete">(緊急)在聊天室內已讀確認按鈕右邊設定一個刪除紀錄按鈕</string>
|
||||||
<string name="AgeCheckSkip">跳過年齡驗證</string>
|
<string name="AgeCheckSkip">跳過年齡驗證</string>
|
||||||
<string name="LINELabOnly">只在LINE Lab畫面中加入設定按鈕</string>
|
<string name="LINELabOnly">只在LINE Lab畫面中加入設定按鈕</string>
|
||||||
|
<string name="canceled_message">更改已取消訊息的通知對話</string>
|
||||||
|
<string name="canceled_message_txt">這是一條已取消的訊息</string>
|
||||||
|
<string name="hide_canceled_message">隱藏已取消訊息的訊息</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -155,4 +155,7 @@
|
|||||||
<string name="AgeCheckSkip">Skip Age Verification</string>
|
<string name="AgeCheckSkip">Skip Age Verification</string>
|
||||||
<string name="GroupNotification">GroupNotification</string>
|
<string name="GroupNotification">GroupNotification</string>
|
||||||
<string name="LINELabOnly">Add the settings button exclusively to the LINE Lab screen.</string>
|
<string name="LINELabOnly">Add the settings button exclusively to the LINE Lab screen.</string>
|
||||||
|
<string name="canceled_message">Change notification talk for canceled messages</string>
|
||||||
|
<string name="canceled_message_txt">This is a canceled message</string>
|
||||||
|
<string name="hide_canceled_message">Hide notification talk for canceled messages</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,15 +1,7 @@
|
|||||||
# Project-wide Gradle settings.
|
|
||||||
# IDE (e.g. Android Studio) users:
|
|
||||||
# Gradle settings configured through the IDE *will override*
|
|
||||||
# any settings specified in this file.
|
|
||||||
# For more details on how to configure your build environment visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
|
||||||
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
|
|
||||||
# Enables namespacing of each library's R class so that its R class includes only the
|
|
||||||
# resources declared in the library itself and none from the library's dependencies,
|
|
||||||
# thereby reducing the size of the R class for that library
|
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
|
android.nonFinalResIds=false
|
||||||
|
android.enableR8.fullMode=true
|
||||||
|
android.experimental.enableNewResourceShrinker=true
|
||||||
|
android.experimental.enableNewResourceShrinker.preciseShrinking=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
distributionSha256Sum=8d97a97984f6cbd2b85fe4c60a743440a347544bf18818048e611f5288d46c94
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@ -4,7 +4,7 @@ pluginManagement {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '8.6.1' apply false
|
id 'com.android.application' version '8.8.0' apply false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
|
Loading…
Reference in New Issue
Block a user