Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
bc0c1980
Commit
bc0c1980
authored
Jan 17, 2022
by
vvb2060
Committed by
John Wu
Jan 18, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support modules update
parent
2997258f
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
119 additions
and
663 deletions
+119
-663
Subject.kt
...c/main/java/com/topjohnwu/magisk/core/download/Subject.kt
+1
-1
UpdateInfo.kt
...c/main/java/com/topjohnwu/magisk/core/model/UpdateInfo.kt
+3
-5
LocalModule.kt
...ava/com/topjohnwu/magisk/core/model/module/LocalModule.kt
+51
-7
Module.kt
...ain/java/com/topjohnwu/magisk/core/model/module/Module.kt
+1
-28
OnlineModule.kt
...va/com/topjohnwu/magisk/core/model/module/OnlineModule.kt
+13
-52
NetworkServices.kt
...java/com/topjohnwu/magisk/data/network/NetworkServices.kt
+4
-0
NetworkService.kt
...va/com/topjohnwu/magisk/data/repository/NetworkService.kt
+1
-0
ModuleRvItem.kt
.../main/java/com/topjohnwu/magisk/ui/module/ModuleRvItem.kt
+8
-44
ModuleViewModel.kt
...in/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
+16
-105
fragment_module_md2.xml
app/src/main/res/layout/fragment_module_md2.xml
+3
-69
include_module_filter.xml
app/src/main/res/layout/include_module_filter.xml
+0
-135
item_module_download.xml
app/src/main/res/layout/item_module_download.xml
+1
-0
item_module_md2.xml
app/src/main/res/layout/item_module_md2.xml
+17
-12
item_repo_md2.xml
app/src/main/res/layout/item_repo_md2.xml
+0
-155
item_section_md2.xml
app/src/main/res/layout/item_section_md2.xml
+0
-50
No files found.
app/src/main/java/com/topjohnwu/magisk/core/download/Subject.kt
View file @
bc0c1980
...
...
@@ -43,7 +43,7 @@ sealed class Subject : Parcelable {
val
action
:
Action
,
override
val
notifyId
:
Int
=
Notifications
.
nextId
()
)
:
Subject
()
{
override
val
url
:
String
get
()
=
module
.
zip
_u
rl
override
val
url
:
String
get
()
=
module
.
zip
U
rl
override
val
title
:
String
get
()
=
module
.
downloadFilename
@IgnoredOnParcel
...
...
app/src/main/java/com/topjohnwu/magisk/core/model/UpdateInfo.kt
View file @
bc0c1980
...
...
@@ -28,11 +28,9 @@ data class StubJson(
@JsonClass
(
generateAdapter
=
true
)
data class
ModuleJson
(
val
id
:
String
,
val
last_update
:
Long
,
val
prop_url
:
String
,
val
zip_url
:
String
,
val
notes_url
:
String
val
version
:
String
,
val
versionCode
:
Int
,
val
zipUrl
:
String
,
)
@JsonClass
(
generateAdapter
=
true
)
...
...
app/src/main/java/com/topjohnwu/magisk/core/model/module/LocalModule.kt
View file @
bc0c1980
package
com.topjohnwu.magisk.core.model.module
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.di.ServiceLocator
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.io.SuFile
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
java.io.IOException
import
java.util.*
data class
LocalModule
(
private
val
path
:
String
,
override
var
id
:
String
=
""
,
override
var
name
:
String
=
""
,
override
var
author
:
String
=
""
,
override
var
version
:
String
=
""
,
override
var
versionCode
:
Int
=
-
1
,
override
var
description
:
String
=
""
,
)
:
Module
()
{
override
var
id
:
String
=
""
override
var
name
:
String
=
""
override
var
version
:
String
=
""
override
var
versionCode
:
Int
=
-
1
var
author
:
String
=
""
var
description
:
String
=
""
var
updateJson
:
String
=
""
var
updateInfo
:
OnlineModule
?
=
null
private
val
removeFile
=
SuFile
(
path
,
"remove"
)
private
val
disableFile
=
SuFile
(
path
,
"disable"
)
...
...
@@ -66,6 +71,30 @@ data class LocalModule(
}
}
@Throws
(
NumberFormatException
::
class
)
private
fun
parseProps
(
props
:
List
<
String
>)
{
for
(
line
in
props
)
{
val
prop
=
line
.
split
(
"="
.
toRegex
(),
2
).
map
{
it
.
trim
()
}
if
(
prop
.
size
!=
2
)
continue
val
key
=
prop
[
0
]
val
value
=
prop
[
1
]
if
(
key
.
isEmpty
()
||
key
[
0
]
==
'#'
)
continue
when
(
key
)
{
"id"
->
id
=
value
"name"
->
name
=
value
"version"
->
version
=
value
"versionCode"
->
versionCode
=
value
.
toInt
()
"author"
->
author
=
value
"description"
->
description
=
value
"updateJson"
->
updateJson
=
value
}
}
}
init
{
runCatching
{
parseProps
(
Shell
.
su
(
"dos2unix < $path/module.prop"
).
exec
().
out
)
...
...
@@ -81,13 +110,28 @@ data class LocalModule(
}
}
suspend
fun
load
():
Boolean
{
if
(
updateJson
.
isEmpty
())
return
false
try
{
val
json
=
ServiceLocator
.
networkService
.
fetchModuleJson
(
updateJson
)
if
(
json
.
versionCode
>
versionCode
)
{
updateInfo
=
OnlineModule
(
this
,
json
)
return
true
}
}
catch
(
e
:
IOException
)
{
Timber
.
w
(
e
)
}
return
false
}
companion
object
{
private
val
PERSIST
get
()
=
"${Const.MAGISKTMP}/mirror/persist/magisk"
suspend
fun
installed
()
=
withContext
(
Dispatchers
.
IO
)
{
SuFile
(
Const
.
MAGISK_PATH
)
.
listFiles
{
_
,
name
->
name
!=
"lost+found"
&&
name
!=
".core"
}
.
listFiles
()
.
orEmpty
()
.
filter
{
!
it
.
isFile
}
.
map
{
LocalModule
(
"${Const.MAGISK_PATH}/${it.name}"
)
}
...
...
app/src/main/java/com/topjohnwu/magisk/core/model/module/Module.kt
View file @
bc0c1980
...
...
@@ -5,37 +5,10 @@ abstract class Module : Comparable<Module> {
protected
set
abstract
var
name
:
String
protected
set
abstract
var
author
:
String
protected
set
abstract
var
version
:
String
protected
set
abstract
var
versionCode
:
Int
protected
set
abstract
var
description
:
String
protected
set
@Throws
(
NumberFormatException
::
class
)
protected
fun
parseProps
(
props
:
List
<
String
>)
{
for
(
line
in
props
)
{
val
prop
=
line
.
split
(
"="
.
toRegex
(),
2
).
map
{
it
.
trim
()
}
if
(
prop
.
size
!=
2
)
continue
val
key
=
prop
[
0
]
val
value
=
prop
[
1
]
if
(
key
.
isEmpty
()
||
key
[
0
]
==
'#'
)
continue
when
(
key
)
{
"id"
->
id
=
value
"name"
->
name
=
value
"version"
->
version
=
value
"versionCode"
->
versionCode
=
value
.
toInt
()
"author"
->
author
=
value
"description"
->
description
=
value
}
}
}
override
operator
fun
compareTo
(
other
:
Module
)
=
name
.
compareTo
(
other
.
name
,
true
)
override
operator
fun
compareTo
(
other
:
Module
)
=
id
.
compareTo
(
other
.
id
)
}
app/src/main/java/com/topjohnwu/magisk/core/model/module/OnlineModule.kt
View file @
bc0c1980
package
com.topjohnwu.magisk.core.model.module
import
android.os.Parcelable
import
androidx.room.Entity
import
androidx.room.PrimaryKey
import
com.topjohnwu.magisk.core.model.ModuleJson
import
com.topjohnwu.magisk.di.ServiceLocator
import
com.topjohnwu.magisk.ktx.legalFilename
import
kotlinx.parcelize.Parcelize
import
java.text.DateFormat
import
java.util.*
@Entity
(
tableName
=
"modules"
)
@Parcelize
data class
OnlineModule
(
@PrimaryKey
override
var
id
:
String
,
override
var
name
:
String
=
""
,
override
var
author
:
String
=
""
,
override
var
version
:
String
=
""
,
override
var
versionCode
:
Int
=
-
1
,
override
var
description
:
String
=
""
,
val
last_update
:
Long
,
val
prop_url
:
String
,
val
zip_url
:
String
,
val
notes_url
:
String
override
var
id
:
String
,
override
var
name
:
String
,
override
var
version
:
String
,
override
var
versionCode
:
Int
,
val
zipUrl
:
String
,
)
:
Module
(),
Parcelable
{
constructor
(
local
:
LocalModule
,
json
:
ModuleJson
)
:
this
(
local
.
id
,
local
.
name
,
json
.
version
,
json
.
versionCode
,
json
.
zipUrl
)
private
val
svc
get
()
=
ServiceLocator
.
networkService
constructor
(
info
:
ModuleJson
)
:
this
(
id
=
info
.
id
,
last_update
=
info
.
last_update
,
prop_url
=
info
.
prop_url
,
zip_url
=
info
.
zip_url
,
notes_url
=
info
.
notes_url
)
val
lastUpdate
get
()
=
Date
(
last_update
)
val
lastUpdateString
get
()
=
DATE_FORMAT
.
format
(
lastUpdate
)
val
downloadFilename
get
()
=
"$name-$version($versionCode).zip"
.
legalFilename
()
suspend
fun
notes
()
=
svc
.
fetchString
(
notes_url
)
@Throws
(
IllegalRepoException
::
class
)
suspend
fun
load
()
{
try
{
val
rawProps
=
svc
.
fetchString
(
prop_url
)
val
props
=
rawProps
.
split
(
"\\n"
.
toRegex
()).
dropLastWhile
{
it
.
isEmpty
()
}
parseProps
(
props
)
}
catch
(
e
:
Exception
)
{
throw
IllegalRepoException
(
"Repo [$id] parse error:"
,
e
)
}
if
(
versionCode
<
0
)
{
throw
IllegalRepoException
(
"Repo [$id] does not contain versionCode"
)
}
}
class
IllegalRepoException
(
msg
:
String
,
cause
:
Throwable
?
=
null
)
:
Exception
(
msg
,
cause
)
companion
object
{
private
val
DATE_FORMAT
=
DateFormat
.
getDateTimeInstance
(
DateFormat
.
MEDIUM
,
DateFormat
.
MEDIUM
)
}
private
fun
String
.
legalFilename
()
=
replace
(
" "
,
"_"
)
.
replace
(
"'"
,
""
).
replace
(
"\""
,
""
)
.
replace
(
"$"
,
""
).
replace
(
"`"
,
""
)
.
replace
(
"*"
,
""
).
replace
(
"/"
,
"_"
)
.
replace
(
"#"
,
""
).
replace
(
"@"
,
""
)
.
replace
(
"\\"
,
"_"
)
}
app/src/main/java/com/topjohnwu/magisk/data/network/NetworkServices.kt
View file @
bc0c1980
package
com.topjohnwu.magisk.data.network
import
com.topjohnwu.magisk.core.model.BranchInfo
import
com.topjohnwu.magisk.core.model.ModuleJson
import
com.topjohnwu.magisk.core.model.UpdateInfo
import
okhttp3.ResponseBody
import
retrofit2.http.*
...
...
@@ -37,6 +38,9 @@ interface RawServices {
@GET
suspend
fun
fetchString
(
@Url
url
:
String
):
String
@GET
suspend
fun
fetchModuleJson
(
@Url
url
:
String
):
ModuleJson
}
interface
GithubApiServices
{
...
...
app/src/main/java/com/topjohnwu/magisk/data/repository/NetworkService.kt
View file @
bc0c1980
...
...
@@ -65,6 +65,7 @@ class NetworkService(
}
suspend
fun
fetchFile
(
url
:
String
)
=
wrap
{
raw
.
fetchFile
(
url
)
}
suspend
fun
fetchString
(
url
:
String
)
=
wrap
{
raw
.
fetchString
(
url
)
}
suspend
fun
fetchModuleJson
(
url
:
String
)
=
wrap
{
raw
.
fetchModuleJson
(
url
)
}
private
suspend
fun
fetchMainVersion
()
=
api
.
fetchBranch
(
MAGISK_MAIN
,
"master"
).
commit
.
sha
}
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleRvItem.kt
View file @
bc0c1980
...
...
@@ -5,7 +5,6 @@ import com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.model.module.LocalModule
import
com.topjohnwu.magisk.core.model.module.OnlineModule
import
com.topjohnwu.magisk.databinding.DiffRvItem
import
com.topjohnwu.magisk.databinding.ObservableDiffRvItem
import
com.topjohnwu.magisk.databinding.RvContainer
...
...
@@ -16,62 +15,29 @@ object InstallModule : DiffRvItem<InstallModule>() {
override
val
layoutRes
=
R
.
layout
.
item_module_download
}
class
SectionTitle
(
val
title
:
Int
,
_button
:
Int
=
0
,
_icon
:
Int
=
0
)
:
ObservableDiffRvItem
<
SectionTitle
>()
{
override
val
layoutRes
=
R
.
layout
.
item_section_md2
@get
:
Bindable
var
button
=
_button
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
button
)
@get
:
Bindable
var
icon
=
_icon
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
icon
)
@get
:
Bindable
var
hasButton
=
_button
!=
0
&&
_icon
!=
0
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
hasButton
)
}
class
OnlineModuleRvItem
(
override
val
item
:
OnlineModule
)
:
ObservableDiffRvItem
<
OnlineModuleRvItem
>(),
RvContainer
<
OnlineModule
>
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_repo_md2
@get
:
Bindable
var
progress
=
0
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
progress
)
var
hasUpdate
=
false
override
fun
itemSameAs
(
other
:
OnlineModuleRvItem
):
Boolean
=
item
.
id
==
other
.
item
.
id
}
class
LocalModuleRvItem
(
override
val
item
:
LocalModule
)
:
ObservableDiffRvItem
<
LocalModuleRvItem
>(),
RvContainer
<
LocalModule
>
{
override
val
layoutRes
=
R
.
layout
.
item_module_md2
@get
:
Bindable
var
online
:
OnlineModule
?
=
null
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
online
)
@get
:
Bindable
var
isEnabled
=
item
.
enable
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
enabled
)
{
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
enabled
,
BR
.
updateReady
)
{
item
.
enable
=
value
}
@get
:
Bindable
var
isRemoved
=
item
.
remove
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
removed
)
{
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
removed
,
BR
.
updateReady
)
{
item
.
remove
=
value
}
@get
:
Bindable
var
updateReady
:
Boolean
get
()
=
item
.
updateInfo
!=
null
&&
!
isRemoved
&&
isEnabled
set
(
_
)
=
notifyPropertyChanged
(
BR
.
updateReady
)
val
isSuspended
=
(
Info
.
isZygiskEnabled
&&
item
.
isRiru
)
||
(!
Info
.
isZygiskEnabled
&&
item
.
isZygisk
)
...
...
@@ -80,11 +46,9 @@ class LocalModuleRvItem(
else
R
.
string
.
suspend_text_zygisk
.
asText
(
R
.
string
.
zygisk
.
asText
())
val
isUpdated
get
()
=
item
.
updated
val
isModified
get
()
=
isRemoved
||
isUpdated
fun
delete
(
viewModel
:
ModuleViewModel
)
{
fun
delete
()
{
isRemoved
=
!
isRemoved
viewModel
.
updateActiveState
()
}
override
fun
itemSameAs
(
other
:
LocalModuleRvItem
):
Boolean
=
item
.
id
==
other
.
item
.
id
...
...
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
View file @
bc0c1980
package
com.topjohnwu.magisk.ui.module
import
androidx.databinding.Bindable
import
androidx.databinding.ObservableArrayList
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.arch.BaseViewModel
import
com.topjohnwu.magisk.arch.Queryable
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.model.module.LocalModule
import
com.topjohnwu.magisk.core.model.module.OnlineModule
import
com.topjohnwu.magisk.databinding.*
import
com.topjohnwu.magisk.events.OpenReadmeEvent
import
com.topjohnwu.magisk.databinding.RvItem
import
com.topjohnwu.magisk.databinding.adapterOf
import
com.topjohnwu.magisk.databinding.diffListOf
import
com.topjohnwu.magisk.databinding.itemBindingOf
import
com.topjohnwu.magisk.events.SelectModuleEvent
import
com.topjohnwu.magisk.events.SnackbarEvent
import
com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
import
com.topjohnwu.magisk.ktx.addOnListChangedCallback
import
com.topjohnwu.magisk.ktx.reboot
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
class
ModuleViewModel
:
BaseViewModel
()
,
Queryable
{
class
ModuleViewModel
:
BaseViewModel
()
{
val
bottomBarBarrierIds
=
intArrayOf
(
R
.
id
.
module_info
,
R
.
id
.
module_remove
)
val
bottomBarBarrierIds
=
intArrayOf
(
R
.
id
.
module_update
,
R
.
id
.
module_remove
)
override
val
queryDelay
=
1000L
@get
:
Bindable
var
isRemoteLoading
=
false
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
remoteLoading
)
@get
:
Bindable
var
query
=
""
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
query
)
{
submitQuery
()
// Yes we do lie about the search being loaded
searchLoading
=
true
}
@get
:
Bindable
var
searchLoading
=
false
set
(
value
)
=
set
(
value
,
field
,
{
field
=
it
},
BR
.
searchLoading
)
val
itemsSearch
=
diffListOf
<
AnyDiffRvItem
>()
val
itemSearchBinding
=
itemBindingOf
<
AnyDiffRvItem
>
{
it
.
bindExtra
(
BR
.
viewModel
,
this
)
}
private
val
installSectionList
=
ObservableArrayList
<
RvItem
>()
private
val
itemsInstalled
=
diffListOf
<
LocalModuleRvItem
>()
private
val
sectionInstalled
=
SectionTitle
(
R
.
string
.
module_installed
,
R
.
string
.
reboot
,
R
.
drawable
.
ic_restart
).
also
{
it
.
hasButton
=
false
}
val
adapter
=
adapterOf
<
RvItem
>()
val
items
=
MergeObservableList
<
RvItem
>()
...
...
@@ -65,34 +32,19 @@ class ModuleViewModel : BaseViewModel(), Queryable {
it
.
bindExtra
(
BR
.
viewModel
,
this
)
}
// ---
init
{
itemsInstalled
.
addOnListChangedCallback
(
onItemRangeInserted
=
{
_
,
_
,
_
->
if
(
installSectionList
.
isEmpty
())
installSectionList
.
add
(
sectionInstalled
)
},
onItemRangeRemoved
=
{
list
,
_
,
_
->
if
(
list
.
isEmpty
())
installSectionList
.
clear
()
}
)
if
(
Info
.
env
.
isActive
)
{
items
.
insertItem
(
InstallModule
)
.
insertList
(
installSectionList
)
.
insertList
(
itemsInstalled
)
}
}
// ---
override
fun
refresh
():
Job
{
return
viewModelScope
.
launch
{
state
=
State
.
LOADING
loadInstalled
()
state
=
State
.
LOADED
loadUpdateInfo
()
}
}
...
...
@@ -104,62 +56,21 @@ class ModuleViewModel : BaseViewModel(), Queryable {
itemsInstalled
.
update
(
installed
,
diff
)
}
fun
forceRefresh
()
{
itemsInstalled
.
clear
()
refresh
()
submitQuery
()
}
// ---
private
suspend
fun
queryInternal
(
query
:
String
):
List
<
AnyDiffRvItem
>
{
return
if
(
query
.
isBlank
())
{
itemsSearch
.
clear
()
listOf
()
}
else
{
withContext
(
Dispatchers
.
Default
)
{
itemsInstalled
.
filter
{
it
.
item
.
id
.
contains
(
query
,
true
)
||
it
.
item
.
name
.
contains
(
query
,
true
)
||
it
.
item
.
description
.
contains
(
query
,
true
)
}
private
suspend
fun
loadUpdateInfo
()
{
withContext
(
Dispatchers
.
IO
)
{
itemsInstalled
.
forEach
{
it
.
updateReady
=
it
.
item
.
load
()
}
}
}
override
fun
query
()
{
viewModelScope
.
launch
{
val
searched
=
queryInternal
(
query
)
val
diff
=
withContext
(
Dispatchers
.
Default
)
{
itemsSearch
.
calculateDiff
(
searched
)
}
searchLoading
=
false
itemsSearch
.
update
(
searched
,
diff
)
fun
downloadPressed
(
item
:
OnlineModule
?)
=
if
(
item
!=
null
&&
isConnected
.
get
())
{
withExternalRW
{
ModuleInstallDialog
(
item
).
publish
()
}
}
else
{
SnackbarEvent
(
R
.
string
.
no_connection
).
publish
()
}
}
// ---
fun
updateActiveState
()
{
sectionInstalled
.
hasButton
=
itemsInstalled
.
any
{
it
.
isModified
}
}
fun
sectionPressed
(
item
:
SectionTitle
)
=
when
(
item
)
{
sectionInstalled
->
reboot
()
else
->
Unit
}
// The following methods are not used, but kept for future integration
fun
downloadPressed
(
item
:
OnlineModule
)
=
if
(
isConnected
.
get
())
withExternalRW
{
ModuleInstallDialog
(
item
).
publish
()
}
else
{
SnackbarEvent
(
R
.
string
.
no_connection
).
publish
()
}
fun
installPressed
()
=
withExternalRW
{
SelectModuleEvent
().
publish
()
}
fun
infoPressed
(
item
:
OnlineModule
)
=
if
(
isConnected
.
get
())
OpenReadmeEvent
(
item
).
publish
()
else
SnackbarEvent
(
R
.
string
.
no_connection
).
publish
()
fun
infoPressed
(
item
:
LocalModuleRvItem
)
{
infoPressed
(
item
.
online
?:
return
)
}
}
app/src/main/res/layout/fragment_module_md2.xml
View file @
bc0c1980
...
...
@@ -17,21 +17,10 @@
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<!--
The list is reverted to use LinearLayoutManager only. The issue of random crashes lies in
the way StaggeredGridLayoutManager invalidates view in LazySpanLookup. Since we're adding
items in between full-span items the array is not yet invalidated and consecutively crashes
due to index of -end- being out of bounds of the current array.
If you'd like to use StaggeredGridLayoutManager, do so without adding single span items in
between of full-span items.
https://issuetracker.google.com/issues/37034096
-->
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/module_list"
adapter=
"@{viewModel.adapter}"
gone=
"@{viewModel.loading
&& viewModel.items.empty
}"
gone=
"@{viewModel.loading}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.items}"
android:layout_width=
"match_parent"
...
...
@@ -39,46 +28,13 @@
android:clipToPadding=
"false"
android:orientation=
"vertical"
android:paddingTop=
"@dimen/internal_action_bar_size"
android:paddingBottom=
"
56dp
"
android:paddingBottom=
"
@dimen/internal_action_bar_size
"
app:fitsSystemWindowsInsets=
"top|bottom"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_module_md2"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=
"@+id/module_filter_toggle"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"bottom|end"
android:layout_marginStart=
"@dimen/l1"
android:layout_marginEnd=
"@dimen/l1"
android:layout_marginBottom=
"72dp"
app:backgroundTint=
"?colorSurfaceSurfaceVariant"
app:layout_fitsSystemWindowsInsets=
"bottom"
app:srcCompat=
"@drawable/ic_search_md2"
app:tint=
"?colorPrimary"
tools:layout_marginBottom=
"64dp"
/>
<com.google.android.material.circularreveal.cardview.CircularRevealCardView
android:id=
"@+id/module_filter"
style=
"@style/WidgetFoundation.Card"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"bottom"
android:visibility=
"invisible"
app:cardBackgroundColor=
"?colorSurface"
app:cardCornerRadius=
"0dp"
>
<include
android:id=
"@+id/module_filter_include"
layout=
"@layout/include_module_filter"
viewModel=
"@{viewModel}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
</com.google.android.material.circularreveal.cardview.CircularRevealCardView>
<LinearLayout
goneUnless=
"@{viewModel.loading
&& viewModel.items.empty
}"
goneUnless=
"@{viewModel.loading}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
...
...
@@ -98,28 +54,6 @@
</LinearLayout>
<FrameLayout
goneUnless=
"@{viewModel.remoteLoading}"
android:layout_width=
"match_parent"
android:layout_height=
"4dp"
android:layout_gravity=
"bottom"
>
<FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"16dp"
android:layout_gravity=
"center"
tools:ignore=
"UselessParent"
>
<ProgressBar
style=
"@style/WidgetFoundation.ProgressBar.Indeterminate"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"center"
/>
</FrameLayout>
</FrameLayout>
</FrameLayout>
...
...
app/src/main/res/layout/include_module_filter.xml
deleted
100644 → 0
View file @
2997258f
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<import
type=
"com.topjohnwu.magisk.core.Config"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.module.ModuleViewModel"
/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:clipToPadding=
"false"
android:orientation=
"vertical"
android:paddingBottom=
"@dimen/l1"
app:fitsSystemWindowsInsets=
"bottom"
tools:layout_gravity=
"bottom"
tools:paddingBottom=
"64dp"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/module_filter_list"
itemBinding=
"@{viewModel.itemSearchBinding}"
items=
"@{viewModel.itemsSearch}"
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_marginBottom=
"@dimen/l1"
android:clipToPadding=
"false"
android:orientation=
"vertical"
android:paddingTop=
"@dimen/internal_action_bar_size"
app:fitsSystemWindowsInsets=
"top"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constrainedHeight=
"true"
app:layout_constraintBottom_toTopOf=
"@+id/module_filter_title_search"
app:layout_constraintTop_toTopOf=
"parent"
app:reverseLayout=
"false"
app:spanCount=
"2"
tools:listitem=
"@layout/item_repo_md2"
/>
<ImageView
android:layout_width=
"match_parent"
android:layout_height=
"@dimen/l_50"
app:srcCompat=
"@drawable/bg_shadow"
app:tint=
"?colorSurfaceVariant"
app:layout_constraintBottom_toBottomOf=
"@+id/module_filter_list"
/>
<TextView
android:id=
"@+id/module_filter_title_search"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"@dimen/l1"
android:layout_marginEnd=
"@dimen/l1"
android:layout_marginBottom=
"@dimen/l1"
android:text=
"@string/hide_search"
android:textAllCaps=
"true"
android:textAppearance=
"@style/AppearanceFoundation.Caption"
android:textColor=
"?colorPrimary"
android:textStyle=
"bold"
app:layout_constraintBottom_toTopOf=
"@+id/module_filter_search"
/>
<com.google.android.material.card.MaterialCardView
android:id=
"@+id/module_filter_search"
style=
"@style/WidgetFoundation.Card"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"@dimen/l1"
android:layout_marginEnd=
"@dimen/l1"
android:layout_marginBottom=
"@dimen/l_50"
app:cardCornerRadius=
"18dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/module_filter_done"
app:layout_constraintStart_toStartOf=
"parent"
>
<ImageView
style=
"@style/WidgetFoundation.Icon"
android:layout_width=
"48dp"
android:layout_height=
"36dp"
android:layout_gravity=
"center_vertical|start"
android:padding=
"6dp"
app:srcCompat=
"@drawable/ic_search_md2"
app:tint=
"?colorDisabled"
/>
<EditText
android:id=
"@+id/module_filter_search_field"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"48dp"
android:background=
"@null"
android:hint=
"@string/hide_filter_hint"
android:inputType=
"textUri"
android:minHeight=
"36dp"
android:nextFocusRight=
"@id/module_filter_done"
android:paddingStart=
"0dp"
android:paddingEnd=
"@dimen/l1"
android:singleLine=
"true"
android:text=
"@={viewModel.query}"
android:textAppearance=
"@style/AppearanceFoundation.Body"
android:textColor=
"@color/color_text_transient"
android:textColorHint=
"?colorOnSurfaceVariant"
/>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=
"@+id/module_filter_done"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"@dimen/l1"
android:nextFocusLeft=
"@id/module_filter_search_field"
app:backgroundTint=
"?colorPrimary"
app:elevation=
"0dp"
app:fabSize=
"mini"
app:layout_constraintBottom_toBottomOf=
"@+id/module_filter_search"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"@+id/module_filter_search"
app:srcCompat=
"@drawable/ic_check_md2"
app:tint=
"?colorOnPrimary"
/>
<ProgressBar
style=
"@style/WidgetFoundation.ProgressBar.Indeterminate.Circular"
goneUnless=
"@{viewModel.searchLoading}"
android:layout_width=
"56dp"
android:layout_height=
"56dp"
app:layout_constraintBottom_toBottomOf=
"@+id/module_filter_done"
app:layout_constraintEnd_toEndOf=
"@+id/module_filter_done"
app:layout_constraintStart_toStartOf=
"@+id/module_filter_done"
app:layout_constraintTop_toTopOf=
"@+id/module_filter_done"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
app/src/main/res/layout/item_module_download.xml
View file @
bc0c1980
...
...
@@ -18,6 +18,7 @@
style=
"@style/WidgetFoundation.Button.Outlined"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"@dimen/l_50"
android:onClick=
"@{() -> viewModel.installPressed()}"
android:text=
"@string/module_action_install_external"
android:textAllCaps=
"false"
...
...
app/src/main/res/layout/item_module_md2.xml
View file @
bc0c1980
...
...
@@ -98,7 +98,7 @@
app:layout_constraintBottom_toBottomOf=
"@+id/module_version_author"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintHorizontal_bias=
"1"
app:layout_constraintStart_toEndOf=
"@+id/module_
info
"
app:layout_constraintStart_toEndOf=
"@+id/module_
update
"
app:layout_constraintTop_toTopOf=
"@+id/module_title"
/>
<TextView
...
...
@@ -111,6 +111,7 @@
android:layout_marginTop=
"@dimen/l1"
android:layout_marginEnd=
"@dimen/l1"
android:duplicateParentState=
"true"
android:maxLines=
"5"
android:text=
"@{item.item.description}"
android:textAppearance=
"@style/AppearanceFoundation.Caption.Variant"
android:textColor=
"?android:textColorSecondary"
...
...
@@ -126,30 +127,34 @@
android:background=
"?colorSurfaceSurfaceVariant"
app:layout_constraintTop_toBottomOf=
"@+id/module_description"
/>
<
ImageView
android:id=
"@+id/module_
info
"
style=
"@style/WidgetFoundation.
Icon
"
gone=
"@{item.
online == null
}"
<
Button
android:id=
"@+id/module_
update
"
style=
"@style/WidgetFoundation.
Button.Text
"
gone=
"@{item.
item.updateJson.length == 0
}"
android:layout_width=
"wrap_content"
android:
alpha=
".5
"
android:
layout_height=
"wrap_content
"
android:clickable=
"true"
android:enabled=
"@{item.updateReady}"
android:focusable=
"true"
android:onClick=
"@{() -> viewModel.infoPressed(item)}"
android:paddingEnd=
"@dimen/l_50"
android:onClick=
"@{() -> viewModel.downloadPressed(item.item.updateInfo)}"
android:text=
"@string/update"
android:textAllCaps=
"false"
app:icon=
"@drawable/ic_update_md2"
app:iconGravity=
"textEnd"
app:layout_constraintBottom_toBottomOf=
"@+id/module_remove"
app:layout_constraintEnd_toStartOf=
"@+id/module_remove"
app:layout_constraintTop_toTopOf=
"@+id/module_remove"
app:srcCompat=
"@drawable/ic_
info
"
/>
app:srcCompat=
"@drawable/ic_
download_md2
"
/>
<Button
android:id=
"@+id/module_remove"
style=
"@style/WidgetFoundation.Button.Text"
style=
"@style/WidgetFoundation.Button.Text
.Secondary
"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:clickable=
"true"
android:enabled=
"@{!item.updated}"
android:focusable=
"true"
android:onClick=
"@{() -> item.delete(
viewModel
)}"
android:onClick=
"@{() -> item.delete()}"
android:text=
"@{item.removed ? @string/module_state_restore : @string/module_state_remove}"
android:textAllCaps=
"false"
app:icon=
"@{item.removed ? @drawable/ic_restart : @drawable/ic_delete_md2}"
...
...
@@ -165,7 +170,7 @@
android:layout_height=
"wrap_content"
app:barrierDirection=
"start"
app:referencedIds=
"@{viewModel.bottomBarBarrierIds}"
tools:constraint_referenced_ids=
"module_
info
,module_remove"
/>
tools:constraint_referenced_ids=
"module_
update
,module_remove"
/>
<TextView
android:id=
"@+id/module_suspend_text"
...
...
app/src/main/res/layout/item_repo_md2.xml
deleted
100644 → 0
View file @
2997258f
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<import
type=
"com.topjohnwu.magisk.R"
/>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.ui.module.OnlineModuleRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.module.ModuleViewModel"
/>
</data>
<com.google.android.material.card.MaterialCardView
android:id=
"@+id/module_card"
style=
"@style/WidgetFoundation.Card"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:clickable=
"true"
android:focusable=
"true"
android:nextFocusRight=
"@id/module_info"
tools:layout_gravity=
"center"
tools:layout_marginBottom=
"@dimen/l1"
tools:layout_marginEnd=
"@dimen/l1"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
>
<TextView
android:id=
"@+id/module_title"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"@dimen/l1"
android:layout_marginTop=
"@dimen/l1"
android:layout_marginEnd=
"@dimen/l1"
android:text=
"@{item.item.name}"
android:textAppearance=
"@style/AppearanceFoundation.Body"
android:textStyle=
"bold"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"@tools:sample/lorem"
/>
<TextView
android:id=
"@+id/module_version_author"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{@string/module_version_author(item.item.version ?? `?`, item.item.author ?? `?`)}"
android:textAppearance=
"@style/AppearanceFoundation.Caption.Variant"
app:layout_constraintEnd_toEndOf=
"@+id/module_title"
app:layout_constraintStart_toStartOf=
"@+id/module_title"
app:layout_constraintTop_toBottomOf=
"@+id/module_title"
tools:text=
"v1 by topjohnwu"
/>
<TextView
android:id=
"@+id/module_description"
gone=
"@{item.item.description.empty}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"@dimen/l1"
android:layout_marginTop=
"@dimen/l1"
android:layout_marginEnd=
"@dimen/l1"
android:text=
"@{item.item.description}"
android:textAppearance=
"@style/AppearanceFoundation.Caption.Variant"
app:layout_constraintTop_toBottomOf=
"@+id/module_version_author"
tools:lines=
"4"
tools:text=
"@tools:sample/lorem/random"
/>
<View
android:id=
"@+id/module_divider"
android:layout_width=
"match_parent"
android:layout_height=
"1dp"
android:layout_marginTop=
"@dimen/l1"
android:background=
"?colorSurfaceSurfaceVariant"
app:layout_constraintTop_toBottomOf=
"@+id/module_description"
/>
<TextView
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"@dimen/l1"
android:text=
"@{item.item.lastUpdateString}"
android:textAppearance=
"@style/AppearanceFoundation.Caption.Variant"
android:textSize=
"11sp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/module_info"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/module_divider"
tools:ignore=
"SmallSp"
tools:text=
"@tools:sample/date/ddmmyy"
/>
<ImageView
android:id=
"@+id/module_info"
style=
"@style/WidgetFoundation.Icon"
android:layout_width=
"wrap_content"
android:alpha=
".5"
android:nextFocusLeft=
"@id/module_card"
android:onClick=
"@{() -> viewModel.infoPressed(item.item)}"
android:paddingEnd=
"@dimen/l_50"
app:layout_constraintBottom_toBottomOf=
"@+id/module_download"
app:layout_constraintEnd_toStartOf=
"@+id/module_download"
app:layout_constraintTop_toTopOf=
"@+id/module_download"
app:srcCompat=
"@drawable/ic_info"
/>
<ImageView
android:id=
"@+id/module_download"
style=
"@style/WidgetFoundation.Icon.Primary"
isEnabled=
"@{!(item.progress == -100 || (item.progress > 0 && item.progress < 100))}"
srcCompat=
"@{item.hasUpdate ? R.drawable.ic_update_md2 : R.drawable.ic_download_md2}"
android:layout_width=
"wrap_content"
android:contentDescription=
"@string/download"
android:nextFocusLeft=
"@id/module_info"
android:onClick=
"@{() -> viewModel.downloadPressed(item.item)}"
android:paddingStart=
"@dimen/l_50"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/module_divider"
tools:srcCompat=
"@drawable/ic_download_md2"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"bottom"
>
<ProgressBar
style=
"@style/WidgetFoundation.ProgressBar"
goneUnless=
"@{item.progress > 0 && item.progress < 100}"
progressAnimated=
"@{item.progress}"
android:layout_width=
"match_parent"
android:layout_gravity=
"bottom"
tools:progress=
"40"
/>
<androidx.core.widget.ContentLoadingProgressBar
style=
"@style/WidgetFoundation.ProgressBar.Indeterminate"
goneUnless=
"@{item.progress == -100}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginTop=
"-4dp"
android:layout_marginBottom=
"-5dp"
tools:progress=
"40"
/>
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
app/src/main/res/layout/item_section_md2.xml
deleted
100644 → 0
View file @
2997258f
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.ui.module.SectionTitle"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.module.ModuleViewModel"
/>
</data>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:animateLayoutChanges=
"true"
android:gravity=
"center_vertical"
>
<TextView
android:id=
"@+id/module_title"
android:layout_weight=
"1"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{item.title}"
android:textAppearance=
"@style/AppearanceFoundation.Large"
android:textColor=
"@color/color_primary_transient"
android:textStyle=
"bold"
tools:text=
"Installed"
/>
<Button
android:id=
"@+id/module_button"
style=
"@style/WidgetFoundation.Button.Text.Secondary"
invisible=
"@{!item.hasButton}"
android:onClick=
"@{() -> viewModel.sectionPressed(item)}"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"@dimen/l1"
android:text=
"@{item.button}"
android:textAllCaps=
"false"
app:icon=
"@{item.icon}"
tools:text=
"Reboot"
/>
</LinearLayout>
</layout>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment