Module json add changelog

This commit is contained in:
vvb2060 2022-01-21 23:52:42 +08:00 committed by John Wu
parent 691e41e22e
commit bf8b74e996
62 changed files with 183 additions and 447 deletions

View File

@ -78,18 +78,13 @@ dependencies {
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0") implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0")
implementation("dev.rikka.rikkax.insets:insets:1.1.1") implementation("dev.rikka.rikkax.insets:insets:1.1.1")
implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1") implementation("dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1")
implementation("io.noties.markwon:core:4.6.2")
val vBAdapt = "4.0.0" val vBAdapt = "4.0.0"
val bindingAdapter = "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter" val bindingAdapter = "me.tatarka.bindingcollectionadapter2:bindingcollectionadapter"
implementation("${bindingAdapter}:${vBAdapt}") implementation("${bindingAdapter}:${vBAdapt}")
implementation("${bindingAdapter}-recyclerview:${vBAdapt}") implementation("${bindingAdapter}-recyclerview:${vBAdapt}")
val vMarkwon = "4.6.2"
implementation("io.noties.markwon:core:${vMarkwon}")
implementation("io.noties.markwon:html:${vMarkwon}")
implementation("io.noties.markwon:image:${vMarkwon}")
implementation("com.caverock:androidsvg:1.4")
val vLibsu = "3.2.1" val vLibsu = "3.2.1"
implementation("com.github.topjohnwu.libsu:core:${vLibsu}") implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
implementation("com.github.topjohnwu.libsu:io:${vLibsu}") implementation("com.github.topjohnwu.libsu:io:${vLibsu}")

View File

@ -14,20 +14,19 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.ForegroundTracker import com.topjohnwu.magisk.core.ForegroundTracker
import com.topjohnwu.magisk.core.base.BaseService import com.topjohnwu.magisk.core.base.BaseService
import com.topjohnwu.magisk.core.intent import com.topjohnwu.magisk.core.intent
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.core.utils.ProgressInputStream import com.topjohnwu.magisk.core.utils.ProgressInputStream
import com.topjohnwu.magisk.di.ServiceLocator import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ktx.copyAndClose
import com.topjohnwu.magisk.ktx.synchronized import com.topjohnwu.magisk.ktx.synchronized
import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Notifications.mgr import com.topjohnwu.magisk.view.Notifications.mgr
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import okhttp3.ResponseBody import okhttp3.ResponseBody
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.*
import kotlin.collections.HashMap
class DownloadService : BaseService() { class DownloadService : BaseService() {
@ -67,11 +66,11 @@ class DownloadService : BaseService() {
val stream = service.fetchFile(subject.url).toProgressStream(subject) val stream = service.fetchFile(subject.url).toProgressStream(subject)
when (subject) { when (subject) {
is Subject.Manager -> handleAPK(subject, stream) is Subject.Manager -> handleAPK(subject, stream)
else -> stream.copyAndClose(subject.file.outputStream()) else -> stream.toModule(subject.file, service.fetchInstaller().byteStream())
} }
if (ForegroundTracker.hasForeground) { if (ForegroundTracker.hasForeground) {
remove(subject.notifyId) remove(subject.notifyId)
subject.pendingIntent(this@DownloadService).send() subject.pendingIntent(this@DownloadService)?.send()
} else { } else {
notifyFinish(subject) notifyFinish(subject)
} }

View File

@ -1,14 +1,18 @@
package com.topjohnwu.magisk.core.download package com.topjohnwu.magisk.core.download
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Intent
import androidx.core.content.getSystemService
import androidx.core.net.toFile import androidx.core.net.toFile
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.ForegroundTracker
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.isRunningAsStub import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.tasks.HideAPK import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.ktx.copyAndClose import com.topjohnwu.magisk.ktx.copyAndClose
import com.topjohnwu.magisk.ktx.relaunchApp
import com.topjohnwu.magisk.ktx.writeTo import com.topjohnwu.magisk.ktx.writeTo
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -55,9 +59,17 @@ suspend fun DownloadService.handleAPK(subject: Subject.Manager, stream: InputStr
apk.delete() apk.delete()
patched.renameTo(apk) patched.renameTo(apk)
} else { } else {
// Simply relaunch the app val intent = packageManager.getLaunchIntentForPackage(packageName)
intent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
//noinspection InlinedApi
val flag = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
val pending = PendingIntent.getActivity(this, id, intent, flag)
if (ForegroundTracker.hasForeground) {
val alarm = getSystemService<AlarmManager>()
alarm!!.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pending)
}
stopSelf() stopSelf()
relaunchApp(this) Runtime.getRuntime().exit(0)
} }
} else { } else {
write(subject.file.outputStream()) write(subject.file.outputStream())

View File

@ -0,0 +1,38 @@
package com.topjohnwu.magisk.core.download
import android.net.Uri
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
import com.topjohnwu.magisk.ktx.forEach
import com.topjohnwu.magisk.ktx.withStreams
import java.io.InputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
fun InputStream.toModule(file: Uri, installer: InputStream) {
val input = ZipInputStream(buffered())
val output = ZipOutputStream(file.outputStream().buffered())
withStreams(input, output) { zin, zout ->
zout.putNextEntry(ZipEntry("META-INF/"))
zout.putNextEntry(ZipEntry("META-INF/com/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/android/"))
zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary"))
installer.copyTo(zout)
zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script"))
zout.write("#MAGISK\n".toByteArray(charset("UTF-8")))
zin.forEach { entry ->
val path = entry.name
if (path.isNotEmpty() && !path.startsWith("META-INF")) {
zout.putNextEntry(ZipEntry(path))
if (!entry.isDirectory) {
zin.copyTo(zout)
}
}
}
}
}

View File

@ -35,7 +35,7 @@ sealed class Subject : Parcelable {
abstract val title: String abstract val title: String
abstract val notifyId: Int abstract val notifyId: Int
abstract fun pendingIntent(context: Context): PendingIntent abstract fun pendingIntent(context: Context): PendingIntent?
@Parcelize @Parcelize
class Module( class Module(
@ -53,7 +53,7 @@ sealed class Subject : Parcelable {
override fun pendingIntent(context: Context) = when (action) { override fun pendingIntent(context: Context) = when (action) {
Action.Flash -> FlashFragment.installIntent(context, file) Action.Flash -> FlashFragment.installIntent(context, file)
else -> Intent().toPending(context) else -> null
} }
} }

View File

@ -31,6 +31,7 @@ data class ModuleJson(
val version: String, val version: String,
val versionCode: Int, val versionCode: Int,
val zipUrl: String, val zipUrl: String,
val changelog: String,
) )
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)

View File

@ -11,9 +11,10 @@ data class OnlineModule(
override var version: String, override var version: String,
override var versionCode: Int, override var versionCode: Int,
val zipUrl: String, val zipUrl: String,
val changelog: String,
) : Module(), Parcelable { ) : Module(), Parcelable {
constructor(local: LocalModule, json: ModuleJson) : constructor(local: LocalModule, json: ModuleJson) :
this(local.id, local.name, json.version, json.versionCode, json.zipUrl) this(local.id, local.name, json.version, json.versionCode, json.zipUrl, json.changelog)
val downloadFilename get() = "$name-$version($versionCode).zip".legalFilename() val downloadFilename get() = "$name-$version($versionCode).zip".legalFilename()

View File

@ -4,6 +4,7 @@ import android.animation.ValueAnimator
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Paint import android.graphics.Paint
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.text.Spanned
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -29,11 +30,8 @@ import com.google.android.material.chip.Chip
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.di.ServiceLocator import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ktx.coroutineScope
import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.widget.IndeterminateCheckBox import com.topjohnwu.widget.IndeterminateCheckBox
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.math.roundToInt import kotlin.math.roundToInt
@BindingAdapter("gone") @BindingAdapter("gone")
@ -57,10 +55,8 @@ fun setInvisibleUnless(view: View, invisibleUnless: Boolean) {
} }
@BindingAdapter("markdownText") @BindingAdapter("markdownText")
fun setMarkdownText(tv: TextView, text: CharSequence) { fun setMarkdownText(tv: TextView, markdown: Spanned) {
tv.coroutineScope.launch(Dispatchers.IO) { ServiceLocator.markwon.setParsedMarkdown(tv, markdown)
ServiceLocator.markwon.setMarkdown(tv, text.toString())
}
} }
@BindingAdapter("onNavigationClick") @BindingAdapter("onNavigationClick")

View File

@ -1,16 +1,17 @@
package com.topjohnwu.magisk.di package com.topjohnwu.magisk.di
import android.content.Context import android.content.Context
import android.text.method.LinkMovementMethod
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.ProviderInstaller import com.topjohnwu.magisk.ProviderInstaller
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.ktx.precomputedText import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.utils.MarkwonImagePlugin
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import io.noties.markwon.html.HtmlPlugin import io.noties.markwon.utils.NoCopySpannableFactory
import okhttp3.Cache import okhttp3.Cache
import okhttp3.ConnectionSpec
import okhttp3.Dns import okhttp3.Dns
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -46,7 +47,8 @@ private class DnsResolver(client: OkHttpClient) : Dns {
if (Config.doh) { if (Config.doh) {
try { try {
return doh.lookup(hostname) return doh.lookup(hostname)
} catch (e: UnknownHostException) {} } catch (e: UnknownHostException) {
}
} }
return Dns.SYSTEM.lookup(hostname) return Dns.SYSTEM.lookup(hostname)
} }
@ -61,11 +63,16 @@ fun createOkHttpClient(context: Context): OkHttpClient {
builder.addInterceptor(HttpLoggingInterceptor().apply { builder.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BASIC level = HttpLoggingInterceptor.Level.BASIC
}) })
} else {
builder.connectionSpecs(listOf(ConnectionSpec.RESTRICTED_TLS))
} }
builder.dns(DnsResolver(builder.build()))
builder.addInterceptor { chain -> builder.addInterceptor { chain ->
val request = chain.request().newBuilder() val request = chain.request().newBuilder()
request.header("User-Agent", "Magisk ${BuildConfig.VERSION_CODE}") request.header("User-Agent", "Magisk/${BuildConfig.VERSION_CODE}")
request.header("Accept-Language", currentLocale.toLanguageTag())
chain.proceed(request.build()) chain.proceed(request.build())
} }
@ -73,7 +80,6 @@ fun createOkHttpClient(context: Context): OkHttpClient {
Info.hasGMS = false Info.hasGMS = false
} }
builder.dns(DnsResolver(builder.build()))
return builder.build() return builder.build()
} }
@ -96,13 +102,17 @@ inline fun <reified T> createApiService(retrofitBuilder: Retrofit.Builder, baseU
.create(T::class.java) .create(T::class.java)
} }
fun createMarkwon(context: Context, okHttpClient: OkHttpClient): Markwon { fun createMarkwon(context: Context): Markwon {
return Markwon.builder(context) return Markwon.builder(context)
.textSetter { textView, spanned, _, onComplete -> .textSetter { textView, spanned, bufferType, onComplete ->
textView.tag = onComplete textView.apply {
textView.precomputedText = spanned post {
movementMethod = LinkMovementMethod.getInstance()
setSpannableFactory(NoCopySpannableFactory.getInstance())
setText(spanned, bufferType)
onComplete.run()
}
}
} }
.usePlugin(HtmlPlugin.create())
.usePlugin(MarkwonImagePlugin(okHttpClient))
.build() .build()
} }

View File

@ -39,7 +39,7 @@ object ServiceLocator {
// Networking // Networking
val okhttp by lazy { createOkHttpClient(context) } val okhttp by lazy { createOkHttpClient(context) }
val retrofit by lazy { createRetrofit(okhttp) } val retrofit by lazy { createRetrofit(okhttp) }
val markwon by lazy { createMarkwon(context, okhttp) } val markwon by lazy { createMarkwon(context) }
val networkService by lazy { val networkService by lazy {
NetworkService( NetworkService(
createApiService(retrofit, Const.Url.GITHUB_PAGE_URL), createApiService(retrofit, Const.Url.GITHUB_PAGE_URL),

View File

@ -10,9 +10,8 @@ import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import kotlin.coroutines.cancellation.CancellationException import java.io.IOException
abstract class MarkDownDialog : DialogEvent() { abstract class MarkDownDialog : DialogEvent() {
@ -23,17 +22,13 @@ abstract class MarkDownDialog : DialogEvent() {
with(dialog) { with(dialog) {
val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null) val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null)
setView(view) setView(view)
(ownerActivity as BaseActivity).lifecycleScope.launch { val tv = view.findViewById<TextView>(R.id.md_txt)
val tv = view.findViewById<TextView>(R.id.md_txt) (ownerActivity as BaseActivity).lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.IO) { try {
try { ServiceLocator.markwon.setMarkdown(tv, getMarkdownText())
ServiceLocator.markwon.setMarkdown(tv, getMarkdownText()) } catch (e: IOException) {
} catch (e: Exception) { Timber.e(e)
if (e is CancellationException) tv.post { tv.setText(R.string.download_file_error) }
throw e
Timber.e(e)
tv.post { tv.setText(R.string.download_file_error) }
}
} }
} }
} }

View File

@ -1,16 +1,24 @@
package com.topjohnwu.magisk.events.dialog package com.topjohnwu.magisk.events.dialog
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.Action import com.topjohnwu.magisk.core.download.Action
import com.topjohnwu.magisk.core.download.DownloadService import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.core.model.module.OnlineModule import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() { class ModuleInstallDialog(private val item: OnlineModule) : MarkDownDialog() {
private val svc get() = ServiceLocator.networkService
override suspend fun getMarkdownText(): String {
val str = svc.fetchString(item.changelog)
return if (str.length > 1000) str.substring(0, 1000) else str
}
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
super.build(dialog)
dialog.apply { dialog.apply {
fun download(install: Boolean) { fun download(install: Boolean) {
@ -19,21 +27,21 @@ class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() {
DownloadService.start(context, subject) DownloadService.start(context, subject)
} }
setTitle(context.getString(R.string.repo_install_title, item.name)) val title = context.getString(R.string.repo_install_title,
setMessage(context.getString(R.string.repo_install_msg, item.downloadFilename)) item.name, item.version, item.versionCode)
setTitle(title)
setCancelable(true) setCancelable(true)
setButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
text = R.string.download text = R.string.download
icon = R.drawable.ic_download_md2
onClick { download(false) } onClick { download(false) }
} }
setButton(MagiskDialog.ButtonType.POSITIVE) {
if (Info.env.isActive) { text = R.string.install
setButton(MagiskDialog.ButtonType.POSITIVE) { onClick { download(true) }
text = R.string.install }
icon = R.drawable.ic_install setButton(MagiskDialog.ButtonType.NEUTRAL) {
onClick { download(true) } text = android.R.string.cancel
}
} }
} }
} }

View File

@ -299,53 +299,6 @@ val View.activity: Activity get() {
} }
} }
var View.coroutineScope: CoroutineScope
get() = getTag(R.id.coroutineScope) as? CoroutineScope
?: (activity as? BaseActivity)?.lifecycleScope
?: GlobalScope
set(value) = setTag(R.id.coroutineScope, value)
@set:BindingAdapter("precomputedText")
var TextView.precomputedText: CharSequence
get() = text
set(value) {
val callback = tag as? Runnable
coroutineScope.launch(Dispatchers.IO) {
if (SDK_INT >= 29) {
// Internally PrecomputedTextCompat will use platform API on API 29+
// Due to some stupid crap OEM (Samsung) implementation, this can actually
// crash our app. Directly use platform APIs with some workarounds
val pre = PrecomputedText.create(value, textMetricsParams)
post {
try {
text = pre
} catch (e: IllegalArgumentException) {
// Override to computed params to workaround crashes
textMetricsParams = pre.params
text = pre
}
isGone = false
callback?.run()
}
} else {
val tv = this@precomputedText
val params = TextViewCompat.getTextMetricsParams(tv)
val pre = PrecomputedTextCompat.create(value, params)
post {
TextViewCompat.setPrecomputedText(tv, pre)
isGone = false
callback?.run()
}
}
}
}
fun Int.dpInPx(): Int {
val scale = AppContext.resources.displayMetrics.density
return (this * scale + 0.5).toInt()
}
@SuppressLint("PrivateApi") @SuppressLint("PrivateApi")
fun getProperty(key: String, def: String): String { fun getProperty(key: String, def: String): String {
runCatching { runCatching {

View File

@ -4,12 +4,10 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIFragment import com.topjohnwu.magisk.arch.BaseUIFragment
import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
import com.topjohnwu.magisk.di.viewModel import com.topjohnwu.magisk.di.viewModel
import com.topjohnwu.magisk.ktx.coroutineScope
class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Binding>() { class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Binding>() {
@ -19,9 +17,6 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
requireActivity().setTitle(R.string.install) requireActivity().setTitle(R.string.install)
// Allow markwon to run in viewmodel scope
binding.releaseNotes.coroutineScope = viewModel.viewModelScope
} }
override fun onCreateView( override fun onCreateView(

View File

@ -1,6 +1,8 @@
package com.topjohnwu.magisk.ui.install package com.topjohnwu.magisk.ui.install
import android.net.Uri import android.net.Uri
import android.text.SpannableStringBuilder
import android.text.Spanned
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
@ -12,10 +14,12 @@ import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.data.repository.NetworkService import com.topjohnwu.magisk.data.repository.NetworkService
import com.topjohnwu.magisk.databinding.set import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.di.AppContext import com.topjohnwu.magisk.di.AppContext
import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.events.MagiskInstallFileEvent import com.topjohnwu.magisk.events.MagiskInstallFileEvent
import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
import com.topjohnwu.magisk.ui.flash.FlashFragment import com.topjohnwu.magisk.ui.flash.FlashFragment
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
@ -55,23 +59,23 @@ class InstallViewModel(
set(value) = set(value, field, { field = it }, BR.data) set(value) = set(value, field, { field = it }, BR.data)
@get:Bindable @get:Bindable
var notes = "" var notes: Spanned = SpannableStringBuilder()
set(value) = set(value, field, { field = it }, BR.notes) set(value) = set(value, field, { field = it }, BR.notes)
init { init {
viewModelScope.launch { viewModelScope.launch(Dispatchers.IO) {
try { try {
File(AppContext.cacheDir, "${BuildConfig.VERSION_CODE}.md").run { val file = File(AppContext.cacheDir, "${BuildConfig.VERSION_CODE}.md")
notes = when { val text = when {
exists() -> readText() file.exists() -> file.readText()
Const.Url.CHANGELOG_URL.isEmpty() -> "" Const.Url.CHANGELOG_URL.isEmpty() -> ""
else -> { else -> {
val text = svc.fetchString(Const.Url.CHANGELOG_URL) val str = svc.fetchString(Const.Url.CHANGELOG_URL)
writeText(text) file.writeText(str)
text str
}
} }
} }
notes = ServiceLocator.markwon.toMarkdown(text)
} catch (e: IOException) { } catch (e: IOException) {
Timber.e(e) Timber.e(e)
} }

View File

@ -1,233 +0,0 @@
package com.topjohnwu.magisk.utils
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.PictureDrawable
import android.graphics.drawable.ShapeDrawable
import android.net.Uri
import android.text.Spanned
import android.text.style.DynamicDrawableSpan
import android.widget.TextView
import androidx.annotation.WorkerThread
import com.caverock.androidsvg.SVG
import com.caverock.androidsvg.SVGParseException
import com.topjohnwu.magisk.di.AppContext
import com.topjohnwu.superuser.internal.WaitRunnable
import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.MarkwonSpansFactory
import io.noties.markwon.image.*
import io.noties.markwon.image.data.DataUriSchemeHandler
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler
import kotlinx.coroutines.*
import okhttp3.OkHttpClient
import org.commonmark.node.Image
import timber.log.Timber
import java.io.InputStream
// Differences with Markwon stock ImagePlugin:
//
// We assume beforeSetText() will be run in a background thread, and in that method
// we download/decode all drawables before sending the spanned markdown CharSequence
// to the next stage. We also get our surrounding TextView width to properly
// resize our images.
//
// This is required for PrecomputedText to properly take the images into account
// when precomputing the metrics of TextView
//
// Basically, we want nothing to do with AsyncDrawable
class MarkwonImagePlugin(okHttp: OkHttpClient) : AbstractMarkwonPlugin() {
override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) {
builder.setFactory(Image::class.java) { _, props ->
val dest = ImageProps.DESTINATION.require(props)
val size = ImageProps.IMAGE_SIZE.get(props)
ImageSpan(dest, size)
}
}
@WorkerThread
override fun beforeSetText(tv: TextView, markdown: Spanned) {
if (markdown.isEmpty())
return
val spans = markdown.getSpans(0, markdown.length, ImageSpan::class.java)
if (spans == null || spans.isEmpty())
return
// Get TextView sizes before setText() to resize all images
val wr = WaitRunnable {
val width = tv.width - tv.paddingLeft - tv.paddingRight
spans.forEach { it.canvasWidth = width }
}
tv.post(wr)
runBlocking {
// Wait for drawable to be set
spans.forEach { it.await() }
// Wait for canvasWidth to be set
wr.waitUntilDone()
}
}
private val schemeHandlers = HashMap<String, SchemeHandler>(3)
private val mediaDecoders = HashMap<String, MediaDecoder>(0)
private val defaultMediaDecoder = DefaultMediaDecoder.create()
init {
addSchemeHandler(DataUriSchemeHandler.create())
addSchemeHandler(OkHttpNetworkSchemeHandler.create(okHttp))
addMediaDecoder(SVGDecoder())
}
private fun addSchemeHandler(schemeHandler: SchemeHandler) {
for (scheme in schemeHandler.supportedSchemes()) {
schemeHandlers[scheme] = schemeHandler
}
}
private fun addMediaDecoder(mediaDecoder: MediaDecoder) {
for (type in mediaDecoder.supportedTypes()) {
mediaDecoders[type] = mediaDecoder
}
}
// Modified from AsyncDrawableLoaderImpl.execute(asyncDrawable)
fun loadDrawable(destination: String): Drawable? {
val uri = Uri.parse(destination)
var drawable: Drawable? = null
try {
val scheme = uri.scheme
check(scheme != null && scheme.isNotEmpty()) {
"No scheme is found: $destination"
}
// obtain scheme handler
val schemeHandler = schemeHandlers[scheme]
?: throw IllegalStateException("No scheme-handler is found: $destination")
// handle scheme
val imageItem = schemeHandler.handle(destination, uri)
// if resulting imageItem needs further decoding -> proceed
drawable = if (imageItem.hasDecodingNeeded()) {
val withDecodingNeeded = imageItem.asWithDecodingNeeded
val mediaDecoder = mediaDecoders[withDecodingNeeded.contentType()]
?: defaultMediaDecoder
mediaDecoder.decode(
withDecodingNeeded.contentType(),
withDecodingNeeded.inputStream()
)
} else {
imageItem.asWithResult.result()
}
} catch (t: Throwable) {
Timber.e(t, "Error loading image: $destination")
}
// apply intrinsic bounds (but only if they are empty)
if (drawable != null && drawable.bounds.isEmpty)
DrawableUtils.applyIntrinsicBounds(drawable)
return drawable
}
inner class ImageSpan(
dest: String,
private val size: ImageSize?
) : DynamicDrawableSpan(ALIGN_BOTTOM) {
var canvasWidth = 0
private var measured = false
private lateinit var draw: Drawable
private val job: Job
init {
// Asynchronously download/decode images in the background
job = GlobalScope.launch(Dispatchers.IO) {
draw = loadDrawable(dest) ?: ShapeDrawable()
}
}
suspend fun await() = job.join()
override fun getDrawable() = draw
private fun defaultBounds(): Rect {
val bounds: Rect = draw.bounds
if (!bounds.isEmpty) {
return bounds
}
val intrinsicBounds = DrawableUtils.intrinsicBounds(draw)
if (!intrinsicBounds.isEmpty) {
return intrinsicBounds
}
return Rect(0, 0, 1, 1)
}
private fun measure(paint: Paint) {
if (measured || canvasWidth == 0)
return
measured = true
val bound =
SizeResolver.resolveImageSize(size, defaultBounds(), canvasWidth, paint.textSize)
draw.bounds = bound
}
override fun getSize(
paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?
): Int {
measure(paint)
return super.getSize(paint, text, start, end, fm)
}
}
object SizeResolver : ImageSizeResolverDef() {
// Expose protected API
public override fun resolveImageSize(
imageSize: ImageSize?,
imageBounds: Rect,
canvasWidth: Int,
textSize: Float
): Rect {
return super.resolveImageSize(imageSize, imageBounds, canvasWidth, textSize)
}
}
class SVGDecoder: MediaDecoder() {
override fun supportedTypes() = listOf("image/svg+xml")
override fun decode(contentType: String?, inputStream: InputStream): Drawable {
val svg = try {
SVG.getFromInputStream(inputStream)
} catch (e: SVGParseException) {
throw IllegalStateException("Exception decoding SVG", e)
}
val w = svg.documentWidth
val h = svg.documentHeight
if (w <= 0 || h <= 0) {
val picture = svg.renderToPicture()
return PictureDrawable(picture)
}
val density: Float = AppContext.resources.displayMetrics.density
val width = (w * density + .5f).toInt()
val height = (h * density + .5f).toInt()
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.scale(density, density)
svg.renderToCanvas(canvas)
return BitmapDrawable(AppContext.resources, bitmap)
}
}
}

View File

@ -26,12 +26,11 @@ object Notifications {
fun setup(context: Context) { fun setup(context: Context) {
if (SDK_INT >= 26) { if (SDK_INT >= 26) {
var channel = NotificationChannel(UPDATE_NOTIFICATION_CHANNEL, val channel = NotificationChannel(UPDATE_NOTIFICATION_CHANNEL,
context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT) context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT)
mgr.createNotificationChannel(channel) val channel2 = NotificationChannel(PROGRESS_NOTIFICATION_CHANNEL,
channel = NotificationChannel(PROGRESS_NOTIFICATION_CHANNEL, context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW)
context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW) mgr.createNotificationChannels(listOf(channel, channel2))
mgr.createNotificationChannel(channel)
} }
} }

View File

@ -223,11 +223,11 @@
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card" style="@style/WidgetFoundation.Card"
gone="@{viewModel.notes.empty}" gone="@{viewModel.notes.length == 0}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/l1"
android:layout_marginStart="@dimen/l1" android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l1"
android:layout_marginEnd="@dimen/l1" android:layout_marginEnd="@dimen/l1"
android:focusable="false"> android:focusable="false">
@ -237,8 +237,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="15dp" android:layout_margin="15dp"
android:breakStrategy="simple"
android:hyphenationFrequency="none"
android:textAppearance="@style/AppearanceFoundation.Caption" android:textAppearance="@style/AppearanceFoundation.Caption"
android:visibility="gone" tools:ignore="UnusedAttribute"
tools:maxLines="5" tools:maxLines="5"
tools:text="@tools:sample/lorem/random" tools:text="@tools:sample/lorem/random"
tools:visibility="visible" /> tools:visibility="visible" />

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -9,7 +10,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="15dp" android:layout_marginStart="15dp"
android:layout_marginEnd="15dp" android:layout_marginEnd="15dp"
android:breakStrategy="simple"
android:hyphenationFrequency="none"
android:paddingTop="10dp" android:paddingTop="10dp"
android:textAppearance="@style/AppearanceFoundation.Caption" /> android:textAppearance="@style/AppearanceFoundation.Caption"
tools:ignore="UnusedAttribute" />
</ScrollView> </ScrollView>

View File

@ -174,7 +174,6 @@
<string name="yes">نعم</string> <string name="yes">نعم</string>
<string name="no">لا</string> <string name="no">لا</string>
<string name="repo_install_title">تثبيت %1$s</string> <string name="repo_install_title">تثبيت %1$s</string>
<string name="repo_install_msg">هل تريد تثبيت %1$s ?</string>
<string name="download">تنزيل</string> <string name="download">تنزيل</string>
<string name="reboot">إعادة التشغيل</string> <string name="reboot">إعادة التشغيل</string>
<string name="release_notes">معلومات الأصدار الجديد</string> <string name="release_notes">معلومات الأصدار الجديد</string>

View File

@ -44,7 +44,6 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">%1$s faylını yüklə</string> <string name="repo_install_title">%1$s faylını yüklə</string>
<string name="repo_install_msg">%1$s faylını indi yükləmək istəyirsiniz?</string>
<string name="download">Yüklə</string> <string name="download">Yüklə</string>
<string name="reboot">Yenidən Başlat</string> <string name="reboot">Yenidən Başlat</string>
<string name="release_notes">Yeniliklər</string> <string name="release_notes">Yeniliklər</string>

View File

@ -172,8 +172,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Так</string> <string name="yes">Так</string>
<string name="no">Не</string> <string name="no">Не</string>
<string name="repo_install_title">Усталяваць %1$s</string> <string name="repo_install_title">Усталяваць %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Усталяваць %1$s?</string>
<string name="download">Спампаваць</string> <string name="download">Спампаваць</string>
<string name="reboot">Перазапуск</string> <string name="reboot">Перазапуск</string>
<string name="release_notes">Пра выпуск</string> <string name="release_notes">Пра выпуск</string>

View File

@ -36,8 +36,7 @@
<string name="app_changelog">Списък с промени</string> <string name="app_changelog">Списък с промени</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">Инсталиране на %1$s</string> <string name="repo_install_title">Инсталиране на %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Желаете ли да инсталирате %1$s сега?</string>
<string name="download">Изтегляне</string> <string name="download">Изтегляне</string>
<string name="reboot">Рестартиране</string> <string name="reboot">Рестартиране</string>
<string name="magisk_update_title">Достъпно е издание на Magisk.</string> <string name="magisk_update_title">Достъпно е издание на Magisk.</string>

View File

@ -186,8 +186,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes"></string> <string name="yes"></string>
<string name="no">No</string> <string name="no">No</string>
<string name="repo_install_title">Instal·lar %1$s</string> <string name="repo_install_title">Instal·lar %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Vol instal·lar %1$s ara?</string>
<string name="download">Baixar</string> <string name="download">Baixar</string>
<string name="reboot">Reiniciar</string> <string name="reboot">Reiniciar</string>
<string name="release_notes">Notes de llançament</string> <string name="release_notes">Notes de llançament</string>

View File

@ -185,8 +185,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">ANO</string> <string name="yes">ANO</string>
<string name="no">NE</string> <string name="no">NE</string>
<string name="repo_install_title">Instalovat %1$s</string> <string name="repo_install_title">Instalovat %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Chcete nyní nainstalovat %1$s?</string>
<string name="download">Stáhnout</string> <string name="download">Stáhnout</string>
<string name="reboot">Restartovat</string> <string name="reboot">Restartovat</string>
<string name="release_notes">Poznámky k vydání</string> <string name="release_notes">Poznámky k vydání</string>

View File

@ -194,7 +194,6 @@
<string name="yes">Ja</string> <string name="yes">Ja</string>
<string name="no">Nein</string> <string name="no">Nein</string>
<string name="repo_install_title">%1$s installieren</string> <string name="repo_install_title">%1$s installieren</string>
<string name="repo_install_msg">Möchten Sie %1$s jetzt installieren?</string>
<string name="download">Herunterladen</string> <string name="download">Herunterladen</string>
<string name="reboot">Neustart</string> <string name="reboot">Neustart</string>
<string name="release_notes">Anmerkungen zur Veröffentlichung</string> <string name="release_notes">Anmerkungen zur Veröffentlichung</string>

View File

@ -185,8 +185,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Ναι</string> <string name="yes">Ναι</string>
<string name="no">Όχι</string> <string name="no">Όχι</string>
<string name="repo_install_title">Εγκατάσταση %1$s</string> <string name="repo_install_title">Εγκατάσταση %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Θέλετε να εγκαταστήσετε το %1$s τώρα;</string>
<string name="download">Λήψη</string> <string name="download">Λήψη</string>
<string name="reboot">Επανεκκίνηση</string> <string name="reboot">Επανεκκίνηση</string>
<string name="release_notes">Σημειώσεις έκδοσης</string> <string name="release_notes">Σημειώσεις έκδοσης</string>

View File

@ -195,8 +195,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes"></string> <string name="yes"></string>
<string name="no">No</string> <string name="no">No</string>
<string name="repo_install_title">Instalar %1$s</string> <string name="repo_install_title">Instalar %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">¿Desea instalar %1$s ahora?</string>
<string name="download">Descargar</string> <string name="download">Descargar</string>
<string name="reboot">Reiniciar</string> <string name="reboot">Reiniciar</string>
<string name="release_notes">Notas de lanzamiento</string> <string name="release_notes">Notas de lanzamiento</string>

View File

@ -175,8 +175,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Jah</string> <string name="yes">Jah</string>
<string name="no">Ei</string> <string name="no">Ei</string>
<string name="repo_install_title">Installi %1$s</string> <string name="repo_install_title">Installi %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Kas soovid kohe installida %1$s?</string>
<string name="download">Allalaadimine</string> <string name="download">Allalaadimine</string>
<string name="reboot">Taaskäivita</string> <string name="reboot">Taaskäivita</string>
<string name="release_notes">Väljalaskemärkmed</string> <string name="release_notes">Väljalaskemärkmed</string>

View File

@ -171,7 +171,6 @@
<string name="yes">بله</string> <string name="yes">بله</string>
<string name="no">نه</string> <string name="no">نه</string>
<string name="repo_install_title">نصب کرد %1$s</string> <string name="repo_install_title">نصب کرد %1$s</string>
<string name="repo_install_msg">آیا می خواهید %1$s نصب شود؟</string>
<string name="download">انلود کردن</string> <string name="download">انلود کردن</string>
<string name="reboot">راه اندازی مجدد</string> <string name="reboot">راه اندازی مجدد</string>
<string name="release_notes">نکته های نسخه</string> <string name="release_notes">نکته های نسخه</string>

View File

@ -195,8 +195,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Oui</string> <string name="yes">Oui</string>
<string name="no">Non</string> <string name="no">Non</string>
<string name="repo_install_title">Installer %1$s</string> <string name="repo_install_title">Installer %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Voulezvous installer %1$s maintenant?</string>
<string name="download">Télécharger</string> <string name="download">Télécharger</string>
<string name="reboot">Redémarrer</string> <string name="reboot">Redémarrer</string>
<string name="release_notes">Notes de version</string> <string name="release_notes">Notes de version</string>

View File

@ -177,8 +177,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">हाँ</string> <string name="yes">हाँ</string>
<string name="no">नहीं</string> <string name="no">नहीं</string>
<string name="repo_install_title">इंस्टॉल %1$s</string> <string name="repo_install_title">इंस्टॉल %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">क्या आप अभी %1$s इंस्टॉल करना चाहते हैं?</string>
<string name="download">डाउनलोड</string> <string name="download">डाउनलोड</string>
<string name="reboot">रीबूट</string> <string name="reboot">रीबूट</string>
<string name="release_notes">रिलीज नोट्स</string> <string name="release_notes">रिलीज नोट्स</string>

View File

@ -176,8 +176,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Da</string> <string name="yes">Da</string>
<string name="no">Ne</string> <string name="no">Ne</string>
<string name="repo_install_title">Instaliraj %1$s</string> <string name="repo_install_title">Instaliraj %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Da li želite instalirati %1$s sada?</string>
<string name="download">Preuzmi</string> <string name="download">Preuzmi</string>
<string name="reboot">Ponovno pokreni</string> <string name="reboot">Ponovno pokreni</string>
<string name="release_notes">Bilješke o izdavanju aplikacije</string> <string name="release_notes">Bilješke o izdavanju aplikacije</string>

View File

@ -180,8 +180,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Ya</string> <string name="yes">Ya</string>
<string name="no">Tidak</string> <string name="no">Tidak</string>
<string name="repo_install_title">Instal %1$s</string> <string name="repo_install_title">Instal %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Apakah Anda ingin menginstal %1$s sekarang?</string>
<string name="download">Download</string> <string name="download">Download</string>
<string name="reboot">Nyalakan ulang</string> <string name="reboot">Nyalakan ulang</string>
<string name="release_notes">Catatan rilis</string> <string name="release_notes">Catatan rilis</string>

View File

@ -193,8 +193,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes"></string> <string name="yes"></string>
<string name="no">No</string> <string name="no">No</string>
<string name="repo_install_title">Installazione di %1$s</string> <string name="repo_install_title">Installazione di %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Vuoi installare %1$s?</string>
<string name="download">Download</string> <string name="download">Download</string>
<string name="reboot">Riavvia</string> <string name="reboot">Riavvia</string>
<string name="release_notes">Note di rilascio</string> <string name="release_notes">Note di rilascio</string>

View File

@ -183,7 +183,6 @@
<string name="yes">כן</string> <string name="yes">כן</string>
<string name="no">לא</string> <string name="no">לא</string>
<string name="repo_install_title">התקן %1$s</string> <string name="repo_install_title">התקן %1$s</string>
<string name="repo_install_msg">האם ברצונך להתקין את %1$s כעת?</string>
<string name="download">הורדה</string> <string name="download">הורדה</string>
<string name="reboot">הפעלה מחדש</string> <string name="reboot">הפעלה מחדש</string>
<string name="release_notes">הערות שחרור</string> <string name="release_notes">הערות שחרור</string>

View File

@ -197,7 +197,6 @@
<string name="yes">対応</string> <string name="yes">対応</string>
<string name="no">非対応</string> <string name="no">非対応</string>
<string name="repo_install_title">%1$s をインストール</string> <string name="repo_install_title">%1$s をインストール</string>
<string name="repo_install_msg">%1$s をインストールしますか?</string>
<string name="download">ダウンロード</string> <string name="download">ダウンロード</string>
<string name="reboot">再起動</string> <string name="reboot">再起動</string>
<string name="release_notes">更新履歴</string> <string name="release_notes">更新履歴</string>

View File

@ -187,7 +187,6 @@
<string name="yes">დიახ</string> <string name="yes">დიახ</string>
<string name="no">არა</string> <string name="no">არა</string>
<string name="repo_install_title">%1$s-ის ინსტალაცია</string> <string name="repo_install_title">%1$s-ის ინსტალაცია</string>
<string name="repo_install_msg">გნებავთ %1$s-ის ახლავე დაინსტალირება?</string>
<string name="download">გადმოწერა</string> <string name="download">გადმოწერა</string>
<string name="reboot">გადატვირთვა</string> <string name="reboot">გადატვირთვა</string>
<string name="release_notes">რელიზის შენიშვნები</string> <string name="release_notes">რელიზის შენიშვნები</string>

View File

@ -191,7 +191,6 @@
<string name="yes"></string> <string name="yes"></string>
<string name="no">아니오</string> <string name="no">아니오</string>
<string name="repo_install_title">%1$s 설치</string> <string name="repo_install_title">%1$s 설치</string>
<string name="repo_install_msg">정말 %1$s을(를) 설치하시겠습니까?</string>
<string name="download">다운로드</string> <string name="download">다운로드</string>
<string name="reboot">다시 시작</string> <string name="reboot">다시 시작</string>
<string name="release_notes">릴리즈 노트</string> <string name="release_notes">릴리즈 노트</string>

View File

@ -36,8 +36,7 @@
<string name="app_changelog">Pakeitimų sąrašas</string> <string name="app_changelog">Pakeitimų sąrašas</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">Instaliuoti %1$s</string> <string name="repo_install_title">Instaliuoti %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Ar jūs norite instaliuoti %1$s?</string>
<string name="download">Atsisiųsti</string> <string name="download">Atsisiųsti</string>
<string name="reboot">Perkrauti</string> <string name="reboot">Perkrauti</string>
<string name="magisk_update_title">Atsirado nauja Magisk versija!</string> <string name="magisk_update_title">Atsirado nauja Magisk versija!</string>

View File

@ -52,8 +52,7 @@
<string name="reboot_delay_toast">Рестартирање за 5 секунди…</string> <string name="reboot_delay_toast">Рестартирање за 5 секунди…</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">Инсталирај %1$s</string> <string name="repo_install_title">Инсталирај %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Дали сакате да го инсталирате %1$s сега?</string>
<string name="download">Преземи</string> <string name="download">Преземи</string>
<string name="reboot">Рестартирај</string> <string name="reboot">Рестартирај</string>
<string name="release_notes">Белешки за изданието</string> <string name="release_notes">Белешки за изданието</string>

View File

@ -186,8 +186,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Ja</string> <string name="yes">Ja</string>
<string name="no">Nei</string> <string name="no">Nei</string>
<string name="repo_install_title">Installer %1$s</string> <string name="repo_install_title">Installer %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Installer %1$s nå?</string>
<string name="download">Last ned</string> <string name="download">Last ned</string>
<string name="reboot">Omstart</string> <string name="reboot">Omstart</string>
<string name="release_notes">Utgivelsesnotater</string> <string name="release_notes">Utgivelsesnotater</string>

View File

@ -172,8 +172,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Ja</string> <string name="yes">Ja</string>
<string name="no">Nee</string> <string name="no">Nee</string>
<string name="repo_install_title">%1$s installeren</string> <string name="repo_install_title">%1$s installeren %2$s(%3$s)</string>
<string name="repo_install_msg">Wil je %1$s nu installeren?</string>
<string name="download">Downloaden</string> <string name="download">Downloaden</string>
<string name="reboot">Herstarten</string> <string name="reboot">Herstarten</string>
<string name="release_notes">Wijzigingslog</string> <string name="release_notes">Wijzigingslog</string>

View File

@ -177,8 +177,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">ਹਾਂ</string> <string name="yes">ਹਾਂ</string>
<string name="no">ਨਹੀਂ</string> <string name="no">ਨਹੀਂ</string>
<string name="repo_install_title">ਇੰਸਟਾਲ %1$s</string> <string name="repo_install_title">ਇੰਸਟਾਲ %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">ਕੀ ਤੁਸੀਂ ਹੁਣੇ %1$s ਇੰਸਟਾਲ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?</string>
<string name="download">ਡਾਊਨਲੋਡ</string> <string name="download">ਡਾਊਨਲੋਡ</string>
<string name="reboot">ਰੀਬੂਟ</string> <string name="reboot">ਰੀਬੂਟ</string>
<string name="release_notes">ਰੀਲਿਜ਼ ਨੋਟਿਸ</string> <string name="release_notes">ਰੀਲਿਜ਼ ਨੋਟਿਸ</string>

View File

@ -186,8 +186,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Tak</string> <string name="yes">Tak</string>
<string name="no">Nie</string> <string name="no">Nie</string>
<string name="repo_install_title">Instalacja %1$s</string> <string name="repo_install_title">Instalacja %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Czy chcesz teraz zainstalować %1$s ?</string>
<string name="download">Pobierz</string> <string name="download">Pobierz</string>
<string name="reboot">Reboot</string> <string name="reboot">Reboot</string>
<string name="release_notes">Lista zmian</string> <string name="release_notes">Lista zmian</string>

View File

@ -190,8 +190,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Sim</string> <string name="yes">Sim</string>
<string name="no">Não</string> <string name="no">Não</string>
<string name="repo_install_title">Instalar %1$s</string> <string name="repo_install_title">Instalar %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Deseja instalar %1$s agora?</string>
<string name="download">Download</string> <string name="download">Download</string>
<string name="reboot">Reinicializar</string> <string name="reboot">Reinicializar</string>
<string name="release_notes">Notas de versão</string> <string name="release_notes">Notas de versão</string>

View File

@ -32,8 +32,7 @@
<string name="app_changelog">Lista de alterações da aplicação</string> <string name="app_changelog">Lista de alterações da aplicação</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">Instalar %1$s</string> <string name="repo_install_title">Instalar %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Deseja instalar%1$s agora?</string>
<string name="download">Transferir</string> <string name="download">Transferir</string>
<string name="reboot">Reiniciar</string> <string name="reboot">Reiniciar</string>
<string name="magisk_update_title">Nova atualização do Magisk disponível!</string> <string name="magisk_update_title">Nova atualização do Magisk disponível!</string>

View File

@ -193,8 +193,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Da</string> <string name="yes">Da</string>
<string name="no">Nu</string> <string name="no">Nu</string>
<string name="repo_install_title">Instalează %1$s</string> <string name="repo_install_title">Instalează %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Vrei să instalezi acum %1$s?</string>
<string name="download">Descarcă</string> <string name="download">Descarcă</string>
<string name="reboot">Repornește</string> <string name="reboot">Repornește</string>
<string name="release_notes">Note privind versiunea</string> <string name="release_notes">Note privind versiunea</string>

View File

@ -196,8 +196,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Да</string> <string name="yes">Да</string>
<string name="no">Нет</string> <string name="no">Нет</string>
<string name="repo_install_title">Установка %1$s</string> <string name="repo_install_title">Установка %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Установить %1$s ?</string>
<string name="download">Скачать</string> <string name="download">Скачать</string>
<string name="reboot">Перезагрузка</string> <string name="reboot">Перезагрузка</string>
<string name="release_notes">О версии</string> <string name="release_notes">О версии</string>

View File

@ -196,8 +196,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Áno</string> <string name="yes">Áno</string>
<string name="no">Nie</string> <string name="no">Nie</string>
<string name="repo_install_title">Nainštalovať %1$s</string> <string name="repo_install_title">Nainštalovať %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Chcete teraz nainštalovať %1$s?</string>
<string name="download">Stiahnuť</string> <string name="download">Stiahnuť</string>
<string name="reboot">Reštartovať</string> <string name="reboot">Reštartovať</string>
<string name="release_notes">Poznámky k vydaniu</string> <string name="release_notes">Poznámky k vydaniu</string>

View File

@ -194,8 +194,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Po</string> <string name="yes">Po</string>
<string name="no">Jo</string> <string name="no">Jo</string>
<string name="repo_install_title">Instalo %1$s</string> <string name="repo_install_title">Instalo %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Dëshiron të instalosh %1$s tani?</string>
<string name="download">Shkarko</string> <string name="download">Shkarko</string>
<string name="reboot">Rinis</string> <string name="reboot">Rinis</string>
<string name="release_notes">Shënimet e lëshimit</string> <string name="release_notes">Shënimet e lëshimit</string>

View File

@ -34,8 +34,7 @@
<string name="app_changelog">Дневник промена апликације</string> <string name="app_changelog">Дневник промена апликације</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">Инсталирај %1$s</string> <string name="repo_install_title">Инсталирај %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Да ли желите да инсталирате %1$s?</string>
<string name="download">Преузми</string> <string name="download">Преузми</string>
<string name="reboot">Рестартуј</string> <string name="reboot">Рестартуј</string>
<string name="magisk_update_title">Нови Адбејт Магиска Доступан!</string> <string name="magisk_update_title">Нови Адбејт Магиска Доступан!</string>

View File

@ -186,8 +186,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Ja</string> <string name="yes">Ja</string>
<string name="no">Nej</string> <string name="no">Nej</string>
<string name="repo_install_title">Installera %1$s</string> <string name="repo_install_title">Installera %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Vill du installera %1$s nu?</string>
<string name="download">Ladda ned</string> <string name="download">Ladda ned</string>
<string name="reboot">Omstart</string> <string name="reboot">Omstart</string>
<string name="release_notes">Utgivningsanmärkningar</string> <string name="release_notes">Utgivningsanmärkningar</string>

View File

@ -186,8 +186,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">ஆம்</string> <string name="yes">ஆம்</string>
<string name="no">இல்லை</string> <string name="no">இல்லை</string>
<string name="repo_install_title">நிறுவு %1$s</string> <string name="repo_install_title">நிறுவு %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">நீங்கள் இப்போது %1$s ஐ நிறுவ விரும்புகிறீர்களா??</string>
<string name="download">பதிவிறக்கம்</string> <string name="download">பதிவிறக்கம்</string>
<string name="reboot">மறுதொடக்கம்</string> <string name="reboot">மறுதொடக்கம்</string>
<string name="release_notes">வெளியீட்டு குறிப்புகள்</string> <string name="release_notes">வெளியீட்டு குறிப்புகள்</string>

View File

@ -41,8 +41,7 @@
<string name="magisk_update_title">มีการอัพเดต Magisk!</string> <string name="magisk_update_title">มีการอัพเดต Magisk!</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="repo_install_title">ติดตั้ง %1$s</string> <string name="repo_install_title">ติดตั้ง %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">ต้องการติดตั้ง %1$s ตอนนี้หรือไม่?</string>
<string name="download">ดาวน์โหลด</string> <string name="download">ดาวน์โหลด</string>
<string name="reboot">รีบู๊ต</string> <string name="reboot">รีบู๊ต</string>
<string name="release_notes">-้อมูลเพิ่มเติม</string> <string name="release_notes">-้อมูลเพิ่มเติม</string>

View File

@ -187,7 +187,6 @@
<string name="yes">Evet</string> <string name="yes">Evet</string>
<string name="no">Hayır</string> <string name="no">Hayır</string>
<string name="repo_install_title">%1$s yükle</string> <string name="repo_install_title">%1$s yükle</string>
<string name="repo_install_msg">%1$s yüklensin mi?</string>
<string name="download">İndir</string> <string name="download">İndir</string>
<string name="reboot">Yeniden Başlat</string> <string name="reboot">Yeniden Başlat</string>
<string name="release_notes">Sürüm notları</string> <string name="release_notes">Sürüm notları</string>

View File

@ -193,8 +193,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Так</string> <string name="yes">Так</string>
<string name="no">Ні</string> <string name="no">Ні</string>
<string name="repo_install_title">Встановити %1$s</string> <string name="repo_install_title">Встановити %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Бажаєте встановити %1$s?</string>
<string name="download">Завантажити</string> <string name="download">Завантажити</string>
<string name="reboot">Перезавантажити</string> <string name="reboot">Перезавантажити</string>
<string name="release_notes">Особливості версії</string> <string name="release_notes">Особливості версії</string>

View File

@ -193,8 +193,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Đồng ý</string> <string name="yes">Đồng ý</string>
<string name="no">Không</string> <string name="no">Không</string>
<string name="repo_install_title">Cài đặt %1$s</string> <string name="repo_install_title">Cài đặt %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Bạn có muốn cài đặt %1$s ngay bây giờ không?</string>
<string name="download">Tải xuống</string> <string name="download">Tải xuống</string>
<string name="reboot">Khởi động lại</string> <string name="reboot">Khởi động lại</string>
<string name="release_notes">Ghi chú bản phát hành</string> <string name="release_notes">Ghi chú bản phát hành</string>

View File

@ -196,8 +196,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes"></string> <string name="yes"></string>
<string name="no"></string> <string name="no"></string>
<string name="repo_install_title">安装 %1$s</string> <string name="repo_install_title">安装 %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">确认安装 %1$s</string>
<string name="download">下载</string> <string name="download">下载</string>
<string name="reboot">重启</string> <string name="reboot">重启</string>
<string name="release_notes">发布说明</string> <string name="release_notes">发布说明</string>

View File

@ -193,8 +193,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes"></string> <string name="yes"></string>
<string name="no"></string> <string name="no"></string>
<string name="repo_install_title">安裝 %1$s</string> <string name="repo_install_title">安裝 %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">您現在想要安裝 %1$s 嗎?</string>
<string name="download">下載</string> <string name="download">下載</string>
<string name="reboot">重新啟動</string> <string name="reboot">重新啟動</string>
<string name="release_notes">發布說明</string> <string name="release_notes">發布說明</string>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<item name="recyclerScrollListener" type="id" /> <item name="recyclerScrollListener" type="id" />
<item name="coroutineScope" type="id" />
</resources> </resources>

View File

@ -197,8 +197,7 @@
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Yes</string> <string name="yes">Yes</string>
<string name="no">No</string> <string name="no">No</string>
<string name="repo_install_title">Install %1$s</string> <string name="repo_install_title">Install %1$s %2$s(%3$s)</string>
<string name="repo_install_msg">Do you want to install %1$s now?</string>
<string name="download">Download</string> <string name="download">Download</string>
<string name="reboot">Reboot</string> <string name="reboot">Reboot</string>
<string name="release_notes">Release notes</string> <string name="release_notes">Release notes</string>