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
2c12fe6e
Commit
2c12fe6e
authored
Jul 12, 2020
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More efficient databinding
parent
b41b2283
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
386 additions
and
276 deletions
+386
-276
RecyclerViewItems.kt
...ava/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt
+4
-1
LogRvItem.kt
...a/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt
+3
-2
ModuleRvItem.kt
...om/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt
+12
-24
SettingsItem.kt
...om/topjohnwu/magisk/model/entity/recycler/SettingsItem.kt
+4
-3
MainActivity.kt
app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt
+4
-0
MainViewModel.kt
app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt
+0
-5
ReselectionTarget.kt
...rc/main/java/com/topjohnwu/magisk/ui/ReselectionTarget.kt
+0
-7
BaseUIActivity.kt
.../main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt
+1
-2
BaseUIFragment.kt
.../main/java/com/topjohnwu/magisk/ui/base/BaseUIFragment.kt
+6
-0
BaseViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt
+13
-55
CompatDelegate.kt
.../main/java/com/topjohnwu/magisk/ui/base/CompatDelegate.kt
+1
-2
FlashViewModel.kt
...main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
+10
-6
HideViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt
+10
-22
HomeViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
+24
-19
InstallFragment.kt
...n/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt
+1
-2
InstallViewModel.kt
.../java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt
+29
-25
LogViewModel.kt
...src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt
+11
-13
ModuleFragment.kt
...ain/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt
+1
-1
ModuleViewModel.kt
...in/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
+13
-20
SafetynetViewModel.kt
...a/com/topjohnwu/magisk/ui/safetynet/SafetynetViewModel.kt
+29
-33
SettingsItems.kt
...in/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt
+6
-6
SuRequestViewModel.kt
...a/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt
+27
-22
ObservableHost.kt
...rc/main/java/com/topjohnwu/magisk/utils/ObservableHost.kt
+170
-0
include_home_magisk.xml
app/src/main/res/layout/include_home_magisk.xml
+3
-3
include_home_manager.xml
app/src/main/res/layout/include_home_manager.xml
+2
-2
gradle.properties
gradle.properties
+2
-1
No files found.
app/src/main/java/com/topjohnwu/magisk/databinding/RecyclerViewItems.kt
View file @
2c12fe6e
...
...
@@ -4,6 +4,7 @@ import androidx.annotation.CallSuper
import
androidx.databinding.ViewDataBinding
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.utils.DiffObservableList
import
com.topjohnwu.magisk.utils.ObservableHost
import
me.tatarka.bindingcollectionadapter2.ItemBinding
abstract
class
RvItem
{
...
...
@@ -45,4 +46,6 @@ abstract class ComparableRvItem<in T> : RvItem() {
)
=
oldItem
.
genericContentSameAs
(
newItem
)
}
}
}
\ No newline at end of file
}
abstract
class
ObservableItem
<
T
>
:
ComparableRvItem
<
T
>(),
ObservableHost
by
ObservableHost
.
impl
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt
View file @
2c12fe6e
...
...
@@ -3,6 +3,7 @@ package com.topjohnwu.magisk.model.entity.recycler
import
androidx.databinding.Bindable
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.ObservableItem
import
com.topjohnwu.magisk.ktx.timeDateFormat
import
com.topjohnwu.magisk.ktx.toTime
import
com.topjohnwu.magisk.model.entity.MagiskLog
...
...
@@ -16,13 +17,13 @@ class LogItem(val item: MagiskLog) : ObservableItem<LogItem>() {
@Bindable
get
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
top
)
notify
PropertyChanged
(
BR
.
top
)
}
var
isBottom
=
false
@Bindable
get
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
bottom
)
notify
PropertyChanged
(
BR
.
bottom
)
}
override
fun
itemSameAs
(
other
:
LogItem
)
=
item
.
appName
==
other
.
item
.
appName
...
...
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt
View file @
2c12fe6e
package
com.topjohnwu.magisk.model.entity.recycler
import
androidx.databinding.*
import
androidx.databinding.Bindable
import
androidx.databinding.Observable
import
androidx.databinding.ObservableField
import
androidx.databinding.ViewDataBinding
import
androidx.recyclerview.widget.StaggeredGridLayoutManager
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.model.module.Module
import
com.topjohnwu.magisk.core.model.module.Repo
import
com.topjohnwu.magisk.databinding.ComparableRvItem
import
com.topjohnwu.magisk.databinding.ObservableItem
import
com.topjohnwu.magisk.ui.module.ModuleViewModel
object
InstallModule
:
ComparableRvItem
<
InstallModule
>()
{
...
...
@@ -33,19 +37,19 @@ class SectionTitle(
@Bindable
get
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
button
)
notify
PropertyChanged
(
BR
.
button
)
}
var
icon
=
_icon
@Bindable
get
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
icon
)
notify
PropertyChanged
(
BR
.
icon
)
}
var
hasButton
=
button
!=
0
||
icon
!=
0
@Bindable
get
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
hasButton
)
notify
PropertyChanged
(
BR
.
hasButton
)
}
override
fun
onBindingBound
(
binding
:
ViewDataBinding
)
{
...
...
@@ -66,7 +70,7 @@ sealed class RepoItem(val item: Repo) : ObservableItem<RepoItem>() {
@Bindable
get
protected
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
update
)
notify
PropertyChanged
(
BR
.
update
)
}
override
fun
contentSameAs
(
other
:
RepoItem
):
Boolean
=
item
==
other
.
item
...
...
@@ -89,7 +93,7 @@ class ModuleItem(val item: Module) : ObservableItem<ModuleItem>(), Observable {
var
repo
:
Repo
?
=
null
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
repo
)
notify
PropertyChanged
(
BR
.
repo
)
}
@get
:
Bindable
...
...
@@ -97,7 +101,7 @@ class ModuleItem(val item: Module) : ObservableItem<ModuleItem>(), Observable {
get
()
=
item
.
enable
set
(
value
)
{
item
.
enable
=
value
notify
Change
(
BR
.
enabled
)
notify
PropertyChanged
(
BR
.
enabled
)
}
@get
:
Bindable
...
...
@@ -105,7 +109,7 @@ class ModuleItem(val item: Module) : ObservableItem<ModuleItem>(), Observable {
get
()
=
item
.
remove
set
(
value
)
{
item
.
remove
=
value
notify
Change
(
BR
.
removed
)
notify
PropertyChanged
(
BR
.
removed
)
}
val
isUpdated
get
()
=
item
.
updated
...
...
@@ -126,21 +130,5 @@ class ModuleItem(val item: Module) : ObservableItem<ModuleItem>(), Observable {
&&
item
.
name
==
other
.
item
.
name
override
fun
itemSameAs
(
other
:
ModuleItem
):
Boolean
=
item
.
id
==
other
.
item
.
id
}
abstract
class
ObservableItem
<
T
>
:
ComparableRvItem
<
T
>(),
Observable
{
private
val
list
=
PropertyChangeRegistry
()
override
fun
removeOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
?)
{
list
.
remove
(
callback
?:
return
)
}
override
fun
addOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
?)
{
list
.
add
(
callback
?:
return
)
}
fun
notifyChange
(
id
:
Int
)
=
list
.
notifyChange
(
this
,
id
)
}
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SettingsItem.kt
View file @
2c12fe6e
...
...
@@ -11,6 +11,7 @@ import androidx.databinding.ViewDataBinding
import
androidx.recyclerview.widget.StaggeredGridLayoutManager
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.ObservableItem
import
com.topjohnwu.magisk.utils.TransitiveText
import
com.topjohnwu.magisk.view.MagiskDialog
import
org.koin.core.KoinComponent
...
...
@@ -37,7 +38,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
// notify only after the callback invocation; callback can invalidate the backing data,
// which wouldn't be recognized with reverse approach
notify
Change
(
BR
.
description
)
notify
PropertyChanged
(
BR
.
description
)
}
open
fun
refresh
()
{}
...
...
@@ -60,7 +61,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
)
=
object
:
ObservableProperty
<
T
>(
initialValue
)
{
override
fun
afterChange
(
property
:
KProperty
<
*
>,
oldValue
:
T
,
newValue
:
T
)
{
setter
(
newValue
)
notify
Change
(
fieldId
)
notify
PropertyChanged
(
fieldId
)
}
}
...
...
@@ -169,7 +170,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
}
.
applyAdapter
(
entries
)
{
value
=
it
notify
Change
(
BR
.
selectedEntry
)
notify
PropertyChanged
(
BR
.
selectedEntry
)
super
.
onPressed
(
view
,
callback
)
}
.
reveal
()
...
...
app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt
View file @
2c12fe6e
...
...
@@ -18,6 +18,8 @@ import com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
import
com.topjohnwu.magisk.ktx.startAnimations
import
com.topjohnwu.magisk.ui.base.BaseUIActivity
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.ReselectionTarget
import
com.topjohnwu.magisk.ui.home.HomeFragmentDirections
import
com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior
import
com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior
...
...
@@ -26,6 +28,8 @@ import com.topjohnwu.magisk.view.MagiskDialog
import
com.topjohnwu.superuser.Shell
import
org.koin.androidx.viewmodel.ext.android.viewModel
class
MainViewModel
:
BaseViewModel
()
open
class
MainActivity
:
BaseUIActivity
<
MainViewModel
,
ActivityMainMd2Binding
>()
{
override
val
layoutRes
=
R
.
layout
.
activity_main_md2
...
...
app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt
deleted
100644 → 0
View file @
b41b2283
package
com.topjohnwu.magisk.ui
import
com.topjohnwu.magisk.ui.base.BaseViewModel
class
MainViewModel
:
BaseViewModel
()
app/src/main/java/com/topjohnwu/magisk/ui/ReselectionTarget.kt
deleted
100644 → 0
View file @
b41b2283
package
com.topjohnwu.magisk.ui
interface
ReselectionTarget
{
fun
onReselected
()
}
app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt
View file @
2c12fe6e
...
...
@@ -20,7 +20,6 @@ import com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.base.BaseActivity
import
com.topjohnwu.magisk.ktx.snackbar
import
com.topjohnwu.magisk.ktx.startAnimations
import
com.topjohnwu.magisk.ktx.value
import
com.topjohnwu.magisk.model.events.EventHandler
import
com.topjohnwu.magisk.model.events.SnackbarEvent
import
com.topjohnwu.magisk.model.events.ViewEvent
...
...
@@ -116,7 +115,7 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
}
override
fun
peekSystemWindowInsets
(
insets
:
Insets
)
{
viewModel
.
insets
.
value
=
insets
viewModel
.
insets
=
insets
}
protected
fun
ViewEvent
.
dispatchOnSelf
()
=
onEventDispatched
(
this
)
...
...
app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIFragment.kt
View file @
2c12fe6e
...
...
@@ -89,3 +89,9 @@ abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBindi
}
}
interface
ReselectionTarget
{
fun
onReselected
()
}
app/src/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt
View file @
2c12fe6e
...
...
@@ -5,8 +5,6 @@ import androidx.annotation.CallSuper
import
androidx.core.graphics.Insets
import
androidx.databinding.Bindable
import
androidx.databinding.Observable
import
androidx.databinding.ObservableField
import
androidx.databinding.PropertyChangeRegistry
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.MutableLiveData
import
androidx.lifecycle.ViewModel
...
...
@@ -17,30 +15,33 @@ import com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.base.BaseActivity
import
com.topjohnwu.magisk.model.events.*
import
com.topjohnwu.magisk.model.navigation.NavigationWrapper
import
com.topjohnwu.magisk.utils.ObservableHost
import
com.topjohnwu.magisk.utils.observable
import
kotlinx.coroutines.Job
import
org.koin.core.KoinComponent
abstract
class
BaseViewModel
(
initialState
:
State
=
State
.
LOADING
)
:
ViewModel
(),
Observable
,
KoinComponent
{
)
:
ViewModel
(),
Observable
Host
by
ObservableHost
.
impl
,
KoinComponent
{
enum
class
State
{
LOADED
,
LOADING
,
LOADING_FAILED
}
val
loading
@Bindable
get
()
=
state
==
State
.
LOADING
val
loaded
@Bindable
get
()
=
state
==
State
.
LOADED
val
loadingFailed
@Bindable
get
()
=
state
==
State
.
LOADING_FAILED
@get
:
Bindable
val
loading
get
()
=
state
==
State
.
LOADING
@get
:
Bindable
val
loaded
get
()
=
state
==
State
.
LOADED
@get
:
Bindable
val
loadFailed
get
()
=
state
==
State
.
LOADING_FAILED
val
isConnected
get
()
=
Info
.
isConnected
val
viewEvents
:
LiveData
<
ViewEvent
>
get
()
=
_viewEvents
val
insets
=
ObservableField
(
Insets
.
NONE
)
var
state
:
State
=
initialState
set
(
value
)
{
field
=
value
notifyStateChanged
()
}
@get
:
Bindable
var
insets
by
observable
(
Insets
.
NONE
,
BR
.
insets
)
var
state
by
observable
(
initialState
,
BR
.
loading
,
BR
.
loaded
,
BR
.
loadFailed
)
private
val
_viewEvents
=
MutableLiveData
<
ViewEvent
>()
private
var
runningJob
:
Job
?
=
null
...
...
@@ -65,12 +66,6 @@ abstract class BaseViewModel(
protected
open
fun
refresh
():
Job
?
=
null
open
fun
notifyStateChanged
()
{
notifyPropertyChanged
(
BR
.
loading
)
notifyPropertyChanged
(
BR
.
loaded
)
notifyPropertyChanged
(
BR
.
loadingFailed
)
}
@CallSuper
override
fun
onCleared
()
{
isConnected
.
removeOnPropertyChangedCallback
(
refreshCallback
)
...
...
@@ -108,41 +103,4 @@ abstract class BaseViewModel(
_viewEvents
.
postValue
(
NavigationWrapper
(
this
))
}
// The following is copied from androidx.databinding.BaseObservable
@Transient
private
var
callbacks
:
PropertyChangeRegistry
?
=
null
@Synchronized
override
fun
addOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
)
{
if
(
callbacks
==
null
)
{
callbacks
=
PropertyChangeRegistry
()
}
callbacks
?.
add
(
callback
)
}
@Synchronized
override
fun
removeOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
)
{
callbacks
?.
remove
(
callback
)
}
/**
* Notifies listeners that all properties of this instance have changed.
*/
@Synchronized
fun
notifyChange
()
{
callbacks
?.
notifyCallbacks
(
this
,
0
,
null
)
}
/**
* Notifies listeners that a specific property has changed. The getter for the property
* that changes should be marked with [androidx.databinding.Bindable] to generate a field in
* `BR` to be used as `fieldId`.
*
* @param fieldId The generated BR id for the Bindable field.
*/
fun
notifyPropertyChanged
(
fieldId
:
Int
)
{
callbacks
?.
notifyCallbacks
(
this
,
fieldId
,
null
)
}
}
app/src/main/java/com/topjohnwu/magisk/ui/base/CompatDelegate.kt
View file @
2c12fe6e
...
...
@@ -5,7 +5,6 @@ import androidx.core.graphics.Insets
import
androidx.core.view.ViewCompat
import
androidx.core.view.WindowInsetsCompat
import
androidx.fragment.app.Fragment
import
com.topjohnwu.magisk.ktx.value
import
com.topjohnwu.magisk.model.events.ActivityExecutor
import
com.topjohnwu.magisk.model.events.ContextExecutor
import
com.topjohnwu.magisk.model.events.FragmentExecutor
...
...
@@ -44,7 +43,7 @@ class CompatDelegate internal constructor(
insets
.
asInsets
()
.
also
{
view
.
peekSystemWindowInsets
(
it
)
}
.
let
{
view
.
consumeSystemWindowInsets
(
it
)
}
?.
also
{
view
.
viewModel
.
insets
.
value
=
it
}
?.
also
{
view
.
viewModel
.
insets
=
it
}
?.
subtractBy
(
insets
)
?:
insets
}
if
(
ViewCompat
.
isAttachedToWindow
(
view
.
viewRoot
))
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
View file @
2c12fe6e
...
...
@@ -3,9 +3,10 @@ package com.topjohnwu.magisk.ui.flash
import
android.content.res.Resources
import
android.net.Uri
import
android.view.MenuItem
import
androidx.databinding.Bindable
import
androidx.databinding.ObservableArrayList
import
androidx.databinding.ObservableField
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Const
...
...
@@ -19,6 +20,7 @@ import com.topjohnwu.magisk.model.events.SnackbarEvent
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.magisk.utils.observable
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
...
...
@@ -31,8 +33,10 @@ class FlashViewModel(
private
val
resources
:
Resources
)
:
BaseViewModel
()
{
val
showReboot
=
ObservableField
(
Shell
.
rootAccess
())
val
behaviorText
=
ObservableField
(
resources
.
getString
(
R
.
string
.
flashing
))
@get
:
Bindable
var
showReboot
by
observable
(
Shell
.
rootAccess
(),
BR
.
showReboot
)
@get
:
Bindable
var
behaviorText
by
observable
(
resources
.
getString
(
R
.
string
.
flashing
),
BR
.
behaviorText
)
val
adapter
=
BindingAdapter
<
ConsoleItem
>()
val
items
=
diffListOf
<
ConsoleItem
>()
...
...
@@ -59,7 +63,7 @@ class FlashViewModel(
FlashZip
(
installer
,
outItems
,
logItems
).
exec
()
}
Const
.
Value
.
UNINSTALL
->
{
showReboot
.
value
=
false
showReboot
=
false
FlashZip
.
Uninstall
(
installer
,
outItems
,
logItems
).
exec
()
}
Const
.
Value
.
FLASH_MAGISK
->
{
...
...
@@ -70,7 +74,7 @@ class FlashViewModel(
}
Const
.
Value
.
PATCH_FILE
->
{
uri
?:
return
@launch
showReboot
.
value
=
false
showReboot
=
false
MagiskInstaller
.
Patch
(
installer
,
uri
,
outItems
,
logItems
).
exec
()
}
else
->
{
...
...
@@ -84,7 +88,7 @@ class FlashViewModel(
private
fun
onResult
(
success
:
Boolean
)
{
state
=
if
(
success
)
State
.
LOADED
else
State
.
LOADING_FAILED
behaviorText
.
value
=
when
{
behaviorText
=
when
{
success
->
resources
.
getString
(
R
.
string
.
done
)
else
->
resources
.
getString
(
R
.
string
.
failure
)
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt
View file @
2c12fe6e
...
...
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.ui.hide
import
android.content.pm.ApplicationInfo
import
androidx.databinding.Bindable
import
androidx.databinding.ObservableField
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.core.utils.currentLocale
...
...
@@ -18,6 +17,7 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.Queryable
import
com.topjohnwu.magisk.ui.base.filterableListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.magisk.utils.observable
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
...
...
@@ -28,21 +28,16 @@ class HideViewModel(
override
val
queryDelay
=
1000L
var
isShowSystem
=
false
@Bindable
get
set
(
value
)
{
field
=
value
notifyPropertyChanged
(
BR
.
showSystem
)
submitQuery
()
}
@get
:
Bindable
var
isShowSystem
by
observable
(
false
,
BR
.
showSystem
)
{
submitQuery
()
}
@get
:
Bindable
var
query
by
observable
(
""
,
BR
.
query
)
{
submitQuery
()
}
var
query
=
""
@Bindable
get
set
(
value
)
{
field
=
value
notifyPropertyChanged
(
BR
.
query
)
submitQuery
()
}
val
items
=
filterableListOf
<
HideItem
>()
val
itemBinding
=
itemBindingOf
<
HideItem
>
{
it
.
bindExtra
(
BR
.
viewModel
,
this
)
...
...
@@ -51,8 +46,6 @@ class HideViewModel(
it
.
bindExtra
(
BR
.
viewModel
,
this
)
}
val
isFilterExpanded
=
ObservableField
(
false
)
override
fun
refresh
()
=
viewModelScope
.
launch
{
state
=
State
.
LOADING
val
apps
=
magiskRepo
.
fetchApps
()
...
...
@@ -106,10 +99,5 @@ class HideViewModel(
fun
resetQuery
()
{
query
=
""
}
fun
hideFilter
()
{
isFilterExpanded
.
value
=
false
}
}
app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
View file @
2c12fe6e
package
com.topjohnwu.magisk.ui.home
import
android.os.Build
import
androidx.databinding.
ObservableField
import
androidx.databinding.
Bindable
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BuildConfig
import
com.topjohnwu.magisk.R
...
...
@@ -27,6 +27,7 @@ import com.topjohnwu.magisk.model.events.dialog.ManagerInstallDialog
import
com.topjohnwu.magisk.model.events.dialog.UninstallDialog
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.magisk.utils.observable
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.launch
import
me.tatarka.bindingcollectionadapter2.BR
...
...
@@ -40,22 +41,26 @@ class HomeViewModel(
private
val
repoMagisk
:
MagiskRepository
)
:
BaseViewModel
()
{
val
isNoticeVisible
=
ObservableField
(
Config
.
safetyNotice
)
val
stateMagisk
=
ObservableField
(
MagiskState
.
LOADING
)
val
stateManager
=
ObservableField
(
MagiskState
.
LOADING
)
val
stateMagiskRemoteVersion
=
ObservableField
(
R
.
string
.
loading
.
res
())
val
stateMagiskInstalledVersion
get
()
=
@get
:
Bindable
var
isNoticeVisible
by
observable
(
Config
.
safetyNotice
,
BR
.
noticeVisible
)
@get
:
Bindable
var
stateMagisk
by
observable
(
MagiskState
.
LOADING
,
BR
.
stateMagisk
)
@get
:
Bindable
var
stateManager
by
observable
(
MagiskState
.
LOADING
,
BR
.
stateManager
)
@get
:
Bindable
var
magiskRemoteVersion
by
observable
(
R
.
string
.
loading
.
res
(),
BR
.
magiskRemoteVersion
)
val
magiskInstalledVersion
get
()
=
"${Info.env.magiskVersionString} (${Info.env.magiskVersionCode})"
val
stateM
agiskMode
get
()
=
R
.
string
.
home_status_normal
.
res
()
val
m
agiskMode
get
()
=
R
.
string
.
home_status_normal
.
res
()
val
stateManagerRemoteVersion
=
ObservableField
(
R
.
string
.
loading
.
res
())
val
stateManagerInstalledVersion
=
Info
.
stub
?.
let
{
@get
:
Bindable
var
managerRemoteVersion
by
observable
(
R
.
string
.
loading
.
res
(),
BR
.
managerRemoteVersion
)
val
managerInstalledVersion
=
Info
.
stub
?.
let
{
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) (${it.version})"
}
?:
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
val
statePackageName
=
packageName
val
stateManagerProgress
=
ObservableField
(
0
)
@get
:
Bindable
var
stateManagerProgress
by
observable
(
0
,
BR
.
stateManagerProgress
)
val
items
=
listOf
(
DeveloperItem
.
Mainline
,
DeveloperItem
.
App
,
DeveloperItem
.
Project
)
val
itemBinding
=
itemBindingOf
<
HomeItem
>
{
...
...
@@ -70,28 +75,28 @@ class HomeViewModel(
init
{
RemoteFileService
.
progressBroadcast
.
observeForever
{
when
(
it
?.
second
)
{
is
Manager
->
stateManagerProgress
.
value
=
it
.
first
.
times
(
100f
).
roundToInt
()
is
Manager
->
stateManagerProgress
=
it
.
first
.
times
(
100f
).
roundToInt
()
}
}
}
override
fun
refresh
()
=
viewModelScope
.
launch
{
repoMagisk
.
fetchUpdate
()
?.
apply
{
stateMagisk
.
value
=
when
{
stateMagisk
=
when
{
!
Info
.
env
.
isActive
->
MagiskState
.
NOT_INSTALLED
magisk
.
isObsolete
->
MagiskState
.
OBSOLETE
else
->
MagiskState
.
UP_TO_DATE
}
stateManager
.
value
=
when
{
stateManager
=
when
{
!
app
.
isUpdateChannelCorrect
&&
isConnected
.
value
->
MagiskState
.
NOT_INSTALLED
app
.
isObsolete
->
MagiskState
.
OBSOLETE
else
->
MagiskState
.
UP_TO_DATE
}
stateMagiskRemoteVersion
.
value
=
magiskRemoteVersion
=
"${magisk.version} (${magisk.versionCode})"
stateManagerRemoteVersion
.
value
=
managerRemoteVersion
=
"${app.version} (${app.versionCode}) (${stub.versionCode})"
launch
{
...
...
@@ -122,7 +127,7 @@ class HomeViewModel(
fun
hideNotice
()
{
Config
.
safetyNotice
=
false
isNoticeVisible
.
value
=
false
isNoticeVisible
=
false
}
private
suspend
fun
ensureEnv
()
{
...
...
@@ -133,7 +138,7 @@ class HomeViewModel(
// Don't bother checking env when magisk is not installed, loading or already has been shown
if
(
invalidStates
.
any
{
it
==
stateMagisk
.
value
}
||
invalidStates
.
any
{
it
==
stateMagisk
}
||
shownDialog
||
// don't care for emulators either
Build
.
DEVICE
.
orEmpty
().
contains
(
"generic"
)
||
...
...
app/src/main/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt
View file @
2c12fe6e
...
...
@@ -4,7 +4,6 @@ import android.content.Intent
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
import
com.topjohnwu.magisk.ktx.value
import
com.topjohnwu.magisk.model.events.RequestFileEvent
import
com.topjohnwu.magisk.ui.base.BaseUIFragment
import
org.koin.androidx.viewmodel.ext.android.viewModel
...
...
@@ -16,7 +15,7 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
super
.
onActivityResult
(
requestCode
,
resultCode
,
data
)
viewModel
.
data
.
value
=
RequestFileEvent
.
resolve
(
requestCode
,
resultCode
,
data
)
viewModel
.
data
=
RequestFileEvent
.
resolve
(
requestCode
,
resultCode
,
data
)
}
override
fun
onStart
()
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt
View file @
2c12fe6e
...
...
@@ -2,21 +2,21 @@ package com.topjohnwu.magisk.ui.install
import
android.net.Uri
import
android.widget.Toast
import
androidx.databinding.
ObservableField
import
androidx.databinding.
Bindable
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.download.DownloadService
import
com.topjohnwu.magisk.core.download.RemoteFileService
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.ktx.addOnPropertyChangedCallback
import
com.topjohnwu.magisk.ktx.value
import
com.topjohnwu.magisk.model.entity.internal.Configuration
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.events.RequestFileEvent
import
com.topjohnwu.magisk.model.events.dialog.SecondSlotWarningDialog
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.utils.observable
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.launch
import
org.koin.core.get
...
...
@@ -29,11 +29,26 @@ class InstallViewModel(
val
isRooted
get
()
=
Shell
.
rootAccess
()
val
isAB
get
()
=
Info
.
isAB
val
step
=
ObservableField
(
0
)
val
method
=
ObservableField
(-
1
)
val
progress
=
ObservableField
(
0
)
val
data
=
ObservableField
(
null
as
Uri
?)
val
notes
=
ObservableField
(
""
)
@get
:
Bindable
var
step
by
observable
(
0
,
BR
.
step
)
@get
:
Bindable
var
method
by
observable
(-
1
,
BR
.
method
)
{
when
(
it
)
{
R
.
id
.
method_patch
->
{
Utils
.
toast
(
R
.
string
.
patch_file_msg
,
Toast
.
LENGTH_LONG
)
RequestFileEvent
().
publish
()
}
R
.
id
.
method_inactive_slot
->
{
SecondSlotWarningDialog
().
publish
()
}
}
}
@get
:
Bindable
var
progress
by
observable
(
0
,
BR
.
progress
)
@get
:
Bindable
var
data
by
observable
(
null
as
Uri
?,
BR
.
data
)
@get
:
Bindable
var
notes
by
observable
(
""
,
BR
.
notes
)
init
{
RemoteFileService
.
reset
()
...
...
@@ -42,29 +57,18 @@ class InstallViewModel(
if
(
subject
!
is
DownloadSubject
.
Magisk
)
{
return
@observeForever
}
this
.
progress
.
value
=
progress
.
times
(
100
).
roundToInt
()
if
(
this
.
progress
.
value
>=
100
)
{
this
.
progress
=
progress
.
times
(
100
).
roundToInt
()
if
(
this
.
progress
>=
100
)
{
state
=
State
.
LOADED
}
}
viewModelScope
.
launch
{
notes
.
value
=
stringRepo
.
getString
(
Info
.
remote
.
magisk
.
note
)
}
method
.
addOnPropertyChangedCallback
{
when
(
it
!!
)
{
R
.
id
.
method_patch
->
{
Utils
.
toast
(
R
.
string
.
patch_file_msg
,
Toast
.
LENGTH_LONG
)
RequestFileEvent
().
publish
()
}
R
.
id
.
method_inactive_slot
->
{
SecondSlotWarningDialog
().
publish
()
}
}
notes
=
stringRepo
.
getString
(
Info
.
remote
.
magisk
.
note
)
}
}
fun
step
(
nextStep
:
Int
)
{
step
.
value
=
nextStep
step
=
nextStep
}
fun
install
()
=
DownloadService
(
get
())
{
...
...
@@ -73,9 +77,9 @@ class InstallViewModel(
// ---
private
fun
resolveConfiguration
()
=
when
(
method
.
value
)
{
private
fun
resolveConfiguration
()
=
when
(
method
)
{
R
.
id
.
method_download
->
Configuration
.
Download
R
.
id
.
method_patch
->
Configuration
.
Patch
(
data
.
value
!!
)
R
.
id
.
method_patch
->
Configuration
.
Patch
(
data
!!
)
R
.
id
.
method_direct
->
Configuration
.
Flash
.
Primary
R
.
id
.
method_inactive_slot
->
Configuration
.
Flash
.
Secondary
else
->
throw
IllegalArgumentException
(
"Unknown value"
)
...
...
app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt
View file @
2c12fe6e
package
com.topjohnwu.magisk.ui.log
import
androidx.databinding.
ObservableField
import
androidx.databinding.
Bindable
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.data.repository.LogRepository
import
com.topjohnwu.magisk.ktx.value
import
com.topjohnwu.magisk.model.entity.recycler.LogItem
import
com.topjohnwu.magisk.model.entity.recycler.TextItem
import
com.topjohnwu.magisk.model.events.SnackbarEvent
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.magisk.utils.observable
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.*
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
java.io.File
import
java.io.IOException
...
...
@@ -38,19 +40,15 @@ class LogViewModel(
}
// --- magisk log
va
l
consoleText
=
ObservableField
(
" "
)
@get
:
Bindable
va
r
consoleText
by
observable
(
" "
,
BR
.
consoleText
)
override
fun
refresh
()
=
viewModelScope
.
launch
{
consoleText
.
value
=
repo
.
fetchMagiskLogs
()
val
deferred
=
withContext
(
Dispatchers
.
Default
)
{
async
{
val
suLogs
=
repo
.
fetchSuLogs
().
map
{
LogItem
(
it
)
}
suLogs
to
items
.
calculateDiff
(
suLogs
)
}
consoleText
=
repo
.
fetchMagiskLogs
()
val
(
suLogs
,
diff
)
=
withContext
(
Dispatchers
.
Default
)
{
val
suLogs
=
repo
.
fetchSuLogs
().
map
{
LogItem
(
it
)
}
suLogs
to
items
.
calculateDiff
(
suLogs
)
}
delay
(
500
)
val
(
suLogs
,
diff
)
=
deferred
.
await
()
items
.
firstOrNull
()
?.
isTop
=
false
items
.
lastOrNull
()
?.
isBottom
=
false
items
.
update
(
suLogs
,
diff
)
...
...
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt
View file @
2c12fe6e
...
...
@@ -15,7 +15,7 @@ import com.topjohnwu.magisk.ktx.hideKeyboard
import
com.topjohnwu.magisk.model.events.InstallExternalModuleEvent
import
com.topjohnwu.magisk.model.events.ViewEvent
import
com.topjohnwu.magisk.ui.MainActivity
import
com.topjohnwu.magisk.ui.ReselectionTarget
import
com.topjohnwu.magisk.ui.
base.
ReselectionTarget
import
com.topjohnwu.magisk.ui.base.BaseUIFragment
import
com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
import
com.topjohnwu.magisk.utils.MotionRevealHelper
...
...
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
View file @
2c12fe6e
...
...
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.ui.module
import
androidx.databinding.Bindable
import
androidx.databinding.ObservableArrayList
import
androidx.databinding.ObservableField
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
...
...
@@ -25,6 +24,7 @@ import com.topjohnwu.magisk.model.events.SnackbarEvent
import
com.topjohnwu.magisk.model.events.dialog.ModuleInstallDialog
import
com.topjohnwu.magisk.ui.base.*
import
com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
import
com.topjohnwu.magisk.utils.observable
import
kotlinx.coroutines.*
import
me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
import
kotlin.math.roundToInt
...
...
@@ -53,18 +53,18 @@ class ModuleViewModel(
private
var
queryJob
:
Job
?
=
null
private
var
remoteJob
:
Job
?
=
null
var
query
=
""
@Bindable
get
set
(
value
)
{
if
(
field
==
value
)
return
field
=
value
notifyPropertyChanged
(
BR
.
query
)
submitQuery
()
// Yes we do lie about the search being loaded
searchLoading
.
value
=
true
}
@get
:
Bindable
var
isRemoteLoading
by
observable
(
false
,
BR
.
remoteLoading
)
@get
:
Bindable
var
query
by
observable
(
""
,
BR
.
query
)
{
submitQuery
()
// Yes we do lie about the search being loaded
searchLoading
=
true
}
val
searchLoading
=
ObservableField
(
false
)
@get
:
Bindable
var
searchLoading
by
observable
(
false
,
BR
.
searchLoading
)
val
itemsSearch
=
diffListOf
<
RepoItem
>()
val
itemSearchBinding
=
itemBindingOf
<
RepoItem
>
{
it
.
bindExtra
(
BR
.
viewModel
,
this
)
...
...
@@ -80,13 +80,6 @@ class ModuleViewModel(
private
val
itemsUpdatable
=
diffListOf
<
RepoItem
.
Update
>()
private
val
itemsRemote
=
diffListOf
<
RepoItem
.
Remote
>()
var
isRemoteLoading
=
false
@Bindable
get
private
set
(
value
)
{
field
=
value
notifyPropertyChanged
(
BR
.
remoteLoading
)
}
val
adapter
=
adapterOf
<
ComparableRvItem
<*>>()
val
items
=
MergeObservableList
<
ComparableRvItem
<*>>()
.
insertItem
(
InstallModule
)
...
...
@@ -261,7 +254,7 @@ class ModuleViewModel(
val
diff
=
withContext
(
Dispatchers
.
Default
)
{
itemsSearch
.
calculateDiff
(
searched
)
}
searchLoading
.
value
=
false
searchLoading
=
false
itemsSearch
.
update
(
searched
,
diff
)
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/safetynet/SafetynetViewModel.kt
View file @
2c12fe6e
package
com.topjohnwu.magisk.ui.safetynet
import
androidx.databinding.Bindable
import
androidx.databinding.ObservableField
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.ktx.value
import
com.topjohnwu.magisk.model.events.CheckSafetyNetEvent
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.safetynet.SafetyNetState.*
import
com.topjohnwu.magisk.utils.observable
import
org.json.JSONObject
enum
class
SafetyNetState
{
...
...
@@ -21,19 +20,23 @@ data class SafetyNetResult(
class
SafetynetViewModel
:
BaseViewModel
()
{
private
var
currentState
=
IDLE
set
(
value
)
{
field
=
value
notifyStateChanged
()
}
val
safetyNetTitle
=
ObservableField
(
R
.
string
.
empty
)
val
ctsState
=
ObservableField
(
false
)
val
basicIntegrityState
=
ObservableField
(
false
)
val
evalType
=
ObservableField
(
""
)
@get
:
Bindable
var
safetyNetTitle
by
observable
(
R
.
string
.
empty
,
BR
.
safetyNetTitle
)
@get
:
Bindable
var
ctsState
by
observable
(
false
,
BR
.
ctsState
)
@get
:
Bindable
var
basicIntegrityState
by
observable
(
false
,
BR
.
basicIntegrityState
)
@get
:
Bindable
var
evalType
by
observable
(
""
)
@get
:
Bindable
val
isChecking
get
()
=
currentState
==
LOADING
@get
:
Bindable
val
isFailed
get
()
=
currentState
==
FAILED
@get
:
Bindable
val
isSuccess
get
()
=
currentState
==
PASS
val
isChecking
@Bindable
get
()
=
currentState
==
LOADING
val
isFailed
@Bindable
get
()
=
currentState
==
FAILED
val
isSuccess
@Bindable
get
()
=
currentState
==
PASS
private
var
currentState
by
observable
(
IDLE
,
BR
.
checking
,
BR
.
failed
,
BR
.
success
)
init
{
cachedResult
?.
also
{
...
...
@@ -41,13 +44,6 @@ class SafetynetViewModel : BaseViewModel() {
}
?:
attest
()
}
override
fun
notifyStateChanged
()
{
super
.
notifyStateChanged
()
notifyPropertyChanged
(
BR
.
loading
)
notifyPropertyChanged
(
BR
.
failed
)
notifyPropertyChanged
(
BR
.
success
)
}
private
fun
attest
()
{
currentState
=
LOADING
CheckSafetyNetEvent
()
{
...
...
@@ -70,26 +66,26 @@ class SafetynetViewModel : BaseViewModel() {
val
eval
=
optString
(
"evaluationType"
)
val
result
=
cts
&&
basic
cachedResult
=
this
ctsState
.
value
=
cts
basicIntegrityState
.
value
=
basic
evalType
.
value
=
if
(
eval
.
contains
(
"HARDWARE"
))
"HARDWARE"
else
"BASIC"
ctsState
=
cts
basicIntegrityState
=
basic
evalType
=
if
(
eval
.
contains
(
"HARDWARE"
))
"HARDWARE"
else
"BASIC"
currentState
=
if
(
result
)
PASS
else
FAILED
safetyNetTitle
.
value
=
safetyNetTitle
=
if
(
result
)
R
.
string
.
safetynet_attest_success
else
R
.
string
.
safetynet_attest_failure
}.
onFailure
{
currentState
=
FAILED
ctsState
.
value
=
false
basicIntegrityState
.
value
=
false
evalType
.
value
=
"N/A"
safetyNetTitle
.
value
=
R
.
string
.
safetynet_res_invalid
ctsState
=
false
basicIntegrityState
=
false
evalType
=
"N/A"
safetyNetTitle
=
R
.
string
.
safetynet_res_invalid
}
}
?:
{
currentState
=
FAILED
ctsState
.
value
=
false
basicIntegrityState
.
value
=
false
evalType
.
value
=
"N/A"
safetyNetTitle
.
value
=
R
.
string
.
safetynet_api_error
ctsState
=
false
basicIntegrityState
=
false
evalType
=
"N/A"
safetyNetTitle
=
R
.
string
.
safetynet_api_error
}()
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt
View file @
2c12fe6e
...
...
@@ -46,7 +46,7 @@ object Language : SettingsItem.Selector() {
entryValues
=
values
val
selectedLocale
=
currentLocale
.
getDisplayName
(
currentLocale
)
value
=
names
.
indexOfFirst
{
it
==
selectedLocale
}.
let
{
if
(
it
==
-
1
)
0
else
it
}
notify
Change
(
BR
.
selectedEntry
)
notify
PropertyChanged
(
BR
.
selectedEntry
)
}
}
}
...
...
@@ -79,8 +79,8 @@ object Hide : SettingsItem.Input() {
override
var
value
:
String
=
resources
.
getString
(
R
.
string
.
re_app_name
)
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
value
)
notify
Change
(
BR
.
error
)
notify
PropertyChanged
(
BR
.
value
)
notify
PropertyChanged
(
BR
.
error
)
}
@get
:
Bindable
...
...
@@ -112,8 +112,8 @@ object DownloadPath : SettingsItem.Input() {
var
result
=
value
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
result
)
notify
Change
(
BR
.
path
)
notify
PropertyChanged
(
BR
.
result
)
notify
PropertyChanged
(
BR
.
path
)
}
@get
:
Bindable
...
...
@@ -143,7 +143,7 @@ object UpdateChannelUrl : SettingsItem.Input() {
var
result
=
value
set
(
value
)
{
field
=
value
notify
Change
(
BR
.
result
)
notify
PropertyChanged
(
BR
.
result
)
}
override
fun
refresh
()
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt
View file @
2c12fe6e
...
...
@@ -6,8 +6,9 @@ import android.content.pm.PackageManager
import
android.content.res.Resources
import
android.graphics.drawable.Drawable
import
android.os.CountDownTimer
import
androidx.databinding.
ObservableField
import
androidx.databinding.
Bindable
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.magiskdb.PolicyDao
...
...
@@ -15,10 +16,10 @@ import com.topjohnwu.magisk.core.model.MagiskPolicy.Companion.ALLOW
import
com.topjohnwu.magisk.core.model.MagiskPolicy.Companion.DENY
import
com.topjohnwu.magisk.core.su.SuRequestHandler
import
com.topjohnwu.magisk.core.utils.BiometricHelper
import
com.topjohnwu.magisk.ktx.value
import
com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem
import
com.topjohnwu.magisk.model.events.DieEvent
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.utils.observable
import
com.topjohnwu.superuser.internal.UiThreadHandler
import
kotlinx.coroutines.launch
import
me.tatarka.bindingcollectionadapter2.BindingListViewAdapter
...
...
@@ -32,16 +33,20 @@ class SuRequestViewModel(
private
val
res
:
Resources
)
:
BaseViewModel
()
{
val
icon
=
ObservableField
<
Drawable
?>(
null
)
val
title
=
ObservableField
(
""
)
val
packageName
=
ObservableField
(
""
)
val
denyText
=
ObservableField
(
res
.
getString
(
R
.
string
.
deny
))
val
warningText
=
ObservableField
<
CharSequence
>(
res
.
getString
(
R
.
string
.
su_warning
))
val
selectedItemPosition
=
ObservableField
(
0
)
val
grantEnabled
=
ObservableField
(
false
)
@get
:
Bindable
var
icon
by
observable
(
null
as
Drawable
?,
BR
.
icon
)
@get
:
Bindable
var
title
by
observable
(
""
,
BR
.
title
)
@get
:
Bindable
var
packageName
by
observable
(
""
,
BR
.
packageName
)
@get
:
Bindable
var
denyText
by
observable
(
res
.
getString
(
R
.
string
.
deny
),
BR
.
denyText
)
@get
:
Bindable
var
warningText
by
observable
(
res
.
getString
(
R
.
string
.
su_warning
),
BR
.
warningText
)
@get
:
Bindable
var
selectedItemPosition
by
observable
(
0
,
BR
.
selectedItemPosition
)
@get
:
Bindable
var
grantEnabled
by
observable
(
false
,
BR
.
grantEnabled
)
private
val
items
=
res
.
getStringArray
(
R
.
array
.
allow_timeout
).
map
{
SpinnerRvItem
(
it
)
}
val
adapter
=
BindingListViewAdapter
<
SpinnerRvItem
>(
1
).
apply
{
...
...
@@ -89,7 +94,7 @@ class SuRequestViewModel(
fun
respond
(
action
:
Int
)
{
timer
.
cancel
()
val
pos
=
selectedItemPosition
.
value
val
pos
=
selectedItemPosition
timeoutPrefs
.
edit
().
putInt
(
policy
.
packageName
,
pos
).
apply
()
respond
(
action
,
Config
.
Value
.
TIMEOUT_LIST
[
pos
])
...
...
@@ -99,16 +104,16 @@ class SuRequestViewModel(
fun
cancelTimer
()
{
timer
.
cancel
()
denyText
.
value
=
res
.
getString
(
R
.
string
.
deny
)
denyText
=
res
.
getString
(
R
.
string
.
deny
)
}
override
fun
onStart
()
{
icon
.
value
=
policy
.
applicationInfo
.
loadIcon
(
pm
)
title
.
value
=
policy
.
appName
packageName
.
value
=
policy
.
packageName
icon
=
policy
.
applicationInfo
.
loadIcon
(
pm
)
title
=
policy
.
appName
packageName
=
policy
.
packageName
UiThreadHandler
.
handler
.
post
{
// Delay is required to properly do selection
selectedItemPosition
.
value
=
timeoutPrefs
.
getInt
(
policy
.
packageName
,
0
)
selectedItemPosition
=
timeoutPrefs
.
getInt
(
policy
.
packageName
,
0
)
}
// Set timer
...
...
@@ -122,14 +127,14 @@ class SuRequestViewModel(
)
:
CountDownTimer
(
millis
,
interval
)
{
override
fun
onTick
(
remains
:
Long
)
{
if
(!
grantEnabled
.
value
&&
remains
<=
millis
-
1000
)
{
grantEnabled
.
value
=
true
if
(!
grantEnabled
&&
remains
<=
millis
-
1000
)
{
grantEnabled
=
true
}
denyText
.
value
=
"${res.getString(R.string.deny)} (${(remains / 1000) + 1})"
denyText
=
"${res.getString(R.string.deny)} (${(remains / 1000) + 1})"
}
override
fun
onFinish
()
{
denyText
.
value
=
res
.
getString
(
R
.
string
.
deny
)
denyText
=
res
.
getString
(
R
.
string
.
deny
)
respond
(
DENY
)
}
...
...
app/src/main/java/com/topjohnwu/magisk/utils/ObservableHost.kt
0 → 100644
View file @
2c12fe6e
package
com.topjohnwu.magisk.utils
import
androidx.databinding.Observable
import
androidx.databinding.PropertyChangeRegistry
import
kotlin.properties.ReadWriteProperty
import
kotlin.reflect.KProperty
/**
* Modified from https://github.com/skoumalcz/teanity/blob/1.2/core/src/main/java/com/skoumal/teanity/observable/Notifyable.kt
*
* Interface that allows user to be observed via DataBinding or manually by assigning listeners.
*
* @see [androidx.databinding.Observable]
* */
interface
ObservableHost
:
Observable
{
/**
* Notifies all observers that something has changed. By default implementation this method is
* synchronous, hence observers will never be notified in undefined order. Observers might
* choose to refresh the view completely, which is beyond the scope of this function.
* */
fun
notifyChange
(
host
:
Observable
=
this
)
/**
* Notifies all observers about field with [fieldId] has been changed. This will happen
* synchronously before or after [notifyChange] has been called. It will never be called during
* the execution of aforementioned method.
* */
fun
notifyPropertyChanged
(
fieldId
:
Int
,
host
:
Observable
=
this
)
companion
object
{
val
impl
:
ObservableHost
get
()
=
ObservableHostImpl
()
}
}
private
class
ObservableHostImpl
:
ObservableHost
{
private
var
callbacks
:
PropertyChangeRegistry
?
=
null
override
fun
addOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
)
{
synchronized
(
this
)
{
callbacks
?:
PropertyChangeRegistry
().
also
{
callbacks
=
it
}
}.
add
(
callback
)
}
override
fun
removeOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
)
{
synchronized
(
this
)
{
callbacks
?:
return
}.
remove
(
callback
)
}
override
fun
notifyChange
(
host
:
Observable
)
{
synchronized
(
this
)
{
callbacks
?:
return
}.
notifyCallbacks
(
host
,
0
,
null
)
}
override
fun
notifyPropertyChanged
(
fieldId
:
Int
,
host
:
Observable
)
{
synchronized
(
this
)
{
callbacks
?:
return
}.
notifyCallbacks
(
host
,
fieldId
,
null
)
}
}
/**
* Declares delegated property in [ObservableHost] parent. This property is available for DataBinding
* to be observed as usual. The only caveat is that in order for binding to generate the [fieldId]
* it has to be annotated accordingly.
*
* The annotation however give very strict control over your internal fields and overall reduce
* overhead in notifying observers. (In comparison to [androidx.databinding.ObservableField])
* It helps the kotlin code to feel more,... _native_, while respecting the original functionality.
*
* # Examples:
*
* ## The most basic usage would probably be:
* ```kotlin
* @get:Bindable
* var myField by observable(defaultValue, BR.myField)
* private set
* ```
*
* ## You can use the field as public read/write, of course:
* ```kotlin
* @get:Bindable
* var myField by observable(defaultValue, BR.myField)
* ```
*
* ## Please beware that delegated property instantiates one class per property
* We discourage using simple getters via delegated properties. Instead you can do something like
* this:
*
* ```kotlin
* @get:Bindable
* var myField by observable(defaultValue, BR.myField, BR.myTransformedField)
*
* var myTransformedField
* @Bindable get() {
* return myField.transform()
* }
* set(value) {
* myField = value.transform()
* }
* ```
*
* */
// Optimize for the most common use case
// Generic type is reified to optimize primitive types
inline
fun
<
reified
T
>
ObservableHost
.
observable
(
initialValue
:
T
,
fieldId
:
Int
)
=
object
:
ReadWriteProperty
<
ObservableHost
,
T
>
{
private
var
field
=
initialValue
override
fun
getValue
(
thisRef
:
ObservableHost
,
property
:
KProperty
<
*
>):
T
{
return
field
}
@Synchronized
override
fun
setValue
(
thisRef
:
ObservableHost
,
property
:
KProperty
<
*
>,
value
:
T
)
{
if
(
field
!=
value
)
{
field
=
value
notifyPropertyChanged
(
fieldId
)
}
}
}
inline
fun
<
reified
T
>
ObservableHost
.
observable
(
initialValue
:
T
,
vararg
fieldIds
:
Int
)
=
object
:
ReadWriteProperty
<
ObservableHost
,
T
>
{
private
var
field
=
initialValue
override
fun
getValue
(
thisRef
:
ObservableHost
,
property
:
KProperty
<
*
>):
T
{
return
field
}
@Synchronized
override
fun
setValue
(
thisRef
:
ObservableHost
,
property
:
KProperty
<
*
>,
value
:
T
)
{
if
(
field
!=
value
)
{
field
=
value
fieldIds
.
forEach
{
notifyPropertyChanged
(
it
)
}
}
}
}
inline
fun
<
reified
T
>
ObservableHost
.
observable
(
initialValue
:
T
,
vararg
fieldIds
:
Int
,
crossinline
afterChanged
:
(
T
)
->
Unit
)
=
object
:
ReadWriteProperty
<
ObservableHost
,
T
>
{
private
var
field
=
initialValue
override
fun
getValue
(
thisRef
:
ObservableHost
,
property
:
KProperty
<
*
>):
T
{
return
field
}
@Synchronized
override
fun
setValue
(
thisRef
:
ObservableHost
,
property
:
KProperty
<
*
>,
value
:
T
)
{
if
(
field
!=
value
)
{
field
=
value
fieldIds
.
forEach
{
notifyPropertyChanged
(
it
)
}
afterChanged
(
value
)
}
}
}
app/src/main/res/layout/include_home_magisk.xml
View file @
2c12fe6e
...
...
@@ -132,7 +132,7 @@
<TextView
style=
"@style/W.Home.ItemContent.Right"
android:text=
"@{viewModel.isConnected ? viewModel.
stateM
agiskRemoteVersion : @string/not_available}"
android:text=
"@{viewModel.isConnected ? viewModel.
m
agiskRemoteVersion : @string/not_available}"
tools:text=
"20.1 (12345)"
/>
</LinearLayout>
...
...
@@ -149,7 +149,7 @@
<TextView
style=
"@style/W.Home.ItemContent.Right"
android:text=
"@{Info.env.isActive ? viewModel.
stateM
agiskInstalledVersion : @string/not_available}"
android:text=
"@{Info.env.isActive ? viewModel.
m
agiskInstalledVersion : @string/not_available}"
tools:text=
"20.1 (12345)"
/>
</LinearLayout>
...
...
@@ -166,7 +166,7 @@
<TextView
style=
"@style/W.Home.ItemContent.Right"
android:text=
"@{Info.env.isActive ? viewModel.
stateM
agiskMode : @string/not_available}"
android:text=
"@{Info.env.isActive ? viewModel.
m
agiskMode : @string/not_available}"
tools:text=
"Normal"
/>
</LinearLayout>
...
...
app/src/main/res/layout/include_home_manager.xml
View file @
2c12fe6e
...
...
@@ -121,7 +121,7 @@
<TextView
style=
"@style/W.Home.ItemContent.Right"
android:text=
"@{viewModel.isConnected ? viewModel.
stateM
anagerRemoteVersion : @string/not_available}"
android:text=
"@{viewModel.isConnected ? viewModel.
m
anagerRemoteVersion : @string/not_available}"
tools:text=
"8.0.0 (123) (10)"
/>
</LinearLayout>
...
...
@@ -138,7 +138,7 @@
<TextView
style=
"@style/W.Home.ItemContent.Right"
android:text=
"@{viewModel.
stateM
anagerInstalledVersion}"
android:text=
"@{viewModel.
m
anagerInstalledVersion}"
tools:text=
"8.0.0 (123) (10)"
/>
</LinearLayout>
...
...
gradle.properties
View file @
2c12fe6e
...
...
@@ -25,6 +25,7 @@ org.gradle.daemon=true
android.useAndroidX
=
true
android.enableJetifier
=
true
android.enableR8.fullMode
=
true
android.databinding.incremental
=
true
android.injected.testOnly
=
false
kapt.incremental.apt
=
true
\ No newline at end of file
kapt.incremental.apt
=
true
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