Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
严启真
jadx
提交
65f7c802
J
jadx
项目概览
严启真
/
jadx
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jadx
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
65f7c802
编写于
6月 18, 2022
作者:
S
Skylot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat(gui): add reload and live reload actions (#1537)
上级
d2e6bb23
变更
17
显示空白变更内容
内联
并排
Showing
17 changed file
with
363 addition
and
26 deletion
+363
-26
jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
+1
-0
jadx-core/src/main/java/jadx/core/utils/Utils.java
jadx-core/src/main/java/jadx/core/utils/Utils.java
+1
-1
jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java
jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java
+28
-16
jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java
jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java
+11
-0
jadx-gui/src/main/java/jadx/gui/settings/data/ProjectData.java
...gui/src/main/java/jadx/gui/settings/data/ProjectData.java
+9
-0
jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
+49
-8
jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java
.../src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java
+2
-1
jadx-gui/src/main/java/jadx/gui/utils/fileswatcher/FilesWatcher.java
...c/main/java/jadx/gui/utils/fileswatcher/FilesWatcher.java
+125
-0
jadx-gui/src/main/java/jadx/gui/utils/fileswatcher/LiveReloadWorker.java
...in/java/jadx/gui/utils/fileswatcher/LiveReloadWorker.java
+92
-0
jadx-gui/src/main/java/jadx/gui/utils/ui/ActionHandler.java
jadx-gui/src/main/java/jadx/gui/utils/ui/ActionHandler.java
+23
-0
jadx-gui/src/main/resources/i18n/Messages_de_DE.properties
jadx-gui/src/main/resources/i18n/Messages_de_DE.properties
+3
-0
jadx-gui/src/main/resources/i18n/Messages_en_US.properties
jadx-gui/src/main/resources/i18n/Messages_en_US.properties
+3
-0
jadx-gui/src/main/resources/i18n/Messages_es_ES.properties
jadx-gui/src/main/resources/i18n/Messages_es_ES.properties
+3
-0
jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties
jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties
+3
-0
jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties
jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties
+3
-0
jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties
jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties
+3
-0
jadx-gui/src/main/resources/icons/ui/refresh.svg
jadx-gui/src/main/resources/icons/ui/refresh.svg
+4
-0
未找到文件。
jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
浏览文件 @
65f7c802
...
...
@@ -269,6 +269,7 @@ public class RootNode {
public
void
runPreDecompileStage
()
{
boolean
debugEnabled
=
LOG
.
isDebugEnabled
();
for
(
IDexTreeVisitor
pass
:
preDecompilePasses
)
{
Utils
.
checkThreadInterrupt
();
long
start
=
debugEnabled
?
System
.
currentTimeMillis
()
:
0
;
try
{
pass
.
init
(
this
);
...
...
jadx-core/src/main/java/jadx/core/utils/Utils.java
浏览文件 @
65f7c802
...
...
@@ -428,7 +428,7 @@ public class Utils {
}
public
static
void
checkThreadInterrupt
()
{
if
(
Thread
.
i
nterrupted
())
{
if
(
Thread
.
currentThread
().
isI
nterrupted
())
{
throw
new
JadxRuntimeException
(
"Thread interrupted"
);
}
}
...
...
jadx-gui/src/main/java/jadx/gui/jobs/BackgroundExecutor.java
浏览文件 @
65f7c802
...
...
@@ -3,6 +3,7 @@ package jadx.gui.jobs;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.Future
;
...
...
@@ -11,6 +12,7 @@ import java.util.concurrent.TimeUnit;
import
java.util.concurrent.atomic.AtomicLong
;
import
java.util.function.Consumer
;
import
java.util.function.Supplier
;
import
java.util.stream.Collectors
;
import
javax.swing.SwingUtilities
;
import
javax.swing.SwingWorker
;
...
...
@@ -19,6 +21,7 @@ import org.slf4j.Logger;
import
org.slf4j.LoggerFactory
;
import
jadx.core.utils.exceptions.JadxRuntimeException
;
import
jadx.gui.settings.JadxSettings
;
import
jadx.gui.ui.MainWindow
;
import
jadx.gui.ui.panel.ProgressPanel
;
import
jadx.gui.utils.NLS
;
...
...
@@ -33,16 +36,16 @@ import static jadx.gui.utils.UiUtils.calcProgress;
public
class
BackgroundExecutor
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
BackgroundExecutor
.
class
);
private
final
MainWindow
mainWindow
;
private
final
JadxSettings
settings
;
private
final
ProgressPanel
progressPane
;
private
ThreadPoolExecutor
taskQueueExecutor
;
private
final
Map
<
Long
,
IBackgroundTask
>
taskRunning
=
new
ConcurrentHashMap
<>();
private
final
AtomicLong
idSupplier
=
new
AtomicLong
(
0
);
public
BackgroundExecutor
(
MainWindow
mainWindow
)
{
this
.
mainWindow
=
mainWindow
;
this
.
progressPane
=
mainWindow
.
getProgressPane
(
);
public
BackgroundExecutor
(
JadxSettings
settings
,
ProgressPanel
progressPane
)
{
this
.
settings
=
Objects
.
requireNonNull
(
settings
)
;
this
.
progressPane
=
Objects
.
requireNonNull
(
progressPane
);
reset
();
}
...
...
@@ -68,9 +71,16 @@ public class BackgroundExecutor {
public
synchronized
void
cancelAll
()
{
try
{
taskRunning
.
values
().
forEach
(
Cancelable:
:
cancel
);
taskQueueExecutor
.
shutdown
();
boolean
complete
=
taskQueueExecutor
.
awaitTermination
(
30
,
TimeUnit
.
SECONDS
);
LOG
.
debug
(
"Background task executor terminated with status: {}"
,
complete
?
"complete"
:
"interrupted"
);
taskQueueExecutor
.
shutdownNow
();
boolean
complete
=
taskQueueExecutor
.
awaitTermination
(
3
,
TimeUnit
.
SECONDS
);
if
(
complete
)
{
LOG
.
debug
(
"Background task executor canceled successfully"
);
}
else
{
String
taskNames
=
taskRunning
.
values
().
stream
()
.
map
(
IBackgroundTask:
:
getTitle
)
.
collect
(
Collectors
.
joining
(
", "
));
LOG
.
debug
(
"Background task executor cancel failed. Running tasks: {}"
,
taskNames
);
}
}
catch
(
Exception
e
)
{
LOG
.
error
(
"Error terminating task executor"
,
e
);
}
finally
{
...
...
@@ -131,8 +141,16 @@ public class BackgroundExecutor {
try
{
runJobs
();
}
finally
{
t
askComplete
(
id
);
t
ry
{
task
.
onDone
(
this
);
// treat UI task operations as part of the task to not mix with others
UiUtils
.
uiRunAndWait
(()
->
{
progressPane
.
setVisible
(
false
);
task
.
onFinish
(
this
);
});
}
finally
{
taskComplete
(
id
);
}
}
return
status
;
}
...
...
@@ -146,7 +164,7 @@ public class BackgroundExecutor {
progressPane
.
changeVisibility
(
this
,
true
);
}
status
=
TaskStatus
.
STARTED
;
int
threadsCount
=
mainWindow
.
getSettings
()
.
getThreadsCount
();
int
threadsCount
=
settings
.
getThreadsCount
();
executor
=
(
ThreadPoolExecutor
)
Executors
.
newFixedThreadPool
(
threadsCount
);
for
(
Runnable
job
:
jobs
)
{
executor
.
execute
(
job
);
...
...
@@ -258,12 +276,6 @@ public class BackgroundExecutor {
};
}
@Override
protected
void
done
()
{
progressPane
.
setVisible
(
false
);
task
.
onFinish
(
this
);
}
@Override
public
TaskStatus
getStatus
()
{
return
status
;
...
...
jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java
浏览文件 @
65f7c802
...
...
@@ -169,6 +169,17 @@ public class JadxProject {
throw
new
JadxRuntimeException
(
"Can't get working dir"
);
}
public
boolean
isEnableLiveReload
()
{
return
data
.
isEnableLiveReload
();
}
public
void
setEnableLiveReload
(
boolean
newValue
)
{
if
(
newValue
!=
data
.
isEnableLiveReload
())
{
data
.
setEnableLiveReload
(
newValue
);
changed
();
}
}
private
void
changed
()
{
JadxSettings
settings
=
mainWindow
.
getSettings
();
if
(
settings
!=
null
&&
settings
.
isAutoSaveProject
())
{
...
...
jadx-gui/src/main/java/jadx/gui/settings/data/ProjectData.java
浏览文件 @
65f7c802
...
...
@@ -19,6 +19,7 @@ public class ProjectData {
private
List
<
TabViewState
>
openTabs
=
Collections
.
emptyList
();
private
int
activeTab
=
-
1
;
private
@Nullable
Path
cacheDir
;
private
boolean
enableLiveReload
=
false
;
public
List
<
Path
>
getFiles
()
{
return
files
;
...
...
@@ -94,4 +95,12 @@ public class ProjectData {
public
void
setCacheDir
(
Path
cacheDir
)
{
this
.
cacheDir
=
cacheDir
;
}
public
boolean
isEnableLiveReload
()
{
return
enableLiveReload
;
}
public
void
setEnableLiveReload
(
boolean
enableLiveReload
)
{
this
.
enableLiveReload
=
enableLiveReload
;
}
}
jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
浏览文件 @
65f7c802
...
...
@@ -16,6 +16,7 @@ import java.awt.event.ActionEvent;
import
java.awt.event.ActionListener
;
import
java.awt.event.FocusAdapter
;
import
java.awt.event.FocusEvent
;
import
java.awt.event.InputEvent
;
import
java.awt.event.KeyAdapter
;
import
java.awt.event.KeyEvent
;
import
java.awt.event.MouseAdapter
;
...
...
@@ -135,7 +136,9 @@ import jadx.gui.utils.Link;
import
jadx.gui.utils.NLS
;
import
jadx.gui.utils.SystemInfo
;
import
jadx.gui.utils.UiUtils
;
import
jadx.gui.utils.fileswatcher.LiveReloadWorker
;
import
jadx.gui.utils.logs.LogCollector
;
import
jadx.gui.utils.ui.ActionHandler
;
import
static
io
.
reactivex
.
internal
.
functions
.
Functions
.
EMPTY_RUNNABLE
;
import
static
javax
.
swing
.
KeyStroke
.
getKeyStroke
;
...
...
@@ -152,6 +155,7 @@ public class MainWindow extends JFrame {
private
static
final
ImageIcon
ICON_OPEN
=
UiUtils
.
openSvgIcon
(
"ui/openDisk"
);
private
static
final
ImageIcon
ICON_ADD_FILES
=
UiUtils
.
openSvgIcon
(
"ui/addFile"
);
private
static
final
ImageIcon
ICON_SAVE_ALL
=
UiUtils
.
openSvgIcon
(
"ui/menu-saveall"
);
private
static
final
ImageIcon
ICON_RELOAD
=
UiUtils
.
openSvgIcon
(
"ui/refresh"
);
private
static
final
ImageIcon
ICON_EXPORT
=
UiUtils
.
openSvgIcon
(
"ui/export"
);
private
static
final
ImageIcon
ICON_EXIT
=
UiUtils
.
openSvgIcon
(
"ui/exit"
);
private
static
final
ImageIcon
ICON_SYNC
=
UiUtils
.
openSvgIcon
(
"ui/pagination"
);
...
...
@@ -196,6 +200,9 @@ public class MainWindow extends JFrame {
private
JToggleButton
deobfToggleBtn
;
private
JCheckBoxMenuItem
deobfMenuItem
;
private
JCheckBoxMenuItem
liveReloadMenuItem
;
private
final
LiveReloadWorker
liveReloadWorker
;
private
transient
Link
updateLink
;
private
transient
ProgressPanel
progressPane
;
private
transient
Theme
editorTheme
;
...
...
@@ -208,18 +215,18 @@ public class MainWindow extends JFrame {
this
.
cacheObject
=
new
CacheObject
();
this
.
project
=
new
JadxProject
(
this
);
this
.
wrapper
=
new
JadxWrapper
(
this
);
this
.
liveReloadWorker
=
new
LiveReloadWorker
(
this
);
resetCache
();
FontUtils
.
registerBundledFonts
();
initUI
();
this
.
backgroundExecutor
=
new
BackgroundExecutor
(
settings
,
progressPane
);
initMenuAndToolbar
();
registerMouseNavigationButtons
();
UiUtils
.
setWindowIcons
(
this
);
loadSettings
();
update
();
this
.
backgroundExecutor
=
new
BackgroundExecutor
(
this
);
update
();
checkForUpdate
();
}
...
...
@@ -405,7 +412,7 @@ public class MainWindow extends JFrame {
return
loadedFile
.
resolveSibling
(
fileName
);
}
public
void
reopen
()
{
public
synchronized
void
reopen
()
{
saveAll
();
closeAll
();
loadFiles
(
EMPTY_RUNNABLE
);
...
...
@@ -439,6 +446,10 @@ public class MainWindow extends JFrame {
UiUtils
.
errorMessage
(
this
,
NLS
.
str
(
"message.memoryLow"
));
return
;
}
if
(
status
!=
TaskStatus
.
COMPLETE
)
{
LOG
.
warn
(
"Loading task incomplete, status: {}"
,
status
);
return
;
}
checkLoadedStatus
();
onOpen
();
exportMappingsMenu
.
setEnabled
(
true
);
...
...
@@ -485,6 +496,7 @@ public class MainWindow extends JFrame {
deobfToggleBtn
.
setSelected
(
settings
.
isDeobfuscationOn
());
initTree
();
update
();
updateLiveReload
(
project
.
isEnableLiveReload
());
BreakpointManager
.
init
(
project
.
getFilePaths
().
get
(
0
).
toAbsolutePath
().
getParent
());
backgroundExecutor
.
execute
(
NLS
.
str
(
"progress.load"
),
...
...
@@ -492,6 +504,21 @@ public class MainWindow extends JFrame {
status
->
runInitialBackgroundJobs
());
}
public
void
updateLiveReload
(
boolean
state
)
{
if
(
liveReloadWorker
.
isStarted
()
==
state
)
{
return
;
}
project
.
setEnableLiveReload
(
state
);
liveReloadMenuItem
.
setEnabled
(
false
);
backgroundExecutor
.
execute
(
(
state
?
"Starting"
:
"Stopping"
)
+
" live reload"
,
()
->
liveReloadWorker
.
updateState
(
state
),
s
->
{
liveReloadMenuItem
.
setState
(
state
);
liveReloadMenuItem
.
setEnabled
(
true
);
});
}
private
void
addTreeCustomNodes
()
{
treeRoot
.
replaceCustomNode
(
ApkSignature
.
getApkSignature
(
wrapper
));
treeRoot
.
replaceCustomNode
(
new
SummaryNode
(
this
));
...
...
@@ -829,6 +856,19 @@ public class MainWindow extends JFrame {
};
saveProjectAsAction
.
putValue
(
Action
.
SHORT_DESCRIPTION
,
NLS
.
str
(
"file.save_project_as"
));
ActionHandler
reload
=
new
ActionHandler
(
ev
->
UiUtils
.
uiRun
(
this
::
reopen
));
reload
.
setNameAndDesc
(
NLS
.
str
(
"file.reload"
));
reload
.
setIcon
(
ICON_RELOAD
);
reload
.
setKeyBinding
(
getKeyStroke
(
KeyEvent
.
VK_F5
,
0
));
ActionHandler
liveReload
=
new
ActionHandler
(
ev
->
updateLiveReload
(!
project
.
isEnableLiveReload
()));
liveReload
.
setName
(
NLS
.
str
(
"file.live_reload"
));
liveReload
.
setShortDescription
(
NLS
.
str
(
"file.live_reload_desc"
));
liveReload
.
setKeyBinding
(
getKeyStroke
(
KeyEvent
.
VK_F5
,
InputEvent
.
SHIFT_DOWN_MASK
));
liveReloadMenuItem
=
new
JCheckBoxMenuItem
(
liveReload
);
liveReloadMenuItem
.
setState
(
project
.
isEnableLiveReload
());
Action
exportMappingsAsTiny2
=
new
AbstractAction
(
"Tiny v2 file"
)
{
@Override
public
void
actionPerformed
(
ActionEvent
e
)
{
...
...
@@ -1045,6 +1085,9 @@ public class MainWindow extends JFrame {
file
.
add
(
saveProjectAction
);
file
.
add
(
saveProjectAsAction
);
file
.
addSeparator
();
file
.
add
(
reload
);
file
.
add
(
liveReloadMenuItem
);
file
.
addSeparator
();
file
.
add
(
exportMappingsMenu
);
file
.
addSeparator
();
file
.
add
(
saveAllAction
);
...
...
@@ -1114,6 +1157,8 @@ public class MainWindow extends JFrame {
toolbar
.
add
(
openAction
);
toolbar
.
add
(
addFilesAction
);
toolbar
.
addSeparator
();
toolbar
.
add
(
reload
);
toolbar
.
addSeparator
();
toolbar
.
add
(
saveAllAction
);
toolbar
.
add
(
exportAction
);
toolbar
.
addSeparator
();
...
...
@@ -1452,10 +1497,6 @@ public class MainWindow extends JFrame {
return
backgroundExecutor
;
}
public
ProgressPanel
getProgressPane
()
{
return
progressPane
;
}
public
JRoot
getTreeRoot
()
{
return
treeRoot
;
}
...
...
jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java
浏览文件 @
65f7c802
...
...
@@ -11,6 +11,7 @@ import java.awt.event.KeyAdapter;
import
java.awt.event.KeyEvent
;
import
java.awt.event.MouseListener
;
import
java.awt.event.MouseMotionListener
;
import
java.util.Objects
;
import
javax.swing.AbstractAction
;
import
javax.swing.Action
;
...
...
@@ -75,7 +76,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
public
AbstractCodeArea
(
ContentPanel
contentPanel
,
JNode
node
)
{
this
.
contentPanel
=
contentPanel
;
this
.
node
=
node
;
this
.
node
=
Objects
.
requireNonNull
(
node
)
;
setMarkOccurrences
(
false
);
setEditable
(
false
);
...
...
jadx-gui/src/main/java/jadx/gui/utils/fileswatcher/FilesWatcher.java
0 → 100644
浏览文件 @
65f7c802
package
jadx.gui.utils.fileswatcher
;
import
java.io.IOException
;
import
java.nio.file.FileSystems
;
import
java.nio.file.FileVisitResult
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.nio.file.SimpleFileVisitor
;
import
java.nio.file.WatchEvent
;
import
java.nio.file.WatchKey
;
import
java.nio.file.WatchService
;
import
java.nio.file.attribute.BasicFileAttributes
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.concurrent.atomic.AtomicBoolean
;
import
java.util.function.BiConsumer
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
jadx.core.utils.Utils
;
import
static
java
.
nio
.
file
.
LinkOption
.
NOFOLLOW_LINKS
;
import
static
java
.
nio
.
file
.
StandardWatchEventKinds
.
ENTRY_CREATE
;
import
static
java
.
nio
.
file
.
StandardWatchEventKinds
.
ENTRY_DELETE
;
import
static
java
.
nio
.
file
.
StandardWatchEventKinds
.
ENTRY_MODIFY
;
import
static
java
.
nio
.
file
.
StandardWatchEventKinds
.
OVERFLOW
;
public
class
FilesWatcher
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
FilesWatcher
.
class
);
private
final
WatchService
watcher
=
FileSystems
.
getDefault
().
newWatchService
();
private
final
Map
<
WatchKey
,
Path
>
keys
=
new
HashMap
<>();
private
final
Map
<
Path
,
Set
<
Path
>>
files
=
new
HashMap
<>();
private
final
AtomicBoolean
cancel
=
new
AtomicBoolean
(
false
);
private
final
BiConsumer
<
Path
,
WatchEvent
.
Kind
<
Path
>>
listener
;
public
FilesWatcher
(
List
<
Path
>
paths
,
BiConsumer
<
Path
,
WatchEvent
.
Kind
<
Path
>>
listener
)
throws
IOException
{
this
.
listener
=
listener
;
for
(
Path
path
:
paths
)
{
if
(
Files
.
isDirectory
(
path
,
NOFOLLOW_LINKS
))
{
registerDirs
(
path
);
}
else
{
Path
parentDir
=
path
.
toAbsolutePath
().
getParent
();
register
(
parentDir
);
files
.
merge
(
parentDir
,
Collections
.
singleton
(
path
),
Utils:
:
mergeSets
);
}
}
}
public
void
cancel
()
{
cancel
.
set
(
true
);
}
@SuppressWarnings
(
"unchecked"
)
public
void
watch
()
{
cancel
.
set
(
false
);
LOG
.
debug
(
"File watcher started for {} dirs"
,
keys
.
size
());
while
(!
cancel
.
get
())
{
WatchKey
key
;
try
{
key
=
watcher
.
take
();
}
catch
(
InterruptedException
e
)
{
LOG
.
debug
(
"File watcher interrupted"
);
return
;
}
Path
dir
=
keys
.
get
(
key
);
if
(
dir
==
null
)
{
LOG
.
warn
(
"Unknown directory key: {}"
,
key
);
continue
;
}
for
(
WatchEvent
<?>
event
:
key
.
pollEvents
())
{
if
(
cancel
.
get
()
||
Thread
.
interrupted
())
{
return
;
}
WatchEvent
.
Kind
<?>
kind
=
event
.
kind
();
if
(
kind
==
OVERFLOW
)
{
continue
;
}
Path
fileName
=
((
WatchEvent
<
Path
>)
event
).
context
();
Path
path
=
dir
.
resolve
(
fileName
);
Set
<
Path
>
files
=
this
.
files
.
get
(
dir
);
if
(
files
==
null
||
files
.
contains
(
path
))
{
listener
.
accept
(
path
,
(
WatchEvent
.
Kind
<
Path
>)
kind
);
}
if
(
kind
==
ENTRY_CREATE
)
{
try
{
if
(
Files
.
isDirectory
(
path
,
NOFOLLOW_LINKS
))
{
registerDirs
(
path
);
}
}
catch
(
Exception
e
)
{
LOG
.
warn
(
"Failed to update directory watch: {}"
,
path
,
e
);
}
}
}
boolean
valid
=
key
.
reset
();
if
(!
valid
)
{
keys
.
remove
(
key
);
if
(
keys
.
isEmpty
())
{
LOG
.
debug
(
"File watcher stopped: all watch keys removed"
);
return
;
}
}
}
}
private
void
registerDirs
(
Path
start
)
throws
IOException
{
Files
.
walkFileTree
(
start
,
new
SimpleFileVisitor
<
Path
>()
{
@Override
public
FileVisitResult
preVisitDirectory
(
Path
dir
,
BasicFileAttributes
attrs
)
throws
IOException
{
register
(
dir
);
return
FileVisitResult
.
CONTINUE
;
}
});
}
private
void
register
(
Path
dir
)
throws
IOException
{
WatchKey
key
=
dir
.
register
(
watcher
,
ENTRY_CREATE
,
ENTRY_DELETE
,
ENTRY_MODIFY
);
keys
.
put
(
key
,
dir
);
}
}
jadx-gui/src/main/java/jadx/gui/utils/fileswatcher/LiveReloadWorker.java
0 → 100644
浏览文件 @
65f7c802
package
jadx.gui.utils.fileswatcher
;
import
java.nio.file.Path
;
import
java.nio.file.WatchEvent
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.TimeUnit
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
io.reactivex.processors.PublishProcessor
;
import
jadx.gui.ui.MainWindow
;
import
jadx.gui.utils.UiUtils
;
public
class
LiveReloadWorker
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
LiveReloadWorker
.
class
);
private
final
MainWindow
mainWindow
;
private
final
PublishProcessor
<
Path
>
processor
;
private
volatile
boolean
started
=
false
;
private
ExecutorService
executor
;
private
FilesWatcher
watcher
;
@SuppressWarnings
(
"ResultOfMethodCallIgnored"
)
public
LiveReloadWorker
(
MainWindow
mainWindow
)
{
this
.
mainWindow
=
mainWindow
;
this
.
processor
=
PublishProcessor
.
create
();
this
.
processor
.
debounce
(
1
,
TimeUnit
.
SECONDS
)
.
subscribe
(
path
->
{
LOG
.
debug
(
"Reload triggered"
);
UiUtils
.
uiRun
(
mainWindow:
:
reopen
);
});
}
public
boolean
isStarted
()
{
return
started
;
}
public
synchronized
void
updateState
(
boolean
enabled
)
{
if
(
this
.
started
==
enabled
)
{
return
;
}
if
(
enabled
)
{
LOG
.
debug
(
"Starting live reload worker"
);
start
();
}
else
{
LOG
.
debug
(
"Stopping live reload worker"
);
stop
();
}
}
private
void
onUpdate
(
Path
path
,
WatchEvent
.
Kind
<
Path
>
pathKind
)
{
LOG
.
debug
(
"Path updated: {}"
,
path
);
processor
.
onNext
(
path
);
}
private
synchronized
void
start
()
{
try
{
watcher
=
new
FilesWatcher
(
mainWindow
.
getProject
().
getFilePaths
(),
this
::
onUpdate
);
executor
=
Executors
.
newSingleThreadExecutor
();
started
=
true
;
executor
.
submit
(
watcher:
:
watch
);
}
catch
(
Exception
e
)
{
LOG
.
warn
(
"Failed to start live reload worker"
,
e
);
resetState
();
}
}
private
synchronized
void
stop
()
{
try
{
watcher
.
cancel
();
executor
.
shutdownNow
();
boolean
canceled
=
executor
.
awaitTermination
(
5
,
TimeUnit
.
SECONDS
);
if
(!
canceled
)
{
LOG
.
warn
(
"Failed to cancel live reload worker"
);
}
}
catch
(
Exception
e
)
{
LOG
.
warn
(
"Failed to stop live reload worker"
,
e
);
}
finally
{
resetState
();
}
}
private
void
resetState
()
{
started
=
false
;
executor
=
null
;
watcher
=
null
;
}
}
jadx-gui/src/main/java/jadx/gui/utils/ui/ActionHandler.java
浏览文件 @
65f7c802
...
...
@@ -4,6 +4,8 @@ import java.awt.event.ActionEvent;
import
java.util.function.Consumer
;
import
javax.swing.AbstractAction
;
import
javax.swing.ImageIcon
;
import
javax.swing.KeyStroke
;
public
class
ActionHandler
extends
AbstractAction
{
...
...
@@ -13,6 +15,27 @@ public class ActionHandler extends AbstractAction {
this
.
consumer
=
consumer
;
}
public
void
setName
(
String
name
)
{
putValue
(
NAME
,
name
);
}
public
void
setNameAndDesc
(
String
name
)
{
setName
(
name
);
setShortDescription
(
name
);
}
public
void
setShortDescription
(
String
desc
)
{
putValue
(
SHORT_DESCRIPTION
,
desc
);
}
public
void
setIcon
(
ImageIcon
icon
)
{
putValue
(
SMALL_ICON
,
icon
);
}
public
void
setKeyBinding
(
KeyStroke
keyStroke
)
{
putValue
(
ACCELERATOR_KEY
,
keyStroke
);
}
@Override
public
void
actionPerformed
(
ActionEvent
e
)
{
consumer
.
accept
(
e
);
...
...
jadx-gui/src/main/resources/i18n/Messages_de_DE.properties
浏览文件 @
65f7c802
...
...
@@ -26,6 +26,9 @@ file.open_title=Datei öffnen
file.new_project
=
Neues Projekt
file.save_project
=
Projekt speichern
file.save_project_as
=
Projekt speichern als…
#file.reload=Reload files
#file.live_reload=Live reload
#file.live_reload_desc=Auto reload files on changes
#file.export_mappings_as=
file.save_all
=
Alles speichern
file.export_gradle
=
Als Gradle-Projekt speichern
...
...
jadx-gui/src/main/resources/i18n/Messages_en_US.properties
浏览文件 @
65f7c802
...
...
@@ -26,6 +26,9 @@ file.open_title=Open file
file.new_project
=
New project
file.save_project
=
Save project
file.save_project_as
=
Save project as...
file.reload
=
Reload files
file.live_reload
=
Live reload
file.live_reload_desc
=
Auto reload files on changes
file.export_mappings_as
=
Export mappings as...
file.save_all
=
Save all
file.export_gradle
=
Save as gradle project
...
...
jadx-gui/src/main/resources/i18n/Messages_es_ES.properties
浏览文件 @
65f7c802
...
...
@@ -26,6 +26,9 @@ file.open_title=Abrir archivo
#file.new_project=
#file.save_project=
#file.save_project_as=
#file.reload=Reload files
#file.live_reload=Live reload
#file.live_reload_desc=Auto reload files on changes
#file.export_mappings_as=
file.save_all
=
Guardar todo
file.export_gradle
=
Guardar como proyecto Gradle
...
...
jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties
浏览文件 @
65f7c802
...
...
@@ -26,6 +26,9 @@ file.open_title=파일 열기
file.new_project
=
새 프로젝트
file.save_project
=
프로젝트 저장
file.save_project_as
=
다른 이름으로 프로젝트 저장...
#file.reload=Reload files
#file.live_reload=Live reload
#file.live_reload_desc=Auto reload files on changes
#file.export_mappings_as=
file.save_all
=
모두 저장
file.export_gradle
=
Gradle 프로젝트로 저장
...
...
jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties
浏览文件 @
65f7c802
...
...
@@ -26,6 +26,9 @@ file.open_title=打开文件
file.new_project
=
新建项目
file.save_project
=
保存项目
file.save_project_as
=
另存项目为...
#file.reload=Reload files
#file.live_reload=Live reload
#file.live_reload_desc=Auto reload files on changes
#file.export_mappings_as=
file.save_all
=
全部保存
file.export_gradle
=
另存为 Gradle 项目
...
...
jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties
浏览文件 @
65f7c802
...
...
@@ -26,6 +26,9 @@ file.open_title=開啟檔案
file.new_project
=
新建專案
file.save_project
=
儲存專案
file.save_project_as
=
另存專案...
#file.reload=Reload files
#file.live_reload=Live reload
#file.live_reload_desc=Auto reload files on changes
#file.export_mappings_as=
file.save_all
=
全部儲存
file.export_gradle
=
另存為 gradle 專案
...
...
jadx-gui/src/main/resources/icons/ui/refresh.svg
0 → 100644
浏览文件 @
65f7c802
<!-- Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
<svg
xmlns=
"http://www.w3.org/2000/svg"
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
>
<path
fill=
"#6E6E6E"
fill-rule=
"evenodd"
d=
"M12.5747152,11.8852806 C11.4741474,13.1817355 9.83247882,14.0044386 7.99865879,14.0044386 C5.03907292,14.0044386 2.57997332,11.8615894 2.08820756,9.0427473 L3.94774327,9.10768372 C4.43372186,10.8898575 6.06393114,12.2000519 8.00015362,12.2000519 C9.30149237,12.2000519 10.4645985,11.6082097 11.2349873,10.6790094 L9.05000019,8.71167959 L14.0431479,8.44999981 L14.3048222,13.4430431 L12.5747152,11.8852806 Z M3.42785637,4.11741586 C4.52839138,2.82452748 6.16775464,2.00443857 7.99865879,2.00443857 C10.918604,2.00443857 13.3513802,4.09026967 13.8882946,6.8532307 L12.0226389,6.78808057 C11.5024872,5.05935553 9.89838095,3.8000774 8.00015362,3.8000774 C6.69867367,3.8000774 5.53545628,4.39204806 4.76506921,5.32142241 L6.95482203,7.29304326 L1.96167436,7.55472304 L1.70000005,2.56167973 L3.42785637,4.11741586 Z"
transform=
"rotate(3 8.002 8.004)"
/>
</svg>
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录