diff --git a/README.md b/README.md index 67ea64901..abbaecb8c 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,16 @@ options: --log-level - set log level, values: quiet, progress, error, warn, info, debug, default: progress -v, --verbose - verbose output (set --log-level to DEBUG) -q, --quiet - turn off output (set --log-level to QUIET) - --disable-plugins - comma separated list of plugin ids to disable, default: + --disable-plugins - comma separated list of plugin ids to disable + --config - load configuration from file, can be: + path to '.json' file + short name - uses file with this name from config directory + 'none' - to disable config loading + --save-config - save current options into configuration file and exit, can be: + empty - for default config + path to '.json' file + short name - file will be saved in config directory + --print-files - print files and directories used by jadx (config, cache, temp) --version - print jadx version -h, --help - print this help @@ -193,7 +202,7 @@ Plugin options (-P=): kotlin-smap: Use kotlin.SourceDebugExtension annotation for rename class alias - kotlin-smap.class-alias-source-dbg - rename class alias from SourceDebugExtension, values: [yes, no], default: no rename-mappings: various mappings support - - rename-mappings.format - mapping format, values: [AUTO, TINY_FILE, TINY_2_FILE, ENIGMA_FILE, ENIGMA_DIR, SRG_FILE, XSRG_FILE, JAM_FILE, CSRG_FILE, TSRG_FILE, TSRG_2_FILE, PROGUARD_FILE, INTELLIJ_MIGRATION_MAP_FILE, RECAF_SIMPLE_FILE, JOBF_FILE], default: AUTO + - rename-mappings.format - mapping format, values: [AUTO, TINY_FILE, TINY_2_FILE, ENIGMA_FILE, ENIGMA_DIR, PROGUARD_FILE, SRG_FILE, XSRG_FILE, JAM_FILE, CSRG_FILE, TSRG_FILE, TSRG_2_FILE, INTELLIJ_MIGRATION_MAP_FILE, RECAF_SIMPLE_FILE, JOBF_FILE], default: AUTO - rename-mappings.invert - invert mapping on load, values: [yes, no], default: no smali-input: Load .smali files - smali-input.api-level - Android API level, default: 27 diff --git a/jadx-cli/build.gradle.kts b/jadx-cli/build.gradle.kts index afb5ec9dd..9e7914d4d 100644 --- a/jadx-cli/build.gradle.kts +++ b/jadx-cli/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation("org.jcommander:jcommander:2.0") implementation("ch.qos.logback:logback-classic:1.5.21") + implementation("com.google.code.gson:gson:2.13.2") } application { diff --git a/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java b/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java index bc25d8658..3dd346796 100644 --- a/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java +++ b/jadx-cli/src/main/java/jadx/cli/JCommanderWrapper.java @@ -41,12 +41,12 @@ public class JCommanderWrapper { public boolean parse(String[] args) { try { - jc.parse(args); + String[] fixedArgs = fixArgsForEmptySaveConfig(args); + jc.parse(fixedArgs); applyFiles(argsObj); return true; } catch (ParameterException e) { System.err.println("Arguments parse error: " + e.getMessage()); - printUsage(); return false; } } @@ -96,6 +96,41 @@ public class JCommanderWrapper { return value; } + /** + * Workaround to allow empty value (i.e. zero arity) for '--save-config' option + * Insert empty string arg if another option start right after this one, or it is a last one. + */ + private String[] fixArgsForEmptySaveConfig(String[] args) { + int len = args.length; + for (int i = 0; i < len; i++) { + String arg = args[i]; + if (arg.equals("--save-config")) { + int next = i + 1; + if (next == len) { + return insertEmptyArg(args, next, true); + } + if (next < len) { + String nextArg = args[next]; + if (nextArg.startsWith("-")) { + return insertEmptyArg(args, next, false); + } + } + break; + } + } + return args; + } + + private static String[] insertEmptyArg(String[] args, int i, boolean add) { + List strings = new ArrayList<>(Arrays.asList(args)); + if (add) { + strings.add(""); + } else { + strings.add(i, ""); + } + return strings.toArray(new String[0]); + } + public void printUsage() { LogHelper.setLogLevel(LogHelper.LogLevelEnum.ERROR); // mute logger while printing help @@ -182,7 +217,9 @@ public class JCommanderWrapper { } if (addDefaults) { String defaultValue = getDefaultValue(args, f); - if (defaultValue != null && !description.contains("(default)")) { + if (defaultValue != null + && !defaultValue.isEmpty() + && !description.contains("(default)")) { opt.append(", default: ").append(defaultValue); } } diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java index ae7e836ba..5ca53f571 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java @@ -13,6 +13,7 @@ import jadx.api.impl.NoOpCodeCache; import jadx.api.impl.SimpleCodeWriter; import jadx.api.usage.impl.EmptyUsageInfoCache; import jadx.cli.LogHelper.LogLevelEnum; +import jadx.cli.config.JadxConfigAdapter; import jadx.cli.plugins.JadxFilesGetter; import jadx.core.utils.exceptions.JadxArgsValidateException; import jadx.plugins.tools.JadxExternalPluginsLoader; @@ -35,15 +36,17 @@ public class JadxCLI { public static int execute(String[] args, @Nullable Consumer argsMod) { try { - JadxCLIArgs cliArgs = new JadxCLIArgs(); - if (cliArgs.processArgs(args)) { - JadxArgs jadxArgs = buildArgs(cliArgs); - if (argsMod != null) { - argsMod.accept(jadxArgs); - } - return runSave(jadxArgs, cliArgs); + JadxCLIArgs cliArgs = JadxCLIArgs.processArgs(args, + new JadxCLIArgs(), + new JadxConfigAdapter<>(JadxCLIArgs.class, "cli")); + if (cliArgs == null) { + return 0; } - return 0; + JadxArgs jadxArgs = buildArgs(cliArgs); + if (argsMod != null) { + argsMod.accept(jadxArgs); + } + return runSave(jadxArgs, cliArgs); } catch (JadxArgsValidateException e) { LOG.error("Incorrect arguments: {}", e.getMessage()); return 1; @@ -54,8 +57,6 @@ public class JadxCLI { } private static JadxArgs buildArgs(JadxCLIArgs cliArgs) { - LogHelper.initLogLevel(cliArgs); - LogHelper.applyLogLevels(); JadxArgs jadxArgs = cliArgs.toJadxArgs(); jadxArgs.setCodeCache(new NoOpCodeCache()); jadxArgs.setUsageInfoCache(new EmptyUsageInfoCache()); diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 2e16e0b9f..ca4281bcc 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -31,22 +31,32 @@ import jadx.api.args.IntegerFormat; import jadx.api.args.ResourceNameSource; import jadx.api.args.UseSourceNameAsClassNameAlias; import jadx.api.args.UserRenamesMappingsMode; +import jadx.cli.config.IJadxConfig; +import jadx.cli.config.JadxConfigAdapter; +import jadx.cli.config.JadxConfigExclude; +import jadx.commons.app.JadxCommonFiles; +import jadx.commons.app.JadxTempFiles; import jadx.core.deobf.conditions.DeobfWhitelist; import jadx.core.export.ExportGradleType; import jadx.core.utils.exceptions.JadxArgsValidateException; +import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; -public class JadxCLIArgs { +public class JadxCLIArgs implements IJadxConfig { + @JadxConfigExclude @Parameter(description = " (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .apkm, .jadx.kts)") protected List files = Collections.emptyList(); + @JadxConfigExclude @Parameter(names = { "-d", "--output-dir" }, description = "output directory") protected String outDir; + @JadxConfigExclude @Parameter(names = { "-ds", "--output-dir-src" }, description = "output directory for sources") protected String outDirSrc; + @JadxConfigExclude @Parameter(names = { "-dr", "--output-dir-res" }, description = "output directory for resources") protected String outDirRes; @@ -59,9 +69,11 @@ public class JadxCLIArgs { @Parameter(names = { "-j", "--threads-count" }, description = "processing threads count") protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT; + @JadxConfigExclude @Parameter(names = { "--single-class" }, description = "decompile a single class, full name, raw or alias") protected String singleClass = null; + @JadxConfigExclude @Parameter(names = { "--single-class-output" }, description = "file or dir for write if decompile a single class") protected String singleClassOutput = null; @@ -166,6 +178,7 @@ public class JadxCLIArgs { ) protected String deobfuscationWhitelistStr = DeobfWhitelist.DEFAULT_STR; + @JadxConfigExclude @Parameter( names = { "--deobf-cfg-file" }, description = "deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format)," @@ -241,7 +254,7 @@ public class JadxCLIArgs { + "\n 'printable' - remove non-printable chars from identifiers," + "\nor single 'none' - to disable all renames" + "\nor single 'all' - to enable all (default)", - converter = RenameConverter.class + listConverter = RenameConverter.class ) protected Set renameFlags = EnumSet.allOf(RenameEnum.class); @@ -287,27 +300,106 @@ public class JadxCLIArgs { ) protected LogHelper.LogLevelEnum logLevel = LogHelper.LogLevelEnum.PROGRESS; + @JadxConfigExclude @Parameter(names = { "-v", "--verbose" }, description = "verbose output (set --log-level to DEBUG)") protected boolean verbose = false; + @JadxConfigExclude @Parameter(names = { "-q", "--quiet" }, description = "turn off output (set --log-level to QUIET)") protected boolean quiet = false; + @JadxConfigExclude @Parameter(names = { "--disable-plugins" }, description = "comma separated list of plugin ids to disable") protected String disablePlugins = ""; + @JadxConfigExclude + @Parameter( + names = { "--config" }, + defaultValueDescription = "", + description = "load configuration from file, can be:" + + "\n path to '.json' file" + + "\n short name - uses file with this name from config directory" + + "\n 'none' - to disable config loading" + ) + protected String config = ""; + + @JadxConfigExclude + @Parameter( + names = { "--save-config" }, + defaultValueDescription = "", + description = "save current options into configuration file and exit, can be:" + + "\n empty - for default config" + + "\n path to '.json' file" + + "\n short name - file will be saved in config directory" + ) + protected String saveConfig = null; + + @JadxConfigExclude + @Parameter(names = { "--print-files" }, description = "print files and directories used by jadx (config, cache, temp)") + protected boolean printFiles = false; + + @JadxConfigExclude @Parameter(names = { "--version" }, description = "print jadx version") protected boolean printVersion = false; + @JadxConfigExclude @Parameter(names = { "-h", "--help" }, description = "print this help", help = true) protected boolean printHelp = false; @DynamicParameter(names = "-P", description = "Plugin options", hidden = true) protected Map pluginOptions = new HashMap<>(); + /** + * Obsolete method without config support, + * prefer {@link #processArgs(String[], JadxCLIArgs, JadxConfigAdapter)} + */ public boolean processArgs(String[] args) { - JCommanderWrapper jcw = new JCommanderWrapper(this); - return jcw.parse(args) && process(jcw); + return processArgs(args, this, null) != null; + } + + public static @Nullable T processArgs( + String[] args, T argsObj, @Nullable JadxConfigAdapter configAdapter) { + JCommanderWrapper jcw = new JCommanderWrapper(argsObj); + if (!jcw.parse(args)) { + return null; + } + applyArgs(argsObj); + + // process commands and early exit flags + if (!argsObj.process(jcw)) { + return null; + } + if (configAdapter != null) { + if (argsObj.printFiles) { + printFilesAndDirs(configAdapter.getDefaultConfigFileName()); + return null; + } + if (!argsObj.config.equalsIgnoreCase("none")) { + // load config file and merge with command line args + configAdapter.useConfigRef(argsObj.config); + T configObj = configAdapter.load(); + if (configObj != null) { + jcw.overrideProvided(configObj); + argsObj = configObj; + } + } + } + // verify result object + argsObj.verify(); + applyArgs(argsObj); + + // save config if requested + if (argsObj.saveConfig != null) { + saveConfig(argsObj, configAdapter); + return null; + } + return argsObj; + } + + private static void applyArgs(T argsObj) { + // apply log levels + LogHelper.initLogLevel(argsObj); + LogHelper.applyLogLevels(); } public boolean process(JCommanderWrapper jcw) { @@ -322,9 +414,7 @@ public class JadxCLIArgs { System.out.println(JadxDecompiler.getVersion()); return false; } - if (threadsCount <= 0) { - throw new JadxArgsValidateException("Threads count must be positive, got: " + threadsCount); - } + // unknown options added to 'files', run checks for (String fileName : files) { if (fileName.startsWith("-")) { throw new JadxArgsValidateException("Unknown option: " + fileName); @@ -333,6 +423,29 @@ public class JadxCLIArgs { return true; } + private static void printFilesAndDirs(String defaultConfigFileName) { + System.out.println("Files and directories used by jadx:"); + System.out.println(" - default config file: " + JadxCommonFiles.getConfigDir().resolve(defaultConfigFileName).toAbsolutePath()); + System.out.println(" - config directory: " + JadxCommonFiles.getConfigDir().toAbsolutePath()); + System.out.println(" - cache directory: " + JadxCommonFiles.getCacheDir().toAbsolutePath()); + System.out.println(" - temp directory: " + JadxTempFiles.getTempRootDir().getParent().toAbsolutePath()); + } + + public void verify() { + if (threadsCount <= 0) { + throw new JadxArgsValidateException("Threads count must be positive, got: " + threadsCount); + } + } + + private static void saveConfig(T argsObj, @Nullable JadxConfigAdapter configAdapter) { + if (configAdapter == null) { + throw new JadxRuntimeException("Config adapter set to null, can't save config"); + } + configAdapter.useConfigRef(argsObj.saveConfig); + configAdapter.save(argsObj); + System.out.println("Config saved to " + configAdapter.getConfigPath().toAbsolutePath()); + } + public JadxArgs toJadxArgs() { JadxArgs args = new JadxArgs(); args.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList())); @@ -383,7 +496,7 @@ public class JadxCLIArgs { args.setAllowInlineKotlinLambda(allowInlineKotlinLambda); args.setExtractFinally(extractFinally); args.setRestoreSwitchOverString(restoreSwitchOverString); - args.setRenameFlags(renameFlags); + args.setRenameFlags(buildEnumSetForRenameFlags()); args.setFsCaseSensitive(fsCaseSensitive); args.setCommentsLevel(commentsLevel); args.setIntegerFormat(integerFormat); @@ -394,6 +507,12 @@ public class JadxCLIArgs { return args; } + private EnumSet buildEnumSetForRenameFlags() { + EnumSet set = EnumSet.noneOf(RenameEnum.class); + set.addAll(renameFlags); + return set; + } + public List getFiles() { return files; } @@ -426,14 +545,26 @@ public class JadxCLIArgs { return skipResources; } + public void setSkipResources(boolean skipResources) { + this.skipResources = skipResources; + } + public boolean isSkipSources() { return skipSources; } + public void setSkipSources(boolean skipSources) { + this.skipSources = skipSources; + } + public int getThreadsCount() { return threadsCount; } + public void setThreadsCount(int threadsCount) { + this.threadsCount = threadsCount; + } + public boolean isFallbackMode() { return fallbackMode; } @@ -442,82 +573,170 @@ public class JadxCLIArgs { return useDx; } + public void setUseDx(boolean useDx) { + this.useDx = useDx; + } + public DecompilationMode getDecompilationMode() { return decompilationMode; } + public void setDecompilationMode(DecompilationMode decompilationMode) { + this.decompilationMode = decompilationMode; + } + public boolean isShowInconsistentCode() { return showInconsistentCode; } + public void setShowInconsistentCode(boolean showInconsistentCode) { + this.showInconsistentCode = showInconsistentCode; + } + public boolean isUseImports() { return useImports; } + public void setUseImports(boolean useImports) { + this.useImports = useImports; + } + public boolean isDebugInfo() { return debugInfo; } + public void setDebugInfo(boolean debugInfo) { + this.debugInfo = debugInfo; + } + public boolean isAddDebugLines() { return addDebugLines; } + public void setAddDebugLines(boolean addDebugLines) { + this.addDebugLines = addDebugLines; + } + public boolean isInlineAnonymousClasses() { return inlineAnonymousClasses; } + public void setInlineAnonymousClasses(boolean inlineAnonymousClasses) { + this.inlineAnonymousClasses = inlineAnonymousClasses; + } + public boolean isInlineMethods() { return inlineMethods; } + public void setInlineMethods(boolean inlineMethods) { + this.inlineMethods = inlineMethods; + } + public boolean isMoveInnerClasses() { return moveInnerClasses; } + public void setMoveInnerClasses(boolean moveInnerClasses) { + this.moveInnerClasses = moveInnerClasses; + } + public boolean isAllowInlineKotlinLambda() { return allowInlineKotlinLambda; } + public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) { + this.allowInlineKotlinLambda = allowInlineKotlinLambda; + } + public boolean isExtractFinally() { return extractFinally; } + public void setExtractFinally(boolean extractFinally) { + this.extractFinally = extractFinally; + } + public boolean isRestoreSwitchOverString() { return restoreSwitchOverString; } + public void setRestoreSwitchOverString(boolean restoreSwitchOverString) { + this.restoreSwitchOverString = restoreSwitchOverString; + } + public Path getUserRenamesMappingsPath() { return userRenamesMappingsPath; } + public void setUserRenamesMappingsPath(Path userRenamesMappingsPath) { + this.userRenamesMappingsPath = userRenamesMappingsPath; + } + public UserRenamesMappingsMode getUserRenamesMappingsMode() { return userRenamesMappingsMode; } + public void setUserRenamesMappingsMode(UserRenamesMappingsMode userRenamesMappingsMode) { + this.userRenamesMappingsMode = userRenamesMappingsMode; + } + public boolean isDeobfuscationOn() { return deobfuscationOn; } + public void setDeobfuscationOn(boolean deobfuscationOn) { + this.deobfuscationOn = deobfuscationOn; + } + public int getDeobfuscationMinLength() { return deobfuscationMinLength; } + public void setDeobfuscationMinLength(int deobfuscationMinLength) { + this.deobfuscationMinLength = deobfuscationMinLength; + } + public int getDeobfuscationMaxLength() { return deobfuscationMaxLength; } + public void setDeobfuscationMaxLength(int deobfuscationMaxLength) { + this.deobfuscationMaxLength = deobfuscationMaxLength; + } + public String getDeobfuscationWhitelistStr() { return deobfuscationWhitelistStr; } + public void setDeobfuscationWhitelistStr(String deobfuscationWhitelistStr) { + this.deobfuscationWhitelistStr = deobfuscationWhitelistStr; + } + public String getGeneratedRenamesMappingFile() { return generatedRenamesMappingFile; } + public void setGeneratedRenamesMappingFile(String generatedRenamesMappingFile) { + this.generatedRenamesMappingFile = generatedRenamesMappingFile; + } + public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() { return generatedRenamesMappingFileMode; } + public void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode) { + this.generatedRenamesMappingFileMode = generatedRenamesMappingFileMode; + } + + public int getSourceNameRepeatLimit() { + return sourceNameRepeatLimit; + } + + public void setSourceNameRepeatLimit(int sourceNameRepeatLimit) { + this.sourceNameRepeatLimit = sourceNameRepeatLimit; + } + public UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() { if (useSourceNameAsClassNameAlias != null) { return useSourceNameAsClassNameAlias; @@ -529,8 +748,8 @@ public class JadxCLIArgs { } } - public int getSourceNameRepeatLimit() { - return sourceNameRepeatLimit; + public void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) { + this.useSourceNameAsClassNameAlias = useSourceNameAsClassNameAlias; } /** @@ -541,50 +760,98 @@ public class JadxCLIArgs { return getUseSourceNameAsClassNameAlias().toBoolean(); } + public void setDeobfuscationUseSourceNameAsAlias(Boolean deobfuscationUseSourceNameAsAlias) { + this.deobfuscationUseSourceNameAsAlias = deobfuscationUseSourceNameAsAlias; + } + public ResourceNameSource getResourceNameSource() { return resourceNameSource; } + public void setResourceNameSource(ResourceNameSource resourceNameSource) { + this.resourceNameSource = resourceNameSource; + } + public UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() { return useKotlinMethodsForVarNames; } + public void setUseKotlinMethodsForVarNames(UseKotlinMethodsForVarNames useKotlinMethodsForVarNames) { + this.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames; + } + public IntegerFormat getIntegerFormat() { return integerFormat; } + public void setIntegerFormat(IntegerFormat integerFormat) { + this.integerFormat = integerFormat; + } + public int getTypeUpdatesLimitCount() { return typeUpdatesLimitCount; } + public void setTypeUpdatesLimitCount(int typeUpdatesLimitCount) { + this.typeUpdatesLimitCount = typeUpdatesLimitCount; + } + public boolean isEscapeUnicode() { return escapeUnicode; } + public void setEscapeUnicode(boolean escapeUnicode) { + this.escapeUnicode = escapeUnicode; + } + public boolean isCfgOutput() { return cfgOutput; } + public void setCfgOutput(boolean cfgOutput) { + this.cfgOutput = cfgOutput; + } + public boolean isRawCfgOutput() { return rawCfgOutput; } + public void setRawCfgOutput(boolean rawCfgOutput) { + this.rawCfgOutput = rawCfgOutput; + } + public boolean isReplaceConsts() { return replaceConsts; } + public void setReplaceConsts(boolean replaceConsts) { + this.replaceConsts = replaceConsts; + } + public boolean isRespectBytecodeAccessModifiers() { return respectBytecodeAccessModifiers; } + public void setRespectBytecodeAccessModifiers(boolean respectBytecodeAccessModifiers) { + this.respectBytecodeAccessModifiers = respectBytecodeAccessModifiers; + } + public boolean isExportAsGradleProject() { return exportAsGradleProject; } + public void setExportAsGradleProject(boolean exportAsGradleProject) { + this.exportAsGradleProject = exportAsGradleProject; + } + public boolean isSkipXmlPrettyPrint() { return skipXmlPrettyPrint; } + public void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) { + this.skipXmlPrettyPrint = skipXmlPrettyPrint; + } + public boolean isRenameCaseSensitive() { return renameFlags.contains(RenameEnum.CASE); } @@ -601,26 +868,70 @@ public class JadxCLIArgs { return fsCaseSensitive; } + public void setFsCaseSensitive(boolean fsCaseSensitive) { + this.fsCaseSensitive = fsCaseSensitive; + } + public boolean isUseHeadersForDetectResourceExtensions() { return useHeadersForDetectResourceExtensions; } + public void setUseHeadersForDetectResourceExtensions(boolean useHeadersForDetectResourceExtensions) { + this.useHeadersForDetectResourceExtensions = useHeadersForDetectResourceExtensions; + } + public CommentsLevel getCommentsLevel() { return commentsLevel; } + public void setCommentsLevel(CommentsLevel commentsLevel) { + this.commentsLevel = commentsLevel; + } + public LogHelper.LogLevelEnum getLogLevel() { return logLevel; } + public void setLogLevel(LogHelper.LogLevelEnum logLevel) { + this.logLevel = logLevel; + } + public Map getPluginOptions() { return pluginOptions; } + public void setPluginOptions(Map pluginOptions) { + this.pluginOptions = pluginOptions; + } + public String getDisablePlugins() { return disablePlugins; } + public void setDisablePlugins(String disablePlugins) { + this.disablePlugins = disablePlugins; + } + + public void setExportGradleType(@Nullable ExportGradleType exportGradleType) { + this.exportGradleType = exportGradleType; + } + + public void setOutputFormat(String outputFormat) { + this.outputFormat = outputFormat; + } + + public Set getRenameFlags() { + return renameFlags; + } + + public void setRenameFlags(Set renameFlags) { + this.renameFlags = renameFlags; + } + + public String getConfig() { + return config; + } + static class RenameConverter implements IStringConverter> { private final String paramName; diff --git a/jadx-cli/src/main/java/jadx/cli/LogHelper.java b/jadx-cli/src/main/java/jadx/cli/LogHelper.java index 2565de1f6..ea3c1a8e6 100644 --- a/jadx-cli/src/main/java/jadx/cli/LogHelper.java +++ b/jadx-cli/src/main/java/jadx/cli/LogHelper.java @@ -43,10 +43,9 @@ public class LogHelper { return null; } if (args.quiet) { - return LogLevelEnum.QUIET; - } - if (args.verbose) { - return LogLevelEnum.DEBUG; + args.logLevel = LogLevelEnum.QUIET; + } else if (args.verbose) { + args.logLevel = LogLevelEnum.DEBUG; } return args.logLevel; } diff --git a/jadx-cli/src/main/java/jadx/cli/config/IJadxConfig.java b/jadx-cli/src/main/java/jadx/cli/config/IJadxConfig.java new file mode 100644 index 000000000..4d4702587 --- /dev/null +++ b/jadx-cli/src/main/java/jadx/cli/config/IJadxConfig.java @@ -0,0 +1,7 @@ +package jadx.cli.config; + +/** + * Marker interface for jadx config objects + */ +public interface IJadxConfig { +} diff --git a/jadx-cli/src/main/java/jadx/cli/config/JadxConfigAdapter.java b/jadx-cli/src/main/java/jadx/cli/config/JadxConfigAdapter.java new file mode 100644 index 000000000..cdb74af64 --- /dev/null +++ b/jadx-cli/src/main/java/jadx/cli/config/JadxConfigAdapter.java @@ -0,0 +1,114 @@ +package jadx.cli.config; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Consumer; + +import org.jetbrains.annotations.Nullable; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import jadx.commons.app.JadxCommonFiles; +import jadx.core.utils.GsonUtils; +import jadx.core.utils.exceptions.JadxArgsValidateException; +import jadx.core.utils.exceptions.JadxRuntimeException; + +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static java.nio.file.StandardOpenOption.WRITE; + +public class JadxConfigAdapter { + private static final ExclusionStrategy GSON_EXCLUSION_STRATEGY = new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes f) { + return f.getAnnotation(JadxConfigExclude.class) != null; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + }; + + private final Class configCls; + private final String defaultConfigFileName; + private final Gson gson; + + private Path configPath; + + public JadxConfigAdapter(Class configCls, String defaultConfigName) { + this(configCls, defaultConfigName, gsonBuilder -> { + }); + } + + public JadxConfigAdapter(Class configCls, String defaultConfigName, Consumer applyGsonOptions) { + this.configCls = configCls; + this.defaultConfigFileName = defaultConfigName + ".json"; + GsonBuilder gsonBuilder = GsonUtils.defaultGsonBuilder(); + gsonBuilder.setExclusionStrategies(GSON_EXCLUSION_STRATEGY); + applyGsonOptions.accept(gsonBuilder); + this.gson = gsonBuilder.create(); + } + + public void useConfigRef(String configRef) { + this.configPath = resolveConfigRef(configRef); + } + + public Path getConfigPath() { + return configPath; + } + + public String getDefaultConfigFileName() { + return defaultConfigFileName; + } + + public @Nullable T load() { + if (!Files.isRegularFile(configPath)) { + // file not found + return null; + } + try (JsonReader reader = gson.newJsonReader(Files.newBufferedReader(configPath))) { + return gson.fromJson(reader, configCls); + } catch (Exception e) { + throw new JadxRuntimeException("Failed to load config file: " + configPath, e); + } + } + + public void save(T configObject) { + try (JsonWriter writer = gson.newJsonWriter( + Files.newBufferedWriter(configPath, StandardCharsets.UTF_8, WRITE, CREATE, TRUNCATE_EXISTING))) { + gson.toJson(configObject, configCls, writer); + } catch (Exception e) { + throw new JadxRuntimeException("Failed to save config file: " + configPath, e); + } + } + + public String objectToJsonString(T configObject) { + return gson.toJson(configObject, configCls); + } + + public T jsonStringToObject(String jsonStr) { + return gson.fromJson(jsonStr, configCls); + } + + private Path resolveConfigRef(String configRef) { + if (configRef == null || configRef.isEmpty()) { + // use default config file + return JadxCommonFiles.getConfigDir().resolve(defaultConfigFileName); + } + if (configRef.contains("/") || configRef.contains("\\")) { + if (!configRef.toLowerCase().endsWith(".json")) { + throw new JadxArgsValidateException("Config file extension should be '.json'"); + } + return Path.of(configRef); + } + // treat as a short name + return JadxCommonFiles.getConfigDir().resolve(configRef + ".json"); + } +} diff --git a/jadx-cli/src/main/java/jadx/cli/config/JadxConfigExclude.java b/jadx-cli/src/main/java/jadx/cli/config/JadxConfigExclude.java new file mode 100644 index 000000000..6731c5814 --- /dev/null +++ b/jadx-cli/src/main/java/jadx/cli/config/JadxConfigExclude.java @@ -0,0 +1,11 @@ +package jadx.cli.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface JadxConfigExclude { +} diff --git a/jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxTempFiles.java b/jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxTempFiles.java index e4122ab90..39f9bf8ef 100644 --- a/jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxTempFiles.java +++ b/jadx-commons/jadx-app-commons/src/main/java/jadx/commons/app/JadxTempFiles.java @@ -18,7 +18,9 @@ public class JadxTempFiles { String jadxTmpDir = System.getenv("JADX_TMP_DIR"); Path dir; if (jadxTmpDir != null) { - dir = Files.createTempDirectory(Paths.get(jadxTmpDir), JADX_TMP_INSTANCE_PREFIX); + Path customTmpRootDir = Paths.get(jadxTmpDir); + Files.createDirectories(customTmpRootDir); + dir = Files.createTempDirectory(customTmpRootDir, JADX_TMP_INSTANCE_PREFIX); } else { dir = Files.createTempDirectory(JADX_TMP_INSTANCE_PREFIX); } diff --git a/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java b/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java index d967f61dc..0f98ab82c 100644 --- a/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/GsonUtils.java @@ -4,13 +4,13 @@ import java.lang.reflect.Type; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.InstanceCreator; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import com.google.gson.Strictness; public class GsonUtils { @@ -21,15 +21,11 @@ public class GsonUtils { public static GsonBuilder defaultGsonBuilder() { return new GsonBuilder() .disableJdkUnsafe() + .disableInnerClassSerialization() + .setStrictness(Strictness.STRICT) .setPrettyPrinting(); } - public static void fillObjectFromJsonString(GsonBuilder builder, Object obj, String jsonStr) { - Class type = obj.getClass(); - Gson gson = builder.registerTypeAdapter(type, (InstanceCreator) t -> obj).create(); - gson.fromJson(jsonStr, type); - } - public static InterfaceReplace interfaceReplace(Class replaceCls) { return new InterfaceReplace<>(replaceCls); } diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java index 40c603e24..d48363f2d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java @@ -45,7 +45,7 @@ public class AndroidResourcesUtils { addResourceFields(resCls, resStorage, true); return resCls; } - LOG.info("Can't find 'R' class in app package: {}", appPackage); + LOG.debug("Can't find 'R' class in app package: {}", appPackage); List candidates = root.searchClassByShortName("R"); if (candidates.size() == 1) { ClassNode resClsCandidate = candidates.get(0); diff --git a/jadx-gui/src/main/java/jadx/gui/JadxGUI.java b/jadx-gui/src/main/java/jadx/gui/JadxGUI.java index 07a250716..69fabed18 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxGUI.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxGUI.java @@ -7,14 +7,14 @@ import javax.swing.SwingUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jadx.cli.JCommanderWrapper; -import jadx.cli.LogHelper; +import jadx.cli.JadxCLIArgs; +import jadx.cli.config.JadxConfigAdapter; import jadx.commons.app.JadxSystemInfo; import jadx.core.Jadx; import jadx.core.utils.files.FileUtils; import jadx.gui.logs.LogCollector; import jadx.gui.settings.JadxSettings; -import jadx.gui.settings.JadxSettingsAdapter; +import jadx.gui.settings.JadxSettingsData; import jadx.gui.ui.MainWindow; import jadx.gui.ui.dialog.ExceptionDialog; import jadx.gui.utils.LafManager; @@ -25,20 +25,16 @@ public class JadxGUI { public static void main(String[] args) { try { - JadxSettings cliArgs = new JadxSettings(); - JCommanderWrapper jcw = new JCommanderWrapper(cliArgs); - if (!jcw.parse(args) || !cliArgs.process(jcw)) { + JadxConfigAdapter configAdapter = JadxSettings.buildConfigAdapter(); + JadxSettingsData settingsData = JadxCLIArgs.processArgs(args, new JadxSettingsData(), configAdapter); + if (settingsData == null) { return; } - LogHelper.initLogLevel(cliArgs); - LogHelper.applyLogLevels(); + JadxSettings settings = new JadxSettings(configAdapter); + settings.loadSettingsData(settingsData); + LogCollector.register(); printSystemInfo(); - - JadxSettings settings = JadxSettingsAdapter.load(); - // overwrite loaded settings by command line arguments - jcw.overrideProvided(settings); - LafManager.init(settings); NLS.setLocale(settings.getLangLocale()); ExceptionDialog.registerUncaughtExceptionHandler(); diff --git a/jadx-gui/src/main/java/jadx/gui/settings/FontLoader.java b/jadx-gui/src/main/java/jadx/gui/settings/FontLoader.java new file mode 100644 index 000000000..e25d992bd --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/FontLoader.java @@ -0,0 +1,48 @@ +package jadx.gui.settings; + +import java.awt.Font; + +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.core.utils.Utils; +import jadx.gui.utils.FontUtils; + +public class FontLoader { + private static final Logger LOG = LoggerFactory.getLogger(FontLoader.class); + + private final Font defaultFont; + + private Font font; + + public FontLoader(Font defaultFont) { + this.defaultFont = defaultFont; + this.font = defaultFont; + } + + public Font getFont() { + return font; + } + + public void setFont(@Nullable Font font) { + this.font = Utils.getOrElse(font, defaultFont); + } + + public void load(String fontStr) { + if (fontStr == null || fontStr.isEmpty()) { + font = defaultFont; + } else { + try { + font = FontUtils.loadByStr(fontStr); + } catch (Exception e) { + LOG.warn("Failed to load font: {}, reset to default", fontStr, e); + font = defaultFont; + } + } + } + + public String getFontStr() { + return FontUtils.convertToStr(font); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxConfigExcludeExport.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxConfigExcludeExport.java new file mode 100644 index 000000000..cf909bc38 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxConfigExcludeExport.java @@ -0,0 +1,14 @@ +package jadx.gui.settings; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark fields excluded from config export/copy actions. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface JadxConfigExcludeExport { +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxGUIArgs.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxGUIArgs.java new file mode 100644 index 000000000..f8fb561a2 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxGUIArgs.java @@ -0,0 +1,24 @@ +package jadx.gui.settings; + +import com.beust.jcommander.Parameter; + +import jadx.cli.JadxCLIArgs; +import jadx.cli.config.JadxConfigExclude; + +public class JadxGUIArgs extends JadxCLIArgs { + + @JadxConfigExclude + @Parameter( + names = { "-sc", "--select-class" }, + description = "GUI: Open the selected class and show the decompiled code" + ) + private String cmdSelectClass = null; + + public String getCmdSelectClass() { + return cmdSelectClass; + } + + public void setCmdSelectClass(String cmdSelectClass) { + this.cmdSelectClass = cmdSelectClass; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java index 31b13e0ff..9daa093cf 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java @@ -36,6 +36,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.FileUtils; import jadx.gui.cache.manager.CacheManager; import jadx.gui.settings.data.ProjectData; +import jadx.gui.settings.data.SaveOptionEnum; import jadx.gui.settings.data.TabViewState; import jadx.gui.ui.MainWindow; import jadx.gui.ui.codearea.EditorViewState; @@ -267,7 +268,7 @@ public class JadxProject { private void changed() { JadxSettings settings = mainWindow.getSettings(); - if (settings != null && settings.getSaveOption() == JadxSettings.SAVEOPTION.ALWAYS) { + if (settings != null && settings.getSaveOption() == SaveOptionEnum.ALWAYS) { save(); } else { saved = false; diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java index f9d6a5ecd..b83918c18 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -6,16 +6,10 @@ import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.Window; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Consumer; import javax.swing.JFrame; @@ -24,7 +18,9 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.beust.jcommander.Parameter; +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; import jadx.api.CommentsLevel; import jadx.api.DecompilationMode; @@ -34,233 +30,183 @@ import jadx.api.args.IntegerFormat; import jadx.api.args.ResourceNameSource; import jadx.api.args.UseSourceNameAsClassNameAlias; import jadx.api.args.UserRenamesMappingsMode; -import jadx.cli.JadxCLIArgs; -import jadx.cli.LogHelper; +import jadx.cli.config.JadxConfigAdapter; +import jadx.cli.config.JadxConfigExclude; +import jadx.core.utils.GsonUtils; import jadx.gui.cache.code.CodeCacheMode; import jadx.gui.cache.usage.UsageCacheMode; +import jadx.gui.settings.data.SaveOptionEnum; import jadx.gui.settings.data.ShortcutsWrapper; import jadx.gui.ui.MainWindow; -import jadx.gui.ui.action.ActionModel; import jadx.gui.ui.tab.dnd.TabDndGhostType; -import jadx.gui.utils.FontUtils; -import jadx.gui.utils.LafManager; import jadx.gui.utils.LangLocale; -import jadx.gui.utils.NLS; -import jadx.gui.utils.shortcut.Shortcut; +import jadx.gui.utils.PathTypeAdapter; +import jadx.gui.utils.RectangleTypeAdapter; -public class JadxSettings extends JadxCLIArgs { +import static jadx.gui.settings.JadxSettingsData.CURRENT_SETTINGS_VERSION; + +public class JadxSettings { private static final Logger LOG = LoggerFactory.getLogger(JadxSettings.class); - private static final Path USER_HOME = Paths.get(System.getProperty("user.home")); private static final int RECENT_PROJECTS_COUNT = 30; - private static final int CURRENT_SETTINGS_VERSION = 22; - private static final Font DEFAULT_FONT = new RSyntaxTextArea().getFont(); - static final Set SKIP_FIELDS = new HashSet<>(Arrays.asList( - "files", "input", "outDir", "outDirSrc", "outDirRes", "outputFormat", - "deobfuscationMapFile", - "disablePlugins", - "verbose", "quiet", "logLevel", - "printVersion", "printHelp")); + private final JadxConfigAdapter configAdapter; + private final ShortcutsWrapper shortcutsWrapper = new ShortcutsWrapper(); - private Path lastSaveProjectPath = USER_HOME; - private Path lastOpenFilePath = USER_HOME; - private Path lastSaveFilePath = USER_HOME; - private boolean flattenPackage = false; - private boolean checkForUpdates = true; - private boolean disableTooltipOnHover = false; - private List recentProjects = new ArrayList<>(); - private float uiZoom = 1.0f; - private String fontStr = ""; - private String smaliFontStr = ""; - private String editorTheme = ""; + private final FontLoader fontLoader = new FontLoader(DEFAULT_FONT); + // TODO: use default monospaced font + private final FontLoader smaliFontLoader = new FontLoader(DEFAULT_FONT); + private JadxSettingsData settingsData; - // Deprecated. Keep for backward compatibility - private String editorThemePath = "/org/fife/ui/rsyntaxtextarea/themes/default.xml"; - - private String lafTheme = LafManager.INITIAL_THEME_NAME; - private LangLocale langLocale = NLS.defaultLocale(); - private boolean autoStartJobs = false; - private String excludedPackages = ""; - private SAVEOPTION saveOption = SAVEOPTION.ASK; - - public enum SAVEOPTION { - ASK, - NEVER, - ALWAYS + public JadxSettings(JadxConfigAdapter configAdapter) { + this.configAdapter = configAdapter; } - public SAVEOPTION getSaveOption() { - return saveOption; + public static JadxConfigAdapter buildConfigAdapter() { + return new JadxConfigAdapter<>(JadxSettingsData.class, "gui", gsonBuilder -> { + gsonBuilder.registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton()); + gsonBuilder.registerTypeHierarchyAdapter(Rectangle.class, RectangleTypeAdapter.singleton()); + }); } - public void setSaveOption(SAVEOPTION saveOption) { - this.saveOption = saveOption; + public String getSettingsJsonString() { + return configAdapter.objectToJsonString(settingsData); } - private Map shortcuts = new HashMap<>(); - - @JadxSettingsAdapter.GsonExclude - private ShortcutsWrapper shortcutsWrapper = null; - - private boolean showHeapUsageBar = false; - private boolean alwaysSelectOpened = false; - private boolean enablePreviewTab = false; - private boolean useAlternativeFileDialog = false; - - private Map windowPos = new HashMap<>(); - private int mainWindowExtendedState = JFrame.NORMAL; - private boolean codeAreaLineWrap = false; - private int srhResourceSkipSize = 1000; - private int searchResultsPerPage = 50; - private boolean useAutoSearch = true; - private boolean keepCommonDialogOpen = false; - private boolean smaliAreaShowBytecode = false; - private LineNumbersMode lineNumbersMode = LineNumbersMode.AUTO; - - private int mainWindowVerticalSplitterLoc = 300; - private int debuggerStackFrameSplitterLoc = 300; - private int debuggerVarTreeSplitterLoc = 700; - - private String adbDialogPath = ""; - private String adbDialogHost = "localhost"; - private String adbDialogPort = "5037"; - - private CodeCacheMode codeCacheMode = CodeCacheMode.DISK; - private UsageCacheMode usageCacheMode = UsageCacheMode.DISK; - private @Nullable String cacheDir = null; // null - default (system), "." - at project dir, other - custom - - private boolean jumpOnDoubleClick = true; - - private XposedCodegenLanguage xposedCodegenLanguage = XposedCodegenLanguage.JAVA; - private JadxUpdateChannel jadxUpdateChannel = JadxUpdateChannel.STABLE; - - /** - * UI setting: the width of the tree showing the classes, resources, ... - */ - private int treeWidth = 130; - - private boolean dockLogViewer = true; - - private boolean dockQuickTabs = false; - - private TabDndGhostType tabDndGhostType = TabDndGhostType.OUTLINE; - - private int settingsVersion = CURRENT_SETTINGS_VERSION; - - @JadxSettingsAdapter.GsonExclude - @Parameter(names = { "-sc", "--select-class" }, description = "GUI: Open the selected class and show the decompiled code") - private String cmdSelectClass = null; - - public static JadxSettings makeDefault() { - JadxSettings jadxSettings = new JadxSettings(); - jadxSettings.fixOnLoad(); - return jadxSettings; + public void loadSettingsFromJsonString(String jsonStr) { + loadSettingsData(configAdapter.jsonStringToObject(jsonStr)); } - public JadxSettings() { - this.logLevel = LogHelper.LogLevelEnum.INFO; + public void loadSettingsData(JadxSettingsData settingsData) { + this.settingsData = settingsData; + upgradeSettings(settingsData.getSettingsVersion()); + fixOnLoad(); + // update custom fields + shortcutsWrapper.updateShortcuts(settingsData.getShortcuts()); + fontLoader.load(settingsData.getFontStr()); + smaliFontLoader.load(settingsData.getSmaliFontStr()); + } + + private void upgradeSettings(int fromVersion) { + if (settingsData.getSettingsVersion() == CURRENT_SETTINGS_VERSION) { + return; + } + LOG.debug("upgrade settings from version: {} to {}", fromVersion, CURRENT_SETTINGS_VERSION); + if (fromVersion <= 22) { + fromVersion++; + } + if (fromVersion != CURRENT_SETTINGS_VERSION) { + LOG.warn("Incorrect settings upgrade. Expected version: {}, got: {}", CURRENT_SETTINGS_VERSION, fromVersion); + } + settingsData.setSettingsVersion(CURRENT_SETTINGS_VERSION); + sync(); + } + + private void fixOnLoad() { + if (settingsData.getThreadsCount() <= 0) { + settingsData.setThreadsCount(JadxArgs.DEFAULT_THREADS_COUNT); + } + if (settingsData.getDeobfuscationMinLength() < 0) { + settingsData.setDeobfuscationMinLength(0); + } + if (settingsData.getDeobfuscationMaxLength() < 0) { + settingsData.setDeobfuscationMaxLength(0); + } } public void sync() { - JadxSettingsAdapter.store(this); + configAdapter.save(settingsData); } - private void partialSync(Consumer updater) { - JadxSettings settings = JadxSettingsAdapter.load(); - updater.accept(settings); - JadxSettingsAdapter.store(settings); + public String exportSettingsString() { + Gson gson = GsonUtils.defaultGsonBuilder() + .setExclusionStrategies(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes f) { + return f.getAnnotation(JadxConfigExclude.class) != null + || f.getAnnotation(JadxConfigExcludeExport.class) != null; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + }) + .create(); + return gson.toJson(settingsData); } - public void fixOnLoad() { - if (threadsCount <= 0) { - threadsCount = JadxArgs.DEFAULT_THREADS_COUNT; - } - if (deobfuscationMinLength < 0) { - deobfuscationMinLength = 0; - } - if (deobfuscationMaxLength < 0) { - deobfuscationMaxLength = 0; - } - if (settingsVersion != CURRENT_SETTINGS_VERSION) { - upgradeSettings(settingsVersion); - } + public JadxArgs toJadxArgs() { + return settingsData.toJadxArgs(); } - public int getSettingsVersion() { - return settingsVersion; - } - - public void setSettingsVersion(int settingsVersion) { - this.settingsVersion = settingsVersion; + public List getFiles() { + return settingsData.getFiles(); } public String getCmdSelectClass() { - return cmdSelectClass; + return settingsData.getCmdSelectClass(); } public Path getLastOpenFilePath() { - return lastOpenFilePath; + return settingsData.getLastOpenFilePath(); } public void setLastOpenFilePath(Path lastOpenFilePath) { - this.lastOpenFilePath = lastOpenFilePath; - partialSync(settings -> settings.lastOpenFilePath = lastOpenFilePath); + settingsData.setLastOpenFilePath(lastOpenFilePath); } public Path getLastSaveProjectPath() { - return lastSaveProjectPath; - } - - public Path getLastSaveFilePath() { - return lastSaveFilePath; + return settingsData.getLastSaveProjectPath(); } public void setLastSaveProjectPath(Path lastSaveProjectPath) { - this.lastSaveProjectPath = lastSaveProjectPath; - partialSync(settings -> settings.lastSaveProjectPath = lastSaveProjectPath); + settingsData.setLastSaveProjectPath(lastSaveProjectPath); + } + + public Path getLastSaveFilePath() { + return settingsData.getLastSaveFilePath(); } public void setLastSaveFilePath(Path lastSaveFilePath) { - this.lastSaveFilePath = lastSaveFilePath; - partialSync(settings -> settings.lastSaveFilePath = lastSaveFilePath); + settingsData.setLastSaveFilePath(lastSaveFilePath); } public boolean isFlattenPackage() { - return flattenPackage; + return settingsData.isFlattenPackage(); } public void setFlattenPackage(boolean flattenPackage) { - this.flattenPackage = flattenPackage; - partialSync(settings -> settings.flattenPackage = flattenPackage); + settingsData.setFlattenPackage(flattenPackage); } public boolean isCheckForUpdates() { - return checkForUpdates; + return settingsData.isCheckForUpdates(); } public void setCheckForUpdates(boolean checkForUpdates) { - this.checkForUpdates = checkForUpdates; + settingsData.setCheckForUpdates(checkForUpdates); sync(); } public boolean isDisableTooltipOnHover() { - return disableTooltipOnHover; + return settingsData.isDisableTooltipOnHover(); } public void setDisableTooltipOnHover(boolean disableTooltipOnHover) { - this.disableTooltipOnHover = disableTooltipOnHover; + settingsData.setDisableTooltipOnHover(disableTooltipOnHover); } public List getRecentProjects() { - return Collections.unmodifiableList(recentProjects); + return Collections.unmodifiableList(settingsData.getRecentProjects()); } public void addRecentProject(@Nullable Path projectPath) { if (projectPath == null) { return; } + List recentProjects = settingsData.getRecentProjects(); Path normPath = projectPath.toAbsolutePath().normalize(); recentProjects.remove(normPath); recentProjects.add(0, normPath); @@ -268,23 +214,20 @@ public class JadxSettings extends JadxCLIArgs { if (count > RECENT_PROJECTS_COUNT) { recentProjects.subList(RECENT_PROJECTS_COUNT, count).clear(); } - partialSync(settings -> settings.recentProjects = recentProjects); } public void removeRecentProject(Path projectPath) { + List recentProjects = settingsData.getRecentProjects(); recentProjects.remove(projectPath); - partialSync(settings -> settings.recentProjects = recentProjects); } public void saveWindowPos(Window window) { WindowLocation pos = new WindowLocation(window.getClass().getSimpleName(), window.getBounds()); - WindowLocation prevPos = windowPos.put(pos.getWindowId(), pos); - if (prevPos == null || !prevPos.equals(pos)) { - partialSync(settings -> settings.windowPos = windowPos); - } + settingsData.getWindowPos().put(pos.getWindowId(), pos); } public boolean loadWindowPos(Window window) { + Map windowPos = settingsData.getWindowPos(); WindowLocation pos = windowPos.get(window.getClass().getSimpleName()); if (pos == null || pos.getBounds() == null) { return false; @@ -311,595 +254,625 @@ public class JadxSettings extends JadxCLIArgs { return false; } + public int getMainWindowExtendedState() { + return settingsData.getMainWindowExtendedState(); + } + + public void setMainWindowExtendedState(int mainWindowExtendedState) { + settingsData.setMainWindowExtendedState(mainWindowExtendedState); + } + public boolean isShowHeapUsageBar() { - return showHeapUsageBar; + return settingsData.isShowHeapUsageBar(); } public void setShowHeapUsageBar(boolean showHeapUsageBar) { - this.showHeapUsageBar = showHeapUsageBar; - partialSync(settings -> settings.showHeapUsageBar = showHeapUsageBar); + settingsData.setShowHeapUsageBar(showHeapUsageBar); } public boolean isAlwaysSelectOpened() { - return alwaysSelectOpened; + return settingsData.isAlwaysSelectOpened(); } public void setAlwaysSelectOpened(boolean alwaysSelectOpened) { - this.alwaysSelectOpened = alwaysSelectOpened; - partialSync(settings -> settings.alwaysSelectOpened = alwaysSelectOpened); + settingsData.setAlwaysSelectOpened(alwaysSelectOpened); } public boolean isEnablePreviewTab() { - return enablePreviewTab; + return settingsData.isEnablePreviewTab(); } public void setEnablePreviewTab(boolean enablePreviewTab) { - this.enablePreviewTab = enablePreviewTab; - partialSync(settings -> settings.enablePreviewTab = enablePreviewTab); + settingsData.setEnablePreviewTab(enablePreviewTab); } public boolean isUseAlternativeFileDialog() { - return useAlternativeFileDialog; + return settingsData.isUseAlternativeFileDialog(); } public void setUseAlternativeFileDialog(boolean useAlternativeFileDialog) { - this.useAlternativeFileDialog = useAlternativeFileDialog; + settingsData.setUseAlternativeFileDialog(useAlternativeFileDialog); } public String getExcludedPackages() { - return excludedPackages; + return settingsData.getExcludedPackages(); } public void setExcludedPackages(String excludedPackages) { - this.excludedPackages = excludedPackages; - } - - public void setThreadsCount(int threadsCount) { - this.threadsCount = threadsCount; - } - - public void setFallbackMode(boolean fallbackMode) { - this.fallbackMode = fallbackMode; - } - - public void setUseDx(boolean useDx) { - this.useDx = useDx; - } - - public void setSkipResources(boolean skipResources) { - this.skipResources = skipResources; - } - - public void setSkipSources(boolean skipSources) { - this.skipSources = skipSources; - } - - public void setDecompilationMode(DecompilationMode decompilationMode) { - this.decompilationMode = decompilationMode; - } - - public void setShowInconsistentCode(boolean showInconsistentCode) { - this.showInconsistentCode = showInconsistentCode; + settingsData.setExcludedPackages(excludedPackages); } public LangLocale getLangLocale() { - return this.langLocale; + return settingsData.getLangLocale(); } public void setLangLocale(LangLocale langLocale) { - this.langLocale = langLocale; + settingsData.setLangLocale(langLocale); } - public void setCfgOutput(boolean cfgOutput) { - this.cfgOutput = cfgOutput; + public boolean isAutoStartJobs() { + return settingsData.isAutoStartJobs(); } - public void setRawCfgOutput(boolean rawCfgOutput) { - this.rawCfgOutput = rawCfgOutput; + public void setAutoStartJobs(boolean autoStartJobs) { + settingsData.setAutoStartJobs(autoStartJobs); } - public void setVerbose(boolean verbose) { - this.verbose = verbose; + public ShortcutsWrapper getShortcuts() { + return shortcutsWrapper; } - public void setDebugInfo(boolean useDebugInfo) { - this.debugInfo = useDebugInfo; + public int getTreeWidth() { + return settingsData.getTreeWidth(); } - public void setUserRenamesMappingsMode(UserRenamesMappingsMode mode) { - this.userRenamesMappingsMode = mode; + public void setTreeWidth(int treeWidth) { + settingsData.setTreeWidth(treeWidth); } - public void setDeobfuscationOn(boolean deobfuscationOn) { - this.deobfuscationOn = deobfuscationOn; + public float getUiZoom() { + return settingsData.getUiZoom(); } - public void setDeobfuscationMinLength(int deobfuscationMinLength) { - this.deobfuscationMinLength = deobfuscationMinLength; + public void setUiZoom(float uiZoom) { + settingsData.setUiZoom(uiZoom); } - public void setDeobfuscationMaxLength(int deobfuscationMaxLength) { - this.deobfuscationMaxLength = deobfuscationMaxLength; + public Font getFont() { + return fontLoader.getFont(); } - public void setDeobfuscationWhitelistStr(String value) { - this.deobfuscationWhitelistStr = value; + public void setFont(@Nullable Font font) { + fontLoader.setFont(font); + settingsData.setFontStr(fontLoader.getFontStr()); } - public void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode mode) { - this.generatedRenamesMappingFileMode = mode; + public Font getSmaliFont() { + return smaliFontLoader.getFont(); } - public void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) { - this.useSourceNameAsClassNameAlias = useSourceNameAsClassNameAlias; + public void setSmaliFont(@Nullable Font font) { + smaliFontLoader.setFont(font); + settingsData.setSmaliFontStr(smaliFontLoader.getFontStr()); + } + + public String getEditorTheme() { + return settingsData.getEditorTheme(); + } + + public void setEditorTheme(String editorTheme) { + settingsData.setEditorTheme(editorTheme); + } + + public String getLafTheme() { + return settingsData.getLafTheme(); + } + + public void setLafTheme(String lafTheme) { + settingsData.setLafTheme(lafTheme); + } + + public boolean isCodeAreaLineWrap() { + return settingsData.isCodeAreaLineWrap(); + } + + public void setCodeAreaLineWrap(boolean lineWrap) { + settingsData.setCodeAreaLineWrap(lineWrap); + } + + public int getSearchResultsPerPage() { + return settingsData.getSearchResultsPerPage(); + } + + public void setSearchResultsPerPage(int searchResultsPerPage) { + settingsData.setSearchResultsPerPage(searchResultsPerPage); + } + + public boolean isUseAutoSearch() { + return settingsData.isUseAutoSearch(); + } + + public void saveUseAutoSearch(boolean useAutoSearch) { + settingsData.setUseAutoSearch(useAutoSearch); + sync(); + } + + public void saveKeepCommonDialogOpen(boolean keepCommonDialogOpen) { + settingsData.setKeepCommonDialogOpen(keepCommonDialogOpen); + sync(); + } + + public boolean isKeepCommonDialogOpen() { + return settingsData.isKeepCommonDialogOpen(); + } + + public int getMainWindowVerticalSplitterLoc() { + return settingsData.getMainWindowVerticalSplitterLoc(); + } + + public void setMainWindowVerticalSplitterLoc(int location) { + settingsData.setMainWindowVerticalSplitterLoc(location); + } + + public int getDebuggerStackFrameSplitterLoc() { + return settingsData.getDebuggerStackFrameSplitterLoc(); + } + + public void setDebuggerStackFrameSplitterLoc(int location) { + settingsData.setDebuggerStackFrameSplitterLoc(location); + } + + public int getDebuggerVarTreeSplitterLoc() { + return settingsData.getDebuggerVarTreeSplitterLoc(); + } + + public void setDebuggerVarTreeSplitterLoc(int location) { + settingsData.setDebuggerVarTreeSplitterLoc(location); + } + + public String getAdbDialogHost() { + return settingsData.getAdbDialogHost(); + } + + public void setAdbDialogHost(String adbDialogHost) { + settingsData.setAdbDialogHost(adbDialogHost); + } + + public String getAdbDialogPath() { + return settingsData.getAdbDialogPath(); + } + + public void setAdbDialogPath(String adbDialogPath) { + settingsData.setAdbDialogPath(adbDialogPath); + } + + public String getAdbDialogPort() { + return settingsData.getAdbDialogPort(); + } + + public void setAdbDialogPort(String adbDialogPort) { + settingsData.setAdbDialogPort(adbDialogPort); + } + + public CommentsLevel getCommentsLevel() { + return settingsData.getCommentsLevel(); + } + + public void setCommentsLevel(CommentsLevel level) { + settingsData.setCommentsLevel(level); + } + + public int getTypeUpdatesLimitCount() { + return settingsData.getTypeUpdatesLimitCount(); + } + + public void setTypeUpdatesLimitCount(int typeUpdatesLimitCount) { + settingsData.setTypeUpdatesLimitCount(typeUpdatesLimitCount); + } + + public LineNumbersMode getLineNumbersMode() { + return settingsData.getLineNumbersMode(); + } + + public void setLineNumbersMode(LineNumbersMode lineNumbersMode) { + settingsData.setLineNumbersMode(lineNumbersMode); + } + + public CodeCacheMode getCodeCacheMode() { + return settingsData.getCodeCacheMode(); + } + + public void setCodeCacheMode(CodeCacheMode codeCacheMode) { + settingsData.setCodeCacheMode(codeCacheMode); + } + + public UsageCacheMode getUsageCacheMode() { + return settingsData.getUsageCacheMode(); + } + + public void setUsageCacheMode(UsageCacheMode usageCacheMode) { + settingsData.setUsageCacheMode(usageCacheMode); + } + + public @Nullable String getCacheDir() { + return settingsData.getCacheDir(); + } + + public void setCacheDir(@Nullable String cacheDir) { + settingsData.setCacheDir(cacheDir); + } + + public boolean isJumpOnDoubleClick() { + return settingsData.isJumpOnDoubleClick(); + } + + public void setJumpOnDoubleClick(boolean jumpOnDoubleClick) { + settingsData.setJumpOnDoubleClick(jumpOnDoubleClick); + } + + public boolean isDockLogViewer() { + return settingsData.isDockLogViewer(); + } + + public void saveDockLogViewer(boolean dockLogViewer) { + settingsData.setDockLogViewer(dockLogViewer); + sync(); + } + + public boolean isDockQuickTabs() { + return settingsData.isDockQuickTabs(); + } + + public void saveDockQuickTabs(boolean dockQuickTabs) { + settingsData.setDockQuickTabs(dockQuickTabs); + sync(); + } + + public XposedCodegenLanguage getXposedCodegenLanguage() { + return settingsData.getXposedCodegenLanguage(); + } + + public void setXposedCodegenLanguage(XposedCodegenLanguage language) { + settingsData.setXposedCodegenLanguage(language); + } + + public JadxUpdateChannel getJadxUpdateChannel() { + return settingsData.getJadxUpdateChannel(); + } + + public void setJadxUpdateChannel(JadxUpdateChannel channel) { + settingsData.setJadxUpdateChannel(channel); + } + + public TabDndGhostType getTabDndGhostType() { + return settingsData.getTabDndGhostType(); + } + + public void setTabDndGhostType(TabDndGhostType tabDndGhostType) { + settingsData.setTabDndGhostType(tabDndGhostType); + } + + public boolean isRestoreSwitchOverString() { + return settingsData.isRestoreSwitchOverString(); + } + + public void setRestoreSwitchOverString(boolean restoreSwitchOverString) { + settingsData.setRestoreSwitchOverString(restoreSwitchOverString); + } + + public boolean isRenamePrintable() { + return settingsData.isRenamePrintable(); + } + + public UserRenamesMappingsMode getUserRenamesMappingsMode() { + return settingsData.getUserRenamesMappingsMode(); + } + + public void setUserRenamesMappingsMode(UserRenamesMappingsMode userRenamesMappingsMode) { + settingsData.setUserRenamesMappingsMode(userRenamesMappingsMode); + } + + public boolean isInlineAnonymousClasses() { + return settingsData.isInlineAnonymousClasses(); + } + + public void setInlineAnonymousClasses(boolean inlineAnonymousClasses) { + settingsData.setInlineAnonymousClasses(inlineAnonymousClasses); + } + + public boolean isRespectBytecodeAccessModifiers() { + return settingsData.isRespectBytecodeAccessModifiers(); + } + + public void setRespectBytecodeAccessModifiers(boolean respectBytecodeAccessModifiers) { + settingsData.setRespectBytecodeAccessModifiers(respectBytecodeAccessModifiers); + } + + public boolean isRenameCaseSensitive() { + return settingsData.isRenameCaseSensitive(); + } + + public DecompilationMode getDecompilationMode() { + return settingsData.getDecompilationMode(); + } + + public void setDecompilationMode(DecompilationMode decompilationMode) { + settingsData.setDecompilationMode(decompilationMode); + } + + public boolean isInlineMethods() { + return settingsData.isInlineMethods(); + } + + public void setInlineMethods(boolean inlineMethods) { + settingsData.setInlineMethods(inlineMethods); + } + + public boolean isFsCaseSensitive() { + return settingsData.isFsCaseSensitive(); + } + + public void setFsCaseSensitive(boolean fsCaseSensitive) { + settingsData.setFsCaseSensitive(fsCaseSensitive); + } + + public boolean isExtractFinally() { + return settingsData.isExtractFinally(); + } + + public void setExtractFinally(boolean extractFinally) { + settingsData.setExtractFinally(extractFinally); + } + + public int getSourceNameRepeatLimit() { + return settingsData.getSourceNameRepeatLimit(); } public void setSourceNameRepeatLimit(int sourceNameRepeatLimit) { - this.sourceNameRepeatLimit = sourceNameRepeatLimit; + settingsData.setSourceNameRepeatLimit(sourceNameRepeatLimit); } - /** - * @deprecated Use {@link #setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias)} instead. - */ - @Deprecated - public void setDeobfuscationUseSourceNameAsAlias(boolean deobfuscationUseSourceNameAsAlias) { - final var useSourceName = UseSourceNameAsClassNameAlias.create(deobfuscationUseSourceNameAsAlias); - setUseSourceNameAsClassNameAlias(useSourceName); + public boolean isRenameValid() { + return settingsData.isRenameValid(); + } + + public boolean isSkipXmlPrettyPrint() { + return settingsData.isSkipXmlPrettyPrint(); + } + + public void setSkipXmlPrettyPrint(boolean skipXmlPrettyPrint) { + settingsData.setSkipXmlPrettyPrint(skipXmlPrettyPrint); + } + + public UseSourceNameAsClassNameAlias getUseSourceNameAsClassNameAlias() { + return settingsData.getUseSourceNameAsClassNameAlias(); + } + + public void setUseSourceNameAsClassNameAlias(UseSourceNameAsClassNameAlias useSourceNameAsClassNameAlias) { + settingsData.setUseSourceNameAsClassNameAlias(useSourceNameAsClassNameAlias); + } + + public boolean isShowInconsistentCode() { + return settingsData.isShowInconsistentCode(); + } + + public void setShowInconsistentCode(boolean showInconsistentCode) { + settingsData.setShowInconsistentCode(showInconsistentCode); + } + + public boolean isCfgOutput() { + return settingsData.isCfgOutput(); + } + + public void setCfgOutput(boolean cfgOutput) { + settingsData.setCfgOutput(cfgOutput); + } + + public boolean isEscapeUnicode() { + return settingsData.isEscapeUnicode(); + } + + public void setEscapeUnicode(boolean escapeUnicode) { + settingsData.setEscapeUnicode(escapeUnicode); + } + + public JadxArgs.UseKotlinMethodsForVarNames getUseKotlinMethodsForVarNames() { + return settingsData.getUseKotlinMethodsForVarNames(); } public void setUseKotlinMethodsForVarNames(JadxArgs.UseKotlinMethodsForVarNames useKotlinMethodsForVarNames) { - this.useKotlinMethodsForVarNames = useKotlinMethodsForVarNames; + settingsData.setUseKotlinMethodsForVarNames(useKotlinMethodsForVarNames); } - public void setResourceNameSource(ResourceNameSource source) { - this.resourceNameSource = source; + public String getDeobfuscationWhitelistStr() { + return settingsData.getDeobfuscationWhitelistStr(); } - public void setUseHeadersForDetectResourceExtension(boolean enable) { - this.useHeadersForDetectResourceExtensions = enable; + public void setDeobfuscationWhitelistStr(String deobfuscationWhitelistStr) { + settingsData.setDeobfuscationWhitelistStr(deobfuscationWhitelistStr); + } + + public String getGeneratedRenamesMappingFile() { + return settingsData.getGeneratedRenamesMappingFile(); + } + + public boolean isRawCfgOutput() { + return settingsData.isRawCfgOutput(); + } + + public void setRawCfgOutput(boolean rawCfgOutput) { + settingsData.setRawCfgOutput(rawCfgOutput); + } + + public boolean isMoveInnerClasses() { + return settingsData.isMoveInnerClasses(); + } + + public void setMoveInnerClasses(boolean moveInnerClasses) { + settingsData.setMoveInnerClasses(moveInnerClasses); + } + + public boolean isUseDx() { + return settingsData.isUseDx(); + } + + public void setUseDx(boolean useDx) { + settingsData.setUseDx(useDx); + } + + public boolean isAddDebugLines() { + return settingsData.isAddDebugLines(); + } + + public boolean isUseHeadersForDetectResourceExtensions() { + return settingsData.isUseHeadersForDetectResourceExtensions(); + } + + public void setUseHeadersForDetectResourceExtensions(boolean useHeadersForDetectResourceExtensions) { + settingsData.setUseHeadersForDetectResourceExtensions(useHeadersForDetectResourceExtensions); + } + + public Map getPluginOptions() { + return settingsData.getPluginOptions(); + } + + public boolean isDeobfuscationOn() { + return settingsData.isDeobfuscationOn(); + } + + public void setDeobfuscationOn(boolean deobfuscationOn) { + settingsData.setDeobfuscationOn(deobfuscationOn); + } + + public boolean isReplaceConsts() { + return settingsData.isReplaceConsts(); + } + + public void setReplaceConsts(boolean replaceConsts) { + settingsData.setReplaceConsts(replaceConsts); + } + + public boolean isAllowInlineKotlinLambda() { + return settingsData.isAllowInlineKotlinLambda(); + } + + public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) { + settingsData.setAllowInlineKotlinLambda(allowInlineKotlinLambda); + } + + public void setDeobfuscationUseSourceNameAsAlias(Boolean deobfuscationUseSourceNameAsAlias) { + settingsData.setDeobfuscationUseSourceNameAsAlias(deobfuscationUseSourceNameAsAlias); + } + + public void setRenameFlags(Set renameFlags) { + settingsData.setRenameFlags(renameFlags); } public void updateRenameFlag(JadxArgs.RenameEnum flag, boolean enabled) { if (enabled) { - renameFlags.add(flag); + settingsData.getRenameFlags().add(flag); } else { - renameFlags.remove(flag); + settingsData.getRenameFlags().remove(flag); } } - public void setEscapeUnicode(boolean escapeUnicode) { - this.escapeUnicode = escapeUnicode; + public void setUserRenamesMappingsPath(Path userRenamesMappingsPath) { + settingsData.setUserRenamesMappingsPath(userRenamesMappingsPath); } - public void setReplaceConsts(boolean replaceConsts) { - this.replaceConsts = replaceConsts; + public boolean isSkipSources() { + return settingsData.isSkipSources(); } - public void setRespectBytecodeAccessModifiers(boolean respectBytecodeAccessModifiers) { - this.respectBytecodeAccessModifiers = respectBytecodeAccessModifiers; + public boolean isDebugInfo() { + return settingsData.isDebugInfo(); } - public void setUseImports(boolean useImports) { - this.useImports = useImports; + public void setDebugInfo(boolean debugInfo) { + settingsData.setDebugInfo(debugInfo); } - public void setInlineAnonymousClasses(boolean inlineAnonymousClasses) { - this.inlineAnonymousClasses = inlineAnonymousClasses; + public boolean isSkipResources() { + return settingsData.isSkipResources(); } - public void setInlineMethods(boolean inlineMethods) { - this.inlineMethods = inlineMethods; + public void setSkipResources(boolean skipResources) { + settingsData.setSkipResources(skipResources); } - public void setMoveInnerClasses(boolean moveInnerClasses) { - this.moveInnerClasses = moveInnerClasses; + public ResourceNameSource getResourceNameSource() { + return settingsData.getResourceNameSource(); } - public void setAllowInlineKotlinLambda(boolean allowInlineKotlinLambda) { - this.allowInlineKotlinLambda = allowInlineKotlinLambda; + public void setResourceNameSource(ResourceNameSource resourceNameSource) { + settingsData.setResourceNameSource(resourceNameSource); } - public void setExtractFinally(boolean extractFinally) { - this.extractFinally = extractFinally; - } - - public void setRestoreSwitchOverString(boolean restoreSwitchOverString) { - this.restoreSwitchOverString = restoreSwitchOverString; - } - - public void setFsCaseSensitive(boolean fsCaseSensitive) { - this.fsCaseSensitive = fsCaseSensitive; - } - - public boolean isAutoStartJobs() { - return autoStartJobs; - } - - public void setAutoStartJobs(boolean autoStartJobs) { - this.autoStartJobs = autoStartJobs; - } - - public ShortcutsWrapper getShortcuts() { - if (shortcutsWrapper == null) { - shortcutsWrapper = new ShortcutsWrapper(); - shortcutsWrapper.updateShortcuts(shortcuts); - } - return shortcutsWrapper; - } - - public void setExportAsGradleProject(boolean exportAsGradleProject) { - this.exportAsGradleProject = exportAsGradleProject; - } - - public int getTreeWidth() { - return treeWidth; - } - - public void setTreeWidth(int treeWidth) { - this.treeWidth = treeWidth; - partialSync(settings -> settings.treeWidth = JadxSettings.this.treeWidth); - } - - public float getUiZoom() { - return uiZoom; - } - - public void setUiZoom(float uiZoom) { - this.uiZoom = uiZoom; - } - - @JadxSettingsAdapter.GsonExclude - private Font cachedFont = null; - - public Font getFont() { - if (cachedFont != null) { - return cachedFont; - } - if (fontStr.isEmpty()) { - return DEFAULT_FONT; - } - try { - Font font = FontUtils.loadByStr(fontStr); - this.cachedFont = font; - return font; - } catch (Exception e) { - LOG.warn("Failed to load font: {}, reset to default", fontStr, e); - setFont(DEFAULT_FONT); - return DEFAULT_FONT; - } - } - - public void setFont(@Nullable Font font) { - if (font == null) { - setFontStr(""); - } else { - setFontStr(FontUtils.convertToStr(font)); - this.cachedFont = font; - } - } - - public String getFontStr() { - return fontStr; - } - - public void setFontStr(String fontStr) { - this.fontStr = fontStr; - this.cachedFont = null; - } - - public Font getSmaliFont() { - if (smaliFontStr.isEmpty()) { - return DEFAULT_FONT; - } - try { - return FontUtils.loadByStr(smaliFontStr); - } catch (Exception e) { - LOG.warn("Failed to load font: {} for smali, reset to default", smaliFontStr, e); - setSmaliFont(DEFAULT_FONT); - return DEFAULT_FONT; - } - } - - public void setSmaliFont(@Nullable Font font) { - if (font == null) { - this.smaliFontStr = ""; - } else { - this.smaliFontStr = FontUtils.convertToStr(font); - } - } - - public void setLogLevel(LogHelper.LogLevelEnum level) { - this.logLevel = level; - } - - public String getEditorTheme() { - return editorTheme; - } - - public void setEditorTheme(String editorTheme) { - this.editorTheme = editorTheme; - } - - public String getLafTheme() { - return lafTheme; - } - - public void setLafTheme(String lafTheme) { - this.lafTheme = lafTheme; - } - - public int getMainWindowExtendedState() { - return mainWindowExtendedState; - } - - public void setMainWindowExtendedState(int mainWindowExtendedState) { - this.mainWindowExtendedState = mainWindowExtendedState; - partialSync(settings -> settings.mainWindowExtendedState = mainWindowExtendedState); - } - - public void setCodeAreaLineWrap(boolean lineWrap) { - this.codeAreaLineWrap = lineWrap; - } - - public boolean isCodeAreaLineWrap() { - return this.codeAreaLineWrap; - } - - public int getSrhResourceSkipSize() { - return srhResourceSkipSize; - } - - public void setSrhResourceSkipSize(int size) { - srhResourceSkipSize = size; - } - - public int getSearchResultsPerPage() { - return searchResultsPerPage; - } - - public void setSearchResultsPerPage(int searchResultsPerPage) { - this.searchResultsPerPage = searchResultsPerPage; - } - - public boolean isUseAutoSearch() { - return useAutoSearch; - } - - public void setUseAutoSearch(boolean useAutoSearch) { - this.useAutoSearch = useAutoSearch; - partialSync(settings -> settings.useAutoSearch = useAutoSearch); - } - - public void setKeepCommonDialogOpen(boolean yes) { - keepCommonDialogOpen = yes; - } - - public boolean getKeepCommonDialogOpen() { - return keepCommonDialogOpen; - } - - public void setSmaliAreaShowBytecode(boolean yes) { - smaliAreaShowBytecode = yes; - } - - public boolean getSmaliAreaShowBytecode() { - return smaliAreaShowBytecode; - } - - public void setMainWindowVerticalSplitterLoc(int location) { - mainWindowVerticalSplitterLoc = location; - partialSync(settings -> settings.mainWindowVerticalSplitterLoc = location); - } - - public int getMainWindowVerticalSplitterLoc() { - return mainWindowVerticalSplitterLoc; - } - - public void setDebuggerStackFrameSplitterLoc(int location) { - debuggerStackFrameSplitterLoc = location; - partialSync(settings -> settings.debuggerStackFrameSplitterLoc = location); - } - - public int getDebuggerStackFrameSplitterLoc() { - return debuggerStackFrameSplitterLoc; - } - - public void setDebuggerVarTreeSplitterLoc(int location) { - debuggerVarTreeSplitterLoc = location; - partialSync(settings -> debuggerVarTreeSplitterLoc = location); - } - - public int getDebuggerVarTreeSplitterLoc() { - return debuggerVarTreeSplitterLoc; - } - - public String getAdbDialogPath() { - return adbDialogPath; - } - - public void setAdbDialogPath(String path) { - this.adbDialogPath = path; - } - - public String getAdbDialogHost() { - return adbDialogHost; - } - - public void setAdbDialogHost(String host) { - this.adbDialogHost = host; - } - - public String getAdbDialogPort() { - return adbDialogPort; - } - - public void setAdbDialogPort(String port) { - this.adbDialogPort = port; - } - - public void setCommentsLevel(CommentsLevel level) { - this.commentsLevel = level; - } - - public LineNumbersMode getLineNumbersMode() { - return lineNumbersMode; + public IntegerFormat getIntegerFormat() { + return settingsData.getIntegerFormat(); } public void setIntegerFormat(IntegerFormat format) { - this.integerFormat = format; + settingsData.setIntegerFormat(format); } - public void setTypeUpdatesLimitCount(int typeUpdatesLimitCount) { - this.typeUpdatesLimitCount = typeUpdatesLimitCount; + public boolean isFallbackMode() { + return settingsData.isFallbackMode(); } - public void setLineNumbersMode(LineNumbersMode lineNumbersMode) { - this.lineNumbersMode = lineNumbersMode; + public boolean isUseImports() { + return settingsData.isUseImports(); } - public void setPluginOptions(Map pluginOptions) { - this.pluginOptions = pluginOptions; + public void setUseImports(boolean useImports) { + settingsData.setUseImports(useImports); } - public CodeCacheMode getCodeCacheMode() { - return codeCacheMode; + public int getDeobfuscationMinLength() { + return settingsData.getDeobfuscationMinLength(); } - public void setCodeCacheMode(CodeCacheMode codeCacheMode) { - this.codeCacheMode = codeCacheMode; + public void setDeobfuscationMinLength(int deobfuscationMinLength) { + settingsData.setDeobfuscationMinLength(deobfuscationMinLength); } - public UsageCacheMode getUsageCacheMode() { - return usageCacheMode; + public GeneratedRenamesMappingFileMode getGeneratedRenamesMappingFileMode() { + return settingsData.getGeneratedRenamesMappingFileMode(); } - public void setUsageCacheMode(UsageCacheMode usageCacheMode) { - this.usageCacheMode = usageCacheMode; + public void setGeneratedRenamesMappingFileMode(GeneratedRenamesMappingFileMode generatedRenamesMappingFileMode) { + settingsData.setGeneratedRenamesMappingFileMode(generatedRenamesMappingFileMode); } - public @Nullable String getCacheDir() { - return cacheDir; + public int getDeobfuscationMaxLength() { + return settingsData.getDeobfuscationMaxLength(); } - public void setCacheDir(@Nullable String cacheDir) { - this.cacheDir = cacheDir; + public void setDeobfuscationMaxLength(int deobfuscationMaxLength) { + settingsData.setDeobfuscationMaxLength(deobfuscationMaxLength); } - public boolean isJumpOnDoubleClick() { - return jumpOnDoubleClick; + public int getThreadsCount() { + return settingsData.getThreadsCount(); } - public void setJumpOnDoubleClick(boolean jumpOnDoubleClick) { - this.jumpOnDoubleClick = jumpOnDoubleClick; + public void setThreadsCount(int threadsCount) { + settingsData.setThreadsCount(threadsCount); } - public boolean isDockLogViewer() { - return dockLogViewer; + public SaveOptionEnum getSaveOption() { + return settingsData.getSaveOption(); } - public void setDockLogViewer(boolean dockLogViewer) { - this.dockLogViewer = dockLogViewer; - partialSync(settings -> settings.dockLogViewer = dockLogViewer); + public void setSaveOption(SaveOptionEnum saveOption) { + settingsData.setSaveOption(saveOption); } - public boolean isDockQuickTabs() { - return dockQuickTabs; + public boolean isSmaliAreaShowBytecode() { + return settingsData.isSmaliAreaShowBytecode(); } - public void setDockQuickTabs(boolean dockQuickTabs) { - this.dockQuickTabs = dockQuickTabs; - partialSync(settings -> settings.dockQuickTabs = dockQuickTabs); - } - - public XposedCodegenLanguage getXposedCodegenLanguage() { - return xposedCodegenLanguage; - } - - public void setXposedCodegenLanguage(XposedCodegenLanguage language) { - this.xposedCodegenLanguage = language; - } - - public JadxUpdateChannel getJadxUpdateChannel() { - return jadxUpdateChannel; - } - - public void setJadxUpdateChannel(JadxUpdateChannel channel) { - this.jadxUpdateChannel = channel; - } - - public void setTabDndGhostType(TabDndGhostType tabDndGhostType) { - this.tabDndGhostType = tabDndGhostType; - } - - public TabDndGhostType getTabDndGhostType() { - return this.tabDndGhostType; - } - - private void upgradeSettings(int fromVersion) { - LOG.debug("upgrade settings from version: {} to {}", fromVersion, CURRENT_SETTINGS_VERSION); - if (fromVersion <= 10) { - fromVersion = 11; - } - if (fromVersion == 11) { - inlineMethods = true; - fromVersion++; - } - if (fromVersion == 12) { - alwaysSelectOpened = false; - fromVersion++; - } - if (fromVersion == 13) { - lafTheme = LafManager.INITIAL_THEME_NAME; - fromVersion++; - } - if (fromVersion == 14) { - useKotlinMethodsForVarNames = JadxArgs.UseKotlinMethodsForVarNames.APPLY; - fromVersion++; - } - if (fromVersion == 15) { - generatedRenamesMappingFileMode = GeneratedRenamesMappingFileMode.getDefault(); - fromVersion++; - } - if (fromVersion == 16) { - if (fallbackMode) { - decompilationMode = DecompilationMode.FALLBACK; - } else { - decompilationMode = DecompilationMode.AUTO; - } - fromVersion++; - } - if (fromVersion == 17) { - checkForUpdates = true; - fromVersion++; - } - if (fromVersion == 18) { - xposedCodegenLanguage = XposedCodegenLanguage.JAVA; - fromVersion++; - } - if (fromVersion == 19) { - tabDndGhostType = TabDndGhostType.OUTLINE; - fromVersion++; - } - if (fromVersion == 20) { - jadxUpdateChannel = JadxUpdateChannel.STABLE; - fromVersion++; - } - if (fromVersion == 21) { - migrateUseSourceNameAsClassNameAlias(); - fromVersion++; - } - if (fromVersion != CURRENT_SETTINGS_VERSION) { - LOG.warn("Incorrect settings upgrade. Expected version: {}, got: {}", CURRENT_SETTINGS_VERSION, fromVersion); - } - settingsVersion = CURRENT_SETTINGS_VERSION; - sync(); - } - - @SuppressWarnings("deprecation") - private void migrateUseSourceNameAsClassNameAlias() { - final var deobfuscationUseSourceNameAsAlias = this.deobfuscationUseSourceNameAsAlias; - if (deobfuscationUseSourceNameAsAlias != null) { - useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.create(deobfuscationUseSourceNameAsAlias); - } + public void setSmaliAreaShowBytecode(boolean smaliAreaShowBytecode) { + settingsData.setSmaliAreaShowBytecode(smaliAreaShowBytecode); } } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsAdapter.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsAdapter.java deleted file mode 100644 index abd2d5151..000000000 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsAdapter.java +++ /dev/null @@ -1,108 +0,0 @@ -package jadx.gui.settings; - -import java.awt.Rectangle; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.Modifier; -import java.nio.file.Path; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.ExclusionStrategy; -import com.google.gson.FieldAttributes; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; - -import jadx.core.utils.GsonUtils; -import jadx.gui.utils.PathTypeAdapter; -import jadx.gui.utils.RectangleTypeAdapter; - -public class JadxSettingsAdapter { - - private static final Logger LOG = LoggerFactory.getLogger(JadxSettingsAdapter.class); - - private static final JadxSettingsStorage STORAGE = new JadxSettingsStorage(); - - private static final ExclusionStrategy EXCLUDE_FIELDS = new ExclusionStrategy() { - @Override - public boolean shouldSkipField(FieldAttributes f) { - return JadxSettings.SKIP_FIELDS.contains(f.getName()) - || f.hasModifier(Modifier.PUBLIC) - || f.hasModifier(Modifier.TRANSIENT) - || f.hasModifier(Modifier.STATIC) - || f.getAnnotation(GsonExclude.class) != null; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - return false; - } - }; - - private static final Gson GSON = makeGsonBuilder().create(); - - private JadxSettingsAdapter() { - } - - public static JadxSettings load() { - try { - JadxSettings settings = fromString(STORAGE.load()); - if (settings == null) { - LOG.debug("Created new settings."); - settings = JadxSettings.makeDefault(); - } else { - settings.fixOnLoad(); - } - return settings; - } catch (Exception e) { - LOG.error("Error load settings. Settings will reset", e); - return new JadxSettings(); - } - } - - public static void store(JadxSettings settings) { - try { - STORAGE.save(makeString(settings)); - } catch (Exception e) { - LOG.error("Error store settings", e); - } - } - - public static JadxSettings fromString(String jsonSettings) { - if (jsonSettings == null) { - return null; - } - return GSON.fromJson(jsonSettings, JadxSettings.class); - } - - public static String makeString(JadxSettings settings) { - return GSON.toJson(settings); - } - - public static JsonObject makeJsonObject(JadxSettings settings) { - return GSON.toJsonTree(settings).getAsJsonObject(); - } - - public static void fill(JadxSettings settings, String jsonStr) { - GsonUtils.fillObjectFromJsonString(makeGsonBuilder(), settings, jsonStr); - } - - private static GsonBuilder makeGsonBuilder() { - return GsonUtils.defaultGsonBuilder() - .setExclusionStrategies(EXCLUDE_FIELDS) - .registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton()) - .registerTypeHierarchyAdapter(Rectangle.class, RectangleTypeAdapter.singleton()); - } - - /** - * Annotation for specifying fields that should not be saved/loaded - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface GsonExclude { - } -} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsData.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsData.java new file mode 100644 index 000000000..8c3f1634f --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsData.java @@ -0,0 +1,481 @@ +package jadx.gui.settings; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.JFrame; + +import org.jetbrains.annotations.Nullable; + +import jadx.cli.LogHelper; +import jadx.gui.cache.code.CodeCacheMode; +import jadx.gui.cache.usage.UsageCacheMode; +import jadx.gui.settings.data.SaveOptionEnum; +import jadx.gui.ui.action.ActionModel; +import jadx.gui.ui.tab.dnd.TabDndGhostType; +import jadx.gui.utils.LafManager; +import jadx.gui.utils.LangLocale; +import jadx.gui.utils.NLS; +import jadx.gui.utils.shortcut.Shortcut; + +/** + * Data class to hold all jadx-gui settings. + * Also inherit all options from jadx-cli. + * Serialized/deserialized as JSON in {@link jadx.cli.config.JadxConfigAdapter}. + * Annotation {@link JadxConfigExcludeExport} used to exclude environment (files, window states) + * fields from copy/export. + */ +public class JadxSettingsData extends JadxGUIArgs { + public static final int CURRENT_SETTINGS_VERSION = 23; + + private static final Path USER_HOME = Paths.get(System.getProperty("user.home")); + + @JadxConfigExcludeExport + private Path lastSaveProjectPath = USER_HOME; + @JadxConfigExcludeExport + private Path lastOpenFilePath = USER_HOME; + @JadxConfigExcludeExport + private Path lastSaveFilePath = USER_HOME; + @JadxConfigExcludeExport + private List recentProjects = new ArrayList<>(); + + @JadxConfigExcludeExport + private Map windowPos = new HashMap<>(); + @JadxConfigExcludeExport + private int mainWindowExtendedState = JFrame.NORMAL; + + private boolean flattenPackage = false; + private boolean checkForUpdates = true; + private JadxUpdateChannel jadxUpdateChannel = JadxUpdateChannel.STABLE; + + private float uiZoom = 1.0f; + private String fontStr = ""; + private String smaliFontStr = ""; + private String editorTheme = ""; + + private String lafTheme = LafManager.INITIAL_THEME_NAME; + private LangLocale langLocale = NLS.defaultLocale(); + private boolean autoStartJobs = false; + private String excludedPackages = ""; + private SaveOptionEnum saveOption = SaveOptionEnum.ASK; + + private Map shortcuts = new HashMap<>(); + + private boolean showHeapUsageBar = false; + private boolean alwaysSelectOpened = false; + private boolean enablePreviewTab = false; + private boolean useAlternativeFileDialog = false; + private boolean codeAreaLineWrap = false; + private int searchResultsPerPage = 50; + private boolean useAutoSearch = true; + private boolean keepCommonDialogOpen = false; + private boolean smaliAreaShowBytecode = false; + private LineNumbersMode lineNumbersMode = LineNumbersMode.AUTO; + + private int mainWindowVerticalSplitterLoc = 300; + private int debuggerStackFrameSplitterLoc = 300; + private int debuggerVarTreeSplitterLoc = 700; + + private String adbDialogPath = ""; + private String adbDialogHost = "localhost"; + private String adbDialogPort = "5037"; + + private CodeCacheMode codeCacheMode = CodeCacheMode.DISK; + private UsageCacheMode usageCacheMode = UsageCacheMode.DISK; + + /** + * Cache dir option values: + * null - default (system) + * "." - at project dir + * other - custom + */ + private @Nullable String cacheDir = null; + + private boolean jumpOnDoubleClick = true; + private boolean disableTooltipOnHover = false; + + private XposedCodegenLanguage xposedCodegenLanguage = XposedCodegenLanguage.JAVA; + + private int treeWidth = 130; + private boolean dockLogViewer = true; + private boolean dockQuickTabs = false; + private TabDndGhostType tabDndGhostType = TabDndGhostType.OUTLINE; + + private int settingsVersion = CURRENT_SETTINGS_VERSION; + + public JadxSettingsData() { + this.logLevel = LogHelper.LogLevelEnum.INFO; + } + + public String getAdbDialogHost() { + return adbDialogHost; + } + + public void setAdbDialogHost(String adbDialogHost) { + this.adbDialogHost = adbDialogHost; + } + + public String getAdbDialogPath() { + return adbDialogPath; + } + + public void setAdbDialogPath(String adbDialogPath) { + this.adbDialogPath = adbDialogPath; + } + + public String getAdbDialogPort() { + return adbDialogPort; + } + + public void setAdbDialogPort(String adbDialogPort) { + this.adbDialogPort = adbDialogPort; + } + + public boolean isAlwaysSelectOpened() { + return alwaysSelectOpened; + } + + public void setAlwaysSelectOpened(boolean alwaysSelectOpened) { + this.alwaysSelectOpened = alwaysSelectOpened; + } + + public boolean isAutoStartJobs() { + return autoStartJobs; + } + + public void setAutoStartJobs(boolean autoStartJobs) { + this.autoStartJobs = autoStartJobs; + } + + public @Nullable String getCacheDir() { + return cacheDir; + } + + public void setCacheDir(@Nullable String cacheDir) { + this.cacheDir = cacheDir; + } + + public boolean isCheckForUpdates() { + return checkForUpdates; + } + + public void setCheckForUpdates(boolean checkForUpdates) { + this.checkForUpdates = checkForUpdates; + } + + public boolean isCodeAreaLineWrap() { + return codeAreaLineWrap; + } + + public void setCodeAreaLineWrap(boolean codeAreaLineWrap) { + this.codeAreaLineWrap = codeAreaLineWrap; + } + + public CodeCacheMode getCodeCacheMode() { + return codeCacheMode; + } + + public void setCodeCacheMode(CodeCacheMode codeCacheMode) { + this.codeCacheMode = codeCacheMode; + } + + public int getDebuggerStackFrameSplitterLoc() { + return debuggerStackFrameSplitterLoc; + } + + public void setDebuggerStackFrameSplitterLoc(int debuggerStackFrameSplitterLoc) { + this.debuggerStackFrameSplitterLoc = debuggerStackFrameSplitterLoc; + } + + public int getDebuggerVarTreeSplitterLoc() { + return debuggerVarTreeSplitterLoc; + } + + public void setDebuggerVarTreeSplitterLoc(int debuggerVarTreeSplitterLoc) { + this.debuggerVarTreeSplitterLoc = debuggerVarTreeSplitterLoc; + } + + public boolean isDisableTooltipOnHover() { + return disableTooltipOnHover; + } + + public void setDisableTooltipOnHover(boolean disableTooltipOnHover) { + this.disableTooltipOnHover = disableTooltipOnHover; + } + + public boolean isDockLogViewer() { + return dockLogViewer; + } + + public void setDockLogViewer(boolean dockLogViewer) { + this.dockLogViewer = dockLogViewer; + } + + public boolean isDockQuickTabs() { + return dockQuickTabs; + } + + public void setDockQuickTabs(boolean dockQuickTabs) { + this.dockQuickTabs = dockQuickTabs; + } + + public String getEditorTheme() { + return editorTheme; + } + + public void setEditorTheme(String editorTheme) { + this.editorTheme = editorTheme; + } + + public boolean isEnablePreviewTab() { + return enablePreviewTab; + } + + public void setEnablePreviewTab(boolean enablePreviewTab) { + this.enablePreviewTab = enablePreviewTab; + } + + public String getExcludedPackages() { + return excludedPackages; + } + + public void setExcludedPackages(String excludedPackages) { + this.excludedPackages = excludedPackages; + } + + public boolean isFlattenPackage() { + return flattenPackage; + } + + public void setFlattenPackage(boolean flattenPackage) { + this.flattenPackage = flattenPackage; + } + + public JadxUpdateChannel getJadxUpdateChannel() { + return jadxUpdateChannel; + } + + public void setJadxUpdateChannel(JadxUpdateChannel jadxUpdateChannel) { + this.jadxUpdateChannel = jadxUpdateChannel; + } + + public boolean isJumpOnDoubleClick() { + return jumpOnDoubleClick; + } + + public void setJumpOnDoubleClick(boolean jumpOnDoubleClick) { + this.jumpOnDoubleClick = jumpOnDoubleClick; + } + + public boolean isKeepCommonDialogOpen() { + return keepCommonDialogOpen; + } + + public void setKeepCommonDialogOpen(boolean keepCommonDialogOpen) { + this.keepCommonDialogOpen = keepCommonDialogOpen; + } + + public String getLafTheme() { + return lafTheme; + } + + public void setLafTheme(String lafTheme) { + this.lafTheme = lafTheme; + } + + public LangLocale getLangLocale() { + return langLocale; + } + + public void setLangLocale(LangLocale langLocale) { + this.langLocale = langLocale; + } + + public Path getLastOpenFilePath() { + return lastOpenFilePath; + } + + public void setLastOpenFilePath(Path lastOpenFilePath) { + this.lastOpenFilePath = lastOpenFilePath; + } + + public Path getLastSaveFilePath() { + return lastSaveFilePath; + } + + public void setLastSaveFilePath(Path lastSaveFilePath) { + this.lastSaveFilePath = lastSaveFilePath; + } + + public Path getLastSaveProjectPath() { + return lastSaveProjectPath; + } + + public void setLastSaveProjectPath(Path lastSaveProjectPath) { + this.lastSaveProjectPath = lastSaveProjectPath; + } + + public LineNumbersMode getLineNumbersMode() { + return lineNumbersMode; + } + + public void setLineNumbersMode(LineNumbersMode lineNumbersMode) { + this.lineNumbersMode = lineNumbersMode; + } + + public int getMainWindowExtendedState() { + return mainWindowExtendedState; + } + + public void setMainWindowExtendedState(int mainWindowExtendedState) { + this.mainWindowExtendedState = mainWindowExtendedState; + } + + public int getMainWindowVerticalSplitterLoc() { + return mainWindowVerticalSplitterLoc; + } + + public void setMainWindowVerticalSplitterLoc(int mainWindowVerticalSplitterLoc) { + this.mainWindowVerticalSplitterLoc = mainWindowVerticalSplitterLoc; + } + + public List getRecentProjects() { + return recentProjects; + } + + public void setRecentProjects(List recentProjects) { + this.recentProjects = recentProjects; + } + + public SaveOptionEnum getSaveOption() { + return saveOption; + } + + public void setSaveOption(SaveOptionEnum saveOption) { + this.saveOption = saveOption; + } + + public int getSearchResultsPerPage() { + return searchResultsPerPage; + } + + public void setSearchResultsPerPage(int searchResultsPerPage) { + this.searchResultsPerPage = searchResultsPerPage; + } + + public int getSettingsVersion() { + return settingsVersion; + } + + public void setSettingsVersion(int settingsVersion) { + this.settingsVersion = settingsVersion; + } + + public Map getShortcuts() { + return shortcuts; + } + + public void setShortcuts(Map shortcuts) { + this.shortcuts = shortcuts; + } + + public boolean isShowHeapUsageBar() { + return showHeapUsageBar; + } + + public void setShowHeapUsageBar(boolean showHeapUsageBar) { + this.showHeapUsageBar = showHeapUsageBar; + } + + public boolean isSmaliAreaShowBytecode() { + return smaliAreaShowBytecode; + } + + public void setSmaliAreaShowBytecode(boolean smaliAreaShowBytecode) { + this.smaliAreaShowBytecode = smaliAreaShowBytecode; + } + + public String getFontStr() { + return fontStr; + } + + public void setFontStr(String fontStr) { + this.fontStr = fontStr; + } + + public String getSmaliFontStr() { + return smaliFontStr; + } + + public void setSmaliFontStr(String smaliFontStr) { + this.smaliFontStr = smaliFontStr; + } + + public TabDndGhostType getTabDndGhostType() { + return tabDndGhostType; + } + + public void setTabDndGhostType(TabDndGhostType tabDndGhostType) { + this.tabDndGhostType = tabDndGhostType; + } + + public int getTreeWidth() { + return treeWidth; + } + + public void setTreeWidth(int treeWidth) { + this.treeWidth = treeWidth; + } + + public float getUiZoom() { + return uiZoom; + } + + public void setUiZoom(float uiZoom) { + this.uiZoom = uiZoom; + } + + public UsageCacheMode getUsageCacheMode() { + return usageCacheMode; + } + + public void setUsageCacheMode(UsageCacheMode usageCacheMode) { + this.usageCacheMode = usageCacheMode; + } + + public boolean isUseAlternativeFileDialog() { + return useAlternativeFileDialog; + } + + public void setUseAlternativeFileDialog(boolean useAlternativeFileDialog) { + this.useAlternativeFileDialog = useAlternativeFileDialog; + } + + public boolean isUseAutoSearch() { + return useAutoSearch; + } + + public void setUseAutoSearch(boolean useAutoSearch) { + this.useAutoSearch = useAutoSearch; + } + + public Map getWindowPos() { + return windowPos; + } + + public void setWindowPos(Map windowPos) { + this.windowPos = windowPos; + } + + public XposedCodegenLanguage getXposedCodegenLanguage() { + return xposedCodegenLanguage; + } + + public void setXposedCodegenLanguage(XposedCodegenLanguage xposedCodegenLanguage) { + this.xposedCodegenLanguage = xposedCodegenLanguage; + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsStorage.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsStorage.java deleted file mode 100644 index fb09c070d..000000000 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsStorage.java +++ /dev/null @@ -1,59 +0,0 @@ -package jadx.gui.settings; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.prefs.Preferences; - -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import jadx.core.utils.StringUtils; -import jadx.core.utils.files.FileUtils; -import jadx.gui.JadxGUI; -import jadx.gui.utils.files.JadxFiles; - -/** - * Jadx settings storage. Select first available option: - * 1. json file in system 'config' directory (preferred) - * 2. using java preferences api: use Windows registry or xml on Linux/Mac (obsolete, load only) - */ -public class JadxSettingsStorage { - private static final Logger LOG = LoggerFactory.getLogger(JadxSettingsStorage.class); - - private final Path configFile = initConfigFile(); - - public synchronized @Nullable String load() throws IOException { - if (Files.exists(configFile)) { - return FileUtils.readFile(configFile); - } - return null; - } - - public synchronized void save(String jsonStr) throws IOException { - FileUtils.writeFile(configFile, jsonStr); - } - - private static Path initConfigFile() { - Path confPath = JadxFiles.GUI_CONF; - if (!Files.exists(confPath)) { - copyFromPreferences(confPath); - } - LOG.debug("Using config: {}", confPath); - return confPath; - } - - private static void copyFromPreferences(Path confPath) { - try { - Preferences prefs = Preferences.userNodeForPackage(JadxGUI.class); - String str = prefs.get("jadx.gui.settings", ""); - if (StringUtils.notEmpty(str)) { - FileUtils.writeFile(confPath, str); - LOG.warn("Settings moved from java preferences to config file: {}", confPath); - } - } catch (Exception e) { - LOG.warn("Failed to load settings from preferences", e); - } - } -} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxUpdateChannel.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxUpdateChannel.java new file mode 100644 index 000000000..8de36a4b2 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxUpdateChannel.java @@ -0,0 +1,6 @@ +package jadx.gui.settings; + +public enum JadxUpdateChannel { + STABLE, + UNSTABLE, +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxUpdateChannel.kt b/jadx-gui/src/main/java/jadx/gui/settings/JadxUpdateChannel.kt deleted file mode 100644 index 3067b3eaa..000000000 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxUpdateChannel.kt +++ /dev/null @@ -1,6 +0,0 @@ -package jadx.gui.settings - -enum class JadxUpdateChannel { - STABLE, - UNSTABLE, -} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/XposedCodegenLanguage.java b/jadx-gui/src/main/java/jadx/gui/settings/XposedCodegenLanguage.java new file mode 100644 index 000000000..6668a8ea6 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/XposedCodegenLanguage.java @@ -0,0 +1,6 @@ +package jadx.gui.settings; + +public enum XposedCodegenLanguage { + JAVA, + KOTLIN, +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/XposedCodegenLanguage.kt b/jadx-gui/src/main/java/jadx/gui/settings/XposedCodegenLanguage.kt deleted file mode 100644 index 6ef40fab9..000000000 --- a/jadx-gui/src/main/java/jadx/gui/settings/XposedCodegenLanguage.kt +++ /dev/null @@ -1,6 +0,0 @@ -package jadx.gui.settings - -enum class XposedCodegenLanguage { - JAVA, - KOTLIN, -} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/data/SaveOptionEnum.java b/jadx-gui/src/main/java/jadx/gui/settings/data/SaveOptionEnum.java new file mode 100644 index 000000000..bee7f6f30 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/settings/data/SaveOptionEnum.java @@ -0,0 +1,7 @@ +package jadx.gui.settings.data; + +public enum SaveOptionEnum { + ASK, + NEVER, + ALWAYS +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java index db811f313..cb6f6c088 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/ui/JadxSettingsWindow.java @@ -42,8 +42,6 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.JsonObject; - import jadx.api.CommentsLevel; import jadx.api.DecompilationMode; import jadx.api.JadxArgs; @@ -56,14 +54,14 @@ import jadx.api.args.UseSourceNameAsClassNameAlias; import jadx.api.plugins.events.JadxEvents; import jadx.api.plugins.events.types.ReloadSettingsWindow; import jadx.api.plugins.gui.ISettingsGroup; -import jadx.core.utils.GsonUtils; import jadx.core.utils.StringUtils; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.gui.settings.JadxSettings; -import jadx.gui.settings.JadxSettingsAdapter; +import jadx.gui.settings.JadxSettingsData; import jadx.gui.settings.JadxUpdateChannel; import jadx.gui.settings.LineNumbersMode; import jadx.gui.settings.XposedCodegenLanguage; +import jadx.gui.settings.data.SaveOptionEnum; import jadx.gui.settings.ui.cache.CacheSettingsGroup; import jadx.gui.settings.ui.font.JadxFontDialog; import jadx.gui.settings.ui.plugins.PluginSettings; @@ -99,7 +97,7 @@ public class JadxSettingsWindow extends JDialog { public JadxSettingsWindow(MainWindow mainWindow, JadxSettings settings) { this.mainWindow = mainWindow; this.settings = settings; - this.startSettings = JadxSettingsAdapter.makeString(settings); + this.startSettings = settings.getSettingsJsonString(); this.startSettingsHash = calcSettingsHash(); this.prevLang = settings.getLangLocale(); @@ -279,7 +277,7 @@ public class JadxSettingsWindow extends JDialog { JCheckBox useHeaders = new JCheckBox(); useHeaders.setSelected(settings.isUseHeadersForDetectResourceExtensions()); useHeaders.addItemListener(e -> { - settings.setUseHeadersForDetectResourceExtension(e.getStateChange() == ItemEvent.SELECTED); + settings.setUseHeadersForDetectResourceExtensions(e.getStateChange() == ItemEvent.SELECTED); needReload(); }); @@ -376,10 +374,10 @@ public class JadxSettingsWindow extends JDialog { } private SettingsGroup makeProjectGroup() { - JComboBox dropdown = new JComboBox<>(JadxSettings.SAVEOPTION.values()); + JComboBox dropdown = new JComboBox<>(SaveOptionEnum.values()); dropdown.setSelectedItem(settings.getSaveOption()); dropdown.addActionListener(e -> { - settings.setSaveOption((JadxSettings.SAVEOPTION) dropdown.getSelectedItem()); + settings.setSaveOption((SaveOptionEnum) dropdown.getSelectedItem()); needReload(); }); @@ -730,16 +728,14 @@ public class JadxSettingsWindow extends JDialog { needReload(); }); - JComboBox xposedCodegenLanguage = - new JComboBox<>(XposedCodegenLanguage.getEntries().toArray(new XposedCodegenLanguage[0])); + JComboBox xposedCodegenLanguage = new JComboBox<>(XposedCodegenLanguage.values()); xposedCodegenLanguage.setSelectedItem(settings.getXposedCodegenLanguage()); xposedCodegenLanguage.addActionListener(e -> { settings.setXposedCodegenLanguage((XposedCodegenLanguage) xposedCodegenLanguage.getSelectedItem()); mainWindow.loadSettings(); }); - JComboBox updateChannel = - new JComboBox<>(JadxUpdateChannel.getEntries().toArray(new JadxUpdateChannel[0])); + JComboBox updateChannel = new JComboBox<>(JadxUpdateChannel.values()); updateChannel.setSelectedItem(settings.getJadxUpdateChannel()); updateChannel.addActionListener(e -> { settings.setJadxUpdateChannel((JadxUpdateChannel) updateChannel.getSelectedItem()); @@ -788,7 +784,7 @@ public class JadxSettingsWindow extends JDialog { private void cancel() { closeGroups(false); - JadxSettingsAdapter.fill(settings, startSettings); + settings.loadSettingsFromJsonString(startSettings); mainWindow.loadSettings(); dispose(); } @@ -800,8 +796,7 @@ public class JadxSettingsWindow extends JDialog { NLS.str("preferences.reset_title"), JOptionPane.YES_NO_OPTION); if (res == JOptionPane.YES_OPTION) { - String defaults = JadxSettingsAdapter.makeString(JadxSettings.makeDefault()); - JadxSettingsAdapter.fill(settings, defaults); + settings.loadSettingsData(new JadxSettingsData()); mainWindow.loadSettings(); needReload(); getContentPane().removeAll(); @@ -812,15 +807,7 @@ public class JadxSettingsWindow extends JDialog { } private void copySettings() { - JsonObject settingsJson = JadxSettingsAdapter.makeJsonObject(this.settings); - // remove irrelevant preferences - settingsJson.remove("windowPos"); - settingsJson.remove("mainWindowExtendedState"); - settingsJson.remove("lastSaveProjectPath"); - settingsJson.remove("lastOpenFilePath"); - settingsJson.remove("lastSaveFilePath"); - settingsJson.remove("recentProjects"); - String settingsText = GsonUtils.buildGson().toJson(settingsJson); + String settingsText = settings.exportSettingsString(); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); StringSelection selection = new StringSelection(settingsText); clipboard.setContents(selection, selection); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index c3459152c..c9964bb4d 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -118,6 +118,7 @@ import jadx.gui.plugins.mappings.RenameMappingsGui; import jadx.gui.plugins.quark.QuarkDialog; import jadx.gui.settings.JadxProject; import jadx.gui.settings.JadxSettings; +import jadx.gui.settings.data.SaveOptionEnum; import jadx.gui.settings.ui.JadxSettingsWindow; import jadx.gui.tree.TreeExpansionService; import jadx.gui.treemodel.ApkSignatureNode; @@ -700,10 +701,10 @@ public class MainWindow extends JFrame { return true; } // Check if we saved settings that indicate what to do - if (settings.getSaveOption() == JadxSettings.SAVEOPTION.NEVER) { + if (settings.getSaveOption() == SaveOptionEnum.NEVER) { return true; } - if (settings.getSaveOption() == JadxSettings.SAVEOPTION.ALWAYS) { + if (settings.getSaveOption() == SaveOptionEnum.ALWAYS) { saveProject(); return true; } @@ -723,7 +724,7 @@ public class MainWindow extends JFrame { switch (res) { case JOptionPane.YES_OPTION: if (remember.isSelected()) { - settings.setSaveOption(JadxSettings.SAVEOPTION.ALWAYS); + settings.setSaveOption(SaveOptionEnum.ALWAYS); settings.sync(); } saveProject(); @@ -731,7 +732,7 @@ public class MainWindow extends JFrame { case JOptionPane.NO_OPTION: if (remember.isSelected()) { - settings.setSaveOption(JadxSettings.SAVEOPTION.NEVER); + settings.setSaveOption(SaveOptionEnum.NEVER); settings.sync(); } return true; @@ -1163,12 +1164,12 @@ public class MainWindow extends JFrame { JCheckBoxMenuItem dockLog = new JCheckBoxMenuItem(NLS.str("menu.dock_log")); dockLog.setState(settings.isDockLogViewer()); - dockLog.addActionListener(event -> settings.setDockLogViewer(!settings.isDockLogViewer())); + dockLog.addActionListener(event -> settings.saveDockLogViewer(!settings.isDockLogViewer())); ActionHandler quickTabsAction = new ActionHandler(ev -> { boolean visible = quickTabsTree == null; setQuickTabsVisibility(visible); - settings.setDockQuickTabs(visible); + settings.saveDockQuickTabs(visible); }); quickTabsAction.setNameAndDesc(NLS.str("menu.dock_quick_tabs")); quickTabsAction.setIcon(Icons.QUICK_TABS); @@ -1586,6 +1587,7 @@ public class MainWindow extends JFrame { shortcutsController.loadSettings(); } + @SuppressWarnings("finally") private void closeWindow() { saveAll(); if (!ensureProjectIsSaved()) { @@ -1599,6 +1601,7 @@ public class MainWindow extends JFrame { if (debuggerPanel != null) { saveSplittersInfo(); } + settings.sync(); closeAll(); UiUtils.uiRunAndWait(() -> { heapUsageBar.reset(); @@ -1758,7 +1761,7 @@ public class MainWindow extends JFrame { } Runnable undock = () -> { hideDockedLog(); - settings.setDockLogViewer(false); + settings.saveDockLogViewer(false); LogViewerDialog.open(this, logOptions); }; logPanel = new LogPanel(this, logOptions, undock, this::hideDockedLog); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java index 19940185e..862aa7890 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java @@ -162,6 +162,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea { public void actionPerformed(ActionEvent e) { boolean wrap = !getLineWrap(); settings.setCodeAreaLineWrap(wrap); + settings.sync(); contentPanel.getTabbedPane().getTabs().forEach(v -> { if (v instanceof AbstractCodeContentPanel) { AbstractCodeArea codeArea = ((AbstractCodeContentPanel) v).getCodeArea(); @@ -172,7 +173,6 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea { } } }); - settings.sync(); } }); popupMenu.add(wrapItem); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java index bbb844a7d..069846503 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java @@ -75,7 +75,7 @@ public final class SmaliArea extends AbstractCodeArea { @Override public void actionPerformed(ActionEvent e) { JadxSettings settings = getContentPanel().getMainWindow().getSettings(); - settings.setSmaliAreaShowBytecode(!settings.getSmaliAreaShowBytecode()); + settings.setSmaliAreaShowBytecode(!settings.isSmaliAreaShowBytecode()); contentPanel.getTabbedPane().getTabs().forEach(v -> { if (v instanceof ClassCodeContentPanel) { switchModel(); @@ -156,7 +156,7 @@ public final class SmaliArea extends AbstractCodeArea { } private boolean shouldUseSmaliPrinterV2() { - return getContentPanel().getMainWindow().getSettings().getSmaliAreaShowBytecode(); + return getContentPanel().getMainWindow().getSettings().isSmaliAreaShowBytecode(); } private abstract class SmaliModel { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/ADBDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/ADBDialog.java index aaa28381e..78bc7ce89 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/ADBDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/ADBDialog.java @@ -46,6 +46,7 @@ import jadx.gui.device.debugger.DebugSettings; import jadx.gui.device.protocol.ADB; import jadx.gui.device.protocol.ADBDevice; import jadx.gui.device.protocol.ADBDeviceInfo; +import jadx.gui.settings.JadxSettings; import jadx.gui.ui.MainWindow; import jadx.gui.ui.panel.IDebugController; import jadx.gui.utils.NLS; @@ -427,13 +428,17 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J @Override public void dispose() { clear(); - super.dispose(); - boolean save = mainWindow.getSettings().getAdbDialogPath().equals(pathTextField.getText()); - boolean save1 = mainWindow.getSettings().getAdbDialogHost().equals(hostTextField.getText()); - boolean save2 = mainWindow.getSettings().getAdbDialogPort().equals(portTextField.getText()); - if (save || save1 || save2) { - mainWindow.getSettings().sync(); + JadxSettings settings = mainWindow.getSettings(); + boolean changed = !settings.getAdbDialogPath().equals(pathTextField.getText()); + changed |= !settings.getAdbDialogHost().equals(hostTextField.getText()); + changed |= !settings.getAdbDialogPort().equals(portTextField.getText()); + if (changed) { + settings.setAdbDialogPath(pathTextField.getText()); + settings.setAdbDialogHost(hostTextField.getText()); + settings.setAdbDialogPort(portTextField.getText()); + settings.sync(); } + super.dispose(); } @Override diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java index d308390c9..105ff7ea4 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommonSearchDialog.java @@ -153,7 +153,7 @@ public abstract class CommonSearchDialog extends JFrame { } else { tabsController.codeJump(node); } - if (!mainWindow.getSettings().getKeepCommonDialogOpen()) { + if (!mainWindow.getSettings().isKeepCommonDialogOpen()) { dispose(); } } @@ -210,11 +210,8 @@ public abstract class CommonSearchDialog extends JFrame { copyBtn.addActionListener(event -> copyAllSearchResults()); JCheckBox cbKeepOpen = new JCheckBox(NLS.str("search_dialog.keep_open")); - cbKeepOpen.setSelected(mainWindow.getSettings().getKeepCommonDialogOpen()); - cbKeepOpen.addActionListener(e -> { - mainWindow.getSettings().setKeepCommonDialogOpen(cbKeepOpen.isSelected()); - mainWindow.getSettings().sync(); - }); + cbKeepOpen.setSelected(mainWindow.getSettings().isKeepCommonDialogOpen()); + cbKeepOpen.addActionListener(e -> mainWindow.getSettings().saveKeepCommonDialogOpen(cbKeepOpen.isSelected())); cbKeepOpen.setAlignmentY(Component.CENTER_ALIGNMENT); JPanel buttonPane = new JPanel(); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/ExceptionDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/ExceptionDialog.java index 043474e8b..276dda6f5 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/ExceptionDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/ExceptionDialog.java @@ -32,10 +32,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.api.JadxDecompiler; +import jadx.cli.config.JadxConfigAdapter; import jadx.commons.app.JadxSystemInfo; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.gui.settings.JadxSettings; -import jadx.gui.settings.JadxSettingsAdapter; +import jadx.gui.settings.JadxSettingsData; import jadx.gui.utils.LafManager; import jadx.gui.utils.Link; @@ -130,10 +131,10 @@ public class ExceptionDialog extends JDialog { JPanel buttonPanel = new JPanel(); JButton exitButton = new JButton("Terminate Jadx"); - exitButton.addActionListener((event) -> System.exit(1)); + exitButton.addActionListener(event -> System.exit(1)); buttonPanel.add(exitButton); JButton closeButton = new JButton("Go back to Jadx"); - closeButton.addActionListener((event) -> setVisible(false)); + closeButton.addActionListener(event -> setVisible(false)); buttonPanel.add(closeButton); JScrollPane messageAreaScroller = new JScrollPane(messageArea); messageAreaScroller.setMinimumSize(new Dimension(600, 400)); @@ -152,7 +153,7 @@ public class ExceptionDialog extends JDialog { final int y = (screenSize.height - getHeight()) / 2; setLocation(x, y); - getRootPane().registerKeyboardAction((event) -> setVisible(false), + getRootPane().registerKeyboardAction(event -> setVisible(false), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); @@ -177,8 +178,14 @@ public class ExceptionDialog extends JDialog { } public static void main(String[] args) { - JadxSettings settings = JadxSettingsAdapter.load(); - LafManager.init(settings); + JadxConfigAdapter configAdapter = JadxSettings.buildConfigAdapter(); + configAdapter.useConfigRef(""); + JadxSettingsData settingsData = configAdapter.load(); + if (settingsData != null) { + JadxSettings settings = new JadxSettings(configAdapter); + settings.loadSettingsData(settingsData); + LafManager.init(settings); + } showTestExceptionDialog(); } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java index 5fef29044..f71b24af2 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java @@ -39,7 +39,7 @@ public class LogViewerDialog extends JFrame { UiUtils.setWindowIcons(this); Runnable dock = () -> { - mainWindow.getSettings().setDockLogViewer(true); + mainWindow.getSettings().saveDockLogViewer(true); dispose(); mainWindow.showLogViewer(LogOptions.current()); }; diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java index a5f098f1d..fc23b718f 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/SearchDialog.java @@ -270,7 +270,7 @@ public class SearchDialog extends CommonSearchDialog { autoSearchCB.setSelected(autoSearch); autoSearchCB.addActionListener(ev -> { boolean newValue = autoSearchCB.isSelected(); - mainWindow.getSettings().setUseAutoSearch(newValue); + mainWindow.getSettings().saveUseAutoSearch(newValue); searchBtn.setVisible(!newValue); initSearchEvents(); if (newValue) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialogPlus.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialogPlus.java index bde05c77f..9af7192b1 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialogPlus.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/UsageDialogPlus.java @@ -597,11 +597,8 @@ public class UsageDialogPlus extends CommonSearchDialog { getRootPane().setDefaultButton(openBtn); JCheckBox cbKeepOpen = new JCheckBox(NLS.str("search_dialog.keep_open")); - cbKeepOpen.setSelected(mainWindow.getSettings().getKeepCommonDialogOpen()); - cbKeepOpen.addActionListener(e -> { - mainWindow.getSettings().setKeepCommonDialogOpen(cbKeepOpen.isSelected()); - mainWindow.getSettings().sync(); - }); + cbKeepOpen.setSelected(mainWindow.getSettings().isKeepCommonDialogOpen()); + cbKeepOpen.addActionListener(e -> mainWindow.getSettings().saveKeepCommonDialogOpen(cbKeepOpen.isSelected())); cbKeepOpen.setAlignmentY(Component.CENTER_ALIGNMENT); JPanel buttonPane = new JPanel(); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/PathTypeAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/PathTypeAdapter.java index df3530e82..20e4bf0a1 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/PathTypeAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/PathTypeAdapter.java @@ -11,7 +11,7 @@ import com.google.gson.stream.JsonWriter; public class PathTypeAdapter { - private static final TypeAdapter SINGLETON = new TypeAdapter() { + private static final TypeAdapter SINGLETON = new TypeAdapter<>() { @Override public void write(JsonWriter out, Path value) throws IOException { if (value == null) { diff --git a/jadx-gui/src/main/java/jadx/gui/utils/RectangleTypeAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/RectangleTypeAdapter.java index 216b12221..457db9677 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/RectangleTypeAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/RectangleTypeAdapter.java @@ -10,7 +10,7 @@ import com.google.gson.stream.JsonWriter; public class RectangleTypeAdapter { - private static final TypeAdapter SINGLETON = new TypeAdapter() { + private static final TypeAdapter SINGLETON = new TypeAdapter<>() { @Override public void write(JsonWriter out, Rectangle value) throws IOException { if (value == null) {