mirror of
https://github.com/topjohnwu/Magisk
synced 2025-11-07 22:52:31 +01:00
Compare commits
108 Commits
canary-281
...
v30.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
975120d6a6 | ||
|
|
e489b3b6dd | ||
|
|
589a270b8d | ||
|
|
7961be5cfa | ||
|
|
959430e030 | ||
|
|
2923c8ccd1 | ||
|
|
7df4a9d74f | ||
|
|
bf4ed295da | ||
|
|
a5fca960dc | ||
|
|
f99912b9db | ||
|
|
a54bdb54e4 | ||
|
|
cd9851a1fe | ||
|
|
9ca469898c | ||
|
|
0665549473 | ||
|
|
9d7a14b335 | ||
|
|
62e29fee74 | ||
|
|
e472db552b | ||
|
|
466e4bd4e1 | ||
|
|
4cf525c588 | ||
|
|
c8aec2510d | ||
|
|
ccbfe0e66e | ||
|
|
23ea28de6f | ||
|
|
55c3ee3a6f | ||
|
|
2a42ca2b8f | ||
|
|
a897e82fa4 | ||
|
|
ffa15831d3 | ||
|
|
a344ebf28c | ||
|
|
78f7fa348e | ||
|
|
d8c448b99d | ||
|
|
d4b83b6a44 | ||
|
|
e5d36d1d24 | ||
|
|
ff18cb8e70 | ||
|
|
37a9724a54 | ||
|
|
d660401063 | ||
|
|
88541d6f49 | ||
|
|
ecd6129fe5 | ||
|
|
6dfe9df9e2 | ||
|
|
e81de7ec36 | ||
|
|
c78da1ce24 | ||
|
|
7b2d40987c | ||
|
|
3a37e8c9c5 | ||
|
|
58b405bce1 | ||
|
|
810174ef73 | ||
|
|
690a5ac033 | ||
|
|
89aad31f7e | ||
|
|
7124db98e3 | ||
|
|
0860e859f7 | ||
|
|
04008949b8 | ||
|
|
39f2940bd1 | ||
|
|
1460317ebd | ||
|
|
12340c9bd5 | ||
|
|
c4590fe2ba | ||
|
|
b36066bbcd | ||
|
|
65d1c5827c | ||
|
|
1d675c8b2e | ||
|
|
0b494ed7df | ||
|
|
48d9fc24eb | ||
|
|
83426f7f36 | ||
|
|
0e86d4dbcb | ||
|
|
5e050d7456 | ||
|
|
898580bf90 | ||
|
|
86621a4c46 | ||
|
|
a834e72b71 | ||
|
|
d8cf42af16 | ||
|
|
8c79d66b7b | ||
|
|
fada8b148a | ||
|
|
dc0acea47c | ||
|
|
78d1200608 | ||
|
|
527bbc0368 | ||
|
|
4c89c7e2b3 | ||
|
|
adbea7e313 | ||
|
|
76962f965e | ||
|
|
a4b8c5e46b | ||
|
|
83c707439c | ||
|
|
25dd6121f4 | ||
|
|
67f35ad027 | ||
|
|
0c4b8afbc5 | ||
|
|
34b30d7ce1 | ||
|
|
2215088973 | ||
|
|
8b7fb6cdde | ||
|
|
94c7dbedf2 | ||
|
|
b1dc47a047 | ||
|
|
62b1310d97 | ||
|
|
0a86916d3a | ||
|
|
9907ce57aa | ||
|
|
b92626cacc | ||
|
|
5a762f0a8e | ||
|
|
5dd7a7d804 | ||
|
|
7831f40691 | ||
|
|
4f4b1ff885 | ||
|
|
97901979dd | ||
|
|
287316842c | ||
|
|
608786e8f3 | ||
|
|
9684a35cab | ||
|
|
e3e4202954 | ||
|
|
23c2054d46 | ||
|
|
a20a2a8fa0 | ||
|
|
a2896be4a6 | ||
|
|
e9220a28d9 | ||
|
|
cf12087e21 | ||
|
|
00c1b36837 | ||
|
|
03e034795d | ||
|
|
79c0fafe43 | ||
|
|
d499819ba0 | ||
|
|
86da917174 | ||
|
|
30bd7d6555 | ||
|
|
e5a12f0f5f | ||
|
|
c85a8434c6 |
4
.github/actions/setup/action.yml
vendored
4
.github/actions/setup/action.yml
vendored
@@ -45,7 +45,7 @@ runs:
|
|||||||
env:
|
env:
|
||||||
SCCACHE_DIRECT: false
|
SCCACHE_DIRECT: false
|
||||||
SCCACHE_DIR: ${{ github.workspace }}/.sccache
|
SCCACHE_DIR: ${{ github.workspace }}/.sccache
|
||||||
SCCACHE_CACHE_SIZE: 2G
|
SCCACHE_CACHE_SIZE: ${{ inputs.is-asset-build == 'true' && '2G' || '300M' }}
|
||||||
SCCACHE_IDLE_TIMEOUT: 0
|
SCCACHE_IDLE_TIMEOUT: 0
|
||||||
run: |
|
run: |
|
||||||
bash $GITHUB_ACTION_PATH/sccache.sh
|
bash $GITHUB_ACTION_PATH/sccache.sh
|
||||||
@@ -70,7 +70,7 @@ runs:
|
|||||||
.gradle/caches
|
.gradle/caches
|
||||||
.gradle/wrapper
|
.gradle/wrapper
|
||||||
!.gradle/caches/build-cache-*
|
!.gradle/caches/build-cache-*
|
||||||
key: gradle-cache-${{ hashFiles('gradle/**') }}
|
key: gradle-cache-${{ hashFiles('app/gradle/**') }}
|
||||||
restore-keys: gradle-cache-
|
restore-keys: gradle-cache-
|
||||||
|
|
||||||
- name: Restore Gradle dependencies
|
- name: Restore Gradle dependencies
|
||||||
|
|||||||
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -6,10 +6,7 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- "app/**"
|
- "app/**"
|
||||||
- "native/**"
|
- "native/**"
|
||||||
- "buildSrc/**"
|
|
||||||
- "build.py"
|
- "build.py"
|
||||||
- "gradle.properties"
|
|
||||||
- "gradle/libs.versions.toml"
|
|
||||||
- ".github/workflows/build.yml"
|
- ".github/workflows/build.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
@@ -39,7 +36,7 @@ jobs:
|
|||||||
run: ./build.py -v all
|
run: ./build.py -v all
|
||||||
|
|
||||||
- name: Stop gradle daemon
|
- name: Stop gradle daemon
|
||||||
run: ./gradlew --stop
|
run: ./app/gradlew --stop
|
||||||
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -75,7 +72,7 @@ jobs:
|
|||||||
run: python build.py -v -c .github/ci.prop all
|
run: python build.py -v -c .github/ci.prop all
|
||||||
|
|
||||||
- name: Stop gradle daemon
|
- name: Stop gradle daemon
|
||||||
run: ./gradlew --stop
|
run: ./app/gradlew --stop
|
||||||
|
|
||||||
avd-test:
|
avd-test:
|
||||||
name: Test API ${{ matrix.version }} (x86_64)
|
name: Test API ${{ matrix.version }} (x86_64)
|
||||||
@@ -85,12 +82,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
|
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, "36.0-CANARY"]
|
||||||
type: [""]
|
type: [""]
|
||||||
include:
|
include:
|
||||||
- version: 36
|
- version: "36.0-CANARY"
|
||||||
type: "google_apis"
|
|
||||||
- version: 36
|
|
||||||
type: "google_apis_ps16k"
|
type: "google_apis_ps16k"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -113,7 +108,7 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
env:
|
env:
|
||||||
AVD_TEST_LOG: 1
|
AVD_TEST_LOG: 1
|
||||||
run: scripts/avd_test.sh ${{ matrix.version }} ${{ matrix.type }}
|
run: scripts/avd.sh test ${{ matrix.version }} ${{ matrix.type }}
|
||||||
|
|
||||||
- name: Upload logs on error
|
- name: Upload logs on error
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
@@ -155,7 +150,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
FORCE_32_BIT: 1
|
FORCE_32_BIT: 1
|
||||||
AVD_TEST_LOG: 1
|
AVD_TEST_LOG: 1
|
||||||
run: scripts/avd_test.sh ${{ matrix.version }}
|
run: scripts/avd.sh test ${{ matrix.version }}
|
||||||
|
|
||||||
- name: Upload logs on error
|
- name: Upload logs on error
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
@@ -197,7 +192,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Run Cuttlefish test
|
- name: Run Cuttlefish test
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
run: su $USER -c 'scripts/cuttlefish.sh test'
|
run: sudo -E -u $USER scripts/cuttlefish.sh test
|
||||||
|
|
||||||
- name: Upload logs on error
|
- name: Upload logs on error
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
|
|||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,19 +2,13 @@ out
|
|||||||
*.zip
|
*.zip
|
||||||
*.jks
|
*.jks
|
||||||
*.apk
|
*.apk
|
||||||
|
*.log
|
||||||
/config.prop
|
/config.prop
|
||||||
/notes.md
|
/notes.md
|
||||||
/update.sh
|
|
||||||
/app/dict.txt
|
|
||||||
|
|
||||||
# Built binaries
|
# Built binaries
|
||||||
native/out
|
native/out
|
||||||
|
|
||||||
# Android Studio / Gradle
|
# Android Studio
|
||||||
*.iml
|
*.iml
|
||||||
.gradle
|
|
||||||
.idea
|
.idea
|
||||||
.kotlin
|
|
||||||
/local.properties
|
|
||||||
/build
|
|
||||||
/captures
|
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,9 +10,6 @@
|
|||||||
[submodule "libcxx"]
|
[submodule "libcxx"]
|
||||||
path = native/src/external/libcxx
|
path = native/src/external/libcxx
|
||||||
url = https://github.com/topjohnwu/libcxx.git
|
url = https://github.com/topjohnwu/libcxx.git
|
||||||
[submodule "zopfli"]
|
|
||||||
path = native/src/external/zopfli
|
|
||||||
url = https://github.com/google/zopfli.git
|
|
||||||
[submodule "cxx-rs"]
|
[submodule "cxx-rs"]
|
||||||
path = native/src/external/cxx-rs
|
path = native/src/external/cxx-rs
|
||||||
url = https://github.com/topjohnwu/cxx.git
|
url = https://github.com/topjohnwu/cxx.git
|
||||||
|
|||||||
@@ -16,13 +16,7 @@ Some highlight features:
|
|||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
[Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads.
|
[Github](https://github.com/topjohnwu/Magisk/releases) is the only source where you can get official Magisk information and downloads.
|
||||||
|
|
||||||
Click the icon below to download Magisk apk.
|
|
||||||
|
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28104)
|
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
|
|
||||||
|
|||||||
7
app/.gitignore
vendored
Normal file
7
app/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/dict.txt
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.gradle
|
||||||
|
.kotlin
|
||||||
|
/local.properties
|
||||||
|
/build
|
||||||
@@ -35,7 +35,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":app:core"))
|
implementation(project(":core"))
|
||||||
coreLibraryDesugaring(libs.jdk.libs)
|
coreLibraryDesugaring(libs.jdk.libs)
|
||||||
|
|
||||||
implementation(libs.indeterminate.checkbox)
|
implementation(libs.indeterminate.checkbox)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.core.widget.ImageViewCompat
|
|||||||
import androidx.databinding.BindingAdapter
|
import androidx.databinding.BindingAdapter
|
||||||
import androidx.databinding.InverseBindingAdapter
|
import androidx.databinding.InverseBindingAdapter
|
||||||
import androidx.databinding.InverseBindingListener
|
import androidx.databinding.InverseBindingListener
|
||||||
|
import androidx.databinding.InverseMethod
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
@@ -33,9 +34,11 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
|||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
import com.google.android.material.slider.Slider
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||||
import com.topjohnwu.magisk.utils.TextHolder
|
import com.topjohnwu.magisk.utils.TextHolder
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import com.topjohnwu.widget.IndeterminateCheckBox
|
import com.topjohnwu.widget.IndeterminateCheckBox
|
||||||
@@ -306,3 +309,38 @@ fun TextView.setText(text: TextHolder) {
|
|||||||
fun Spinner.setAdapter(items: Array<Any>, layoutRes: Int) {
|
fun Spinner.setAdapter(items: Array<Any>, layoutRes: Int) {
|
||||||
adapter = ArrayAdapter(context, layoutRes, items)
|
adapter = ArrayAdapter(context, layoutRes, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("labelFormatter")
|
||||||
|
fun Slider.setLabelFormatter(formatter: (Float) -> Int) {
|
||||||
|
setLabelFormatter { value -> resources.getString(formatter(value)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@InverseBindingAdapter(attribute = "android:value")
|
||||||
|
fun Slider.getValueBinding() = value
|
||||||
|
|
||||||
|
@BindingAdapter("android:valueAttrChanged")
|
||||||
|
fun Slider.setListener(attrChange: InverseBindingListener) {
|
||||||
|
addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
|
||||||
|
override fun onStartTrackingTouch(slider: Slider) = Unit
|
||||||
|
override fun onStopTrackingTouch(slider: Slider) = attrChange.onChange()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@InverseMethod("sliderValueToPolicy")
|
||||||
|
fun policyToSliderValue(policy: Int): Float {
|
||||||
|
return when (policy) {
|
||||||
|
SuPolicy.DENY -> 1f
|
||||||
|
SuPolicy.RESTRICT -> 2f
|
||||||
|
SuPolicy.ALLOW -> 3f
|
||||||
|
else -> 1f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sliderValueToPolicy(value: Float): Int {
|
||||||
|
return when (value) {
|
||||||
|
1f -> SuPolicy.DENY
|
||||||
|
2f -> SuPolicy.RESTRICT
|
||||||
|
3f -> SuPolicy.ALLOW
|
||||||
|
else -> SuPolicy.DENY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.dialog
|
|||||||
import com.topjohnwu.magisk.core.AppContext
|
import com.topjohnwu.magisk.core.AppContext
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.R
|
import com.topjohnwu.magisk.core.R
|
||||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
|
||||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
import com.topjohnwu.magisk.core.download.Subject
|
||||||
import com.topjohnwu.magisk.view.MagiskDialog
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
@@ -11,15 +10,10 @@ import java.io.File
|
|||||||
|
|
||||||
class ManagerInstallDialog : MarkDownDialog() {
|
class ManagerInstallDialog : MarkDownDialog() {
|
||||||
|
|
||||||
private val svc get() = ServiceLocator.networkService
|
|
||||||
|
|
||||||
override suspend fun getMarkdownText(): String {
|
override suspend fun getMarkdownText(): String {
|
||||||
val text = svc.fetchString(Info.remote.magisk.note)
|
val text = Info.update.note
|
||||||
// Cache the changelog
|
// Cache the changelog
|
||||||
AppContext.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach {
|
File(AppContext.cacheDir, "${Info.update.versionCode}.md").writeText(text)
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
File(AppContext.cacheDir, "${Info.remote.magisk.versionCode}.md").writeText(text)
|
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,16 +91,15 @@ class HomeViewModel(
|
|||||||
|
|
||||||
override suspend fun doLoadWork() {
|
override suspend fun doLoadWork() {
|
||||||
appState = State.LOADING
|
appState = State.LOADING
|
||||||
Info.getRemote(svc)?.apply {
|
Info.fetchUpdate(svc)?.apply {
|
||||||
appState = when {
|
appState = when {
|
||||||
BuildConfig.APP_VERSION_CODE < magisk.versionCode -> State.OUTDATED
|
BuildConfig.APP_VERSION_CODE < versionCode -> State.OUTDATED
|
||||||
else -> State.UP_TO_DATE
|
else -> State.UP_TO_DATE
|
||||||
}
|
}
|
||||||
|
|
||||||
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
|
val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL
|
||||||
managerRemoteVersion =
|
managerRemoteVersion =
|
||||||
("${magisk.version} (${magisk.versionCode})" +
|
("$version (${versionCode})" + if (isDebug) " (D)" else "").asText()
|
||||||
if (isDebug) " (D)" else "").asText()
|
|
||||||
} ?: run {
|
} ?: run {
|
||||||
appState = State.INVALID
|
appState = State.INVALID
|
||||||
managerRemoteVersion = CoreR.string.not_available.asText()
|
managerRemoteVersion = CoreR.string.not_available.asText()
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ import com.topjohnwu.magisk.BR
|
|||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||||
import com.topjohnwu.magisk.core.AppContext
|
import com.topjohnwu.magisk.core.AppContext
|
||||||
import com.topjohnwu.magisk.core.BuildConfig
|
import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Const
|
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.base.ContentResultCallback
|
import com.topjohnwu.magisk.core.base.ContentResultCallback
|
||||||
import com.topjohnwu.magisk.core.ktx.toast
|
import com.topjohnwu.magisk.core.ktx.toast
|
||||||
@@ -70,17 +69,16 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
|||||||
init {
|
init {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val file = File(AppContext.cacheDir, "${BuildConfig.APP_VERSION_CODE}.md")
|
val noteFile = File(AppContext.cacheDir, "${APP_VERSION_CODE}.md")
|
||||||
val text = when {
|
val noteText = when {
|
||||||
file.exists() -> file.readText()
|
noteFile.exists() -> noteFile.readText()
|
||||||
Const.Url.CHANGELOG_URL.isEmpty() -> ""
|
|
||||||
else -> {
|
else -> {
|
||||||
val str = svc.fetchString(Const.Url.CHANGELOG_URL)
|
val note = svc.fetchUpdate(APP_VERSION_CODE).note
|
||||||
file.writeText(str)
|
noteFile.writeText(note)
|
||||||
str
|
note
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val spanned = markwon.toMarkdown(text)
|
val spanned = markwon.toMarkdown(noteText)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
notes = spanned
|
notes = spanned
|
||||||
}
|
}
|
||||||
@@ -100,13 +98,15 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveState(state: Bundle) {
|
override fun onSaveState(state: Bundle) {
|
||||||
state.putParcelable(INSTALL_STATE_KEY, InstallState(
|
state.putParcelable(
|
||||||
methodId,
|
INSTALL_STATE_KEY, InstallState(
|
||||||
step,
|
methodId,
|
||||||
Config.keepVerity,
|
step,
|
||||||
Config.keepEnc,
|
Config.keepVerity,
|
||||||
Config.recovery
|
Config.keepEnc,
|
||||||
))
|
Config.recovery
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRestoreState(state: Bundle) {
|
override fun onRestoreState(state: Bundle) {
|
||||||
@@ -124,6 +124,7 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel()
|
|||||||
override fun onActivityLaunch() {
|
override fun onActivityLaunch() {
|
||||||
AppContext.toast(CoreR.string.patch_file_msg, Toast.LENGTH_LONG)
|
AppContext.toast(CoreR.string.patch_file_msg, Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(result: Uri) {
|
override fun onActivityResult(result: Uri) {
|
||||||
uri.value = result
|
uri.value = result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,19 +147,11 @@ object UpdateChannel : BaseSettingsItem.Selector() {
|
|||||||
get() = Config.updateChannel
|
get() = Config.updateChannel
|
||||||
set(value) {
|
set(value) {
|
||||||
Config.updateChannel = value
|
Config.updateChannel = value
|
||||||
Info.remote = Info.EMPTY_REMOTE
|
Info.resetUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val title = CoreR.string.settings_update_channel_title.asText()
|
override val title = CoreR.string.settings_update_channel_title.asText()
|
||||||
|
|
||||||
override val entryRes = CoreR.array.update_channel
|
override val entryRes = CoreR.array.update_channel
|
||||||
override fun entries(res: Resources): Array<String> {
|
|
||||||
return super.entries(res).let {
|
|
||||||
if (!Const.APP_IS_CANARY && !BuildConfig.DEBUG)
|
|
||||||
it.copyOfRange(0, Config.Value.CANARY_CHANNEL)
|
|
||||||
else it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object UpdateChannelUrl : BaseSettingsItem.Input() {
|
object UpdateChannelUrl : BaseSettingsItem.Input() {
|
||||||
@@ -169,7 +161,7 @@ object UpdateChannelUrl : BaseSettingsItem.Input() {
|
|||||||
get() = Config.customChannelUrl
|
get() = Config.customChannelUrl
|
||||||
set(value) {
|
set(value) {
|
||||||
Config.customChannelUrl = value
|
Config.customChannelUrl = value
|
||||||
Info.remote = Info.EMPTY_REMOTE
|
Info.resetUpdate()
|
||||||
notifyPropertyChanged(BR.description)
|
notifyPropertyChanged(BR.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,6 +322,12 @@ object Reauthenticate : BaseSettingsItem.Toggle() {
|
|||||||
override var value by Config::suReAuth
|
override var value by Config::suReAuth
|
||||||
|
|
||||||
override fun refresh() {
|
override fun refresh() {
|
||||||
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Info.showSuperUser
|
isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Restrict : BaseSettingsItem.Toggle() {
|
||||||
|
override val title = CoreR.string.settings_su_restrict_title.asText()
|
||||||
|
override val description = CoreR.string.settings_su_restrict_summary.asText()
|
||||||
|
override var value by Config::suRestrict
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ import com.topjohnwu.magisk.core.ktx.activity
|
|||||||
import com.topjohnwu.magisk.core.ktx.toast
|
import com.topjohnwu.magisk.core.ktx.toast
|
||||||
import com.topjohnwu.magisk.core.tasks.AppMigration
|
import com.topjohnwu.magisk.core.tasks.AppMigration
|
||||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||||
|
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||||
import com.topjohnwu.magisk.databinding.bindExtra
|
import com.topjohnwu.magisk.databinding.bindExtra
|
||||||
import com.topjohnwu.magisk.events.AddHomeIconEvent
|
import com.topjohnwu.magisk.events.AddHomeIconEvent
|
||||||
import com.topjohnwu.magisk.events.AuthEvent
|
import com.topjohnwu.magisk.events.AuthEvent
|
||||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||||
@@ -83,6 +83,9 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
// Can hide overlay windows on 12.0+
|
// Can hide overlay windows on 12.0+
|
||||||
list.remove(Tapjack)
|
list.remove(Tapjack)
|
||||||
}
|
}
|
||||||
|
if (Const.Version.atLeast_30_1()) {
|
||||||
|
list.add(Restrict)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list
|
return list
|
||||||
@@ -127,7 +130,8 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createHosts() {
|
private fun createHosts() {
|
||||||
Shell.cmd("add_hosts_module").submit {
|
viewModelScope.launch {
|
||||||
|
RootUtils.addSystemlessHosts()
|
||||||
AppContext.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
|
AppContext.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import android.graphics.drawable.Drawable
|
|||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||||
import com.topjohnwu.magisk.databinding.DiffItem
|
import com.topjohnwu.magisk.databinding.DiffItem
|
||||||
import com.topjohnwu.magisk.databinding.ItemWrapper
|
import com.topjohnwu.magisk.databinding.ItemWrapper
|
||||||
import com.topjohnwu.magisk.databinding.ObservableRvItem
|
import com.topjohnwu.magisk.databinding.ObservableRvItem
|
||||||
import com.topjohnwu.magisk.databinding.set
|
import com.topjohnwu.magisk.databinding.set
|
||||||
|
import com.topjohnwu.magisk.core.R as CoreR
|
||||||
|
|
||||||
class PolicyRvItem(
|
class PolicyRvItem(
|
||||||
private val viewModel: SuperuserViewModel,
|
private val viewModel: SuperuserViewModel,
|
||||||
@@ -33,14 +35,34 @@ class PolicyRvItem(
|
|||||||
var isExpanded = false
|
var isExpanded = false
|
||||||
set(value) = set(value, field, { field = it }, BR.expanded)
|
set(value) = set(value, field, { field = it }, BR.expanded)
|
||||||
|
|
||||||
|
val showSlider = Config.suRestrict || item.policy == SuPolicy.RESTRICT
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var isEnabled
|
var isEnabled
|
||||||
get() = item.policy == SuPolicy.ALLOW
|
get() = item.policy >= SuPolicy.ALLOW
|
||||||
set(value) = setImpl(value, isEnabled) {
|
set(value) = setImpl(value, isEnabled) {
|
||||||
notifyPropertyChanged(BR.enabled)
|
notifyPropertyChanged(BR.enabled)
|
||||||
viewModel.togglePolicy(this, value)
|
viewModel.updatePolicy(this, if (it) SuPolicy.ALLOW else SuPolicy.DENY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@get:Bindable
|
||||||
|
var sliderValue
|
||||||
|
get() = item.policy
|
||||||
|
set(value) = setImpl(value, sliderValue) {
|
||||||
|
notifyPropertyChanged(BR.sliderValue)
|
||||||
|
notifyPropertyChanged(BR.enabled)
|
||||||
|
viewModel.updatePolicy(this, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sliderValueToPolicyString: (Float) -> Int = { value ->
|
||||||
|
when (value.toInt()) {
|
||||||
|
1 -> CoreR.string.deny
|
||||||
|
2 -> CoreR.string.restrict
|
||||||
|
3 -> CoreR.string.grant
|
||||||
|
else -> CoreR.string.deny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@get:Bindable
|
@get:Bindable
|
||||||
var shouldNotify
|
var shouldNotify
|
||||||
get() = item.notification
|
get() = item.notification
|
||||||
|
|||||||
@@ -156,15 +156,16 @@ class SuperuserViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun togglePolicy(item: PolicyRvItem, enable: Boolean) {
|
fun updatePolicy(item: PolicyRvItem, policy: Int) {
|
||||||
val items = itemsPolicies.filter { it.item.uid == item.item.uid }
|
val items = itemsPolicies.filter { it.item.uid == item.item.uid }
|
||||||
fun updateState() {
|
fun updateState() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val res = if (enable) R.string.su_snack_grant else R.string.su_snack_deny
|
val res = if (policy >= SuPolicy.ALLOW) R.string.su_snack_grant else R.string.su_snack_deny
|
||||||
item.item.policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY
|
item.item.policy = policy
|
||||||
db.update(item.item)
|
db.update(item.item)
|
||||||
items.forEach {
|
items.forEach {
|
||||||
it.notifyPropertyChanged(BR.enabled)
|
it.notifyPropertyChanged(BR.enabled)
|
||||||
|
it.notifyPropertyChanged(BR.sliderValue)
|
||||||
}
|
}
|
||||||
SnackbarEvent(res.asText(item.appName)).publish()
|
SnackbarEvent(res.asText(item.appName)).publish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/log_track_container"
|
android:id="@+id/log_track_container"
|
||||||
bullet="@{item.log.action == 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
|
bullet="@{item.log.action >= 2 ? R.drawable.ic_check_md2 : R.drawable.ic_close_md2}"
|
||||||
isBottom="@{item.isBottom}"
|
isBottom="@{item.isBottom}"
|
||||||
isSelected="@{item.log.action != 2}"
|
isSelected="@{item.log.action != 2}"
|
||||||
isTop="@{item.isTop}"
|
isTop="@{item.isTop}"
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
<import type="com.topjohnwu.magisk.databinding.DataBindingAdaptersKt" />
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="item"
|
name="item"
|
||||||
type="com.topjohnwu.magisk.ui.superuser.PolicyRvItem" />
|
type="com.topjohnwu.magisk.ui.superuser.PolicyRvItem" />
|
||||||
@@ -85,16 +87,32 @@
|
|||||||
app:layout_constraintVertical_bias="0"
|
app:layout_constraintVertical_bias="0"
|
||||||
tools:text="com.topjohnwu.magisk" />
|
tools:text="com.topjohnwu.magisk" />
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<FrameLayout
|
||||||
android:id="@+id/policy_indicator"
|
android:id="@+id/policy_indicator"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="@dimen/l1"
|
android:layout_marginEnd="@dimen/l1"
|
||||||
android:checked="@={item.enabled}"
|
|
||||||
android:nextFocusLeft="@id/policy"
|
android:nextFocusLeft="@id/policy"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
gone="@{item.showSlider}"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="@={item.enabled}" />
|
||||||
|
|
||||||
|
<com.google.android.material.slider.Slider
|
||||||
|
goneUnless="@{item.showSlider}"
|
||||||
|
labelFormatter="@{item.sliderValueToPolicyString}"
|
||||||
|
android:layout_width="96dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:stepSize="1"
|
||||||
|
android:value="@={DataBindingAdaptersKt.policyToSliderValue(item.sliderValue)}"
|
||||||
|
android:valueFrom="1"
|
||||||
|
android:valueTo="3" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
tasks.register("clean") {
|
plugins {
|
||||||
|
id("MagiskPlugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("clean", Delete::class) {
|
||||||
|
delete(rootProject.layout.buildDirectory)
|
||||||
|
|
||||||
subprojects.forEach {
|
subprojects.forEach {
|
||||||
dependsOn(":app:${it.name}:clean")
|
dependsOn(":${it.name}:clean")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,12 @@ gradlePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
|
||||||
compilerOptions {
|
|
||||||
languageVersion = KotlinVersion.KOTLIN_2_0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
|
implementation(kotlin("gradle-plugin", libs.versions.kotlin.get()))
|
||||||
implementation(libs.android.gradle.plugin)
|
implementation(libs.android.gradle.plugin)
|
||||||
implementation(libs.ksp.plugin)
|
implementation(libs.ksp.plugin)
|
||||||
implementation(libs.navigation.safe.args.plugin)
|
implementation(libs.navigation.safe.args.plugin)
|
||||||
implementation(libs.lsparanoid.plugin)
|
implementation(libs.lsparanoid.plugin)
|
||||||
|
implementation(libs.moshi.plugin)
|
||||||
implementation(libs.jgit)
|
implementation(libs.jgit)
|
||||||
}
|
}
|
||||||
77
app/buildSrc/src/main/java/AddCommentTask.kt
Normal file
77
app/buildSrc/src/main/java/AddCommentTask.kt
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import com.android.build.api.artifact.ArtifactTransformationRequest
|
||||||
|
import com.android.build.api.dsl.ApkSigningConfig
|
||||||
|
import com.android.builder.internal.packaging.IncrementalPackager
|
||||||
|
import com.android.tools.build.apkzlib.sign.SigningExtension
|
||||||
|
import com.android.tools.build.apkzlib.sign.SigningOptions
|
||||||
|
import com.android.tools.build.apkzlib.zfile.ZFiles
|
||||||
|
import com.android.tools.build.apkzlib.zip.ZFileOptions
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.InputFiles
|
||||||
|
import org.gradle.api.tasks.Internal
|
||||||
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import java.io.File
|
||||||
|
import java.security.KeyStore
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
|
||||||
|
abstract class AddCommentTask: DefaultTask() {
|
||||||
|
@get:Input
|
||||||
|
abstract val comment: Property<String>
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val signingConfig: Property<ApkSigningConfig>
|
||||||
|
|
||||||
|
@get:InputFiles
|
||||||
|
abstract val apkFolder: DirectoryProperty
|
||||||
|
|
||||||
|
@get:OutputDirectory
|
||||||
|
abstract val outFolder: DirectoryProperty
|
||||||
|
|
||||||
|
@get:Internal
|
||||||
|
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
|
||||||
|
val inFile = File(artifact.outputFile)
|
||||||
|
val outFile = outFolder.file(inFile.name).get().asFile
|
||||||
|
|
||||||
|
val privateKey = signingConfig.get().getPrivateKey()
|
||||||
|
val signingOptions = SigningOptions.builder()
|
||||||
|
.setMinSdkVersion(0)
|
||||||
|
.setV1SigningEnabled(true)
|
||||||
|
.setV2SigningEnabled(true)
|
||||||
|
.setKey(privateKey.privateKey)
|
||||||
|
.setCertificates(privateKey.certificate as X509Certificate)
|
||||||
|
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
|
||||||
|
.build()
|
||||||
|
val options = ZFileOptions().apply {
|
||||||
|
noTimestamps = true
|
||||||
|
autoSortFiles = true
|
||||||
|
}
|
||||||
|
outFile.parentFile?.mkdirs()
|
||||||
|
inFile.copyTo(outFile, overwrite = true)
|
||||||
|
ZFiles.apk(outFile, options).use {
|
||||||
|
SigningExtension(signingOptions).register(it)
|
||||||
|
it.eocdComment = comment.get().toByteArray()
|
||||||
|
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
|
||||||
|
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
|
||||||
|
it.get(JarFile.MANIFEST_NAME)?.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
|
||||||
|
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
|
||||||
|
storeFile!!.inputStream().use {
|
||||||
|
keyStore.load(it, storePassword!!.toCharArray())
|
||||||
|
}
|
||||||
|
val keyPwdArray = keyPassword!!.toCharArray()
|
||||||
|
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
|
||||||
|
return entry as KeyStore.PrivateKeyEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,12 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.kotlin.dsl.provideDelegate
|
import org.gradle.kotlin.dsl.provideDelegate
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
import java.util.Random
|
||||||
|
|
||||||
|
// Set non-zero value here to fix the random seed for reproducible builds
|
||||||
|
// CI builds are always reproducible
|
||||||
|
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
|
||||||
|
lateinit var RANDOM: Random
|
||||||
|
|
||||||
private val props = Properties()
|
private val props = Properties()
|
||||||
private var commitHash = ""
|
private var commitHash = ""
|
||||||
@@ -14,7 +20,7 @@ private val defaultAbis = setOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
|
|||||||
object Config {
|
object Config {
|
||||||
operator fun get(key: String): String? {
|
operator fun get(key: String): String? {
|
||||||
val v = props[key] as? String ?: return null
|
val v = props[key] as? String ?: return null
|
||||||
return if (v.isBlank()) null else v
|
return v.ifBlank { null }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun contains(key: String) = get(key) != null
|
fun contains(key: String) = get(key) != null
|
||||||
@@ -28,19 +34,25 @@ object Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Project.rootFile(path: String): File {
|
||||||
|
val file = File(path)
|
||||||
|
return if (file.isAbsolute) file
|
||||||
|
else File(rootProject.file(".."), path)
|
||||||
|
}
|
||||||
|
|
||||||
class MagiskPlugin : Plugin<Project> {
|
class MagiskPlugin : Plugin<Project> {
|
||||||
override fun apply(project: Project) = project.applyPlugin()
|
override fun apply(project: Project) = project.applyPlugin()
|
||||||
|
|
||||||
private fun Project.applyPlugin() {
|
private fun Project.applyPlugin() {
|
||||||
initRandom(rootProject.file("app/dict.txt"))
|
initRandom(rootProject.file("dict.txt"))
|
||||||
props.clear()
|
props.clear()
|
||||||
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
|
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
|
||||||
val configPath: String? by this
|
val configPath: String? by this
|
||||||
val config = configPath?.let { File(it) } ?: rootProject.file("config.prop")
|
val config = rootFile(configPath ?: "config.prop")
|
||||||
if (config.exists())
|
if (config.exists())
|
||||||
config.inputStream().use { props.load(it) }
|
config.inputStream().use { props.load(it) }
|
||||||
|
|
||||||
val repo = FileRepository(rootProject.file(".git"))
|
val repo = FileRepository(rootFile(".git"))
|
||||||
val refId = repo.refDatabase.exactRef("HEAD").objectId
|
val refId = repo.refDatabase.exactRef("HEAD").objectId
|
||||||
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
|
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,11 @@
|
|||||||
|
import com.android.build.api.artifact.SingleArtifact
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.file.DirectoryProperty
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.file.RegularFileProperty
|
import org.gradle.api.file.RegularFileProperty
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.tasks.CacheableTask
|
import org.gradle.api.tasks.CacheableTask
|
||||||
|
import org.gradle.api.tasks.Delete
|
||||||
import org.gradle.api.tasks.Input
|
import org.gradle.api.tasks.Input
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
import org.gradle.api.tasks.InputFiles
|
import org.gradle.api.tasks.InputFiles
|
||||||
@@ -10,22 +13,25 @@ import org.gradle.api.tasks.OutputFile
|
|||||||
import org.gradle.api.tasks.PathSensitive
|
import org.gradle.api.tasks.PathSensitive
|
||||||
import org.gradle.api.tasks.PathSensitivity
|
import org.gradle.api.tasks.PathSensitivity
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.gradle.kotlin.dsl.assign
|
||||||
|
import org.gradle.kotlin.dsl.named
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Random
|
import java.util.Random
|
||||||
|
import java.util.zip.Deflater
|
||||||
|
import java.util.zip.DeflaterOutputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.CipherOutputStream
|
import javax.crypto.CipherOutputStream
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.random.asKotlinRandom
|
import kotlin.random.asKotlinRandom
|
||||||
|
|
||||||
// Set non-zero value here to fix the random seed for reproducible builds
|
|
||||||
// CI builds are always reproducible
|
|
||||||
val RAND_SEED = if (System.getenv("CI") != null) 42 else 0
|
|
||||||
private lateinit var RANDOM: Random
|
|
||||||
private val kRANDOM get() = RANDOM.asKotlinRandom()
|
private val kRANDOM get() = RANDOM.asKotlinRandom()
|
||||||
|
|
||||||
private val c1 = mutableListOf<String>()
|
private val c1 = mutableListOf<String>()
|
||||||
@@ -72,7 +78,7 @@ private fun PrintStream.byteField(name: String, bytes: ByteArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CacheableTask
|
@CacheableTask
|
||||||
abstract class ManifestUpdater: DefaultTask() {
|
private abstract class ManifestUpdater: DefaultTask() {
|
||||||
@get:Input
|
@get:Input
|
||||||
abstract val applicationId: Property<String>
|
abstract val applicationId: Property<String>
|
||||||
|
|
||||||
@@ -182,9 +188,7 @@ abstract class ManifestUpdater: DefaultTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
private fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
||||||
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
|
|
||||||
|
|
||||||
val classNameGenerator = sequence {
|
val classNameGenerator = sequence {
|
||||||
fun notJavaKeyword(name: String) = when (name) {
|
fun notJavaKeyword(name: String) = when (name) {
|
||||||
"do", "if", "for", "int", "new", "try" -> false
|
"do", "if", "for", "int", "new", "try" -> false
|
||||||
@@ -228,7 +232,7 @@ fun genStubClasses(factoryOutDir: File, appOutDir: File) {
|
|||||||
genClass("StubApplication", appOutDir)
|
genClass("StubApplication", appOutDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genEncryptedResources(res: ByteArray, outDir: File) {
|
private fun genEncryptedResources(res: ByteArray, outDir: File) {
|
||||||
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
val mainPkgDir = File(outDir, "com/topjohnwu/magisk")
|
||||||
mainPkgDir.mkdirs()
|
mainPkgDir.mkdirs()
|
||||||
|
|
||||||
@@ -259,3 +263,86 @@ fun genEncryptedResources(res: ByteArray, outDir: File) {
|
|||||||
it.println("}")
|
it.println("}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Project.setupStubApk() {
|
||||||
|
setupAppCommon()
|
||||||
|
|
||||||
|
androidComponents.onVariants { variant ->
|
||||||
|
val variantName = variant.name
|
||||||
|
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
|
||||||
|
val manifestUpdater =
|
||||||
|
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
|
||||||
|
dependsOn("generate${variantCapped}ObfuscatedClass")
|
||||||
|
applicationId = variant.applicationId
|
||||||
|
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
|
||||||
|
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
|
||||||
|
}
|
||||||
|
variant.artifacts.use(manifestUpdater)
|
||||||
|
.wiredWithFiles(
|
||||||
|
ManifestUpdater::mergedManifest,
|
||||||
|
ManifestUpdater::outputManifest)
|
||||||
|
.toTransform(SingleArtifact.MERGED_MANIFEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
androidApp.applicationVariants.all {
|
||||||
|
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||||
|
val variantLowered = name.lowercase()
|
||||||
|
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
|
||||||
|
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
|
||||||
|
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
|
||||||
|
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
|
||||||
|
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||||
|
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
|
||||||
|
|
||||||
|
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
|
||||||
|
inputs.property("seed", RAND_SEED)
|
||||||
|
outputs.dirs(outFactoryClassDir, outAppClassDir)
|
||||||
|
doLast {
|
||||||
|
outFactoryClassDir.mkdirs()
|
||||||
|
outAppClassDir.mkdirs()
|
||||||
|
genStubClasses(outFactoryClassDir, outAppClassDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
|
||||||
|
|
||||||
|
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
|
||||||
|
outputs.dir(outResDir)
|
||||||
|
doLast {
|
||||||
|
val apkTmp = File("${apk}.tmp")
|
||||||
|
providers.exec {
|
||||||
|
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||||
|
}.result.get()
|
||||||
|
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
ZipFile(apkTmp).use { src ->
|
||||||
|
ZipOutputStream(apk.outputStream()).use {
|
||||||
|
it.setLevel(Deflater.BEST_COMPRESSION)
|
||||||
|
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||||
|
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
||||||
|
it.closeEntry()
|
||||||
|
}
|
||||||
|
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
||||||
|
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apkTmp.delete()
|
||||||
|
genEncryptedResources(bos.toByteArray(), outResDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerJavaGeneratingTask(processResourcesTask, outResDir)
|
||||||
|
}
|
||||||
|
// Override optimizeReleaseResources task
|
||||||
|
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||||
|
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
|
||||||
|
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
|
||||||
|
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
|
||||||
|
afterEvaluate {
|
||||||
|
tasks.named("optimizeReleaseResources") {
|
||||||
|
doLast { apk.copyTo(optRes, true) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.named<Delete>("clean") {
|
||||||
|
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
|
||||||
|
}
|
||||||
|
}
|
||||||
5
app/core/.gitignore
vendored
5
app/core/.gitignore
vendored
@@ -1,4 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
src/*/assets
|
src/debug
|
||||||
src/*/jniLibs
|
src/release
|
||||||
src/*/resources
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ plugins {
|
|||||||
id("com.android.library")
|
id("com.android.library")
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
kotlin("plugin.parcelize")
|
kotlin("plugin.parcelize")
|
||||||
|
id("dev.zacsweers.moshix")
|
||||||
id("com.google.devtools.ksp")
|
id("com.google.devtools.ksp")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,10 +27,15 @@ android {
|
|||||||
aidl = true
|
aidl = true
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":app:shared"))
|
api(project(":shared"))
|
||||||
|
coreLibraryDesugaring(libs.jdk.libs)
|
||||||
|
|
||||||
api(libs.timber)
|
api(libs.timber)
|
||||||
api(libs.markwon.core)
|
api(libs.markwon.core)
|
||||||
@@ -48,9 +54,6 @@ dependencies {
|
|||||||
implementation(libs.okhttp.logging)
|
implementation(libs.okhttp.logging)
|
||||||
implementation(libs.okhttp.dnsoverhttps)
|
implementation(libs.okhttp.dnsoverhttps)
|
||||||
|
|
||||||
implementation(libs.moshi)
|
|
||||||
ksp(libs.moshi.codegen)
|
|
||||||
|
|
||||||
implementation(libs.room.runtime)
|
implementation(libs.room.runtime)
|
||||||
implementation(libs.room.ktx)
|
implementation(libs.room.ktx)
|
||||||
ksp(libs.room.compiler)
|
ksp(libs.room.compiler)
|
||||||
|
|||||||
1
app/core/proguard-rules.pro
vendored
1
app/core/proguard-rules.pro
vendored
@@ -38,3 +38,4 @@
|
|||||||
-allowaccessmodification
|
-allowaccessmodification
|
||||||
|
|
||||||
-dontwarn org.junit.**
|
-dontwarn org.junit.**
|
||||||
|
-dontwarn org.apache.**
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:multiArch="true"
|
|
||||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"
|
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"
|
||||||
tools:remove="android:appComponentFactory">
|
tools:remove="android:appComponentFactory">
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ package com.topjohnwu.magisk.core.utils;
|
|||||||
interface IRootUtils {
|
interface IRootUtils {
|
||||||
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
|
android.app.ActivityManager.RunningAppProcessInfo getAppProcess(int pid);
|
||||||
IBinder getFileSystem();
|
IBinder getFileSystem();
|
||||||
|
boolean addSystemlessHosts();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
const val SU_NOTIFICATION = "su_notification"
|
const val SU_NOTIFICATION = "su_notification"
|
||||||
const val SU_REAUTH = "su_reauth"
|
const val SU_REAUTH = "su_reauth"
|
||||||
const val SU_TAPJACK = "su_tapjack"
|
const val SU_TAPJACK = "su_tapjack"
|
||||||
|
const val SU_RESTRICT = "su_restrict"
|
||||||
const val CHECK_UPDATES = "check_update"
|
const val CHECK_UPDATES = "check_update"
|
||||||
const val UPDATE_CHANNEL = "update_channel"
|
const val RELEASE_CHANNEL = "release_channel"
|
||||||
const val CUSTOM_CHANNEL = "custom_channel"
|
const val CUSTOM_CHANNEL = "custom_channel"
|
||||||
const val LOCALE = "locale"
|
const val LOCALE = "locale"
|
||||||
const val DARK_THEME = "dark_theme_extended"
|
const val DARK_THEME = "dark_theme_extended"
|
||||||
@@ -48,7 +49,7 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
SU_AUTO_RESPONSE, SU_REAUTH, SU_TAPJACK)
|
SU_AUTO_RESPONSE, SU_REAUTH, SU_TAPJACK)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Value {
|
object OldValue {
|
||||||
// Update channels
|
// Update channels
|
||||||
const val DEFAULT_CHANNEL = -1
|
const val DEFAULT_CHANNEL = -1
|
||||||
const val STABLE_CHANNEL = 0
|
const val STABLE_CHANNEL = 0
|
||||||
@@ -56,6 +57,15 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
const val CUSTOM_CHANNEL = 2
|
const val CUSTOM_CHANNEL = 2
|
||||||
const val CANARY_CHANNEL = 3
|
const val CANARY_CHANNEL = 3
|
||||||
const val DEBUG_CHANNEL = 4
|
const val DEBUG_CHANNEL = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
object Value {
|
||||||
|
// Update channels
|
||||||
|
const val DEFAULT_CHANNEL = -1
|
||||||
|
const val STABLE_CHANNEL = 0
|
||||||
|
const val BETA_CHANNEL = 1
|
||||||
|
const val DEBUG_CHANNEL = 2
|
||||||
|
const val CUSTOM_CHANNEL = 3
|
||||||
|
|
||||||
// root access mode
|
// root access mode
|
||||||
const val ROOT_ACCESS_DISABLED = 0
|
const val ROOT_ACCESS_DISABLED = 0
|
||||||
@@ -86,14 +96,6 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
val TIMEOUT_LIST = longArrayOf(0, -1, 10, 20, 30, 60)
|
val TIMEOUT_LIST = longArrayOf(0, -1, 10, 20, 30, 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val defaultChannel =
|
|
||||||
if (BuildConfig.DEBUG)
|
|
||||||
Value.DEBUG_CHANNEL
|
|
||||||
else if (Const.APP_IS_CANARY)
|
|
||||||
Value.CANARY_CHANNEL
|
|
||||||
else
|
|
||||||
Value.DEFAULT_CHANNEL
|
|
||||||
|
|
||||||
@JvmField var keepVerity = false
|
@JvmField var keepVerity = false
|
||||||
@JvmField var keepEnc = false
|
@JvmField var keepEnc = false
|
||||||
@JvmField var recovery = false
|
@JvmField var recovery = false
|
||||||
@@ -109,7 +111,7 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
private var checkUpdatePrefs by preference(Key.CHECK_UPDATES, true)
|
private var checkUpdatePrefs by preference(Key.CHECK_UPDATES, true)
|
||||||
private var localePrefs by preference(Key.LOCALE, "")
|
private var localePrefs by preference(Key.LOCALE, "")
|
||||||
var doh by preference(Key.DOH, false)
|
var doh by preference(Key.DOH, false)
|
||||||
var updateChannel by preferenceStrInt(Key.UPDATE_CHANNEL, defaultChannel)
|
var updateChannel by preference(Key.RELEASE_CHANNEL, Value.DEFAULT_CHANNEL)
|
||||||
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
|
var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "")
|
||||||
var downloadDir by preference(Key.DOWNLOAD_DIR, "")
|
var downloadDir by preference(Key.DOWNLOAD_DIR, "")
|
||||||
var randName by preference(Key.RAND_NAME, true)
|
var randName by preference(Key.RAND_NAME, true)
|
||||||
@@ -146,8 +148,10 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
}
|
}
|
||||||
var suReAuth by preference(Key.SU_REAUTH, false)
|
var suReAuth by preference(Key.SU_REAUTH, false)
|
||||||
var suTapjack by preference(Key.SU_TAPJACK, true)
|
var suTapjack by preference(Key.SU_TAPJACK, true)
|
||||||
|
var suRestrict by preference(Key.SU_RESTRICT, false)
|
||||||
|
|
||||||
private const val SU_FINGERPRINT = "su_fingerprint"
|
private const val SU_FINGERPRINT = "su_fingerprint"
|
||||||
|
private const val UPDATE_CHANNEL = "update_channel"
|
||||||
|
|
||||||
fun toBundle(): Bundle {
|
fun toBundle(): Bundle {
|
||||||
val map = prefs.all - Key.NO_MIGRATION
|
val map = prefs.all - Key.NO_MIGRATION
|
||||||
@@ -183,17 +187,23 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
// Settings migration
|
// Migrate su_fingerprint
|
||||||
if (prefs.getBoolean(SU_FINGERPRINT, false))
|
if (prefs.getBoolean(SU_FINGERPRINT, false))
|
||||||
suBiometric = true
|
suBiometric = true
|
||||||
remove(SU_FINGERPRINT)
|
remove(SU_FINGERPRINT)
|
||||||
prefs.getString(Key.UPDATE_CHANNEL, null).also {
|
|
||||||
if (it == null ||
|
// Migrate update_channel
|
||||||
it.toInt() > Value.DEBUG_CHANNEL ||
|
prefs.getString(UPDATE_CHANNEL, null)?.let {
|
||||||
it.toInt() < Value.DEFAULT_CHANNEL) {
|
val channel = when (it.toInt()) {
|
||||||
putString(Key.UPDATE_CHANNEL, defaultChannel.toString())
|
OldValue.STABLE_CHANNEL -> Value.STABLE_CHANNEL
|
||||||
|
OldValue.CANARY_CHANNEL, OldValue.BETA_CHANNEL -> Value.BETA_CHANNEL
|
||||||
|
OldValue.DEBUG_CHANNEL -> Value.DEBUG_CHANNEL
|
||||||
|
OldValue.CUSTOM_CHANNEL -> Value.CUSTOM_CHANNEL
|
||||||
|
else -> Value.DEFAULT_CHANNEL
|
||||||
}
|
}
|
||||||
|
putInt(Key.RELEASE_CHANNEL, channel)
|
||||||
}
|
}
|
||||||
|
remove(UPDATE_CHANNEL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user