code cleanup

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/89/head
androidacy-user 2 years ago
parent 18586c3434
commit 9ecdad4a55

@ -39,6 +39,7 @@ The Androidacy Module Manager serves as a robust alternative to the official Mag
#### Dark | Light
<!--suppress CheckImageSize -->
<img src="docs/dark_screenshot.png" alt="Dark Screenshot" width="320"/> <img src="docs/light_screenshot.png" alt="Light Screenshot" width="320"/>
### Default Repositories

@ -16,7 +16,7 @@ import java.io.InputStream
@Suppress("unused")
class AppUpdateManager private constructor() {
var changes: String? = null
private var changes: String? = null
private val compatDataId = HashMap<String, Int>()
private val updateLock = Any()
private val compatFile: File = File(MainApplication.INSTANCE!!.filesDir, "compat.txt")
@ -132,7 +132,6 @@ class AppUpdateManager private constructor() {
return appUpdateManager.getCompatibilityFlags(moduleId)
}
@JvmStatic
fun shouldForceHide(repoId: String): Boolean {
return if (BuildConfig.DEBUG || repoId.startsWith("repo_") || repoId == "magisk_alt_repo") false else !repoId.startsWith(
"repo_"

@ -12,7 +12,7 @@ import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.Toast
import com.fox2code.foxcompat.app.FoxActivity
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textview.MaterialTextView
import io.sentry.Sentry
@ -22,7 +22,7 @@ import timber.log.Timber
import java.io.PrintWriter
import java.io.StringWriter
class CrashHandler : FoxActivity() {
class CrashHandler : AppCompatActivity() {
@Suppress("DEPRECATION", "KotlinConstantConditions")
@SuppressLint("RestrictedApi")
override fun onCreate(savedInstanceState: Bundle?) {

@ -4,10 +4,10 @@ import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.fox2code.foxcompat.app.FoxActivity
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.button.MaterialButton
class ExpiredActivity : FoxActivity() {
class ExpiredActivity : AppCompatActivity() {
@SuppressLint("RestrictedApi")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

@ -10,7 +10,6 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.Rect
import android.os.Bundle
@ -27,6 +26,7 @@ import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
@ -35,7 +35,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.room.Room
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.foxcompat.view.FoxDisplay
import com.fox2code.mmm.AppUpdateManager.Companion.appUpdateManager
import com.fox2code.mmm.OverScrollManager.OverScrollHelper
@ -71,7 +70,7 @@ import timber.log.Timber
import java.sql.Timestamp
class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
class MainActivity : AppCompatActivity(), OnRefreshListener, OverScrollHelper {
private lateinit var bottomNavigationView: BottomNavigationView
val moduleViewListBuilder: ModuleViewListBuilder = ModuleViewListBuilder(this)
val moduleViewListBuilderOnline: ModuleViewListBuilder = ModuleViewListBuilder(this)
@ -105,6 +104,8 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
} else {
bottomNavigationView.selectedItemId = R.id.online_menu_item
}
// rescan modules
instance!!.scanAsync()
super.onResume()
}
@ -183,8 +184,7 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
val view = findViewById<View>(R.id.root_container)
var startBottom = 0f
var endBottom = 0f
ViewCompat.setWindowInsetsAnimationCallback(
view,
ViewCompat.setWindowInsetsAnimationCallback(view,
object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
// Override methods…
override fun onProgress(
@ -217,8 +217,7 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
startBottom = view.bottom.toFloat()
Timber.d("IME animation prepare: %f", startBottom)
}
}
)
})
// set search view listeners for text edit. filter the appropriate list based on visibility. do the filtering as the user types not just on submit as a background task
textInputEditText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(
@ -316,33 +315,32 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
// set on click listener for reboot fab
rebootFab.setOnClickListener {
// show reboot dialog with options to reboot, reboot to recovery, bootloader, or edl, and use RuntimeUtils to reboot
val rebootDialog = MaterialAlertDialogBuilder(this@MainActivity)
.setTitle(R.string.reboot)
.setItems(
arrayOf(
getString(R.string.reboot),
getString(R.string.reboot_recovery),
getString(R.string.reboot_bootloader),
getString(R.string.reboot_edl)
)
) { _: DialogInterface?, which: Int ->
when (which) {
0 -> RuntimeUtils.reboot(this@MainActivity, RuntimeUtils.RebootMode.REBOOT)
1 -> RuntimeUtils.reboot(
this@MainActivity,
RuntimeUtils.RebootMode.RECOVERY
val rebootDialog =
MaterialAlertDialogBuilder(this@MainActivity).setTitle(R.string.reboot).setItems(
arrayOf(
getString(R.string.reboot),
getString(R.string.reboot_recovery),
getString(R.string.reboot_bootloader),
getString(R.string.reboot_edl)
)
) { _: DialogInterface?, which: Int ->
when (which) {
0 -> RuntimeUtils.reboot(
this@MainActivity,
RuntimeUtils.RebootMode.REBOOT
)
2 -> RuntimeUtils.reboot(
this@MainActivity,
RuntimeUtils.RebootMode.BOOTLOADER
)
1 -> RuntimeUtils.reboot(
this@MainActivity, RuntimeUtils.RebootMode.RECOVERY
)
3 -> RuntimeUtils.reboot(this@MainActivity, RuntimeUtils.RebootMode.EDL)
}
}
.setNegativeButton(R.string.cancel, null)
.create()
2 -> RuntimeUtils.reboot(
this@MainActivity, RuntimeUtils.RebootMode.BOOTLOADER
)
3 -> RuntimeUtils.reboot(this@MainActivity, RuntimeUtils.RebootMode.EDL)
}
}.setNegativeButton(R.string.cancel, null).create()
rebootDialog.show()
}
// get background color and elevation of reboot fab
@ -400,8 +398,6 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
textInputEditText.minimumHeight = FoxDisplay.dpToPixel(16f)
textInputEditText.imeOptions =
EditorInfo.IME_ACTION_SEARCH or EditorInfo.IME_FLAG_NO_FULLSCREEN
textInputEditText.isEnabled = false // Enabled later
this.updateScreenInsets(this.resources.configuration)
// on the bottom nav, there's a settings item. open the settings activity when it's clicked.
bottomNavigationView = findViewById(R.id.bottom_navigation)
@ -521,7 +517,6 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
progressIndicator.max = PRECISION
}
}
updateScreenInsets() // Fix an edge case
val context: Context = this@MainActivity
if (runtimeUtils!!.waitInitialSetupFinished(context, this@MainActivity)) {
if (BuildConfig.DEBUG) Timber.d("waiting...")
@ -583,8 +578,6 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
runOnUiThread {
progressIndicator.setProgressCompat(PRECISION, true)
progressIndicator.visibility = View.GONE
textInputEditText.isEnabled = false
updateScreenInsets(resources.configuration)
}
return
}
@ -613,8 +606,7 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
val currentTmp = current
runOnUiThread {
progressIndicator.setProgressCompat(
currentTmp / max,
true
currentTmp / max, true
)
}
}
@ -641,8 +633,6 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
runOnUiThread {
progressIndicator.setProgressCompat(PRECISION, true)
progressIndicator.visibility = View.GONE
textInputEditText.isEnabled = true
updateScreenInsets(resources.configuration)
}
maybeShowUpgrade()
Timber.i("Finished app opening state!")
@ -662,27 +652,6 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
initMode = false
}
fun updateScreenInsets() {
runOnUiThread { this.updateScreenInsets(this.resources.configuration) }
}
private fun updateScreenInsets(configuration: Configuration) {
val landscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val bottomInset = if (landscape) 0 else this.navigationBarHeight
val statusBarHeight = statusBarHeight + FoxDisplay.dpToPixel(2f)
swipeRefreshLayout!!.setProgressViewOffset(
false,
swipeRefreshLayoutOrigStartOffset + statusBarHeight,
swipeRefreshLayoutOrigEndOffset + statusBarHeight
)
moduleViewListBuilder.setHeaderPx(statusBarHeight)
moduleViewListBuilderOnline.setHeaderPx(statusBarHeight)
moduleViewListBuilder.updateInsets()
//this.actionBarBlur.invalidate();
overScrollInsetTop = statusBarHeight
overScrollInsetBottom = bottomInset
}
private fun updateBlurState() {
if (MainApplication.isBlurEnabled) {
// set bottom navigation bar color to transparent blur
@ -698,98 +667,6 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
}
}
override fun refreshUI() {
super.refreshUI()
if (initMode) return
initMode = true
Timber.i("Item Before")
searchTextInputEditText!!.clearFocus()
searchTextInputEditText!!.text?.clear()
this.updateScreenInsets()
updateBlurState()
moduleViewListBuilder.setQuery(null)
Timber.i("Item After")
moduleViewListBuilder.refreshNotificationsUI(moduleViewAdapter!!)
tryGetMagiskPathAsync(object : InstallerInitializer.Callback {
override fun onPathReceived(path: String?) {
val context: Context = this@MainActivity
val mainActivity = this@MainActivity
runtimeUtils!!.checkShowInitialSetup(context, mainActivity)
// Wait for doSetupNow to finish
while (doSetupNowRunning) {
try {
Thread.sleep(100)
} catch (ignored: InterruptedException) {
Thread.currentThread().interrupt()
}
}
if (peekMagiskVersion() < Constants.MAGISK_VER_CODE_INSTALL_COMMAND) moduleViewListBuilder.addNotification(
NotificationType.MAGISK_OUTDATED
)
if (!MainApplication.isShowcaseMode) moduleViewListBuilder.addNotification(
NotificationType.INSTALL_FROM_STORAGE
)
instance!!.scan()
instance!!.runAfterScan { moduleViewListBuilder.appendInstalledModules() }
commonNext()
}
override fun onFailure(error: Int) {
Timber.e("Error: %s", error)
moduleViewListBuilder.addNotification(errorNotification)
moduleViewListBuilderOnline.addNotification(errorNotification)
commonNext()
}
fun commonNext() {
Timber.i("Common Before")
if (MainApplication.isShowcaseMode) moduleViewListBuilder.addNotification(
NotificationType.SHOWCASE_MODE
)
NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilderOnline)
NotificationType.NO_INTERNET.autoAdd(moduleViewListBuilderOnline)
if (appUpdateManager.checkUpdate(false)) moduleViewListBuilder.addNotification(
NotificationType.UPDATE_AVAILABLE
)
RepoManager.getINSTANCE()!!.updateEnabledStates()
if (RepoManager.getINSTANCE()!!.customRepoManager!!.needUpdate()) {
runOnUiThread {
progressIndicator!!.isIndeterminate = false
progressIndicator!!.max = PRECISION
}
if (BuildConfig.DEBUG) Timber.i("Check Update")
val updateListener: SyncManager.UpdateListener =
object : SyncManager.UpdateListener {
override fun update(value: Int) {
runOnUiThread {
progressIndicator!!.setProgressCompat(
value, true
)
}
}
}
RepoManager.getINSTANCE()!!.update(updateListener)
runOnUiThread {
progressIndicator!!.setProgressCompat(PRECISION, true)
progressIndicator!!.visibility = View.GONE
}
}
if (BuildConfig.DEBUG) Timber.i("Apply")
RepoManager.getINSTANCE()
?.runAfterUpdate { moduleViewListBuilderOnline.appendRemoteModules() }
Timber.i("Common Before applyTo")
moduleViewListBuilder.applyTo(moduleList!!, moduleViewAdapter!!)
moduleViewListBuilderOnline.applyTo(moduleListOnline!!, moduleViewAdapterOnline!!)
Timber.i("Common After")
}
})
initMode = false
}
override fun onWindowUpdated() {
this.updateScreenInsets()
}
override fun onRefresh() {
if (swipeRefreshBlocker > System.currentTimeMillis() || initMode || progressIndicator == null || progressIndicator!!.visibility == View.VISIBLE || doSetupNowRunning) {
swipeRefreshLayout!!.isRefreshing = false
@ -819,6 +696,8 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
}
}
RepoManager.getINSTANCE()!!.update(updateListener)
// rescan modules
instance!!.scan()
NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder)
if (!NotificationType.NO_INTERNET.shouldRemove()) {
moduleViewListBuilderOnline.addNotification(NotificationType.NO_INTERNET)
@ -846,8 +725,7 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
val currentTmp = current
runOnUiThread {
progressIndicator!!.setProgressCompat(
currentTmp / max,
true
currentTmp / max, true
)
}
}
@ -870,16 +748,6 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
}, "Repo update thread").start()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
this.updateScreenInsets()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
this.updateScreenInsets()
}
fun maybeShowUpgrade() {
if (AndroidacyRepoData.instance.memberLevel == null) {
// wait for up to 10 seconds for AndroidacyRepoData to be initialized
@ -946,22 +814,13 @@ class MainActivity : FoxActivity(), OnRefreshListener, OverScrollHelper {
return super.dispatchTouchEvent(event)
}
override fun setOnBackPressedCallback(onBackPressedCallback: OnBackPressedCallback?) {
// if is on online list, go back to installed list
if (moduleListOnline!!.visibility == View.VISIBLE) {
bottomNavigationView.selectedItemId = R.id.installed_menu_item
} else {
super.setOnBackPressedCallback(onBackPressedCallback)
}
}
companion object {
fun getFoxActivity(activity: FoxActivity): FoxActivity {
fun getAppCompatActivity(activity: AppCompatActivity): AppCompatActivity {
return activity
}
fun getFoxActivity(context: Context): FoxActivity {
return context as FoxActivity
fun getAppCompatActivity(context: Context): AppCompatActivity {
return context as AppCompatActivity
}
private const val PRECISION = 100

@ -5,25 +5,28 @@
package com.fox2code.mmm
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo
import android.app.Application
import android.app.Application.ActivityLifecycleCallbacks
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Resources
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationManagerCompat
import androidx.emoji2.text.DefaultEmojiCompatConfig
import androidx.emoji2.text.EmojiCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import androidx.work.Configuration
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.foxcompat.app.FoxApplication
import com.fox2code.foxcompat.app.internal.FoxProcessExt
import com.fox2code.foxcompat.view.FoxThemeWrapper
import com.fox2code.mmm.installer.InstallerInitializer
@ -58,29 +61,36 @@ import java.util.Random
import kotlin.math.abs
@Suppress("unused", "MemberVisibilityCanBePrivate")
class MainApplication : FoxApplication(), Configuration.Provider {
class MainApplication : Application(), Configuration.Provider, ActivityLifecycleCallbacks {
var isTainted = false
@JvmField
var lastActivity: AppCompatActivity? = null
var modulesHaveUpdates = false
@JvmField
var updateModuleCount = 0
@JvmField
var updateModules: List<String> = ArrayList()
@StyleRes
private var managerThemeResId = R.style.Theme_MagiskModuleManager
private var markwonThemeContext: FoxThemeWrapper? = null
@JvmField
var markwon: Markwon? = null
private var existingKey: CharArray? = null
@JvmField
var tracker: Tracker? = null
get() {
if (field == null) {
field = TrackerBuilder.createDefault(BuildConfig.ANALYTICS_ENDPOINT, 1)
.build(Matomo.getInstance(this))
val tracker = field!!
tracker.startNewSession()
tracker.dispatchInterval = 1000
}
return field
}
private var makingNewKey = false
private var isCrashHandler = false
@ -124,7 +134,7 @@ class MainApplication : FoxApplication(), Configuration.Provider {
return existingKey!!
}
fun getMarkwon(): Markwon? {
fun reallyGetMarkwon(): Markwon? {
if (isCrashHandler) return null
if (markwon != null) return markwon
var contextThemeWrapper = markwonThemeContext
@ -140,7 +150,7 @@ class MainApplication : FoxApplication(), Configuration.Provider {
)
)
).build()
return markwon.also { this.markwon = it }
return reallyGetMarkwon().also { this.markwon = it }
}
override fun getWorkManagerConfiguration(): Configuration {
@ -194,32 +204,23 @@ class MainApplication : FoxApplication(), Configuration.Provider {
markwon = null
}
@SuppressLint("NonConstantResourceId")
override fun isLightTheme(): Boolean {
return when (getSharedPreferences("mmm")!!.getString("pref_theme", "system")) {
"system" -> isSystemLightTheme
"dark", "black" -> false
val isLightTheme: Boolean
get() = when (managerThemeResId) {
R.style.Theme_MagiskModuleManager,
R.style.Theme_MagiskModuleManager_Monet,
R.style.Theme_MagiskModuleManager_Dark,
R.style.Theme_MagiskModuleManager_Monet_Dark,
R.style.Theme_MagiskModuleManager_Black,
R.style.Theme_MagiskModuleManager_Monet_Black -> false
else -> true
}
}
private val isSystemLightTheme: Boolean
get() = (this.resources.configuration.uiMode and android.content.res.Configuration.UI_MODE_NIGHT_MASK) != android.content.res.Configuration.UI_MODE_NIGHT_YES
val isDarkTheme: Boolean
get() = !this.isLightTheme
@Synchronized
fun getTracker(): Tracker? {
if (tracker == null) {
tracker = TrackerBuilder.createDefault(BuildConfig.ANALYTICS_ENDPOINT, 1)
.build(Matomo.getInstance(this))
val tracker = tracker!!
tracker.startNewSession()
tracker.dispatchInterval = 1000
}
return tracker
}
override fun onCreate() {
supportedLocales.addAll(
listOf(
@ -251,6 +252,7 @@ class MainApplication : FoxApplication(), Configuration.Provider {
if (INSTANCE == null) INSTANCE = this
relPackageName = this.packageName
super.onCreate()
registerActivityLifecycleCallbacks(this)
initialize(this)
// Initialize Timber
configTimber()
@ -274,7 +276,6 @@ class MainApplication : FoxApplication(), Configuration.Provider {
if (BuildConfig.DEBUG) Timber.d("AMM is running in debug mode")
// analytics
if (BuildConfig.DEBUG) Timber.d("Initializing matomo")
getTracker()
if (!isMatomoAllowed()) {
if (BuildConfig.DEBUG) Timber.d("Matomo is not allowed")
tracker!!.isOptOut = true
@ -282,7 +283,7 @@ class MainApplication : FoxApplication(), Configuration.Provider {
tracker!!.isOptOut = false
}
if (getSharedPreferences("matomo")!!.getBoolean("install_tracked", false)) {
TrackHelper.track().download().with(INSTANCE!!.getTracker())
TrackHelper.track().download().with(INSTANCE!!.tracker)
if (BuildConfig.DEBUG) Timber.d("Sent install event to matomo")
getSharedPreferences("matomo")!!.edit().putBoolean("install_tracked", true).apply()
} else {
@ -345,22 +346,12 @@ class MainApplication : FoxApplication(), Configuration.Provider {
Timber.w("ANDROIDACY_CLIENT_ID is empty, disabling AndroidacyRepoData 1")
editor.apply()
}
getMarkwon()
reallyGetMarkwon()
}
private val intent: Intent?
get() = this.packageManager.getLaunchIntentForPackage(this.packageName)
override fun onCreateFoxActivity(compatActivity: FoxActivity) {
super.onCreateFoxActivity(compatActivity)
compatActivity.setTheme(managerThemeResId)
}
override fun onRefreshUI(compatActivity: FoxActivity) {
super.onRefreshUI(compatActivity)
compatActivity.setThemeRecreate(managerThemeResId)
}
override fun onConfigurationChanged(newConfig: android.content.res.Configuration) {
val newTimeFormatLocale = newConfig.locales[0]
if (timeFormatLocale !== newTimeFormatLocale) {
@ -503,7 +494,6 @@ class MainApplication : FoxApplication(), Configuration.Provider {
private var relPackageName = BuildConfig.APPLICATION_ID
@SuppressLint("StaticFieldLeak")
@JvmStatic
var INSTANCE: MainApplication? = null
private set
get() {
@ -514,7 +504,6 @@ class MainApplication : FoxApplication(), Configuration.Provider {
return field
}
@JvmStatic
var isFirstBoot = false
private var mSharedPrefs: HashMap<Any, Any>? = null
var updateCheckBg: String? = null
@ -555,7 +544,6 @@ class MainApplication : FoxApplication(), Configuration.Provider {
}
@Suppress("NAME_SHADOWING")
@JvmStatic
fun getSharedPreferences(name: String): SharedPreferences? {
// encryptedSharedPreferences is used
var name = name
@ -627,7 +615,6 @@ class MainApplication : FoxApplication(), Configuration.Provider {
val isBlurEnabled: Boolean
get() = getSharedPreferences("mmm")!!.getBoolean("pref_enable_blur", false)
@JvmStatic
val isDeveloper: Boolean
get() {
return if (BuildConfig.DEBUG) true else getSharedPreferences("mmm")!!.getBoolean(
@ -645,7 +632,6 @@ class MainApplication : FoxApplication(), Configuration.Provider {
"mmm"
)!!.getBoolean("pref_use_magisk_install_command", false) && isDeveloper && !InstallerInitializer.isKsu
@JvmStatic
val isBackgroundUpdateCheckEnabled: Boolean
get() {
if (updateCheckBg != null) {
@ -677,17 +663,14 @@ class MainApplication : FoxApplication(), Configuration.Provider {
val bootSharedPreferences: SharedPreferences?
get() = getSharedPreferences("mmm_boot")
@JvmStatic
fun formatTime(timeStamp: Long): String {
// new Date(x) also get the local timestamp for format
return timeFormat.format(Date(timeStamp))
}
@JvmStatic
val isNotificationPermissionGranted: Boolean
get() = NotificationManagerCompat.from((INSTANCE)!!).areNotificationsEnabled()
@JvmStatic
fun isMatomoAllowed(): Boolean {
return getSharedPreferences("mmm")!!.getBoolean(
"pref_analytics_enabled",
@ -695,4 +678,29 @@ class MainApplication : FoxApplication(), Configuration.Provider {
)
}
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
lastActivity = activity as AppCompatActivity
activity.setTheme(managerThemeResId)
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
lastActivity = activity as AppCompatActivity
activity.setTheme(managerThemeResId)
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
}
}

@ -5,9 +5,7 @@
@file:Suppress(
"KotlinConstantConditions",
"UNINITIALIZED_ENUM_COMPANION_WARNING",
"ktConcatNullable",
"BlockingMethodInNonBlockingContext",
"UnusedEquals"
"ktConcatNullable"
)
package com.fox2code.mmm
@ -19,7 +17,6 @@ import android.widget.Toast
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.installer.InstallerInitializer
import com.fox2code.mmm.module.ModuleViewListBuilder
import com.fox2code.mmm.repo.RepoManager
@ -38,13 +35,14 @@ import java.util.Date
import java.util.zip.ZipFile
@Suppress("SameParameterValue")
enum class NotificationType(
@field:StringRes @param:StringRes @JvmField val textId: Int,
@field:DrawableRes @JvmField val iconId: Int,
@field:AttrRes @JvmField val backgroundAttr: Int = androidx.appcompat.R.attr.colorError,
@field:AttrRes @JvmField val foregroundAttr: Int = com.google.android.material.R.attr.colorOnPrimary,
@JvmField val onClickListener: View.OnClickListener? = null,
@JvmField var special: Boolean = false
val onClickListener: View.OnClickListener? = null,
var special: Boolean = false
) : NotificationTypeCst {
@JvmStatic
@ -171,8 +169,7 @@ enum class NotificationType(
v.context,
0,
Intent(
v.context,
UpdateActivity::class.java
v.context, UpdateActivity::class.java
).setAction(UpdateActivity.ACTIONS.DOWNLOAD.toString()),
android.app.PendingIntent.FLAG_UPDATE_CURRENT
)
@ -196,16 +193,16 @@ enum class NotificationType(
androidx.appcompat.R.attr.colorBackgroundFloating,
com.google.android.material.R.attr.colorOnBackground,
View.OnClickListener { v: View? ->
if (MainApplication.getSharedPreferences("mmm")?.getBoolean("pref_require_security", false) == true) {
if (MainApplication.getSharedPreferences("mmm")
?.getBoolean("pref_require_security", false) == true
) {
// block local install for safety
MaterialAlertDialogBuilder(v!!.context)
.setTitle(R.string.install_from_storage)
MaterialAlertDialogBuilder(v!!.context).setTitle(R.string.install_from_storage)
.setMessage(R.string.install_from_storage_safe_modules)
.setPositiveButton(android.R.string.ok, null)
.show()
.setPositiveButton(android.R.string.ok, null).show()
return@OnClickListener
}
val compatActivity = FoxActivity.getFoxActivity(v)
val compatActivity = MainApplication.INSTANCE!!.lastActivity!!
val module = File(
compatActivity.cacheDir, "installer" + File.separator + "module.zip"
)
@ -327,4 +324,4 @@ enum class NotificationType(
return false
}
}
}
}

@ -18,9 +18,9 @@ import android.view.View
import android.webkit.CookieManager
import android.widget.CompoundButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import androidx.room.Room
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.databinding.ActivitySetupBinding
import com.fox2code.mmm.repo.RepoManager
import com.fox2code.mmm.utils.IntentHelper
@ -43,7 +43,7 @@ import java.io.IOException
import java.sql.Timestamp
import java.util.Objects
class SetupActivity : FoxActivity(), LanguageActivity {
class SetupActivity : AppCompatActivity(), LanguageActivity {
private var cachedTheme = 0
@SuppressLint("ApplySharedPref", "RestrictedApi")

@ -13,11 +13,11 @@ import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.FileProvider
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.androidacy.AndroidacyRepoData
import com.fox2code.mmm.utils.io.net.Http
import com.google.android.material.bottomnavigation.BottomNavigationItemView
@ -34,7 +34,7 @@ import java.io.IOException
import java.sql.Timestamp
import java.util.Objects
class UpdateActivity : FoxActivity() {
class UpdateActivity : AppCompatActivity() {
private var chgWv: WebView? = null
private var url: String = String()
@ -234,7 +234,7 @@ class UpdateActivity : FoxActivity() {
updateCancel.setOnClickListener { _: View? ->
// end any download
updateThread.interrupt()
forceBackPressed()
finish()
finish()
}
updateThread.start()

@ -17,30 +17,26 @@ import com.fox2code.mmm.repo.RepoManager
* It will not be obfuscated on release builds
*/
@Suppress("UNUSED_PARAMETER")
@Deprecated("This class is deprecated and will be removed in the future")
@Keep
enum class XHooks {
;
companion object {
@JvmStatic
@Keep
fun onRepoManagerInitialize() {
// Call addXRepo here if you are an XPosed module
}
@JvmStatic
@Keep
fun onRepoManagerInitialized() {
}
@JvmStatic
@Keep
fun isModuleActive(moduleId: String?): Boolean {
return ModuleManager.isModuleActive(moduleId!!)
}
@Suppress("DEPRECATION")
@JvmStatic
@Keep
@Throws(PackageManager.NameNotFoundException::class)
fun checkConfigTargetExists(context: Context, packageName: String, config: String) {
@ -51,13 +47,11 @@ enum class XHooks {
}
@Suppress("UNUSED_PARAMETER")
@JvmStatic
@Keep
fun getConfigIntent(context: Context, packageName: String?, config: String?): Intent? {
return context.packageManager.getLaunchIntentForPackage(packageName!!)
}
@JvmStatic
@Keep
fun onWebViewInitialize(webView: WebView?, allowInstall: Boolean) {
if (webView == null) throw NullPointerException("WebView is null!")

@ -7,13 +7,11 @@
package com.fox2code.mmm.androidacy
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.net.http.SslError
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.webkit.ConsoleMessage
import android.webkit.ConsoleMessage.MessageLevel
@ -28,13 +26,13 @@ import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.webkit.WebResourceErrorCompat
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewClientCompat
import androidx.webkit.WebViewFeature
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.Constants
import com.fox2code.mmm.MainApplication
@ -58,16 +56,14 @@ import java.io.IOException
/**
* Per Androidacy repo implementation agreement, no request of this WebView shall be modified.
*/
class AndroidacyActivity : FoxActivity() {
class AndroidacyActivity : AppCompatActivity() {
private var moduleFile: File? = null
@JvmField
var webView: WebView? = null
var webViewNote: TextView? = null
private var androidacyWebAPI: AndroidacyWebAPI? = null
var progressIndicator: LinearProgressIndicator? = null
@JvmField
var backOnResume = false
var downloadMode = false
@ -86,22 +82,21 @@ class AndroidacyActivity : FoxActivity() {
@Suppress("KotlinConstantConditions")
if (!MainApplication.checkSecret(intent) || intent.data.also { uri = it!! } == null) {
Timber.w("Impersonation detected")
forceBackPressed()
finish()
return
}
var url = uri.toString()
if (!AndroidacyUtil.isAndroidacyLink(url, uri!!)) {
Timber.w("Calling non androidacy link in secure WebView: %s", url)
forceBackPressed()
finish()
return
}
if (!hasWebView()) {
Timber.w("No WebView found to load url: %s", url)
forceBackPressed()
finish()
return
}
// if action bar is shown, hide it
hideActionBar()
markCaptchaAndroidacySolved()
if (!url.contains(AndroidacyUtil.REFERRER)) {
url = if (url.lastIndexOf('/') < url.lastIndexOf('?')) {
@ -135,22 +130,14 @@ class AndroidacyActivity : FoxActivity() {
val config = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_CONFIG)
val compatLevel = intent.getIntExtra(Constants.EXTRA_ANDROIDACY_COMPAT_LEVEL, 0)
this.setContentView(R.layout.webview)
setActionBarBackground(null)
setDisplayHomeAsUpEnabled(true)
if (title.isNullOrEmpty()) {
title = "Androidacy"
}
if (allowInstall || title.isEmpty()) {
hideActionBar()
} else { // Only used for note section
if (!allowInstall && title.isNotEmpty()) {
if (!config.isNullOrEmpty()) {
val configPkg = IntentHelper.getPackageOfConfig(config)
try {
checkConfigTargetExists(this, configPkg, config)
this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24) { _: MenuItem? ->
IntentHelper.openConfig(this, config)
true
}
} catch (ignored: PackageManager.NameNotFoundException) {
}
}
@ -243,9 +230,9 @@ class AndroidacyActivity : FoxActivity() {
) {
Toast.makeText(this@AndroidacyActivity, "Too many requests!", Toast.LENGTH_LONG)
.show()
runOnUiThread { forceBackPressed() }
runOnUiThread { finish() }
} else if (url == pageUrl) {
postOnUiThread { webViewNote!!.visibility = View.VISIBLE }
runOnUiThread { webViewNote!!.visibility = View.VISIBLE }
}
}
@ -291,10 +278,18 @@ class AndroidacyActivity : FoxActivity() {
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
getFoxActivity(webView).startActivityForResult(fileChooserParams.createIntent()) { code: Int, data: Intent? ->
filePathCallback.onReceiveValue(
FileChooserParams.parseResult(code, data)
)
// start file chooser activity
val intent1 = fileChooserParams.createIntent()
try {
@Suppress("DEPRECATION")
startActivityForResult(intent1, 1)
} catch (e: Exception) {
Timber.e(e)
Toast.makeText(
this@AndroidacyActivity,
R.string.file_picker_failure,
Toast.LENGTH_SHORT
).show()
}
return true
}
@ -355,7 +350,7 @@ class AndroidacyActivity : FoxActivity() {
} else if (moduleId != null) {
// Download module
Timber.i("megaIntercept failure. Forcing onBackPress")
forceBackPressed()
finish()
}
}
androidacyWebAPI.consumedAction = true
@ -388,7 +383,7 @@ class AndroidacyActivity : FoxActivity() {
super.onResume()
if (backOnResume) {
backOnResume = false
forceBackPressed()
finish()
} else if (androidacyWebAPI != null) {
androidacyWebAPI!!.consumedAction = false
}

@ -50,11 +50,13 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
private val clientID = BuildConfig.ANDROIDACY_CLIENT_ID
private val testMode: Boolean
private val host: String
override var url: String = "https://production-api.androidacy.com/magisk/repo"
get() {
return if (token == null) field else field + "?token=" + token + "&v=" + BuildConfig.VERSION_CODE + "&c=" + BuildConfig.VERSION_NAME + "&device_id=" + generateDeviceId() + "&client_id=" + BuildConfig.ANDROIDACY_CLIENT_ID
}
@JvmField
var userInfo = arrayOf(arrayOf("role", null), arrayOf("permissions", null))
@JvmField
var memberLevel: String? = null
// Avoid spamming requests to Androidacy
@ -102,7 +104,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
val handler = Handler(Looper.getMainLooper())
handler.post {
Toast.makeText(
INSTANCE,
INSTANCE!!.lastActivity,
INSTANCE!!.getString(R.string.androidacy_api_error, e.errorCode),
Toast.LENGTH_LONG
).show()
@ -118,7 +120,12 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
val editor = getSharedPreferences("androidacy")!!.edit()
editor.remove("pref_androidacy_api_token")
editor.apply()
false
requestNewToken()
isValidToken(
getSharedPreferences("androidacy")!!.getString(
"pref_androidacy_api_token",
null
))
}
}
@ -234,7 +241,7 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
val handler = Handler(mainLooper)
handler.post {
Toast.makeText(
INSTANCE,
INSTANCE!!.lastActivity,
R.string.androidacy_failed_to_validate_token,
Toast.LENGTH_LONG
).show()
@ -408,10 +415,6 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
return false
}
override fun getUrl(): String {
return if (token == null) url else url + "?token=" + token + "&v=" + BuildConfig.VERSION_CODE + "&c=" + BuildConfig.VERSION_NAME + "&device_id=" + generateDeviceId() + "&client_id=" + BuildConfig.ANDROIDACY_CLIENT_ID
}
@Suppress("NAME_SHADOWING")
private fun injectToken(url: String?): String? {
// Do not inject token for non Androidacy urls
@ -471,7 +474,6 @@ class AndroidacyRepoData(cacheRoot: File?, testMode: Boolean) : RepoData(
OK_HTTP_URL_BUILDER.build()
}
@JvmStatic
val instance: AndroidacyRepoData
get() = RepoManager.getINSTANCE()!!.androidacyRepoData!!

@ -11,6 +11,7 @@ import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.utils.io.net.Http.Companion.doHttpGet
import java.io.IOException
@Suppress("MemberVisibilityCanBePrivate", "MemberVisibilityCanBePrivate")
enum class AndroidacyUtil {
;
@ -31,7 +32,6 @@ enum class AndroidacyUtil {
.endsWith("api.androidacy.com") && uri.host?.endsWith("api.androidacy.com") ?: false
}
@JvmStatic
fun isAndroidacyFileUrl(url: String?): Boolean {
if (url == null) return false
for (prefix in arrayOf(
@ -46,7 +46,6 @@ enum class AndroidacyUtil {
// Avoid logging token
@Suppress("NAME_SHADOWING")
@JvmStatic
fun hideToken(url: String): String {
// for token, device_id, and client_id, replace with <hidden> by using replaceAll to match until the next non-alphanumeric character or end
// Also, URL decode
@ -61,7 +60,6 @@ enum class AndroidacyUtil {
return url
}
@JvmStatic
fun getModuleId(moduleUrl: String): String? {
// Get the &module= part
val i = moduleUrl.indexOf("&module=")
@ -84,7 +82,6 @@ enum class AndroidacyUtil {
return null
}
@JvmStatic
fun getModuleTitle(moduleUrl: String): String? {
// Get the &title= part
val i = moduleUrl.indexOf("&moduleTitle=")
@ -139,5 +136,13 @@ enum class AndroidacyUtil {
}
return String(md)
}
fun getMarkdownForModule(moduleId: String): String? {
try {
return getMarkdownFromAPI("https://production-api.androidacy.com/magisk/$moduleId/markdown")
} catch (ignored: IOException) {
}
return null
}
}
}

@ -45,6 +45,7 @@ import java.io.IOException
import java.nio.charset.StandardCharsets
import java.util.Objects
@Suppress("SameReturnValue")
@Keep
class AndroidacyWebAPI(
private val activity: AndroidacyActivity,
@ -62,7 +63,7 @@ class AndroidacyWebAPI(
var notifiedCompatMode = 0
fun forceQuitRaw(error: String?) {
Toast.makeText(activity, error, Toast.LENGTH_LONG).show()
activity.runOnUiThread { activity.forceBackPressed() }
activity.runOnUiThread { activity.finish() }
activity.backOnResume = true // Set backOnResume just in case
downloadMode = false
}
@ -197,7 +198,7 @@ class AndroidacyWebAPI(
// Allow forceQuit and cancel in downloadMode
if (consumedAction && !downloadMode) return
consumedAction = true
activity.runOnUiThread { activity.forceBackPressed() }
activity.runOnUiThread { activity.finish() }
}
/**
@ -374,7 +375,6 @@ class AndroidacyWebAPI(
if (consumedAction) return
consumedAction = true
activity.runOnUiThread {
activity.hideActionBar()
consumedAction = false
}
}
@ -388,7 +388,6 @@ class AndroidacyWebAPI(
if (consumedAction) return
consumedAction = true
activity.runOnUiThread {
activity.showActionBar()
if (!title.isNullOrEmpty()) {
activity.title = title
}
@ -479,7 +478,7 @@ class AndroidacyWebAPI(
*/
@get:JavascriptInterface
val navigationBarHeight: Int
get() = activity.navigationBarHeight
get() = 48
/**
* Return current theme accent color
@ -497,13 +496,6 @@ class AndroidacyWebAPI(
return typedValue.data
}
/**
* Return current theme foreground color
*/
@get:JavascriptInterface
val foregroundColor: Int
get() = if (activity.isLightTheme) Color.BLACK else Color.WHITE
/**
* Return current theme background color
*/

@ -315,7 +315,6 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
).apply()
}
@JvmStatic
fun postNotification(
context: Context,
updateable: HashMap<String, String>,
@ -376,7 +375,6 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build())
}
@JvmStatic
fun onMainActivityCreate(context: Context) {
// Refuse to run if first_launch pref is not false
if (MainApplication.getSharedPreferences("mmm")!!
@ -433,7 +431,6 @@ class BackgroundUpdateChecker(context: Context, workerParams: WorkerParameters)
)
}
@JvmStatic
fun onMainActivityResume(context: Context?) {
NotificationManagerCompat.from(context!!).cancel(NOTIFICATION_ID)
}

@ -7,6 +7,7 @@
package com.fox2code.mmm.installer
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
@ -17,25 +18,21 @@ import android.os.Bundle
import android.os.PowerManager
import android.os.PowerManager.WakeLock
import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import com.fox2code.androidansi.AnsiConstants
import com.fox2code.androidansi.AnsiParser
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.foxcompat.app.FoxActivity.OnBackPressedCallback
import com.fox2code.mmm.AppUpdateManager
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.Constants
import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
import com.fox2code.mmm.XHooks
import com.fox2code.mmm.androidacy.AndroidacyUtil
import com.fox2code.mmm.module.ActionButtonType
import com.fox2code.mmm.utils.FastException
import com.fox2code.mmm.utils.IntentHelper
import com.fox2code.mmm.utils.RuntimeUtils
@ -52,6 +49,7 @@ import com.fox2code.mmm.utils.io.net.Http
import com.fox2code.mmm.utils.sentry.SentryBreadcrumb
import com.fox2code.mmm.utils.sentry.SentryMain
import com.google.android.material.bottomnavigation.BottomNavigationItemView
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.topjohnwu.superuser.CallbackList
@ -70,7 +68,10 @@ import java.util.Enumeration
import java.util.concurrent.Executor
import java.util.zip.ZipEntry
class InstallerActivity : FoxActivity() {
class InstallerActivity : AppCompatActivity() {
private var canGoBack: Boolean = false
private val isLightTheme: Boolean
get() = MainApplication.INSTANCE!!.isLightTheme
private var progressIndicator: LinearProgressIndicator? = null
private var rebootFloatingButton: BottomNavigationItemView? = null
private var cancelFloatingButton: BottomNavigationItemView? = null
@ -89,12 +90,6 @@ class InstallerActivity : FoxActivity() {
if (!moduleCache!!.exists() && !moduleCache!!.mkdirs()) Timber.e("Failed to mkdir module cache dir!")
super.onCreate(savedInstanceState)
TrackHelper.track().screen(this).with(MainApplication.INSTANCE!!.tracker)
setDisplayHomeAsUpEnabled(true)
setActionBarBackground(null)
setOnBackPressedCallback { _: FoxActivity? ->
canceled = true
false
}
val intent = this.intent
val target: String
val name: String?
@ -106,7 +101,7 @@ class InstallerActivity : FoxActivity() {
if (Constants.INTENT_INSTALL_INTERNAL == intent.action) {
if (!MainApplication.checkSecret(intent)) {
Timber.e("Security check failed!")
forceBackPressed()
finish()
return
}
// ensure the intent is from our app, and is either a url or within our directory. replace all instances of .. and url encoded .. and remove whitespace
@ -117,7 +112,7 @@ class InstallerActivity : FoxActivity() {
"https://"
)
) {
forceBackPressed()
finish()
return
}
name = intent.getStringExtra(Constants.EXTRA_INSTALL_NAME)
@ -133,7 +128,7 @@ class InstallerActivity : FoxActivity() {
)
} else {
Toast.makeText(this, "Unknown intent!", Toast.LENGTH_SHORT).show()
forceBackPressed()
finish()
return
}
// Note: Sentry only send this info on crash.
@ -188,8 +183,7 @@ class InstallerActivity : FoxActivity() {
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Fox:Installer")
prgInd?.visibility = View.VISIBLE
if (urlMode) installerTerminal!!.addLine("- Downloading $name")
TrackHelper.track().event("installer_start", name)
.with(MainApplication.INSTANCE!!.tracker)
TrackHelper.track().event("installer_start", name).with(MainApplication.INSTANCE!!.tracker)
Thread(Runnable {
// ensure module cache is is in our cache dir
@ -290,9 +284,7 @@ class InstallerActivity : FoxActivity() {
}
if (!isModule && !isAnyKernel3 && !isInstallZipModule) {
setInstallStateFinished(
false,
"! File is not a valid Magisk module or AnyKernel3 zip",
""
false, "! File is not a valid Magisk module or AnyKernel3 zip", ""
)
return@Runnable
}
@ -325,29 +317,23 @@ class InstallerActivity : FoxActivity() {
if ("Failed to install module zip" == errMessage) throw e // Ignore if in installation state.
Timber.e(e)
setInstallStateFinished(
false,
"! Module is too large to be loaded on this device",
""
false, "! Module is too large to be loaded on this device", ""
)
}
}, "Module install Thread").start()
}
@SuppressLint("RestrictedApi")
@Suppress("KotlinConstantConditions")
@Keep
private fun doInstall(file: File?, noExtensions: Boolean, rootless: Boolean) {
@Suppress("NAME_SHADOWING") var noExtensions = noExtensions
if (canceled) return
UiThreadHandler.runAndWait {
this.onBackPressedCallback = DISABLE_BACK_BUTTON
setDisplayHomeAsUpEnabled(false)
}
// disable back
runOnUiThread { cancelFloatingButton!!.isEnabled = false }
Timber.i("Installing: %s", moduleCache!!.name)
val installerController = InstallerController(
progressIndicator,
installerTerminal,
file!!.absoluteFile,
noExtensions
progressIndicator, installerTerminal, file!!.absoluteFile, noExtensions
)
val installerMonitor: InstallerMonitor
val installJob: Shell.Job
@ -364,13 +350,11 @@ class InstallerActivity : FoxActivity() {
if (zipEntry != null) {
FileOutputStream(
File(
file.parentFile,
"customize.sh"
file.parentFile, "customize.sh"
)
).use { fileOutputStream ->
copy(
zipFile.getInputStream(zipEntry),
fileOutputStream
zipFile.getInputStream(zipEntry), fileOutputStream
)
}
}
@ -482,9 +466,7 @@ class InstallerActivity : FoxActivity() {
installExecutable = extractInstallScript("anykernel3_installer.sh")
if (installExecutable == null) {
setInstallStateFinished(
false,
"! Failed to extract AnyKernel3 install script",
""
false, "! Failed to extract AnyKernel3 install script", ""
)
return
}
@ -496,9 +478,7 @@ class InstallerActivity : FoxActivity() {
installExecutable = extractInstallScript("module_installer_wrapper.sh")
if (installExecutable == null) {
setInstallStateFinished(
false,
"! Failed to extract Magisk module wrapper script",
""
false, "! Failed to extract Magisk module wrapper script", ""
)
return
}
@ -513,9 +493,7 @@ class InstallerActivity : FoxActivity() {
installExecutable = extractInstallScript("module_installer_compat.sh")
if (installExecutable == null) {
setInstallStateFinished(
false,
"! Failed to extract Magisk module install script",
""
false, "! Failed to extract Magisk module install script", ""
)
return
}
@ -523,9 +501,7 @@ class InstallerActivity : FoxActivity() {
ashExec + " \"" + installExecutable.absolutePath + "\"" + " 3 1 \"" + file.absolutePath + "\""
} else {
setInstallStateFinished(
false,
"! Zip file is not a valid Magisk module or AnyKernel3 zip!",
""
false, "! Zip file is not a valid Magisk module or AnyKernel3 zip!", ""
)
return
}
@ -565,8 +541,7 @@ class InstallerActivity : FoxActivity() {
breadcrumb.setData("noExtensions", if (noExtensions) "true" else "false")
breadcrumb.setData("magiskCmdLine", if (magiskCmdLine) "true" else "false")
breadcrumb.setData(
"ansi",
if (installerTerminal!!.isAnsiEnabled) "enabled" else "disabled"
"ansi", if (installerTerminal!!.isAnsiEnabled) "enabled" else "disabled"
)
breadcrumb.setCategory("app.action.install")
SentryMain.addSentryBreadcrumb(breadcrumb)
@ -632,20 +607,14 @@ class InstallerActivity : FoxActivity() {
wakeLock = null
}
// Set the back press to finish the activity and return to the main activity
this.onBackPressedCallback = OnBackPressedCallback { _: FoxActivity? ->
finishAndRemoveTask()
startActivity(Intent(this, MainActivity::class.java))
true
}
setDisplayHomeAsUpEnabled(true)
cancelFloatingButton!!.isEnabled = true
canGoBack = true
progressIndicator!!.visibility = View.GONE
rebootFloatingButton!!.setOnClickListener { _: View? ->
if (MainApplication.shouldPreventReboot()) {
// toast and do nothing
Toast.makeText(
this,
R.string.install_terminal_reboot_prevented,
Toast.LENGTH_SHORT
this, R.string.install_terminal_reboot_prevented, Toast.LENGTH_SHORT
).show()
} else {
val builder = MaterialAlertDialogBuilder(this)
@ -663,13 +632,14 @@ class InstallerActivity : FoxActivity() {
rebootFloatingButton!!.isEnabled = true
cancelFloatingButton!!.isEnabled = true
// handle back button
cancelFloatingButton!!.setOnClickListener { _: View? -> forceBackPressed() }
cancelFloatingButton!!.setOnClickListener { _: View? -> finish() }
if (!message.isNullOrEmpty()) installerTerminal!!.addLine(message)
if (!optionalLink.isNullOrEmpty()) {
this.setActionBarExtraMenuButton(ActionButtonType.supportIconForUrl(optionalLink)) { _: MenuItem? ->
IntentHelper.openUrl(this, optionalLink)
true
}
installerTerminal!!.addLine(
String.format(
this.getString(R.string.install_terminal_support_link), optionalLink
)
)
} else if (success) {
val intent = this.intent
val config =
@ -678,16 +648,34 @@ class InstallerActivity : FoxActivity() {
val configPkg = IntentHelper.getPackageOfConfig(config)
try {
XHooks.checkConfigTargetExists(this, configPkg, config)
this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24) { _: MenuItem? ->
IntentHelper.openConfig(this, config)
val configIntent = Intent()
configIntent.component = ComponentName(configPkg, config)
configIntent.putExtra(Constants.EXTRA_INSTALL_NAME, this.packageName)
configIntent.putExtra(Constants.EXTRA_INSTALL_PATH, this.packageCodePath)
// set menuitem install_terminal_config on bottomnavigationview to launch
// config activity
val bottomNavigationView =
findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNavigationView.menu.findItem(R.id.install_terminal_config).isVisible =
true
bottomNavigationView.setOnItemSelectedListener { item ->
when (item.itemId) {
// config is at position 3
R.id.install_terminal_config -> {
startActivity(configIntent)
true
}
else -> {
false
}
}
}
} catch (e: PackageManager.NameNotFoundException) {
Timber.w("Config package \"$configPkg\" missing for installer view")
installerTerminal!!.addLine(
String.format(
this.getString(R.string.install_terminal_config_missing),
configPkg
this.getString(R.string.install_terminal_config_missing), configPkg
)
)
}
@ -805,8 +793,7 @@ class InstallerActivity : FoxActivity() {
"setSupportLink" -> {
// Only set link if valid
if (arg.isEmpty() || arg.startsWith("https://") && arg.indexOf(
'/',
8
'/', 8
) > 8
) supportLink = arg
}
@ -827,8 +814,8 @@ class InstallerActivity : FoxActivity() {
}
}
class InstallerMonitor(installScript: File) : CallbackList<String?>(
Executor { obj: Runnable -> obj.run() }) {
class InstallerMonitor(installScript: File) :
CallbackList<String?>(Executor { obj: Runnable -> obj.run() }) {
private val installScriptErr: String
private var lastCommand = ""
private var forCleanUp: String? = null

@ -15,13 +15,10 @@ import java.io.IOException
import java.nio.charset.StandardCharsets
class LocalModuleInfo(id: String?) : ModuleInfo(id!!) {
@JvmField
var updateVersion: String? = null
@JvmField
var updateVersionCode = Long.MIN_VALUE
@JvmField
var updateZipUrl: String? = null
private var updateChangeLogUrl: String? = null
@ -37,10 +34,8 @@ class LocalModuleInfo(id: String?) : ModuleInfo(id!!) {
return field
}
@JvmField
var updateChangeLog = ""
@JvmField
var updateChecksum: String? = null
fun checkModuleUpdate() {
if (updateJson != null && flags and FLAG_MM_REMOTE_MODULE == 0) {

@ -14,62 +14,44 @@ import com.fox2code.mmm.utils.io.PropUtils
*/
open class ModuleInfo {
// Magisk standard
@JvmField
var id: String
@JvmField
var name: String?
@JvmField
var version: String? = null
@JvmField
var versionCode: Long = 0
@JvmField
var author: String? = null
@JvmField
var description: String? = null
@JvmField
var updateJson: String? = null
// Community meta
@JvmField
var changeBoot = false
@JvmField
var mmtReborn = false
@JvmField
var support: String? = null
@JvmField
var donate: String? = null
@JvmField
var config: String? = null
// Community restrictions
@JvmField
var needRamdisk = false
@JvmField
var minMagisk = 0
@JvmField
var minApi = 0
@JvmField
var maxApi = 0
// Module status (0 if not from Module Manager)
@JvmField
var flags = 0
// Module safety (null if not provided)
@JvmField
var safe = false
constructor(id: String) {

@ -2,8 +2,6 @@
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
*/
@file:Suppress("unused")
package com.fox2code.mmm.manager
import android.content.SharedPreferences
@ -217,13 +215,11 @@ class ModuleManager private constructor() : SyncManager() {
MainApplication.INSTANCE!!.localModules = value
}
@Suppress("unused")
fun getUpdatableModuleCount(): Int {
afterScan()
return updatableModuleCount
}
@Suppress("unused")
fun setEnabledState(moduleInfo: ModuleInfo, checked: Boolean): Boolean {
if (BuildConfig.DEBUG) Timber.d("setEnabledState(%s, %s)", moduleInfo.id, checked)
if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING) && !checked) return false
@ -243,7 +239,6 @@ class ModuleManager private constructor() : SyncManager() {
return true
}
@Suppress("unused")
fun setUninstallState(moduleInfo: ModuleInfo, checked: Boolean): Boolean {
if (checked && moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING)) return false
val disable = SuFile("/data/adb/modules/" + moduleInfo.id + "/remove")
@ -305,7 +300,6 @@ class ModuleManager private constructor() : SyncManager() {
private const val FLAGS_RESET_UPDATE = FLAG_MM_INVALID or FLAG_MM_UNPROCESSED
private var instAnce: ModuleManager? = null
@JvmStatic
val instance: ModuleManager?
get() {
if (instAnce == null) {

@ -6,17 +6,15 @@ package com.fox2code.mmm.markdown
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.TextView
import android.widget.Toast
import com.fox2code.foxcompat.app.FoxActivity
import androidx.appcompat.app.AppCompatActivity
import com.fox2code.mmm.Constants
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
@ -27,24 +25,21 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.topjohnwu.superuser.internal.UiThreadHandler
import org.matomo.sdk.extra.TrackHelper
import timber.log.Timber
import java.io.IOException
import java.nio.charset.StandardCharsets
class MarkdownActivity : FoxActivity() {
class MarkdownActivity : AppCompatActivity() {
private var header: TextView? = null
private var footer: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TrackHelper.track().screen(this).with(MainApplication.INSTANCE!!.tracker)
setDisplayHomeAsUpEnabled(true)
val intent = this.intent
if (!MainApplication.checkSecret(intent)) {
Timber.e("Impersonation detected!")
forceBackPressed()
return
finish()
}
val url = intent.extras?.getString(Constants.EXTRA_MARKDOWN_URL)
var title = intent.extras!!.getString(Constants.EXTRA_MARKDOWN_TITLE)
@ -60,17 +55,14 @@ class MarkdownActivity : FoxActivity() {
@Suppress("UNUSED_VALUE")
title = url
}
setActionBarBackground(null)
@Suppress("DEPRECATION")
this.window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 0)
@Suppress("DEPRECATION") this.window.setFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
0
)
if (!config.isNullOrEmpty()) {
val configPkg = IntentHelper.getPackageOfConfig(config)
try {
XHooks.checkConfigTargetExists(this, configPkg, config)
this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24) { _: MenuItem? ->
IntentHelper.openConfig(this, config)
true
}
} catch (e: PackageManager.NameNotFoundException) {
Timber.w("Config package \"$configPkg\" missing for markdown view")
}
@ -78,7 +70,7 @@ class MarkdownActivity : FoxActivity() {
// validate the url won't crash the app
if (url.isNullOrEmpty() || url.contains("..")) {
Timber.e("Invalid url %s", url.toString())
forceBackPressed()
finish()
return
}
Timber.i("Url for markdown %s", url.toString())
@ -88,9 +80,6 @@ class MarkdownActivity : FoxActivity() {
header = findViewById(R.id.markdownHeader)
footer = findViewById(R.id.markdownFooter)
updateBlurState()
UiThreadHandler.handler.post { // Fix header/footer height
this.updateScreenInsets(this.resources.configuration)
}
// Really bad created (MSG by Der_Googler)
// set "message" to null to disable dialog
@ -107,10 +96,8 @@ class MarkdownActivity : FoxActivity() {
val markdown = String(rawMarkdown, StandardCharsets.UTF_8)
Timber.i("Done!")
runOnUiThread {
footer?.minimumHeight = this.navigationBarHeight
MainApplication.INSTANCE!!.markwon?.setMarkdown(
textView,
MarkdownUrlLinker.urlLinkify(markdown)
textView, MarkdownUrlLinker.urlLinkify(markdown)
)
if (markdownBackground != null) {
markdownBackground.isClickable = true
@ -140,31 +127,6 @@ class MarkdownActivity : FoxActivity() {
}
}
private fun updateScreenInsets() {
runOnUiThread { this.updateScreenInsets(this.resources.configuration) }
}
private fun updateScreenInsets(configuration: Configuration) {
val landscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val bottomInset = if (landscape) 0 else this.navigationBarHeight
val statusBarHeight = statusBarHeight
val actionBarHeight = actionBarHeight
val combinedBarsHeight = statusBarHeight + actionBarHeight
header!!.minHeight = combinedBarsHeight
footer!!.minHeight = bottomInset
//this.actionBarBlur.invalidate();
}
override fun refreshUI() {
super.refreshUI()
this.updateScreenInsets()
updateBlurState()
}
override fun onWindowUpdated() {
this.updateScreenInsets()
}
private fun addChip(markdownChip: MarkdownChip) {
makeChip(
this.getString(markdownChip.title),
@ -222,12 +184,6 @@ class MarkdownActivity : FoxActivity() {
}
}
override fun onResume() {
super.onResume()
val footer = findViewById<View>(R.id.markdownFooter)
if (footer != null) footer.minimumHeight = this.navigationBarHeight
}
companion object {
private val redirects = HashMap<String, String>(4)
private val variants = arrayOf("readme.md", "README.MD", ".github/README.md")

@ -17,7 +17,6 @@ enum class MarkdownUrlLinker {
}
companion object {
@JvmStatic
fun urlLinkify(url: String): String {
var index = url.indexOf("https://")
if (index == -1) return url

@ -11,7 +11,6 @@ import android.text.Spanned
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.DrawableRes
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.foxcompat.view.FoxDisplay
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
@ -51,7 +50,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("view_notes", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("view_notes", name).with(INSTANCE!!.tracker)
val notesUrl = moduleHolder.repoModule?.notesUrl
if (isAndroidacyLink(notesUrl)) {
try {
@ -146,7 +145,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("view_update_install", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("view_update_install", name).with(INSTANCE!!.tracker)
// if text is reinstall, we need to uninstall first - warn the user but don't proceed
if (moduleHolder.moduleInfo != null) {
// get the text
@ -183,7 +182,7 @@ enum class ActionButtonType {
var markwon: Markwon? = null
val localModuleInfo = moduleHolder.moduleInfo
if (localModuleInfo != null && localModuleInfo.updateChangeLog.isNotEmpty()) {
markwon = INSTANCE!!.getMarkwon()
markwon = INSTANCE!!.reallyGetMarkwon()
// Re-render each time in cse of config changes
desc = markwon!!.toMarkdown(localModuleInfo.updateChangeLog)
}
@ -256,7 +255,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("uninstall_module", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("uninstall_module", name).with(INSTANCE!!.tracker)
Timber.i(Integer.toHexString(moduleHolder.moduleInfo?.flags ?: 0))
if (!instance!!.setUninstallState(
moduleHolder.moduleInfo!!, !moduleHolder.hasFlag(
@ -284,7 +283,7 @@ enum class ActionButtonType {
.show()
} else {
moduleHolder.moduleInfo = null
FoxActivity.getFoxActivity(button).refreshUI()
INSTANCE!!.lastActivity!!
Timber.e("Cleared: %s", moduleId)
}
}
@ -311,7 +310,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("config_module", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("config_module", name).with(INSTANCE!!.tracker)
if (isAndroidacyLink(config)) {
openUrlAndroidacy(button.context, config, true)
} else {
@ -334,7 +333,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("support_module", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("support_module", name).with(INSTANCE!!.tracker)
openUrl(button.context, Objects.requireNonNull(moduleHolder.mainModuleInfo.support))
}
},
@ -353,7 +352,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("donate_module", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("donate_module", name).with(INSTANCE!!.tracker)
openUrl(button.context, moduleHolder.mainModuleInfo.donate)
}
},
@ -369,7 +368,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("warning_module", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("warning_module", name).with(INSTANCE!!.tracker)
MaterialAlertDialogBuilder(button.context).setTitle(R.string.warning)
.setMessage(R.string.warning_message).setPositiveButton(
R.string.understand
@ -391,7 +390,7 @@ enum class ActionButtonType {
} else {
moduleHolder.repoModule?.moduleInfo?.name
}
TrackHelper.track().event("safe_module", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("safe_module", name).with(INSTANCE!!.tracker)
MaterialAlertDialogBuilder(button.context).setTitle(R.string.safe_module)
.setMessage(R.string.safe_message).setPositiveButton(
R.string.understand
@ -410,7 +409,7 @@ enum class ActionButtonType {
moduleHolder.repoModule?.moduleInfo?.name
}
// positive button executes install logic and says reinstall. negative button does nothing
TrackHelper.track().event("remote_module", name).with(INSTANCE!!.getTracker())
TrackHelper.track().event("remote_module", name).with(INSTANCE!!.tracker)
val madb = MaterialAlertDialogBuilder(button.context)
madb.setTitle(R.string.remote_module)
val moduleInfo: ModuleInfo = if (moduleHolder.mainModuleInfo != null) {
@ -464,7 +463,7 @@ enum class ActionButtonType {
}
if (BuildConfig.DEBUG) Timber.d("doAction: remote module for %s", name)
TrackHelper.track().event("view_update_install", name)
.with(INSTANCE!!.getTracker())
.with(INSTANCE!!.tracker)
// Androidacy manage the selection between download and install
if (isAndroidacyLink(updateZipUrl)) {
if (BuildConfig.DEBUG) Timber.d("Androidacy link detected")
@ -485,7 +484,7 @@ enum class ActionButtonType {
var markwon: Markwon? = null
val localModuleInfo = moduleHolder.moduleInfo
if (localModuleInfo != null && localModuleInfo.updateChangeLog.isNotEmpty()) {
markwon = INSTANCE!!.getMarkwon()
markwon = INSTANCE!!.reallyGetMarkwon()
// Re-render each time in cse of config changes
desc = markwon!!.toMarkdown(localModuleInfo.updateChangeLog)
}
@ -555,7 +554,6 @@ enum class ActionButtonType {
iconId = 0
}
@Suppress("unused")
constructor(iconId: Int) {
this.iconId = iconId
}
@ -570,7 +568,6 @@ enum class ActionButtonType {
}
companion object {
@JvmStatic
@DrawableRes
fun supportIconForUrl(url: String?): Int {
var icon = R.drawable.ic_baseline_support_24
@ -590,7 +587,6 @@ enum class ActionButtonType {
return icon
}
@JvmStatic
@DrawableRes
fun donateIconForUrl(url: String?): Int {
var icon = R.drawable.ic_baseline_monetization_on_24

@ -24,7 +24,7 @@ import com.fox2code.mmm.utils.io.net.Http.Companion.hasWebView
import timber.log.Timber
import java.util.Objects
@Suppress("unused", "KotlinConstantConditions", "RedundantSetter")
@Suppress("unused", "KotlinConstantConditions")
class ModuleHolder : Comparable<ModuleHolder?> {
val moduleId: String
val notificationType: NotificationType?
@ -69,9 +69,10 @@ class ModuleHolder : Comparable<ModuleHolder?> {
get() = notificationType == null && separator == null && footerPx == -1
val mainModuleInfo: ModuleInfo
get() = if (repoModule != null && (moduleInfo == null || moduleInfo!!.versionCode < repoModule!!.moduleInfo.versionCode)) repoModule!!.moduleInfo else moduleInfo!!
var updateZipUrl: String? = null
get() = if (moduleInfo == null || repoModule != null && moduleInfo!!.updateVersionCode < repoModule!!.moduleInfo.versionCode) repoModule!!.zipUrl else moduleInfo!!.updateZipUrl
set
get() = if (moduleInfo == null || repoModule != null && moduleInfo!!.updateVersionCode < repoModule!!.moduleInfo.versionCode) repoModule!!.zipUrl else moduleInfo!!.updateZipUrl ?: field
val updateZipRepo: String?
get() = if (moduleInfo == null || repoModule != null && moduleInfo!!.updateVersionCode < repoModule!!.moduleInfo.versionCode) repoModule!!.repoData.preferenceId else "update_json"
val updateZipChecksum: String?

@ -39,7 +39,6 @@ import com.topjohnwu.superuser.internal.UiThreadHandler
import timber.log.Timber
class ModuleViewAdapter : RecyclerView.Adapter<ModuleViewAdapter.ViewHolder>() {
@JvmField
val moduleHolders = ArrayList<ModuleHolder>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.module_entry, parent, false)

@ -33,8 +33,6 @@ class ModuleViewListBuilder(private val activity: Activity) {
private val queryLock = Any()
private var query = ""
private var updating = false
private var headerPx = 0
private var footerPx = 0
private var tries = 0
private var moduleSorter: ModuleSorter = ModuleSorter.UPDATE
private var updateInsets = RUNNABLE
@ -149,13 +147,6 @@ class ModuleViewListBuilder(private val activity: Activity) {
}
}
fun refreshNotificationsUI(moduleViewAdapter: ModuleViewAdapter) {
val notificationCount = notifications.size
notifySizeChanged(
moduleViewAdapter, 0, notificationCount, notificationCount
)
}
private fun matchFilter(moduleHolder: ModuleHolder): Boolean {
val moduleInfo = moduleHolder.mainModuleInfo
val query = query
@ -327,14 +318,6 @@ class ModuleViewListBuilder(private val activity: Activity) {
}
}
@Suppress("ktConcatNullable")
fun setQuery(query: String?) {
synchronized(queryLock) {
Timber.i("Query " + this.query + " -> " + query)
this.query = query?.trim { it <= ' ' }?.lowercase() ?: ""
}
}
fun setQueryChange(query: String?): Boolean {
synchronized(queryLock) {
val newQuery = query?.trim { it <= ' ' }?.lowercase() ?: ""
@ -345,22 +328,6 @@ class ModuleViewListBuilder(private val activity: Activity) {
return true
}
fun setHeaderPx(headerPx: Int) {
if (this.headerPx != headerPx) {
synchronized(updateLock) { this.headerPx = headerPx }
}
}
fun setFooterPx(footerPx: Int) {
if (this.footerPx != footerPx) {
synchronized(updateLock) { this.footerPx = footerPx }
}
}
fun updateInsets() {
updateInsets.run()
}
companion object {
private val RUNNABLE = Runnable {}
private fun notifySizeChanged(

@ -13,10 +13,8 @@ import java.nio.charset.StandardCharsets
class CustomRepoData internal constructor(url: String?, cacheRoot: File?) : RepoData(
url!!, cacheRoot!!
) {
@JvmField
var loadedExternal = false
@JvmField
var override: String? = null
override val isEnabledByDefault: Boolean
get() = override != null || loadedExternal
@ -26,13 +24,13 @@ class CustomRepoData internal constructor(url: String?, cacheRoot: File?) : Repo
val jsonObject = JSONObject(
String(
doHttpGet(
getUrl()!!,
url,
false
), StandardCharsets.UTF_8
)
)
// make sure there's at least a name and a modules or data object
require(!(!jsonObject.has("name") || !jsonObject.has("modules") && !jsonObject.has("data"))) { "Invalid repo: " + getUrl() }
require(!(!jsonObject.has("name") || !jsonObject.has("modules") && !jsonObject.has("data"))) { "Invalid repo: $url" }
name = jsonObject.getString("name").trim { it <= ' ' }
website = jsonObject.optString("website")
support = jsonObject.optString("support")

@ -22,7 +22,6 @@ class CustomRepoManager internal constructor(
) {
private val customRepos: Array<String?> = arrayOfNulls(MAX_CUSTOM_REPOS)
@JvmField
var dirty = false
var repoCount: Int
private set

@ -27,47 +27,33 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
private val supportedProperties = JSONObject()
private val populateLock = Any()
@JvmField
var url: String
open var url: String = ""
@JvmField
var preferenceId: String? = null
@JvmField
var cacheRoot: File
@JvmField
var moduleHashMap: HashMap<String, RepoModule>
private var metaDataCache: JSONObject?
@JvmField
var lastUpdate: Long = 0
@JvmField
var website: String? = null
@JvmField
var support: String? = null
@JvmField
var donate: String? = null
@JvmField
var submitModule: String? = null
@JvmField
var defaultName: String
@JvmField
var defaultWebsite: String
@JvmField
protected var defaultSupport: String? = null
@JvmField
protected var defaultDonate: String? = null
@JvmField
var defaultSubmitModule: String? = null
override var name: String? = null
@ -389,27 +375,6 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
}
}
open fun getUrl(): String? {
return url
}
fun getWebsite(): String {
if (isNonNull(website)) return website!!
return if (defaultWebsite != null) defaultWebsite else url
}
fun getSupport(): String? {
return if (isNonNull(support)) support else defaultSupport
}
fun getDonate(): String? {
return if (isNonNull(donate)) donate else defaultDonate
}
fun getSubmitModule(): String? {
return if (isNonNull(submitModule)) submitModule else defaultSubmitModule
}
// should update (lastUpdate > 15 minutes)
fun shouldUpdate(): Boolean {
if (BuildConfig.DEBUG) Timber.d("Repo $preferenceId should update check called")
@ -449,9 +414,5 @@ open class RepoData(url: String, cacheRoot: File) : XRepo() {
return true
}
companion object {
private fun isNonNull(str: String?): Boolean {
return !str.isNullOrEmpty() && "null" != str
}
}
companion object
}

@ -33,7 +33,7 @@ import timber.log.Timber
import java.io.File
import java.nio.charset.StandardCharsets
@Suppress("NAME_SHADOWING", "unused")
@Suppress("NAME_SHADOWING")
class RepoManager private constructor(mainApplication: MainApplication) : SyncManager() {
private val mainApplication: MainApplication
private val repoData: LinkedHashMap<String?, RepoData>
@ -242,7 +242,7 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
if (!isLastUpdateSuccess || modules.isEmpty()) {
Timber.e("Failed to update %s", repoUpdaters[i]!!.repoData.name)
// Show snackbar on main looper and add some bottom padding
val context: Activity? = MainApplication.INSTANCE!!.lastCompatActivity
val context: Activity? = MainApplication.INSTANCE!!.lastActivity
Handler(Looper.getMainLooper()).post {
if (context != null) {
// Show material dialogue with the repo name. for androidacy repo, show an option to reset the api key. show a message then a list of errors
@ -354,7 +354,6 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
@Volatile
private var INSTANCE: RepoManager? = null
@JvmStatic
fun getINSTANCE(): RepoManager? {
if (INSTANCE == null || !INSTANCE!!.initialized) {
synchronized(lock) {
@ -390,7 +389,6 @@ class RepoManager private constructor(mainApplication: MainApplication) : SyncMa
return INSTANCE
}
@JvmStatic
fun internalIdOfUrl(url: String): String {
return when (url) {
MAGISK_ALT_REPO, MAGISK_ALT_REPO_JSDELIVR -> "magisk_alt_repo"

@ -9,41 +9,29 @@ import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.manager.ModuleInfo
class RepoModule {
@JvmField
val repoData: RepoData
@JvmField
val moduleInfo: ModuleInfo
@JvmField
val id: String
@JvmField
var repoName: String? = null
@JvmField
var lastUpdated: Long = 0
@JvmField
var propUrl: String? = null
@JvmField
var zipUrl: String? = null
@JvmField
var notesUrl: String? = null
@JvmField
var checksum: String? = null
@JvmField
var processed = false
@JvmField
@StringRes
var qualityText = 0
@JvmField
var qualityValue = 0
var safe: Boolean

@ -20,7 +20,6 @@ import java.util.concurrent.atomic.AtomicBoolean
class RepoUpdater(repoData2: RepoData) {
private var indexRaw: ByteArray? = null
@JvmField
var repoData: RepoData = repoData2
private var toUpdate: List<RepoModule>? = null
private var toApply: Collection<RepoModule>? = null
@ -120,7 +119,7 @@ class RepoUpdater(repoData2: RepoData) {
toApply = repoData.moduleHashMap.values
return 0
}
indexRaw = repoData.getUrl()?.let { doHttpGet(it, false) }
indexRaw = doHttpGet(repoData.url, false)
toUpdate = repoData.populate(JSONObject(String(indexRaw!!, StandardCharsets.UTF_8)))
// Since we reuse instances this should work
toApply = HashSet(repoData.moduleHashMap.values)

@ -10,13 +10,13 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.TwoStatePreference
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
@ -101,8 +101,6 @@ class AppearanceFragment : PreferenceFragmentCompat() {
// Refresh activity
UiThreadHandler.handler.postDelayed({
MainApplication.INSTANCE!!.updateTheme()
FoxActivity.getFoxActivity(this)
.setThemeRecreate(MainApplication.INSTANCE!!.getManagerThemeResId())
}, 1)
val intent = Intent(requireContext(), SettingsActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
@ -121,7 +119,7 @@ class AppearanceFragment : PreferenceFragmentCompat() {
}
UiThreadHandler.handler.postDelayed({
MainApplication.INSTANCE!!.updateTheme()
FoxActivity.getFoxActivity(this).setThemeRecreate(MainApplication.INSTANCE!!.getManagerThemeResId())
MainApplication.INSTANCE!!.lastActivity!!
}, 1)
true
}
@ -134,7 +132,6 @@ class AppearanceFragment : PreferenceFragmentCompat() {
disableMonet!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
UiThreadHandler.handler.postDelayed({
MainApplication.INSTANCE!!.updateTheme()
(requireActivity() as FoxActivity).setThemeRecreate(MainApplication.INSTANCE!!.getManagerThemeResId())
}, 1)
true
}
@ -201,7 +198,7 @@ class AppearanceFragment : PreferenceFragmentCompat() {
languageSelectorCta.onPreferenceLongClickListener =
LongClickablePreference.OnPreferenceLongClickListener { _: Preference? ->
val clipboard =
requireContext().getSystemService(FoxActivity.CLIPBOARD_SERVICE) as ClipboardManager
requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
val clip =
ClipData.newPlainText("URL", "https://translate.nift4.org/engage/foxmmm/")
clipboard.setPrimaryClip(clip)

@ -5,11 +5,11 @@ import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
@ -48,7 +48,7 @@ class CreditsFragment : PreferenceFragmentCompat() {
val clipboard = requireContext().getSystemService(FoxActivity.CLIPBOARD_SERVICE) as ClipboardManager
val clipboard = requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
// pref_contributors should lead to the contributors page
var linkClickable: LongClickablePreference? = findPreference("pref_contributors")

@ -5,11 +5,11 @@ import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.R
@ -47,7 +47,7 @@ class InfoFragment : PreferenceFragmentCompat() {
setPreferencesFromResource(R.xml.app_info_preferences, rootKey)
val clipboard =
requireContext().getSystemService(FoxActivity.CLIPBOARD_SERVICE) as ClipboardManager
requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
var linkClickable: LongClickablePreference?
if (BuildConfig.DEBUG || BuildConfig.ENABLE_AUTO_UPDATER) {
linkClickable = findPreference("pref_report_bug")
@ -119,7 +119,7 @@ class InfoFragment : PreferenceFragmentCompat() {
Preference.OnPreferenceClickListener { _: Preference? ->
// open fox
IntentHelper.openUrl(
FoxActivity.getFoxActivity(this), "https://paypal.me/fox2code"
MainApplication.INSTANCE!!.lastActivity!!, "https://paypal.me/fox2code"
)
true
}
@ -151,7 +151,7 @@ class InfoFragment : PreferenceFragmentCompat() {
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show()
// open androidacy
IntentHelper.openUrl(
FoxActivity.getFoxActivity(this),
MainApplication.INSTANCE!!.lastActivity!!,
"https://www.androidacy.com/membership-join/?utm_source=foxmmm&utm_medium=app&utm_campaign=donate"
)
true

@ -10,7 +10,6 @@ import android.view.View
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
@Suppress("unused")
class LongClickablePreference : Preference {
var onPreferenceLongClickListener: OnPreferenceLongClickListener? = null

@ -7,12 +7,12 @@ import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.TwoStatePreference
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication
@ -76,7 +76,7 @@ class PrivacyFragment : PreferenceFragmentCompat() {
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val mgr =
requireContext().getSystemService(FoxActivity.ALARM_SERVICE) as AlarmManager
requireContext().getSystemService(AppCompatActivity.ALARM_SERVICE) as AlarmManager
mgr[AlarmManager.RTC, System.currentTimeMillis() + 100] = mPendingIntent
if (BuildConfig.DEBUG) Timber.d("Restarting app to save crash reporting preference: %s", newValue)
exitProcess(0) // Exit app process

@ -24,7 +24,6 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import androidx.preference.TwoStatePreference
import androidx.room.Room
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.foxcompat.view.FoxDisplay
import com.fox2code.foxcompat.view.FoxViewCompat
import com.fox2code.mmm.BuildConfig
@ -668,12 +667,12 @@ class RepoFragment : PreferenceFragmentCompat() {
preference.title = repoData.name
preference.isVisible = true
// set website, support, and submitmodule as well as donate
if (repoData.getWebsite() != null) {
if (repoData.website != null) {
findPreference<Preference>(preferenceName + "_website")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(
FoxActivity.getFoxActivity(this),
repoData.getWebsite()
MainApplication.INSTANCE!!.lastActivity!!,
repoData.website
)
true
}
@ -681,12 +680,12 @@ class RepoFragment : PreferenceFragmentCompat() {
findPreference<Preference>(preferenceName + "_website")!!.isVisible =
false
}
if (repoData.getSupport() != null) {
if (repoData.support != null) {
findPreference<Preference>(preferenceName + "_support")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(
FoxActivity.getFoxActivity(this),
repoData.getSupport()
MainApplication.INSTANCE!!.lastActivity!!,
repoData.support
)
true
}
@ -694,12 +693,12 @@ class RepoFragment : PreferenceFragmentCompat() {
findPreference<Preference>("${preferenceName}_support")!!.isVisible =
false
}
if (repoData.getSubmitModule() != null) {
if (repoData.submitModule != null) {
findPreference<Preference>(preferenceName + "_submit")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(
FoxActivity.getFoxActivity(this),
repoData.getSubmitModule()
MainApplication.INSTANCE!!.lastActivity!!,
repoData.submitModule
)
true
}
@ -707,12 +706,12 @@ class RepoFragment : PreferenceFragmentCompat() {
findPreference<Preference>(preferenceName + "_submit")!!.isVisible =
false
}
if (repoData.getDonate() != null) {
if (repoData.donate != null) {
findPreference<Preference>(preferenceName + "_donate")!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(
FoxActivity.getFoxActivity(this),
repoData.getDonate()
MainApplication.INSTANCE!!.lastActivity!!,
repoData.donate
)
true
}
@ -752,13 +751,13 @@ class RepoFragment : PreferenceFragmentCompat() {
}
}
preference = findPreference(preferenceName + "_website") ?: return
val homepage = repoData.getWebsite()
val homepage = repoData.website
if (preference != null) {
if (homepage.isNotEmpty()) {
if (homepage?.isNotEmpty() == true) {
preference.isVisible = true
preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(FoxActivity.getFoxActivity(this), homepage)
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, homepage)
true
}
(preference as LongClickablePreference).onPreferenceLongClickListener =
@ -773,14 +772,14 @@ class RepoFragment : PreferenceFragmentCompat() {
}
}
preference = findPreference(preferenceName + "_support") ?: return
val supportUrl = repoData.getSupport()
val supportUrl = repoData.support
if (preference != null) {
if (!supportUrl.isNullOrEmpty()) {
preference.isVisible = true
preference.setIcon(ActionButtonType.supportIconForUrl(supportUrl))
preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(FoxActivity.getFoxActivity(this), supportUrl)
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, supportUrl)
true
}
(preference as LongClickablePreference).onPreferenceLongClickListener =
@ -795,14 +794,14 @@ class RepoFragment : PreferenceFragmentCompat() {
}
}
preference = findPreference(preferenceName + "_donate") ?: return
val donateUrl = repoData.getDonate()
val donateUrl = repoData.donate
if (preference != null) {
if (donateUrl != null) {
preference.isVisible = true
preference.setIcon(ActionButtonType.donateIconForUrl(donateUrl))
preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(FoxActivity.getFoxActivity(this), donateUrl)
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, donateUrl)
true
}
(preference as LongClickablePreference).onPreferenceLongClickListener =
@ -817,13 +816,13 @@ class RepoFragment : PreferenceFragmentCompat() {
}
}
preference = findPreference(preferenceName + "_submit") ?: return
val submissionUrl = repoData.getSubmitModule()
val submissionUrl = repoData.submitModule
if (preference != null) {
if (!submissionUrl.isNullOrEmpty()) {
preference.isVisible = true
preference.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
IntentHelper.openUrl(FoxActivity.getFoxActivity(this), submissionUrl)
IntentHelper.openUrl(MainApplication.INSTANCE!!.lastActivity!!, submissionUrl)
true
}
(preference as LongClickablePreference).onPreferenceLongClickListener =

@ -7,12 +7,12 @@ import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.TwoStatePreference
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication
@ -86,7 +86,7 @@ class SecurityFragment : PreferenceFragmentCompat() {
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val mgr =
requireContext().getSystemService(FoxActivity.ALARM_SERVICE) as AlarmManager
requireContext().getSystemService(AppCompatActivity.ALARM_SERVICE) as AlarmManager
mgr[AlarmManager.RTC, System.currentTimeMillis() + 100] =
mPendingIntent
if (BuildConfig.DEBUG) Timber.d("Restarting app to save showcase mode preference: %s", v)
@ -109,7 +109,7 @@ class SecurityFragment : PreferenceFragmentCompat() {
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val mgr =
requireContext().getSystemService(FoxActivity.ALARM_SERVICE) as AlarmManager
requireContext().getSystemService(AppCompatActivity.ALARM_SERVICE) as AlarmManager
mgr[AlarmManager.RTC, System.currentTimeMillis() + 100] =
mPendingIntent
if (BuildConfig.DEBUG) Timber.d("Restarting app to save showcase mode preference: %s", v)

@ -14,12 +14,12 @@ import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentTransaction
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.ExpiredActivity
import com.fox2code.mmm.MainActivity
@ -38,7 +38,7 @@ import timber.log.Timber
import java.sql.Timestamp
@Suppress("SENSELESS_COMPARISON")
class SettingsActivity : FoxActivity(), LanguageActivity,
class SettingsActivity : AppCompatActivity(), LanguageActivity,
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
private lateinit var bottomNavigationView: BottomNavigationView
lateinit var sharedPreferences: SharedPreferences
@ -89,7 +89,7 @@ class SettingsActivity : FoxActivity(), LanguageActivity,
.commit()
true
}
TrackHelper.track().screen(this).with(INSTANCE!!.getTracker())
TrackHelper.track().screen(this).with(INSTANCE!!.tracker)
setContentView(R.layout.settings_activity)
setTitle(R.string.app_name_v2)
val ts = Timestamp(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000)
@ -205,24 +205,18 @@ class SettingsActivity : FoxActivity(), LanguageActivity,
if (dataStore.sharedPreferences.getBoolean("developer", false)) {
editor.putBoolean("developer", false)
Toast.makeText(
p.context,
R.string.dev_mode_disabled,
Toast.LENGTH_SHORT
p.context, R.string.dev_mode_disabled, Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
p.context,
R.string.dev_mode_enabled,
Toast.LENGTH_SHORT
p.context, R.string.dev_mode_enabled, Toast.LENGTH_SHORT
).show()
}
editor.apply()
// toast yer a wizard harry
if (versionClicks == 3) {
Toast.makeText(
p.context,
R.string.keep_tapping_to_enter_hogwarts,
Toast.LENGTH_LONG
p.context, R.string.keep_tapping_to_enter_hogwarts, Toast.LENGTH_LONG
).show()
}
true
@ -282,8 +276,7 @@ class SettingsActivity : FoxActivity(), LanguageActivity,
}
override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference
caller: PreferenceFragmentCompat, pref: Preference
): Boolean {
val fragment = supportFragmentManager.fragmentFactory.instantiate(
classLoader, pref.fragment.toString()
@ -291,18 +284,7 @@ class SettingsActivity : FoxActivity(), LanguageActivity,
fragment.arguments = pref.extras
@Suppress("DEPRECATION") fragment.setTargetFragment(caller, 0)
supportFragmentManager.beginTransaction().replace(R.id.settings, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).addToBackStack(null)
.commit()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).addToBackStack(null).commit()
return true
}
override fun setOnBackPressedCallback(onBackPressedCallback: OnBackPressedCallback?) {
super.setOnBackPressedCallback(onBackPressedCallback)
// set the active tab to the one from the intent
bottomNavigationView.selectedItemId = when (activeTabFromIntent) {
"installed" -> R.id.installed_menu_item
"online" -> R.id.online_menu_item
else -> R.id.installed_menu_item
}
}
}

@ -14,12 +14,12 @@ import android.widget.EditText
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.AppUpdateManager
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainApplication
@ -301,7 +301,7 @@ class UpdateFragment : PreferenceFragmentCompat() {
}
val clipboard =
requireContext().getSystemService(FoxActivity.CLIPBOARD_SERVICE) as ClipboardManager
requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
val linkClickable = findPreference<LongClickablePreference>("pref_update")
linkClickable!!.isVisible =
BuildConfig.ENABLE_AUTO_UPDATER && (BuildConfig.DEBUG || AppUpdateManager.appUpdateManager.peekHasUpdate())

@ -1,69 +0,0 @@
/*
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
*/
package com.fox2code.mmm.utils
import android.content.Context
import android.util.TypedValue
import android.view.Gravity
import android.view.ViewGroup
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.LinearLayoutCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlin.math.roundToInt
// ProgressDialog is deprecated because it's an bad UX pattern, but sometimes we have no other choice...
enum class BudgetProgressDialog {
;
companion object {
fun build(context: Context, title: String?, message: String?): AlertDialog {
val r = context.resources
val padding = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
20f,
r.displayMetrics
).roundToInt()
val v = LinearLayoutCompat(context)
v.orientation = LinearLayoutCompat.HORIZONTAL
val pb = ProgressBar(context)
v.addView(
pb,
LinearLayoutCompat.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
)
val t = TextView(context)
t.gravity = Gravity.CENTER
v.addView(
t,
LinearLayoutCompat.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT,
4f
)
)
v.setPadding(padding, padding, padding, padding)
t.text = message
return MaterialAlertDialogBuilder(context)
.setTitle(title)
.setView(v)
.setCancelable(false)
.create()
}
fun build(context: Context, title: Int, message: String?): AlertDialog {
return build(context, context.getString(title), message)
}
@JvmStatic
fun build(context: Context, title: Int, message: Int): AlertDialog {
return build(context, title, context.getString(message))
}
}
}

@ -35,7 +35,6 @@ class ExternalHelper private constructor() {
)
)
} else {
@Suppress("DEPRECATION")
context.packageManager.queryIntentActivities(
intent,
PackageManager.MATCH_DEFAULT_ONLY

@ -18,8 +18,9 @@ import android.os.Bundle
import android.os.Environment
import android.util.TypedValue
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityOptionsCompat
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.Constants
import com.fox2code.mmm.MainActivity
@ -43,6 +44,7 @@ import java.io.InputStream
import java.io.OutputStream
import java.net.URISyntaxException
@Suppress("unused")
enum class IntentHelper {;
companion object {
@ -60,7 +62,6 @@ enum class IntentHelper {;
"android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE"
const val FLAG_GRANT_URI_PERMISSION = Intent.FLAG_GRANT_READ_URI_PERMISSION
@JvmStatic
fun openUri(context: Context, uri: String) {
if (uri.startsWith("intent://")) {
try {
@ -86,14 +87,13 @@ enum class IntentHelper {;
} catch (e: ActivityNotFoundException) {
if (BuildConfig.DEBUG) Timber.d(e, "Could not find suitable activity to handle url")
Toast.makeText(
context, FoxActivity.getFoxActivity(context).getString(
context, MainApplication.INSTANCE!!.lastActivity!!.getString(
R.string.no_browser
), Toast.LENGTH_LONG
).show()
}
}
@JvmStatic
fun openCustomTab(context: Context, url: String?) {
if (BuildConfig.DEBUG) Timber.d("Opening url: %s in custom tab", url)
try {
@ -106,14 +106,13 @@ enum class IntentHelper {;
} catch (e: ActivityNotFoundException) {
if (BuildConfig.DEBUG) Timber.d(e, "Could not find suitable activity to handle url")
Toast.makeText(
context, FoxActivity.getFoxActivity(context).getString(
context, MainApplication.INSTANCE!!.lastActivity!!.getString(
R.string.no_browser
), Toast.LENGTH_LONG
).show()
}
}
@JvmStatic
@JvmOverloads
fun openUrlAndroidacy(
context: Context,
@ -154,7 +153,6 @@ enum class IntentHelper {;
}
@Suppress("NAME_SHADOWING")
@JvmStatic
fun getPackageOfConfig(config: String): String {
var config = config
var i = config.indexOf(' ')
@ -164,7 +162,6 @@ enum class IntentHelper {;
return config
}
@JvmStatic
fun openConfig(context: Context, config: String) {
val pkg = getPackageOfConfig(config)
try {
@ -195,7 +192,6 @@ enum class IntentHelper {;
}
}
@JvmStatic
fun openMarkdown(
context: Context,
url: String?,
@ -228,7 +224,6 @@ enum class IntentHelper {;
}
}
@JvmStatic
@JvmOverloads
fun openInstaller(
context: Context,
@ -309,7 +304,7 @@ enum class IntentHelper {;
intent1.putExtras(bundle)
}
intent1.putExtra(EXTRA_TAB_EXIT_ANIMATION_BUNDLE, param)
if (activity is FoxActivity) {
if (activity is AppCompatActivity) {
val typedValue = TypedValue()
activity.getTheme().resolveAttribute(
android.R.attr.background, typedValue, true
@ -318,7 +313,7 @@ enum class IntentHelper {;
intent1.putExtra(EXTRA_TAB_TOOLBAR_COLOR, typedValue.data)
intent1.putExtra(
EXTRA_TAB_COLOR_SCHEME,
if (activity.isLightTheme) EXTRA_TAB_COLOR_SCHEME_LIGHT else EXTRA_TAB_COLOR_SCHEME_DARK
if (MainApplication.INSTANCE!!.isLightTheme) EXTRA_TAB_COLOR_SCHEME_LIGHT else EXTRA_TAB_COLOR_SCHEME_DARK
)
}
}
@ -367,7 +362,7 @@ enum class IntentHelper {;
@SuppressLint("SdCardPath")
fun openFileTo(
compatActivity: FoxActivity, destination: File?, callback: OnFileReceivedCallback
compatActivity: AppCompatActivity, destination: File?, callback: OnFileReceivedCallback
) {
var destinationFolder: File? = null
if ((destination == null) || (destination.parentFile.also {
@ -382,22 +377,21 @@ enum class IntentHelper {;
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, false)
intent.addCategory(Intent.CATEGORY_OPENABLE)
val param = ActivityOptionsCompat.makeCustomAnimation(
compatActivity, android.R.anim.fade_in, android.R.anim.fade_out
).toBundle()
compatActivity.startActivityForResult(intent, param) { result: Int, data: Intent? ->
compatActivity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val resultCode = result.resultCode
val data: Intent? = result.data
val uri = data?.data
if (uri == null || result == Activity.RESULT_CANCELED) {
if (uri == null || resultCode == Activity.RESULT_CANCELED) {
if (BuildConfig.DEBUG) Timber.d("invalid uri received")
callback.onReceived(destination, null, RESPONSE_ERROR)
return@startActivityForResult
return@registerForActivityResult
}
Timber.i("FilePicker returned %s", uri)
if ("http" == uri.scheme || "https" == uri.scheme) {
callback.onReceived(destination, uri, RESPONSE_URL)
return@startActivityForResult
return@registerForActivityResult
}
if (ContentResolver.SCHEME_FILE == uri.scheme || result != Activity.RESULT_OK && result != Activity.RESULT_FIRST_USER) {
if (ContentResolver.SCHEME_FILE == uri.scheme || resultCode != Activity.RESULT_OK && resultCode != Activity.RESULT_FIRST_USER) {
Toast.makeText(
compatActivity, R.string.file_picker_wierd, Toast.LENGTH_SHORT
).show()
@ -421,7 +415,9 @@ enum class IntentHelper {;
inputStream = compatActivity.contentResolver.openInputStream(uri)
}
outputStream = FileOutputStream(destination)
copy(inputStream!!, outputStream)
if (inputStream != null) {
copy(inputStream, outputStream)
}
Timber.i("File saved at %s", destination)
success = true
} catch (e: Exception) {
@ -437,28 +433,25 @@ enum class IntentHelper {;
callback.onReceived(
destination, uri, if (success) RESPONSE_FILE else RESPONSE_ERROR
)
}
}.launch(intent)
}
fun openFileTo(
compatActivity: FoxActivity?,
destination: File,
callback: (File, Uri, Int) -> Unit
) {
openFileTo(compatActivity!!, destination, object : OnFileReceivedCallback {
fun openFileTo(compatActivity: AppCompatActivity, module: File, function: (File, Uri, Int) -> Unit) {
openFileTo(compatActivity, module, object : OnFileReceivedCallback {
override fun onReceived(target: File?, uri: Uri?, response: Int) {
if (response == RESPONSE_ERROR) {
MainActivity.getFoxActivity(compatActivity).runOnUiThread {
MainActivity.getAppCompatActivity(compatActivity).runOnUiThread {
Toast.makeText(
compatActivity, R.string.no_file_provided, Toast.LENGTH_SHORT
).show()
}
} else {
try {
callback(target!!, uri!!, response)
function(target!!, uri!!, response)
} catch (e: Exception) {
Timber.e(e)
compatActivity.forceBackPressed()
compatActivity.finish()
}
}
}

@ -16,7 +16,6 @@ enum class ProcessHelper {
companion object {
private val sPendingIntentId = ThreadLocalRandom.current().nextInt(100, 1000000 + 1)
@JvmStatic
fun restartApplicationProcess(context: Context) {
val mStartActivity = Intent(context, MainActivity::class.java)
mStartActivity.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK

@ -17,10 +17,10 @@ import android.provider.Settings
import android.view.View
import android.widget.CheckBox
import android.widget.CompoundButton
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.MainActivity
import com.fox2code.mmm.MainApplication
@ -49,7 +49,7 @@ class RuntimeUtils {
) != PackageManager.PERMISSION_GRANTED
) {
if (BuildConfig.DEBUG) Timber.i("Request Notification Permission")
if (FoxActivity.getFoxActivity(context)
if (MainApplication.INSTANCE!!.lastActivity!!
.shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
) {
// Show a dialog explaining why we need context permission, which is to show
@ -191,7 +191,6 @@ class RuntimeUtils {
*/
fun waitInitialSetupFinished(context: Context, activity: MainActivity): Boolean {
if (BuildConfig.DEBUG) Timber.i("waitInitialSetupFinished")
if (MainActivity.doSetupNowRunning) activity.updateScreenInsets() // Fix an edge case
try {
// Wait for doSetupNow to finish
while (MainActivity.doSetupNowRunning) {
@ -276,7 +275,7 @@ class RuntimeUtils {
}
companion object {
fun reboot(mainActivity: FoxActivity, reboot: RebootMode) {
fun reboot(mainActivity: AppCompatActivity, reboot: RebootMode) {
// reboot based on the reboot cmd from the enum we were passed
when (reboot) {
RebootMode.REBOOT -> {
@ -308,7 +307,7 @@ class RuntimeUtils {
}
private fun showRebootDialog(
mainActivity: FoxActivity,
mainActivity: AppCompatActivity,
showExtraWarning: Boolean,
function: () -> Unit
) {

@ -4,16 +4,14 @@
package com.fox2code.mmm.utils
import android.content.DialogInterface
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.fox2code.foxcompat.app.FoxActivity
import androidx.appcompat.app.AppCompatActivity
import com.fox2code.mmm.BuildConfig
import com.fox2code.mmm.R
import com.fox2code.mmm.installer.InstallerInitializer.Companion.peekMagiskPath
import com.fox2code.mmm.utils.BudgetProgressDialog.Companion.build
import com.fox2code.mmm.utils.IntentHelper.Companion.openInstaller
import com.fox2code.mmm.utils.io.Files.Companion.getFileName
import com.fox2code.mmm.utils.io.Files.Companion.getFileSize
@ -26,14 +24,21 @@ import java.io.IOException
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
class ZipFileOpener : FoxActivity() {
class ZipFileOpener : AppCompatActivity() {
var loading: AlertDialog? = null
// Adds us as a handler for zip files, so we can pass them to the installer
// We should have a content uri provided to us.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loading = build(this, R.string.loading, R.string.zip_unpacking)
loading = MaterialAlertDialogBuilder(this)
.setTitle(R.string.loading)
.setMessage(R.string.zip_unpacking)
.setCancelable(false)
.setNegativeButton(R.string.cancel) { _: DialogInterface, _: Int ->
finishAndRemoveTask()
}
.show()
Thread(Runnable {
if (BuildConfig.DEBUG) Timber.d("onCreate: %s", intent)
val zipFile: File
@ -115,15 +120,11 @@ class ZipFileOpener : FoxActivity() {
if (zip.getEntry("module.prop").also { entry = it } == null) {
Timber.e("onCreate: Zip file is not a valid magisk module")
if (BuildConfig.DEBUG) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (BuildConfig.DEBUG) Timber.d(
"onCreate: Zip file contents: %s",
zip.stream().map { obj: ZipEntry -> obj.name }
.reduce { a: String, b: String -> "$a, $b" }.orElse("empty")
)
} else {
if (BuildConfig.DEBUG) Timber.d("onCreate: Zip file contents cannot be listed on this version of android")
}
if (BuildConfig.DEBUG) Timber.d(
"onCreate: Zip file contents: %s",
zip.stream().map { obj: ZipEntry -> obj.name }
.reduce { a: String, b: String -> "$a, $b" }.orElse("empty")
)
}
runOnUiThread {
Toast.makeText(this, R.string.invalid_format, Toast.LENGTH_LONG).show()
@ -193,7 +194,7 @@ class ZipFileOpener : FoxActivity() {
.setPositiveButton(R.string.yes) { d: DialogInterface, _: Int ->
d.dismiss()
// Pass the file to the installer
val compatActivity = getFoxActivity(this)
val compatActivity = this
openInstaller(
compatActivity, zipFile.absolutePath,
compatActivity.getString(

@ -42,7 +42,6 @@ enum class Files {
private val is64bit = Build.SUPPORTED_64_BIT_ABIS.isNotEmpty()
// stolen from https://stackoverflow.com/a/25005243
@JvmStatic
fun getFileName(context: Context, uri: Uri): String {
var result: String? = null
if (uri.scheme == "content") {
@ -66,7 +65,6 @@ enum class Files {
}
// based on https://stackoverflow.com/a/63018108
@JvmStatic
fun getFileSize(context: Context, uri: Uri): Long? {
var result: Long? = null
try {
@ -90,7 +88,6 @@ enum class Files {
return result
}
@JvmStatic
@Throws(IOException::class)
fun write(file: File, bytes: ByteArray?) {
// make the dir if necessary
@ -101,13 +98,11 @@ enum class Files {
}
}
@JvmStatic
@Throws(IOException::class)
fun read(file: File?): ByteArray {
FileInputStream(file).use { inputStream -> return readAllBytes(inputStream) }
}
@JvmStatic
@Throws(IOException::class)
fun writeSU(file: File, bytes: ByteArray?) {
// make the dir if necessary
@ -118,7 +113,6 @@ enum class Files {
}
}
@JvmStatic
@Throws(IOException::class)
fun readSU(file: File): ByteArray {
if (file.isFile && file.canRead()) {
@ -130,12 +124,10 @@ enum class Files {
SuFileInputStream.open(file).use { inputStream -> return readAllBytes(inputStream) }
}
@JvmStatic
fun existsSU(file: File): Boolean {
return file.exists() || SuFile(file.absolutePath).exists()
}
@JvmStatic
@Throws(IOException::class)
fun copy(inputStream: InputStream, outputStream: OutputStream) {
var nRead: Int
@ -146,7 +138,6 @@ enum class Files {
outputStream.flush()
}
@JvmStatic
fun closeSilently(closeable: Closeable?) {
try {
closeable?.close()
@ -154,7 +145,6 @@ enum class Files {
}
}
@JvmStatic
fun makeBuffer(capacity: Long): ByteArrayOutputStream {
// Cap buffer to 1 Gib (or 512 Mib for 32bit) to avoid memory errors
return makeBuffer(
@ -170,7 +160,6 @@ enum class Files {
}
}
@JvmStatic
@Throws(IOException::class)
fun readAllBytes(inputStream: InputStream): ByteArray {
val buffer = makeBuffer(inputStream.available())
@ -178,13 +167,11 @@ enum class Files {
return buffer.toByteArray()
}
@JvmStatic
fun fixJavaZipHax(bytes: ByteArray) {
if (bytes.size > 8 && bytes[0x6].toInt() == 0x0 && bytes[0x7].toInt() == 0x0 && bytes[0x8].toInt() == 0x8) bytes[0x7] =
0x8 // Known hax to prevent java zip file read
}
@JvmStatic
@Throws(IOException::class)
fun patchModuleSimple(bytes: ByteArray, outputStream: OutputStream?) {
fixJavaZipHax(bytes)
@ -218,7 +205,6 @@ enum class Files {
zipInputStream.close()
}
@JvmStatic
fun fixSourceArchiveShit(rawModule: ByteArray?) {
// unzip the module, check if it has just one folder within. if so, switch to the folder and zip up contents, and replace the original file with that
try {

@ -19,7 +19,6 @@ enum class GMSProviderInstaller {
companion object {
private var called = false
@JvmStatic
fun installIfNeeded(context: Context?) {
if (context == null) {
throw NullPointerException("Context must not be null")

@ -11,16 +11,14 @@ import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.regex.Pattern
@Suppress("UNUSED_EXPRESSION")
enum class Hashes {
;
@Suppress("UNUSED_EXPRESSION", "MemberVisibilityCanBePrivate")
enum class Hashes {;
companion object {
private val HEX_ARRAY = "0123456789abcdef".toCharArray()
private val nonAlphaNum = Pattern.compile("[^a-zA-Z0-9]")
@JvmStatic
fun bytesToHex(bytes: ByteArray): String {
private fun bytesToHex(bytes: ByteArray): String {
val hexChars = CharArray(bytes.size * 2)
for (j in bytes.indices) {
val v = bytes[j].toInt() and 0xFF
@ -30,17 +28,6 @@ enum class Hashes {
return String(hexChars)
}
@JvmStatic
fun hashMd5(ignoredInput: ByteArray?): String {
throw SecurityException("MD5 is not secure")
}
@JvmStatic
fun hashSha1(ignoredInput: ByteArray?): String {
throw SecurityException("SHA-1 is not secure")
}
@JvmStatic
fun hashSha256(input: ByteArray?): String {
input ?: return ""
return try {
@ -51,7 +38,6 @@ enum class Hashes {
}
}
@JvmStatic
fun hashSha512(input: ByteArray?): String {
input ?: return ""
return try {
@ -66,7 +52,6 @@ enum class Hashes {
* Check if the checksum match a file by picking the correct
* hashing algorithm depending on the length of the checksum
*/
@JvmStatic
fun checkSumMatch(data: ByteArray?, checksum: String?): Boolean {
if (checksum == null) return false
val hash: String = when (checksum.length) {
@ -74,8 +59,6 @@ enum class Hashes {
return true // No checksum
}
32 -> hashMd5(data)
40 -> hashSha1(data)
64 -> hashSha256(data)
128 -> hashSha512(data)
else -> {
@ -87,7 +70,6 @@ enum class Hashes {
return hash == checksum.lowercase()
}
@JvmStatic
fun checkSumValid(checksum: String?): Boolean {
return if (checksum == null) false else when (checksum.length) {
32, 40, 64, 128 -> {
@ -108,7 +90,6 @@ enum class Hashes {
}
}
@JvmStatic
fun checkSumName(checksum: String?): String? {
return if (checksum == null) null else when (checksum.length) {
32 -> "MD5"
@ -125,7 +106,6 @@ enum class Hashes {
}
}
@JvmStatic
fun checkSumFormat(checksum: String?): String? {
return if (checksum == null) null else nonAlphaNum.matcher(checksum.trim { it <= ' ' })
.replaceAll("")

@ -2,8 +2,6 @@
* Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
*/
@file:Suppress("unused")
package com.fox2code.mmm.utils.io
import android.os.Build
@ -373,7 +371,6 @@ enum class PropUtils {
}
}
@JvmStatic
fun readModulePropSimple(inputStream: InputStream?, what: String): String? {
if (inputStream == null) return null
var moduleId: String? = null
@ -404,7 +401,6 @@ enum class PropUtils {
return readModulePropSimple(inputStream, "id")
}
@JvmStatic
fun applyFallbacks(moduleInfo: ModuleInfo) {
if (moduleInfo.support == null || moduleInfo.support!!.isEmpty()) {
moduleInfo.support = moduleSupportsFallbacks[moduleInfo.id]
@ -422,7 +418,6 @@ enum class PropUtils {
}
// Some module are really so low quality that it has become very annoying.
@JvmStatic
fun isLowQualityModule(moduleInfo: ModuleInfo?): Boolean {
var description: String = moduleInfo?.description ?: return true
return (moduleInfo.hasFlag(ModuleInfo.FLAG_METADATA_INVALID) || moduleInfo.name!!.length < 3 || moduleInfo.versionCode < 0 || moduleInfo.author == null || !TextUtils.isGraphic(
@ -438,7 +433,6 @@ enum class PropUtils {
return !TextUtils.isGraphic(name) || name!!.indexOf('\u0000') != -1
}
@JvmStatic
fun isInvalidURL(url: String): Boolean {
val i = url.indexOf('/', 8)
val e = url.indexOf('.', 8)
@ -452,7 +446,6 @@ enum class PropUtils {
moduleId.substring(1).replace('_', ' ')
}
@JvmStatic
fun isNullString(string: String?): Boolean {
return string.isNullOrEmpty() || "null" == string
}

@ -192,7 +192,6 @@ enum class Http {;
private var httpClientWithCacheDoH: OkHttpClient? = null
private var fallbackDNS: FallBackDNS? = null
@JvmStatic
var androidacyUA: String? = null
private var hasWebView = false
private var needCaptchaAndroidacyHost: String? = null
@ -222,7 +221,7 @@ enum class Http {;
Timber.e(t, "No WebView support!")
// show a toast
val context: Context = mainApplication.applicationContext
MainActivity.getFoxActivity(context).runOnUiThread {
MainActivity.getAppCompatActivity(context).runOnUiThread {
Toast.makeText(
mainApplication, R.string.error_creating_cookie_database, Toast.LENGTH_LONG
).show()
@ -441,7 +440,6 @@ enum class Http {;
return if (doh) httpClientDoH else httpClient
}
@JvmStatic
fun getHttpClientWithCache(): OkHttpClient? {
return if (doh) httpClientWithCacheDoH else httpClientWithCache
}
@ -452,7 +450,6 @@ enum class Http {;
}
}
@JvmStatic
fun needCaptchaAndroidacy(): Boolean {
return needCaptchaAndroidacyHost != null
}
@ -461,16 +458,16 @@ enum class Http {;
return needCaptchaAndroidacyHost
}
@JvmStatic
fun markCaptchaAndroidacySolved() {
needCaptchaAndroidacyHost = null
}
@Suppress("unused")
@JvmStatic
@SuppressLint("RestrictedApi")
@Throws(IOException::class)
fun doHttpGet(url: String, allowCache: Boolean): ByteArray {
if (url.isEmpty()) {
throw IOException("Empty URL")
}
var response: Response?
response = try {
(if (allowCache) getHttpClientWithCache() else getHttpClient())!!.newCall(
@ -482,7 +479,7 @@ enum class Http {;
Timber.e(e, "Failed to post %s", url)
// detect ssl errors, i.e., cert authority invalid by looking at the message
if (e.message != null && e.message!!.contains("_CERT_")) {
MainActivity.getFoxActivity(MainApplication.INSTANCE!!).runOnUiThread {
MainApplication.INSTANCE!!.lastActivity!!.runOnUiThread {
// show toast
Toast.makeText(
MainApplication.INSTANCE, R.string.ssl_error, Toast.LENGTH_LONG
@ -558,7 +555,6 @@ enum class Http {;
}
@Suppress("unused")
@JvmStatic
@Throws(IOException::class)
fun doHttpPost(url: String, data: String, allowCache: Boolean): ByteArray {
return doHttpPostRaw(url, data, allowCache) as ByteArray
@ -579,7 +575,7 @@ enum class Http {;
Timber.e(e, "Failed to post %s", url)
// detect ssl errors, i.e., cert authority invalid by looking at the message
if (e.message != null && e.message!!.contains("_CERT_")) {
MainActivity.getFoxActivity(MainApplication.INSTANCE!!).runOnUiThread {
MainApplication.INSTANCE!!.lastActivity!!.runOnUiThread {
// show toast
Toast.makeText(
MainApplication.INSTANCE, R.string.ssl_error, Toast.LENGTH_LONG
@ -643,7 +639,6 @@ enum class Http {;
return responseBody.bytes()
}
@JvmStatic
@Throws(IOException::class)
fun doHttpGet(url: String, progressListener: ProgressListener): ByteArray {
val response: Response
@ -654,7 +649,7 @@ enum class Http {;
Timber.e(e, "Failed to post %s", url)
// detect ssl errors, i.e., cert authority invalid by looking at the message
if (e.message != null && e.message!!.contains("_CERT_")) {
MainActivity.getFoxActivity(MainApplication.INSTANCE!!).runOnUiThread {
MainApplication.INSTANCE!!.lastActivity!!.runOnUiThread {
// show toast
Toast.makeText(
MainApplication.INSTANCE, R.string.ssl_error, Toast.LENGTH_LONG
@ -733,7 +728,6 @@ enum class Http {;
}
// dohttpget with progress listener but as lambda
@JvmStatic
@Throws(IOException::class)
fun doHttpGet(url: String, progressListener: (Int, Int, Boolean) -> Unit): ByteArray {
return doHttpGet(url, object : ProgressListener {
@ -743,23 +737,19 @@ enum class Http {;
})
}
@JvmStatic
fun cleanDnsCache() {
fallbackDNS?.cleanDnsCache()
}
@JvmStatic
fun setDoh(doh: Boolean) {
Timber.i("DoH: " + Companion.doh + " -> " + doh)
Companion.doh = doh
}
@JvmStatic
fun hasWebView(): Boolean {
return hasWebView
}
@JvmStatic
fun hasConnectivity(context: Context): Boolean {
// cache result for 10 seconds so we don't spam the system
if (System.currentTimeMillis() - lastConnectivityCheck < 10000) {

@ -27,7 +27,6 @@ class HttpException : IOException {
}
companion object {
@JvmStatic
fun shouldTimeout(exception: Exception?): Boolean {
return exception is HttpException &&
exception.shouldTimeout()

@ -6,7 +6,6 @@ package com.fox2code.mmm.utils.room
import androidx.room.Entity
@Suppress("unused")
@Entity(tableName = "modulelistcache", primaryKeys = ["codename"], indices = [androidx.room.Index(value = ["codename"], unique = true)])
class ModuleListCache (
var codename: String,

@ -12,7 +12,6 @@ import androidx.room.Query
// contains
// codename (string, primary), version (string), versionCode (int), author (string), description (string), minApi (int), maxApi (int), minMagisk (int), needRamdisk (boolean), support (string), donate (string), config (string), changeBoot (bool), mmtReborn (bool), repoId (string), lastUpdate (bigint), safe (bool)
@Suppress("unused")
@Dao
interface ModuleListCacheDao {
// functions:

@ -7,7 +7,6 @@ package com.fox2code.mmm.utils.room
import androidx.room.Database
import androidx.room.RoomDatabase
@Suppress("unused")
@Database(entities = [ModuleListCache::class], version = 1)
abstract class ModuleListCacheDatabase : RoomDatabase() {
abstract fun moduleListCacheDao(): ModuleListCacheDao

@ -9,7 +9,6 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Suppress("unused")
@Dao
interface ReposListDao {
// contains

@ -7,7 +7,6 @@ package com.fox2code.mmm.utils.room
import androidx.room.Database
import androidx.room.RoomDatabase
@Suppress("unused")
@Database(entities = [ReposList::class], version = 1)
abstract class ReposListDatabase : RoomDatabase() {
abstract fun reposListDao(): ReposListDao

@ -9,7 +9,6 @@ import io.sentry.SentryLevel
import java.util.Objects
class SentryBreadcrumb {
@JvmField
val breadcrumb: Breadcrumb = Breadcrumb()
init {

@ -1,7 +0,0 @@
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<!-- search icon -->
<path android:fillColor="@android:color/white"
android:pathData="M20.71,19.29l-3.89,-3.89C17.54,14.21 18,13.16 18,12c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6s2.69,6 6,6c1.16,0 2.21,-0.46 2.99,-1.2l3.89,3.89c0.39,0.39 1.02,0.39 1.41,0l0,0c0.39,-0.39 0.39,-1.02 0,-1.41zM8,12c0,-2.21 1.79,-4 4,-4s4,1.79 4,4s-1.79,4 -4,4S8,14.21 8,12z"/>
</vector>

@ -3,6 +3,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
@ -10,5 +11,6 @@
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M21.8,12.7l-0.8,-0.6v-0.2l0.8,-0.6c0.2,-0.1 0.2,-0.3 0.1,-0.5l-0.9,-1.5c-0.1,-0.1 -0.2,-0.2 -0.4,-0.2 -0.1,0 -0.1,0 -0.2,0l-1,0.4c-0.1,-0.1 -0.1,-0.1 -0.2,-0.1l-0.2,-1c0,-0.2 -0.2,-0.4 -0.4,-0.4h-1.7c-0.2,0 -0.4,0.2 -0.4,0.3l-0.1,1c0,0 -0.1,0 -0.1,0.1l-0.1,0.1 -1,-0.4c-0.1,0 -0.1,0 -0.2,0 -0.1,0 -0.3,0.1 -0.4,0.2l-0.9,1.5c-0.1,0.2 -0.1,0.4 0.1,0.5l0.8,0.6v0.2l-0.8,0.6c-0.2,0.1 -0.2,0.3 -0.1,0.5l0.9,1.5c0.1,0.1 0.2,0.2 0.4,0.2 0.1,0 0.1,0 0.2,0l1,-0.4c0.1,0.1 0.1,0.1 0.2,0.1l0.2,1c0,0.2 0.2,0.3 0.4,0.3h1.7c0.2,0 0.4,-0.2 0.4,-0.3l0.2,-1c0,0 0.1,0 0.1,-0.1l0.1,-0.1 1,0.4c0.1,0 0.1,0 0.2,0 0.1,0 0.3,-0.1 0.4,-0.2l0.9,-1.5c0.1,-0.2 0.1,-0.4 -0.1,-0.5zM18,13.5c-0.8,0 -1.5,-0.7 -1.5,-1.5s0.7,-1.5 1.5,-1.5 1.5,0.7 1.5,1.5 -0.7,1.5 -1.5,1.5zM17,17h2v4c0,1.1 -0.9,2 -2,2H7c-1.1,0 -2,-0.9 -2,-2V3c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2v4h-2V6H7v12h10v-1z" />
android:pathData="M21.8,12.7l-0.8,-0.6v-0.2l0.8,-0.6c0.2,-0.1 0.2,-0.3 0.1,-0.5l-0.9,-1.5c-0.1,-0.1 -0.2,-0.2 -0.4,-0.2 -0.1,0 -0.1,0 -0.2,0l-1,0.4c-0.1,-0.1 -0.1,-0.1 -0.2,-0.1l-0.2,-1c0,-0.2 -0.2,-0.4 -0.4,-0.4h-1.7c-0.2,0 -0.4,0.2 -0.4,0.3l-0.1,1c0,0 -0.1,0 -0.1,0.1l-0.1,0.1 -1,-0.4c-0.1,0 -0.1,0 -0.2,0 -0.1,0 -0.3,0.1 -0.4,0.2l-0.9,1.5c-0.1,0.2 -0.1,0.4 0.1,0.5l0.8,0.6v0.2l-0.8,0.6c-0.2,0.1 -0.2,0.3 -0.1,0.5l0.9,1.5c0.1,0.1 0.2,0.2 0.4,0.2 0.1,0 0.1,0 0.2,0l1,-0.4c0.1,0.1 0.1,0.1 0.2,0.1l0.2,1c0,0.2 0.2,0.3 0.4,0.3h1.7c0.2,0 0.4,-0.2 0.4,-0.3l0.2,-1c0,0 0.1,0 0.1,-0.1l0.1,-0.1 1,0.4c0.1,0 0.1,0 0.2,0 0.1,0 0.3,-0.1 0.4,-0.2l0.9,-1.5c0.1,-0.2 0.1,-0.4 -0.1,-0.5zM18,13.5c-0.8,0 -1.5,-0.7 -1.5,-1.5s0.7,-1.5 1.5,-1.5 1.5,0.7 1.5,1.5 -0.7,1.5 -1.5,1.5zM17,17h2v4c0,1.1 -0.9,2 -2,2H7c-1.1,0 -2,-0.9 -2,-2V3c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2v4h-2V6H7v12h10v-1z"
tools:ignore="VectorPath" />
</vector>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
-->
<!-- This is the background for the search bar in the main screen. it should look like a floating action button when the search bar is not focused, and like a text field when it is. -->
<!-- Path: app\src\main\res\drawable\search_bar_background.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.12" android:color="?attr/colorOnSurface" android:state_activated="true" android:state_focused="true" android:state_hovered="true" android:state_pressed="true" android:state_selected="true" android:state_window_focused="true" />
<item android:alpha="0.12" android:color="?attr/colorOnSurface" android:state_activated="true" android:state_focused="true" android:state_hovered="true" android:state_pressed="true" android:state_selected="true" />
<item android:alpha="0.12" android:color="?attr/colorOnSurface" android:state_activated="true" android:state_focused="true" android:state_hovered="true" android:state_pressed="true" />
<item android:alpha="0.12" android:color="?attr/colorOnSurface" android:state_activated="true" android:state_focused="true" android:state_hovered="true" />
<item android:alpha="0.12" android:color="?attr/colorOnSurface" android:state_activated="true" android:state_focused="true" />
<item android:alpha="0.12" android:color="?attr/colorOnSurface" android:state_activated="true" />
<item android:alpha="0.12" android:color="?attr/colorOnSurface" />
</selector>

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/system_accent1_100"/>
<corners android:radius="20dp"/>
<padding android:left="0dp"
android:bottom="0dp"
android:right="0dp"
android:top="0dp"/>
</shape>

@ -2,8 +2,9 @@
~ Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/crash_scroll"
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -15,7 +16,6 @@
<!-- first, crash icon -->
<ImageView
android:id="@+id/crash_icon"
android:layout_width="101dp"
android:layout_height="93dp"
android:layout_gravity="center"
@ -25,7 +25,6 @@
<!-- crash_text header -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/crash_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
@ -36,7 +35,6 @@
<!-- textview suggesting user use the submit feedback button instead of manually copying the crash details -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/crash_details_suggestion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
@ -71,7 +69,6 @@
</HorizontalScrollView>
<com.google.android.material.button.MaterialButton
android:id="@+id/copy_button"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="top|end"
@ -99,7 +96,6 @@
android:textSize="18sp" />
<LinearLayout
android:id="@+id/feedback"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@ -112,7 +108,8 @@
android:layout_height="48dp"
android:layout_margin="10dp"
android:hint="@string/feedback_name"
android:inputType="text" />
android:inputType="text"
android:autofillHints="name" />
<!-- feedback form email -->
<EditText
@ -122,7 +119,8 @@
android:enabled="false"
android:layout_margin="10dp"
android:hint="@string/feedback_email"
android:inputType="textEmailAddress" />
android:inputType="textEmailAddress"
android:autofillHints="emailAddress" />
<!-- feedback form message -->
<EditText
@ -132,7 +130,8 @@
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:hint="@string/feedback_message"
android:inputType="textMultiLine" />
android:inputType="textMultiLine"
android:importantForAutofill="no" />
</LinearLayout>

@ -7,7 +7,6 @@
tools:context=".ExpiredActivity">
<LinearLayout
android:id="@+id/mainexpiredlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@ -67,7 +66,6 @@
<com.google.android.material.bottomappbar.BottomAppBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/bottomAppBar"
android:layout_gravity="bottom"
app:fabAlignmentMode="center"
app:fabCradleMargin="10dp">

@ -11,7 +11,8 @@
app:layout_constraintTop_toTopOf="parent"
tools:context=".MainActivity">
<!-- FrameLayout is the best way to fix blurring --> <!-- search layout using textinputlayout and textinputedittext from material design in a linearlayout -->
<!-- FrameLayout is the best way to fix blurring -->
<!-- search layout using textinputlayout and textinputedittext from material design in a linearlayout -->
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_bar"
@ -31,10 +32,9 @@
style="@style/ThemeOverlay.Material3.Search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginBottom="6dp"
android:paddingHorizontal="16dp"
android:paddingBottom="6dp"
android:hint="@string/search"
app:boxStrokeWidth="1dp"
app:layout_constraintBottom_toTopOf="@id/swipe_refresh"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

@ -23,7 +23,6 @@
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/setup_box_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@ -32,7 +31,6 @@
<!-- Title -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/setup_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
@ -40,7 +38,6 @@
android:textAppearance="@style/TextAppearance.Material3.HeadlineLarge" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/setup_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
@ -49,7 +46,6 @@
<!-- inform user that to finish setup, they need to scroll down -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/setup_scroll_down"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="2dp"
@ -61,7 +57,6 @@
<!-- Button to trigger theme selection, half width, and in container with language -->
<LinearLayout
android:id="@+id/LinearLayout02"
android:layout_width="match_parent"
android:layout_height="wrap_content">

@ -22,7 +22,6 @@
<!-- Activity used to download and install app updates -->
<!-- first, upgrade icon -->
<ImageView
android:id="@+id/update_icon"
android:layout_width="101dp"
android:layout_height="101dp"
android:layout_gravity="center"
@ -31,7 +30,6 @@
android:src="@drawable/baseline_system_update_24" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/update_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -41,7 +39,6 @@
android:textAppearance="?attr/textAppearanceHeadline6" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/update_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -68,7 +65,6 @@
<!-- Invisible warning for debug builds -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/update_debug_warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"

@ -12,17 +12,16 @@
tools:context=".markdown.MarkdownActivity">
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/markdownScroll"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/md_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

@ -195,7 +195,6 @@
app:chipIcon="@drawable/ic_baseline_error_24" />
<com.google.android.material.chip.Chip
android:id="@+id/button_action7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loading"
@ -203,7 +202,6 @@
app:chipIcon="@drawable/ic_baseline_error_24" />
<com.google.android.material.chip.Chip
android:id="@+id/button_action8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loading"

@ -4,7 +4,6 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/webViewContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"

@ -3,8 +3,7 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bottom_nav_menu">
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- two options: "Installed" and "Online" -->
<item
android:id="@+id/installed_menu_item"

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bottom_nav_menu">
<!-- two options: "Installed" and "Online" -->
<item
android:id="@+id/back"
android:icon="@drawable/ic_baseline_extension_24"
android:title="@string/modules"
app:showAsAction="ifRoom" />
<item
android:id="@+id/settings_menu_item"
android:icon="@drawable/ic_baseline_settings_24"
android:title="@string/action_settings"
app:showAsAction="ifRoom" />
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 28 KiB

@ -19,7 +19,6 @@
<string name="file_picker_failure">المنتقي عيا يلقط ملفك.</string>
<string name="use_magisk_install_command_desc">شف الأمر ذا منتهي و ممكن يسبب مشاكل عشان كذا حنا مزبنينه عنك. فا انت بكيفك اذا تبا تشغله شغله!</string>
<string name="wrap_text_desc">عرض الخط على شكل أسطر واجد بدال ماتحط كل الخطوط على نفس السطر يوم تثبت إضافة.</string>
<string name="language_support_outdated">اللغة ذي ماهي مكتملة اذا ودك رح للموقع و ترجمها.</string>
<string name="loading">دقايق…</string>
<string name="online_repo">الريبو عن طريق النت</string>
<string name="showcase_mode">التطبيق مقفل</string>
@ -60,10 +59,8 @@
<string name="prevent_reboot_desc">منع تطفية الجهاز بطريقة غلط</string>
<string name="enable_monet">تشغيل Monet</string>
<string name="pref_category_info">معلومة</string>
<string name="licenses">التصاريح</string>
<string name="show_incompatible_pref">إظهار الإضافات الي ماهي مدعومة</string>
<string name="magisk_outdated">فيه تحديث ل ابو شنب اذا تبا تحدثه!</string>
<string name="pref_category_repos">ريبو</string>
<string name="pref_category_security">الأمان</string>
<string name="pref_category_appearance">الشكل</string>
<string name="master_delete">حذف ملفات الإضافة ؟</string>
@ -127,7 +124,6 @@
<string name="crash_reporting_restart_message">التطبيق لازم يطفي عشان تفعل الخيار ذا</string>
<string name="restart">إعادة تشغيل</string>
<string name="androidacy_test_mode_disable_warning">التطبيق بيعاد تشغيله لأجل نوقف النسخة التجريبية</string>
<string name="androidacy_failed_to_parse_token">ماقدرنا ناخذ معلومات الكود من Androidacy. حاول بعدين.</string>
<string name="androidacy_failed_to_validate_token">ماقدرنا ناخذ معلومات الكود من Androidacy. حاول بعدين.</string>
<string name="androidacy_need_captcha">حظرو تحديث Androidacy بسبب Captcha</string>
<string name="api_key_restart">تغير كود API. طف التطبيق لحفظ التغييرات.</string>

@ -72,7 +72,6 @@
<string name="prevent_reboot_pref">منع إعادة التشغيل</string>
<string name="enable_monet">تفعيل Monet</string>
<string name="show_licenses">إظهار التراخيص</string>
<string name="licenses">التراخيص</string>
<string name="show_incompatible_pref">‌عرض الإضافات الغير مدعومة</string>
<string name="magisk_outdated">هناك إصدار جديد من Magisk للتثبيت!</string>
<string name="pref_category_security">الأمان</string>
@ -90,7 +89,6 @@
<string name="module_min_sdk_chip">الإصدار الأدنى المطلوب من Android</string>
<string name="module_max_sdk_chip">الإصدار الأقصى من Android</string>
<string name="prevent_reboot_desc">منع عمليات إعادة التشغيل الغير متوقعة</string>
<string name="pref_category_repos">ريبوز</string>
<string name="theme_pref">خلفية</string>
<string name="source_code">المصدر (سورس كود)</string>
<string name="file_picker_failure">تعذر على منتقي الملفات الحالي الوصول إلى الملف.</string>
@ -126,7 +124,6 @@
<string name="crash_reporting_restart_title">إعادة تشغيل التطبيق لتطبيق التغييرات؟</string>
<string name="crash_reporting_restart_message">يحتاج التطبيق إلى إعادة التشغيل لتطبيق هذا الإعداد</string>
<string name="restart">إعادة التشغيل</string>
<string name="androidacy_failed_to_parse_token">تعذر استرداد التوكن من Androidacy. يرجى إعادة المحاولة لاحقا.</string>
<string name="androidacy_need_captcha">تم حظر تحديث Androidacy بواسطة Captcha</string>
<string name="api_key_restart">تم تغيير مفتاح API. أعد تشغيل التطبيق لتطبيق التغييرات.</string>
<string name="permission_notification_title">السماح بالإشعارات؟</string>
@ -148,7 +145,6 @@
<string name="clear_data_dialogue_title">مسح بيانات التطبيق؟</string>
<string name="clear_data_dialogue_message">أنت على وشك مسح بيانات التطبيق. يرجى تأكيد هذا الإجراء.</string>
<string name="notification_update_desc">قد يزيد من استخدام البطارية</string>
<string name="language_support_outdated">بعض الترجمات للغة الحالية ليست محدثة ، يرجى التفكير في المساهمة في ترجمات التطبيق على GitHub</string>
<string name="crash_reporting_desc">إذا قمت بتعطيل هذا ، فلن يحصل المطور على تقارير الأخطاء تلقائياً، وقد يؤدي ذلك إلى صعوبة استكشاف الأخطاء وإصلاحها</string>
<string name="api_key_summary">استخدم مفتاح API مخصص ل Androidacy. مفيد للمشتركين المميزين ، لإزالة الإعلانات والمزيد.</string>
<string name="androidacy_test_mode_warning">أنت تقوم بإعداد التطبيق لاستخدام نقطة نهاية غير إنتاجية ل Androidacy. قد يؤدي هذا إلى عدم استقرار التطبيق والفشل في تحميل الريبو عبر الإنترنت. لا تبلغ عن الأخطاء إذا كان هذا المفتاح مفعل. سيتم إعادة تشغيل التطبيق لإعادة تحميل الريبو.</string>
@ -208,7 +204,6 @@
<string name="update_debug_download_pref">آلية تنزيل التحديثات الاختبارية</string>
<string name="reset_api_key">إعادة تعيين مفاتيح API</string>
<string name="upgrade_androidacy_promo">الترقية إلى الإصدار المميز</string>
<string name="feedback_submit">إرسال وإعادة تشغيل</string>
<string name="please_feedback">الرجاء مساعدتنا من خلال إخبارنا بما كنت تحاول القيام به عندما حدث هذا.</string>
<string name="feedback_name">الأسم (اختياري)</string>
<string name="feedback_email">البريد الإلكتروني (اختياري)</string>
@ -229,7 +224,6 @@
<string name="pref_category_updates">التحديثات</string>
<string name="update_title">تحديث التطبيق</string>
<string name="update_message">يرجى الانتظار بينما نتحقق من وجود تحديثات ل AMM وتثبيتها. قد يستغرق هذا بضع دقائق</string>
<string name="update_button">الرجاء الانتظار…</string>
<string name="notification_update_summary">يمكن تحديث الإضافات التالية:</string>
<string name="notification_update_module_template">من الإصدار %1$s إلى الإصدار %2$s</string>
<string name="notification_channel_background_update">جاري التحقق من وجود تحديثات…</string>
@ -270,9 +264,7 @@
<string name="add_repo_message">يجب تقديم الريبوز عبر HTTPS ، ويجب أن تتبع المواصفات الموضحة في الوثائق.</string>
<string name="notification_channel_background_update_description">يقوم AMM بالتحقق من وجود تحديثات في الخلفية.</string>
<string name="notification_channel_category_background_update_description">عرض إشعار أثناء التحقق من وجود تحديثات حتى لا يتم قتلها بواسطة النظام</string>
<string name="yer_a_wizard_harry">يا ساحر ، هاري!</string>
<string name="keep_tapping_to_enter_hogwarts">استمر في النقر للقبول في هوجورتس!</string>
<string name="modules">الإضافات</string>
<string name="no_browser">ليس لديك متصفح مثبت. الرجاء تثبيت واحد للمتابعة.</string>
<string name="online">عبر الإنترنت</string>
<string name="safe">آمن</string>
@ -336,7 +328,6 @@
<string name="error_creating_modulelistcache_database">فشل إنشاء ذاكرة تخزين مؤقتة (كيتش) لقاعدة الإضافة</string>
<string name="blur_performance_warning_summary">الجهاز غير متوافق مع التمويه (Blur)</string>
<string name="reboot">إعادة التشغيل</string>
<string name="reboot_system">إعادة التشغيل بشكل طبيعي</string>
<string name="reboot_recovery">إعادة التشغيل إلى الركفري</string>
<string name="reboot_bootloader">إعادة التشغيل إلى البووتلاودر</string>
<string name="reboot_edl">إعادة التشغيل إلى وضع EDL</string>
@ -347,10 +338,6 @@
<string name="app_name_v2">اندرويداسي مدير الإضافات</string>
<string name="app_name_short_v2">AMM</string>
<string name="search">بحث</string>
<string name="title_activity_expired">نشاط منتهي</string>
<string name="title_home">القائمة الرئيسية</string>
<string name="title_dashboard">لوحة المعلومات</string>
<string name="title_notifications">الإشعارات</string>
<string name="expired">هذا الإصدار منتهي!</string>
<string name="expired_message">الإصدار الذي تستخدمه منتهي وليس قابل للتشغيل بعد الآن. الرجاء التحديث إلى آخر اصدار مستقر.</string>
<string name="download_latest">تنزيل الأحدث</string>

@ -38,7 +38,6 @@
<string name="prevent_reboot_desc">Sprječava neočekivana ponovno pokretanje</string>
<string name="enable_monet">Omogući Monet</string>
<string name="show_licenses">Prikaži licence</string>
<string name="licenses">Licence</string>
<string name="show_incompatible_pref">Prikazati nekompatibilne module</string>
<string name="magisk_outdated">Postoji nova verzija Magiska za instaliranje!!!</string>
<string name="pref_category_security">Sigurnost</string>
@ -63,7 +62,6 @@
<string name="disable_low_quality_module_filter_desc">Neki moduli ne deklarišu svoje metapodatke ispravno, što uzrokuje vizualne probleme i/ili ukazuje na loš kvalitet modula.
\nUključite ovo na vlastitu odgovornost!</string>
<string name="manage_repos_pref">Upravljajte repozitorijem</string>
<string name="pref_category_repos">Repozitoriji</string>
<string name="dns_over_https_pref">DNS preko HTTPS</string>
<string name="wrap_text_pref">Prelamanje teksta</string>
<string name="enable_blur_pref">Zamagljeno</string>
@ -90,7 +88,6 @@
\nUključite ovo na vlastitu odgovornost!</string>
<string name="dns_over_https_desc">Može riješiti probleme sa povezivanjem u nekim slučajevima. (Ne odnosi se na WebView.)</string>
<string name="notification_update_title">Pronađeno %1$d ažuriranja modula</string>
<string name="language_support_outdated">Neki prijevodi za trenutni jezik nisu ažurirani, razmislite o doprinosu prijevoda aplikacije na GitHubu</string>
<string name="clear_data_dialogue_message">Upravo ćete obrisati podatke aplikacije. Molimo potvrdite ovu radnju.</string>
<string name="repo_enabled_changed">Omogućili ste ili onemogućili repozitorij. Osvježite listu modula ili ponovo pokrenite aplikaciju.</string>
<string name="language_translated_by">Preveo Jigsaw | Muhamed M</string>
@ -182,7 +179,6 @@
<string name="upgrade_androidacy_promo">Nadogradi na premium</string>
<string name="upgrade_androidacy_promo_desc">Nadogradnja na premium će ukloniti oglase, captcha i preuzimanja za Androidacy Repository i podržati Androidacy i autore modula.</string>
<string name="crash_reporting_restart_message">Aplikacija se mora ponovo pokrenuti da primijeni ovu postavku</string>
<string name="androidacy_failed_to_parse_token">Nije moguće preuzeti token iz Androidacy-a. Molimo pokušajte ponovo kasnije.</string>
<string name="api_key_restart">API ključ je promijenjen. Ponovo pokrenite aplikaciju da primijenite izmjene.</string>
<string name="permission_notification_message">Potrebna nam je dozvola za obavještenja da vas obavijestimo o ažuriranjima aplikacije i modula. Ako ne date ovu dozvolu, automatska provjera ažuriranja će se izgasiti.</string>
<string name="androidacy_repo_disabled_message">Ovoj verziji nedostaju klijentski ključevi za Androidacy Repo. Molimo preuzmite GitHub izdanje ako želite imati koristi od funkcija kao što su recenzije modula, automatske sigurnosne provjere i još mnogo toga.</string>
@ -214,7 +210,6 @@
\n%1$s</string>
<string name="crash_text">Ups, naišli smo na prepreku!</string>
<string name="feedback_message">Recite nam više detalja o tome što ste radili kada se ovo dogodilo. Što više to bolje!</string>
<string name="feedback_submit">Predaj i ponovno pokreni</string>
<string name="please_feedback">Molimo vas da pomognete tako što kažete što ste pokušavali učiniti kada se ovo dogodilo.</string>
<string name="sentry_dialogue_disabled">Izvješćivanje o greškama je onemogućeno. Omogućite ga za slanje povratnih informacija.</string>
<string name="feedback_name">Ime (neobavezno)</string>
@ -250,7 +245,6 @@
<string name="notification_update_ignore_desc">Lista modula koji se neće provjeravati za ažuriranja</string>
<string name="background_update_check_excludes">Moduli koji se neće automatski provjeravati za ažuriranja</string>
<string name="update_title">Ažuriraj aplikaciju</string>
<string name="update_button">Molimo pričekajte…</string>
<string name="invalid_repo_url">URL koji ste unijeli za repositorij nije važeći</string>
<string name="add_repo_message">Repositoriji moraju biti posluženi preko HTTPS, i moraju pratiti specifikaciju označenu u dokumentaciji.</string>
<string name="update_message">Molimo pričekajte dok provjerimo i instaliramo ažuriranja za Fox-ov MMU. Ovo možda potraje par minuta</string>

@ -35,11 +35,9 @@
<string name="showcase_mode_desc">Omezený režim brání správci provádět akce s moduly</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Zobrazit licence</string>
<string name="licenses">Licence</string>
<string name="show_incompatible_pref">Zobrazit nekompatibilní moduly</string>
<string name="show_incompatible_desc">Zobrazit moduly, které nejsou podle jejich metadat kompatibilní s vaším zařízením</string>
<string name="magisk_outdated">Magisk je zastaralý!</string>
<string name="pref_category_repos">Úložiště</string>
<string name="master_delete">Smazat soubory modulu?</string>
<string name="master_delete_no">Ponechat soubory</string>
<string name="master_delete_yes">Smazat soubory</string>
@ -105,7 +103,6 @@
<string name="crash_reporting_desc">Zakážete-li tohle, vývojář nebude dostávat automatické zprávy o chybách, což může ztížit odstraňování problémů</string>
<string name="prevent_reboot_desc">Zabránit neočekávaným rebootům</string>
<string name="androidacy_test_mode_desc">Použít testovací endpoint místo endpointu vydání (Restartuje aplikaci)</string>
<string name="language_support_outdated">Některé překlady pro aktuální jazyk můžou být starší, zvažte prosím přispění k překladům aplikací na GitHubu</string>
<string name="link_copied">Odkaz zkopírován</string>
<string name="androidacy_test_mode_pref">Testovací režim Androidacy</string>
<string name="notification_update_title">Nalezeno %1$d aktualizací modulů</string>
@ -128,7 +125,6 @@
<string name="dont_ask_again">Neptejte se znovu</string>
<string name="androidacy_repo_disabled">Tenhle repozitář je momentálně zakázán</string>
<string name="androidacy_failed_to_validate_token">Nelze oveřit Androidacy token. Prosím zkuste to znovu později.</string>
<string name="androidacy_failed_to_parse_token">Nelze načíst Androidacy token. Prosím zkuste to znovu později.</string>
<string name="androidacy_need_captcha">Aktualizaci Androidacy blokuje Captcha</string>
<string name="api_key_invalid">Klíč Androidacy nelze ověřit. Skontrolujte jej a zkuste opět.</string>
<string name="api_key_valid">Klíč API je platný.</string>
@ -204,7 +200,6 @@
<string name="error_saving_logs">Protokoly se nedají uložit</string>
<string name="share_logs">Sdílet protokoly AMM</string>
<string name="keep_tapping_to_enter_hogwarts">Pokračujte v klikání, aby vás přijali do Bradavic!</string>
<string name="modules">Moduly</string>
<string name="error_no_extras">ERROR: Chybná data při spuštění</string>
<string name="update_debug_warning">Zdá se, že spouštíte debug sestavení. Debug sestavení musíte aktualizovat ručně a aktualizace v aplikaci nejsou podporováný</string>
<string name="error_no_action">CHYBA: Byla zadána neplatná akce. Nebude se pokračovat.</string>
@ -226,7 +221,6 @@
<string name="upgrade_androidacy_promo_desc">Přechodem na prémiovou verzi odstraníte reklamy, CAPTCHA a stahování pro úložiště Androidacy a podpoříte Androidacy a autory modulů.</string>
<string name="crash_text">Uh-oh, narazili jsme na zádrhel!</string>
<string name="feedback_message">Řekněte nám více podrobností o tom, co jste dělali, když se to stalo. Čím více tím lépe!</string>
<string name="feedback_submit">Odeslat a restartovat</string>
<string name="please_feedback">Pomozte nám prosím tím, že nám řekněte, co jste se snažili udělat, když se to stalo.</string>
<string name="sentry_dialogue_disabled">Hlášení o pádu je zakázáno. Chcete-li odeslat zpětnou vazbu, povolte ji.</string>
<string name="feedback_name">Název (volitelné)</string>
@ -285,7 +279,6 @@
<string name="notification_channel_background_update_app">Je dostupná aktualizace aplikace!</string>
<string name="notification_channel_background_update_app_description">Je dostupná aktualizace AMM. Klikněte zde pro aktualizaci.</string>
<string name="setup_update_check_headline">Kontroly aktualizací</string>
<string name="update_button">Čekejte prosím…</string>
<string name="notification_channel_background_update">Kontrola aktualizací…</string>
<string name="zip_intent_module_install">Vážně chceš nainstalovat modul \"%1$s\" ze ZIP souboru \"%2$s\"\?
\n
@ -294,7 +287,6 @@
<string name="update_message">Počkejte prosím, než zkontrolujeme a nainstalujeme aktualizace AMM. Může to trvat pár minut</string>
<string name="error_download_update">Při stahování informací o aktualizaci došlo k chybě.</string>
<string name="low_quality_module_desc">Tento modul obsahuje metadata, která jsou buď neplatná, nebo jsou považována za označení modulu nízké kvality. Doporučuje se odinstalace.</string>
<string name="yer_a_wizard_harry">Jsi čaroděj, Harry!</string>
<string name="no_browser">Nemáte nainstalován prohlížeč. Pro pokračování nejaký nainstalujte.</string>
<string name="setup_background_update_check_require_wifi">Pro kontrolu aktualizací vyžadovat WiFi</string>
<string name="setup_background_update_check_require_wifi_summary">K provádění kontrol aktualizací využívat WiFi nebo jiné neměřené připojení</string>

@ -47,9 +47,7 @@
<string name="enable_monet">Aktiver Monet</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Vis licenser</string>
<string name="licenses">Licenser</string>
<string name="show_incompatible_desc">Vis moduler, der sandsynligvis ikke fungerer på din enhed baseret på deres metadata</string>
<string name="pref_category_repos">Depoter</string>
<string name="pref_category_security">Sikkerhed</string>
<string name="pref_category_appearance">Udseende</string>
<string name="master_delete">Slet modul filerne\?</string>
@ -104,7 +102,6 @@
<string name="crash_reporting_restart_message">Appen skal genstarte for at anvende denne indstilling</string>
<string name="restart">Genstart</string>
<string name="androidacy_test_mode_disable_warning">Appen genstartes for at deaktivere iscenesættelsesslutpunktet</string>
<string name="androidacy_failed_to_parse_token">Kunne ikke hente token fra Androidacy. Prøv igen senere.</string>
<string name="androidacy_failed_to_validate_token">Kunne ikke validere token til Androidacy. Prøv igen senere.</string>
<string name="androidacy_need_captcha">Androidacy-opdatering blokeret af Captcha</string>
<string name="api_key_restart">API-nøgle er blevet ændret. Genstart appen for at anvende ændringer.</string>
@ -134,7 +131,6 @@
\nSlå dette til på eget ansvar!</string>
<string name="remove_repo">Fjern Depot</string>
<string name="androidacy_repo_info">Dette lager kan vise nogle ikke-påtrængende reklamer for at dække server- og udviklingsomkostninger.</string>
<string name="language_support_outdated">Nogle oversættelser til det aktuelle sprog er ikke opdaterede. Overvej venligst at bidrage til app-oversættelserne på GitHub</string>
<string name="api_key_summary">Brug en tilpasset API-nøgle til Androidacy. Nyttigt for premium-abonnenter, til fjernelse annoncer og mere.</string>
<string name="androidacy_test_mode_warning">Du indstiller appen til at bruge et ikke-produktionsslutpunkt til Androidacy. Dette kan resultere i app-ustabilitet og manglende indlæsning af online-depoten. Rapporter IKKE fejl, hvis du har denne funktion slået til. Appen genstartes for at genindlæse repos.</string>
</resources>

@ -22,7 +22,6 @@
<string name="showcase_mode_pref">Sperrmodus</string>
<string name="showcase_mode_desc">Sperrmodus verhindert, dass der Manager Aktionen an Modulen ausführt</string>
<string name="show_licenses">Lizenzen anzeigen</string>
<string name="licenses">Lizenzen</string>
<string name="show_incompatible_pref">Inkompatible Module anzeigen</string>
<string name="show_incompatible_desc">Module anzeigen, die aufgrund ihrer Metadaten nicht mit Ihrem Gerät kompatibel sind</string>
<string name="magisk_outdated">Magisk ist veraltet!</string>
@ -93,7 +92,6 @@
<string name="module_min_sdk_chip">Min. Android</string>
<string name="module_max_sdk_chip">Max. Android</string>
<string name="pref_category_info">Info</string>
<string name="pref_category_repos">Repos</string>
<string name="theme_pref">Thema</string>
<string name="module_id_prefix">Modul-ID:</string>
<string name="low_quality_module">Modul mit niedriger Qualität</string>
@ -106,7 +104,6 @@
<string name="notification_update_pref">Automatische Suche nach Modulaktualisierungen</string>
<string name="notification_update_desc">Könnte Batterie-Nutzung erhöhen</string>
<string name="notification_update_debug_pref">Test-Benachrichtigung</string>
<string name="language_support_outdated">Einige Übersetzungen für die aktuelle Sprache sind nicht aktuell, bitte überlege, auf GitHub beizutragen</string>
<string name="language_translated_by">Übersetzt von nift4</string>
<string name="crash_reporting">Automatisch Entwickler über Bugs informieren</string>
<string name="api_key_invalid">Konnte API-Schlüssel nicht überprüfen. Bitte auf Fehler überprüfen.</string>
@ -126,7 +123,6 @@
<string name="module_needs_ramdisk_desc">Dieses Modul braucht eine installierte Boot-Ramdisk</string>
<string name="api_key_summary">Eigenen API-Schlüssel für Androidacy verwenden. Nützlich für Androidacy-Kunden, um Werbung zu entfernen.</string>
<string name="androidacy_test_mode_warning">Androidacy Betamodus wird aktiviert. Dies könnte Fehler verursachen. Bitte keine Fehlerberichte einreichen, wenn aktiviert. Die App wird neu gestartet.</string>
<string name="androidacy_failed_to_parse_token">Konnte Token nicht von Androidacy laden. Bitte versuche es später noch einmal.</string>
<string name="androidacy_failed_to_validate_token">Konnte Token für Androidacy nicht validieren. Bitte versuche es später noch einmal.</string>
<string name="androidacy_need_captcha">Androidacy-Update von CAPTCHA blockiert</string>
<string name="link_copied">Link kopiert</string>
@ -218,7 +214,6 @@
<string name="upgrade_androidacy_promo">Upgrade auf Premium</string>
<string name="share_logs">Teile Logs</string>
<string name="not_official_build">Diese App ist ein inoffizieller AMM-Build.</string>
<string name="feedback_submit">Absenden und neu starten</string>
<string name="please_feedback">Bitte hilf uns aus, indem du uns erzählst, was du machen wolltest, bevor dies passiert ist.</string>
<string name="sentry_dialogue_disabled">Fehler-Upload deaktiviert. Aktivieren, um Feedback hochzuladen.</string>
<string name="feedback_name">Name (optional)</string>
@ -260,5 +255,4 @@
<string name="update_message">Bitte warten, während AMM-Updates installiert werden. Dies kann ein paar Minuten dauern</string>
<string name="app_name_v2">Androidacy Modul-Manager</string>
<string name="app_name_short_v2">AMM</string>
<string name="update_button">Bitte warten…</string>
</resources>

@ -58,11 +58,9 @@
<string name="enable_monet">Ενεργοποίηση Monet</string>
<string name="pref_category_info">Πληροφορίες</string>
<string name="show_licenses">Εμφάνιση αδειών</string>
<string name="licenses">Άδειες</string>
<string name="show_incompatible_pref">Εμφάνιση ασυμβίβαστων module</string>
<string name="show_incompatible_desc">Εμφάνιση module που είναι απίθανο να εγκατασταθούν στη συσκευή σας με βάση τα μεταδεδομένα τους</string>
<string name="magisk_outdated">Υπάρχει μια νέα έκδοση του Magisk για εγκατάσταση!</string>
<string name="pref_category_repos">Αποθετήρια</string>
<string name="pref_category_security">Ασφάλεια</string>
<string name="pref_category_appearance">Εμφάνιση</string>
<string name="master_delete">Διαγραφή των αρχείων module;</string>
@ -114,8 +112,6 @@
<string name="notification_update_debug_pref">Ειδοποίηση δοκιμής</string>
<!-- Set to true in translation file if your language is right to left -->
<bool name="lang_support_rtl">false</bool>
<integer name="language_support_level">1</integer>
<string name="language_support_outdated">Ορισμένες μεταφράσεις για την τρέχουσα γλώσσα δεν είναι ενημερωμένες, παρακαλούμε να συνεισφέρετε στις μεταφράσεις εφαρμογών στο GitHub</string>
<!-- Replace with your own username when translating -->
<string name="language_translated_by">Μεταφράστηκε από INvxrteD, Tha_14</string>
<string name="report_bugs">Αναφορά σφαλμάτων</string>
@ -168,7 +164,6 @@
<string name="error_saving_logs">Δεν ήταν δυνατή η αποθήκευση των logs</string>
<string name="share_logs">Κοινοποίηση των logs του AMM</string>
<string name="not_official_build">Αυτή η εφαρμογή είναι ανεπίσημη κατασκευή του AMM.</string>
<string name="androidacy_failed_to_parse_token">Το token δεν ανακτήθηκε από το Androidacy. Παρακαλούμε προσπαθήστε ξανά αργότερα.</string>
<string name="androidacy_failed_to_validate_token">Το token δεν επικυρώθηκε από το Androidacy. Παρακαλούμε προσπαθήστε ξανά αργότερα.</string>
<string name="androidacy_need_captcha">Ο εκσυγχρονισμός του Androidacy, μπλοκαρίστηκε από το Captcha</string>
<string name="crash_reporting">Αυτοματοποίηση αναφοράς σφαλμάτων και ποιότητας εκτέλεσης στους προγραμματιστές</string>
@ -178,7 +173,6 @@
<string name="please_wait">Παρακαλώ περιμένετε</string>
<string name="api_key_removed">Επιτυχής επαναφορά του κλειδιού API</string>
<string name="androidacy_test_mode_disable_warning">Η εφαρμογή θα επανεκκινηθεί για να απενεργοποιηθεί το endpoint σταδιοποίησης</string>
<string name="modules">Modules</string>
<string name="no_browser">Δεν έχεις κανέναν φυλλομετρητή εγκατεστημένο. Παρακαλούμε εγκατέστησε έναν για να συνεχίσεις.</string>
<string name="clear_app_data">Καθαρισμός δεδομένων της εφαρμογής</string>
<string name="clear_data_dialogue_title">Θα θέλατε να καθαρίσετε όλα τα δεδομένα που συνοδεύουν την εφαρμογή;</string>
@ -216,7 +210,6 @@
<string name="upgrade_androidacy_promo_desc">Αναβαθμίζοντας σε premium θα αφαιρεθούν, οι διαφημίσεις, οι επαληθεύσεις ταυτότητας, και οι λήψεις για το αποθετήριο Androidacy, ενώ θα υποστηρίξετε τους δημιουργούς του Androidacy και των modules.</string>
<string name="crash_text">Ωχ όχι, βρεθήκαμε σε εμπόδιο!</string>
<string name="feedback_message">Δώστε μας περισσότερες λεπτομέρειες για το τι ακριβώς κάνατε όταν συνέβη αυτό. Όσο πιο πολλές τόσο το καλύτερο!</string>
<string name="feedback_submit">Υποβολή και επανεκκίνηση</string>
<string name="please_feedback">Παρακαλείσθε να μας βοηθήσετε αναφέροντας τι προσπαθούσατε να κάνετε όταν συνέβη αυτό.</string>
<string name="feedback_name">Όνομα (προαιρετικό)</string>
<string name="feedback_email">Email (προαιρετικό)</string>
@ -247,7 +240,6 @@
<string name="permission_notification_message">Χρειαζόμαστε την άδεια για ειδοποιήσεις, ώστε να σε ειδοποιούμε για κάθε δυνατό εκσυγχρονισμό της εφαρμογής και των modules, δεν θα λειτουργήσει ο αυτόματος έλεγχος για αναβαθμίσεις.</string>
<string name="setup_background_update_check">Αυτοματοποιημένος έλεγχος εκσυγχρονισμού</string>
<string name="background_update_check_excludes">Modules αποκλεισμένα από αυτόματο έλεγχο αναβάθμισης</string>
<string name="update_button">Παρακαλώ περιμένετε…</string>
<string name="invalid_repo_url">Το URL που εισήγαγες για το αποθετήριο δεν είναι έγκυρο</string>
<string name="add_repo_message">Τα αποθετήρια πρέπει να εξυπηρετούν μέσω HTTPS, και πρέπει να ακολουθούν τις προδιαγραφές που περιγράφονται στην τεκμηρίωση.</string>
<string name="notification_update_summary">Τα ακόλουθα modules μπορούν να ενημερωθούν:</string>
@ -325,7 +317,6 @@
<string name="error_download_update">Προέκυψε σφάλμα λαμβάνοντας τις πληροφορίες ενημέρωσης.</string>
<string name="update_debug_download_pref">Δοκιμή μηχανισμού λήψης ενημερώσεων</string>
<string name="notification_update_wifi_pref">Απαίτηση wi-fi ή δικτύου χωρίς μετρήσεις για έλεγχο ενημερώσεων. Προτείνεται να είναι σε λειτουργία αν έχετε περιορισμένα δεδομένα κινητής τηλεφωνίας.</string>
<string name="yer_a_wizard_harry">Είσαι μάγος, Χάρι!</string>
<string name="keep_tapping_to_enter_hogwarts">Συνέχισε να πατάς, ώστε να γίνεις δεκτός στο Hogwarts!</string>
<string name="language_not_available">Η γλώσσα %s δεν έχει μεταφραστεί. Μπορείτε να μας βοηθήσετε στην μετάφρασή της;</string>
<string name="analytics_desc">Επιτρέψτε μας να παρακολουθούμε τη χρήση και τις εγκαταστάσεις εφαρμογών. Πλήρως συμβατό με το GDPR και χρησιμοποιεί το Matomo, που φιλοξενείται από το Androidacy.</string>

@ -60,11 +60,9 @@
<string name="enable_monet">Habilitar Monet</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Mostrar licencias</string>
<string name="licenses">Licencias</string>
<string name="show_incompatible_pref">Mostrar módulos incompatibles</string>
<string name="show_incompatible_desc">Muestra los módulos que son incompatibles con su dispositivo basándose en sus metadatos</string>
<string name="magisk_outdated">¡Magisk está desactualizado!</string>
<string name="pref_category_repos">Repositorios</string>
<string name="pref_category_security">Seguridad</string>
<string name="pref_category_appearance">Apariencia</string>
<string name="master_delete">¿Desea borrar los archivos del módulo\?</string>
@ -115,8 +113,6 @@
<!-- Set to true in translation file if your language is right to left -->
<bool name="lang_support_rtl">false</bool>
<!-- Always copy language_support_level when translating -->
<integer name="language_support_level">1</integer>
<string name="language_support_outdated">Algunas traducciones del lenguaje seleccionado no están actualizadas, por favor considera contribuir con las traducciones de la aplicación en GitHub</string>
<!-- Replace with your own username when translating // Staging y non-production endpoint = canal de prueba, release endpoint = Canal estable (quizas se cambie la traducción en futuras actualizaciones) -->
<string name="language_translated_by">Traducido por Leshu7w7, y por Sebastián</string>
<string name="crash_reporting">Informa automáticamente de errores y rendimiento a los desarrolladores</string>
@ -194,9 +190,7 @@
<string name="announcements">Noticias y actualizaciones</string>
<string name="back">Regresa</string>
<string name="notification_update_ignore_pref">Excluir módulos</string>
<string name="update_button">Por favor espere…</string>
<string name="safe">Seguro</string>
<string name="modules">Módulos</string>
<string name="monet_disabled_summary">Monet no es compatible con temas transparentes.</string>
<string name="setup_background_update_check">Comprobación automática de actualizaciones</string>
<string name="androidacy_update_needed">Esta aplicación esta desactualizada.</string>
@ -233,7 +227,6 @@
<string name="repo_enabled_changed">Ha activado o desactivado un repositorio. Por favor actualice la lista de módulos o reinicie la aplicación.</string>
<string name="check_for_updates">Buscar actualizaciones de aplicaciones</string>
<string name="error_download_update">Ocurrió un error al descargar la información de actualización.</string>
<string name="feedback_submit">Enviar y reiniciar</string>
<string name="sentry_dialogue_empty_message">No especificó comentarios adicionales.</string>
<string name="crash_details_copied">¡Stacktrace copiado al portapapeles!</string>
<string name="upgraded">Premium activo</string>
@ -279,9 +272,7 @@
<string name="pref_pkg_info_summary">%1$s v%2$s (%3$d) | %4$s Compilación</string>
<string name="api_key_restart">La clave API ha sido cambiada. Reinicie la aplicación para aplicar los cambios.</string>
<string name="permission_notification_message">Necesitamos el permiso de notificaciones para notificarle sobre actualizaciones de aplicaciones y módulos. Si no concede este permiso, no se ejecutarán las comprobaciones de las actualizaciones automáticas.</string>
<string name="androidacy_failed_to_parse_token">No se pudo recuperar el token de Androidacy. Por favor, inténtelo de nuevo más tarde.</string>
<string name="androidacy_failed_to_validate_token">No se pudo validar el token para Androidacy. Por favor, inténtelo de nuevo más tarde.</string>
<string name="yer_a_wizard_harry">¡Eres un mago, Harry!</string>
<string name="keep_tapping_to_enter_hogwarts">¡Sigue pulsando para ser admitido en Hogwarts!</string>
<string name="setup_androidacy_repo_summary">Presenta reseñas de usuarios, escaneos automáticos de virus, actualizaciones rápidas, una amplia selección y está respaldado por Androidacy.</string>
<string name="setup_magisk_alt_repo_summary">Mucho más laxa que la original. Tiene muchos módulos a costa de cierta seguridad.</string>
@ -357,16 +348,11 @@
<string name="blur_performance_warning_summary">El dispositivo no es compatible con el desenfoque</string>
<string name="search">Buscar</string>
<string name="reboot">Reiniciar</string>
<string name="reboot_system">Reiniciar normalmente</string>
<string name="reboot_recovery">Reiniciar a la recuperación</string>
<string name="reboot_bootloader">Reiniciar al gestor de arranque</string>
<string name="reboot_edl">Reiniciar al modo EDL</string>
<string name="install_terminal_reboot_prevented">El reinicio está desactivado en la configuración de la aplicación</string>
<string name="androidacy_api_error">Error al comunicarse con la API: %d</string>
<string name="title_activity_expired">Actividad expirada</string>
<string name="title_home">Home</string>
<string name="title_dashboard">Panel de control</string>
<string name="title_notifications">Notificaciones</string>
<string name="expired">¡Esta compilación ha expirado!</string>
<string name="expired_message">La compilación que estás utilizando ha expirado y ya no se ejecutará. Actualiza a la última versión estable.</string>
<string name="download_latest">Descargar lo último</string>

@ -52,11 +52,9 @@
<string name="enable_monet">Activar Monet</string>
<string name="pref_category_info">Información</string>
<string name="show_licenses">Mostrar licencias</string>
<string name="licenses">Licencias</string>
<string name="show_incompatible_pref">Mostrar módulos incompatibles</string>
<string name="show_incompatible_desc">Mostrar los módulos que probablemente no funcionen en su dispositivo por función de sus meta datos</string>
<string name="magisk_outdated">¡Hay una nueva versión de Magisk para instalar!</string>
<string name="pref_category_repos">Repositorios</string>
<string name="pref_category_security">Seguridad</string>
<string name="pref_category_appearance">Apariencia</string>
<string name="master_delete_no">Dejar</string>
@ -106,7 +104,6 @@
<string name="crash_reporting_restart_message">La aplicación debe reiniciarse para aplicar esta configuración</string>
<string name="restart">Reiniciar</string>
<string name="androidacy_test_mode_disable_warning">La aplicación se reiniciará para desactivar el punto final de prueba</string>
<string name="androidacy_failed_to_parse_token">No se ha podido recuperar el token de Androidacy. Por favor, inténtelo de nuevo más tarde.</string>
<string name="androidacy_need_captcha">Actualización de Androidacy bloqueada por Captcha</string>
<string name="require_android_12">Requiere Android 12+</string>
<string name="master_delete">¿Borrar los archivos del módulo\?</string>
@ -118,7 +115,6 @@
<string name="use_magisk_install_command_desc">Durante las pruebas causó problemas a la herramienta de diagnóstico de errores de instalación del módulo, por lo que esta opción se oculta tras el modo de desarrollador.
\n¡Active esta opción bajo su propio riesgo!</string>
<string name="androidacy_test_mode_pref">Modo de prueba de Androidacy</string>
<string name="language_support_outdated">Algunas traducciones para el idioma actual no están actualizadas, por favor considere contribuir a las traducciones de la aplicación en GitHub. Español 100%</string>
<string name="crash_reporting_desc">Si desactiva esto, el desarrollador no recibirá informes automáticos de errores, lo que podría dificultar la resolución de problemas</string>
<string name="api_key_removed">La clave API se ha restablecido correctamente</string>
<string name="androidacy_test_mode_warning">Está configurando la aplicación para que utilice un punto final que no es de producción para Androidacy. Esto puede dar lugar a la inestabilidad de la aplicación y a la imposibilidad de cargar el repositorio en línea. NO informe de errores si tiene este interruptor activado. La aplicación se reiniciará para recargar los repos.</string>
@ -215,7 +211,6 @@
<string name="action_settings">Ajustes</string>
<string name="setup_crash_reporting">Habilitar informe de errores</string>
<string name="feedback_message">Danos más detalles acerca de lo que estabas haciendo cuando esto ocurrió. ¡Cuánto más, mejor!</string>
<string name="feedback_submit">Enviar y reiniciar</string>
<string name="please_feedback">Por favor, ayúdanos diciéndonos lo que estabas intentando hacer cuando esto ocurrió.</string>
<string name="sentry_dialogue_disabled">El envío de errores está desactivado. Actívalo para enviar datos.</string>
<string name="feedback_name">Nombre (Opcional)</string>
@ -241,10 +236,8 @@
<string name="update_debug_warning">Parece que estás ejecutando una versión de depuración. Las versiones de depuración deben de ser actualizadas manualmente, y no se pueden actualizar en la app</string>
<string name="background_update_check_excludes">Módulos que excluir de comprobaciones de actualizaciones automáticas</string>
<string name="update_title">Actualizar aplicación</string>
<string name="update_button">Por favor espere…</string>
<string name="update_message">Por favor espere mientras comprobamos e instalamos actualizaciones a AMM. Esto puede tardar algunos minutos</string>
<string name="source_code_summary"><b>Compromiso</b> %1$s @ %2$s</string>
<string name="modules">Módulos</string>
<string name="error_no_action">ERROR: Acción inválida especificada. Imposible continuar.</string>
<string name="checking_for_update">Comprobando actualizaciones…</string>
<string name="no_update_available">Todo actualizado!</string>
@ -327,7 +320,6 @@
<string name="error_encrypted_shared_preferences">Ha ocurrido un error leyendo las preferencias compartidas. Por favor reincia la aplicación.</string>
<string name="notification_update_wifi_pref">Se necesita wi-fi o una red sin medición para comprobar actualizaciones. Se recomienda dejar activado si tienes datos móviles ilimitados.</string>
<string name="notification_channel_category_app_update_description">Notifica cuando hay una actualización disponible</string>
<string name="yer_a_wizard_harry">Eres un mago, Harry!</string>
<string name="keep_tapping_to_enter_hogwarts">Sigue tocando para ser admitido en Hogwarts!</string>
<string name="no_browser">No tienes un navegador instalado. Por favor instalar uno para continuar.</string>
<string name="crash_details_suggestion">El seguimiento de pila se puede encontrar a continuación. Sin embargo, <b>recomendamos mucho</b> que utilices el formulario de comentarios que se encuentra a continuación para enviar tus comentarios. De esta manera, en lugar de copiar manualmente el seguimiento de pila, se enviará automáticamente. Además, de esta manera se desofuscará y se informarán automáticamente detalles adicionales.</string>

@ -35,11 +35,9 @@
<string name="showcase_mode_desc">Lukustusrežiim väldib halduris moodulite haldamist</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Kuva litsentsid</string>
<string name="licenses">Litsentsid</string>
<string name="show_incompatible_pref">Kuva ühildumatud moodulid</string>
<string name="show_incompatible_desc">Kuva moodulid, mis ei ühildu metaandmete alusel sinu seadmega</string>
<string name="magisk_outdated">Magisk on aegunud!</string>
<string name="pref_category_repos">Hoidlad</string>
<string name="master_delete">Kustuta mooduli failid?</string>
<string name="master_delete_no">Säilita failid</string>
<string name="master_delete_yes">Kustuta failid</string>

@ -34,9 +34,7 @@
<string name="prevent_reboot_pref">مانع از راه اندازی مجدد</string>
<string name="enable_monet">فعال کردن سیاهی</string>
<string name="pref_category_info">درباره</string>
<string name="licenses">مجوزها</string>
<string name="show_incompatible_desc">نمایش ماژول‌هایی که بعید است بر اساس فراداده‌هایشان در دستگاه شما کار کنند</string>
<string name="pref_category_repos">مخزن ها</string>
<string name="pref_category_security">امنیت</string>
<string name="pref_category_appearance">ظاهر</string>
<string name="master_delete">فایل های ماژول حذف شود؟</string>

@ -63,11 +63,9 @@
<string name="enable_monet">Activer Monet</string>
<string name="pref_category_info">Infos</string>
<string name="show_licenses">Afficher les licenses</string>
<string name="licenses">Licences</string>
<string name="show_incompatible_pref">Afficher les modules incompatibles</string>
<string name="show_incompatible_desc">Afficher les modules qui sont incompatibles avec votre périphérique basé sur leurs métadonnées</string>
<string name="magisk_outdated">Il y a une nouvelle version de Magisk à installer!</string>
<string name="pref_category_repos">Dépôts</string>
<string name="pref_category_security">Sécurité</string>
<string name="pref_category_appearance">Apparance</string>
<string name="master_delete">Supprimer les fichiers du modules\?</string>
@ -129,9 +127,6 @@
<!-- Set to true in translation file if your language is right to left -->
<bool name="lang_support_rtl">false</bool>
<!-- Always copy language_support_level when translating -->
<integer name="language_support_level">1</integer>
<string name="language_support_outdated">Certaines traductions pour la langue actuelle ne sont
pas à jour, merci de considérer une contribution aux traductions sur Github</string>
<!-- Replace with your own username when translating -->
<string name="language_translated_by">Traduit par xerta555</string>
<string name="crash_reporting">Soumettre automatiquement les bugs et performances aux développeurs</string>
@ -151,7 +146,6 @@
<string name="crash_reporting_restart_message">L\application nécessite un redémarrage pour appliquer les réglages</string>
<string name="restart">Redémarrer</string>
<string name="androidacy_test_mode_disable_warning">L\application sera redémarrée pour dépasser le point de passage de mise à disposition.</string>
<string name="androidacy_failed_to_parse_token">La clé n\a pas pu être retrouvée sur Androidacy. Merci de retenter ultérieurement.</string>
<string name="androidacy_failed_to_validate_token">La clé n\a pas pu être validée chez Androidacy. Merci de retenter ultérieurement.</string>
<string name="androidacy_need_captcha">Mise à jour d\Androidacy bloquée par Captcha</string>
<string name="api_key_restart">La clé d\API à été changée. Redémarrer l\application pour appliquer les changements.</string>
@ -244,7 +238,6 @@
<string name="not_official_build">Cette application n\est pas une fabrication officielle AMM.</string>
<string name="crash_text">Uh-oh, on a un problème!</string>
<string name="feedback_message">Donnez nous plus de détails à propos de ce qui s\est passé quand c\est arrivé. Plus on est foue, plus on rit!</string>
<string name="feedback_submit">Envoyer et redémarrer</string>
<string name="please_feedback">Merci de nous aider en nous disant qu\est ce que vous avez essayé de faire quand c\est arrivé.</string>
<string name="sentry_dialogue_disabled">Le rapport de plantage est désactivé. Activez le pour envoyer le rapport.</string>
<string name="feedback_name">Nom (optionel)</string>
@ -270,7 +263,6 @@
<string name="background_update_check_excludes">Modules à exclure des vérifications de MàJ automatiques</string>
<string name="update_title">MàJ l\application</string>
<string name="update_message">Merci de patienter pendant que nous vérifions et installons les MàJ pour AMM. Celà peut prendre quelques minutes</string>
<string name="update_button">Merci de patienter…</string>
<string name="error_no_extras">ERREURE: Donnée invalides reçues au lancement</string>
<string name="update_debug_warning">Il semblerait que vous soyez en train d\utiliser une fabrication de débuggage. Les fabrications de débuggage doivent être MàJ manuellement, et les MàJ incorporées ne sont pas supportées</string>
<string name="error_no_action">ERREURE: Action spécifiée invalide. Refus de continuer.</string>
@ -308,9 +300,7 @@
<string name="notification_channel_category_app_update_description">ÊTre notifié lorsce qu\une MàJ de l\application est trouvée</string>
<string name="notification_channel_background_update_app">MàJ de l\application disponible!</string>
<string name="notification_channel_background_update_app_description">Une MàJ est disponible pour AMM. Appuyer ici pour MàJ.</string>
<string name="yer_a_wizard_harry">Vous êtes un magicien, Harry!</string>
<string name="keep_tapping_to_enter_hogwarts">Continuez à toquer pour être admis à Poudlard !</string>
<string name="modules">Modules</string>
<string name="no_browser">Vous n\avez pas de navigateur installé. Merci d\en installer un pour continuer.</string>
<string name="setup_update_check_headline">Vérification de MàJ</string>
<string name="setup_background_update_check_require_wifi">Requière le wifi pour les vérifications de MàJ</string>

@ -41,9 +41,7 @@
<string name="enable_monet">मोनेट सक्षम करें</string>
<string name="pref_category_info">जानकारी</string>
<string name="show_licenses">लाइसेंस दिखाएँ</string>
<string name="licenses">लाइसेंसेज</string>
<string name="magisk_outdated">मैजिस्क का एक नया संस्करण इंस्टॉल करने के लिए उपलब्ध है!</string>
<string name="pref_category_repos">रिपोज़</string>
<string name="pref_category_security">सुरक्षा</string>
<string name="app_name">फॉक्स का मैजिस्क मॉड्यूल प्रबंधक</string>
<string name="fail_root_magisk">न तो रूट तक पहुंच सका, न मैजिस्क तक पहुंच सका</string>

@ -19,7 +19,6 @@
<string name="app_name">Fox\'s Magisk Module Manager</string>
<string name="fail_root_magisk">Nem található root vagy Magisk hozzáférés</string>
<string name="failed_download">Nem sikerült letölteni a fájlt</string>
<string name="licenses">Licencek</string>
<string name="magisk_outdated">Elérhető egy újabb Magisk verzió!</string>
<string name="master_delete_fail">Nem sikerült törölni a modulfájlokat</string>
<string name="magisk_builtin_module">Magisk beépített modul</string>
@ -27,7 +26,6 @@
<string name="disable_low_quality_module_filter_pref">Alacsony minőségű modulok mutatása</string>
<string name="disable_low_quality_module_filter_desc">Vannak modulok, amelyek metaadata nem megfelelő, amely vizuális hibákhoz vezet és/vagy a modul gyenge minőségét okozza.
\nCsak saját felelősségedre kapcsold be!</string>
<string name="language_support_outdated">Néhány fordítás a jelenlegi nyelvnél nem naprakész. Fontold meg a fordításban való közreműködést GitHub-on</string>
<string name="crash_reporting_desc">Ha letiltod, akkor a fejlesztő nem kapja meg a hibajelzéseket, ami megnehezíti a hibák javítását</string>
<string name="permission_notification_title">Engedélyezed az értesítéseket\?</string>
<string name="permission_notification_message">Hozzáférés szükséges az értesítésekhez a frissítési értesítések megjelenítéséhez. Ha nem adsz engedélyt, akkor a frissítések keresése nem fog működni a háttérben.</string>
@ -70,7 +68,6 @@
<string name="showcase_mode_desc">Zárolt üzemmódban az alkalmazás nem tud műveletet végrehajtani a modulokkal</string>
<string name="showcase_mode">Az alkalmazás zárolt üzemmódban van</string>
<string name="show_incompatible_pref">Inkompatibilis modulok megjelenítése</string>
<string name="pref_category_repos">Repok</string>
<string name="pref_category_security">Biztonság</string>
<string name="pref_category_appearance">Kinézet</string>
<string name="master_delete">Törlöd a modulfájlokat\?</string>
@ -178,7 +175,6 @@
<string name="api_key_summary">Egyedi Androidacy API kulcs használata. Hasznos a prémium előfizetőknek például a reklámos elrejtéséhez.</string>
<string name="androidacy_test_mode_warning">Az alkalmazást úgy állítja be, hogy egy nem-produktív végpontot használjon az Androidacy számára. Ez az alkalmazás instabilitásához és az online repo betöltésének sikertelenségéhez vezethet. Ne jelentsd a hibákat, ha bekapcsolod ez a funkciót. Az alkalmazás újra fog indulni a repók újratöltéséhez.</string>
<string name="androidacy_test_mode_disable_warning">Az alkalmazás újra fog indulni a staging végpont letiltásához</string>
<string name="androidacy_failed_to_parse_token">Nem sikerült a token beszerzése az Androidacy-tól. Kérlek próbáld később.</string>
<string name="androidacy_failed_to_validate_token">Nem sikerült a token validálása az Androidacy-hoz. Kérlek, próbáld újra.</string>
<string name="androidacy_need_captcha">Az Androidacy frissítést a Captcha blokkolta</string>
<string name="api_key_invalid">Nem sikerült érvényesíteni az API-kulcsot. Kérjük, ellenőrizd és próbálja újra.</string>

@ -60,11 +60,9 @@
<string name="enable_monet">Nyalakan Monet</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Tampilkan lisensi</string>
<string name="licenses">Lisensi</string>
<string name="show_incompatible_pref">Tampilkan modul tidak kompatibel</string>
<string name="show_incompatible_desc">Tampilkan modul yang mungkin tidak cocok dengan perangkat Anda berdasarkan metadata developer</string>
<string name="magisk_outdated">Versi baru Magisk dapat dipasang!</string>
<string name="pref_category_repos">Repo</string>
<string name="pref_category_security">Keamanan</string>
<string name="pref_category_appearance">Penampilan</string>
<string name="master_delete">Hapus berkas modul ini\?</string>
@ -117,9 +115,6 @@
<!-- Set to true in translation file if your language is right to left -->
<bool name="lang_support_rtl">false</bool>
<!-- Always copy language_support_level when translating -->
<integer name="language_support_level">1</integer>
<string name="language_support_outdated">Beberapa translasi untuk bahasa ini sekarang
kurang diperbarui, mohon pertimbangkan untuk melakukan kontribusi ke translasi aplikasi di GitHub</string>
<!-- Replace with your own username when translating -->
<string name="language_translated_by">Diterjemahkan dengan WebLate</string>
<string name="crash_reporting">Secara otomatis melaporkan bug dan kinerja kepada pengembang</string>
@ -139,7 +134,6 @@
<string name="crash_reporting_restart_message">Aplikasi butuh diluncurkan ulang untuk menerapkan setelan</string>
<string name="restart">Luncurkan ulang</string>
<string name="androidacy_test_mode_disable_warning">Aplikasi akan diluncurkan ulang untuk mematikan endpoint staging</string>
<string name="androidacy_failed_to_parse_token">Tidak dapat mendapatkan token dari Androidacy. Mohon dicoba lagi nanti.</string>
<string name="androidacy_failed_to_validate_token">Tidak dapat melakukan validasi terhadap token dari Androidacy. Mohon dicoba lagi nanti.</string>
<string name="androidacy_need_captcha">Pembaruan Androidacy terblokir oleh Captcha</string>
<string name="api_key_restart">Kunci API telah diganti. Luncurkan ulang aplikasi untuk menerapkan perubahan.</string>
@ -233,7 +227,6 @@
<string name="not_official_build">Aplikasi ini adalah build AMM yang tidak resmi.</string>
<string name="crash_text">Uh-oh, kita terkena sebuah halangan!</string>
<string name="feedback_message">Beritahu kita detail lebih lanjut dalam apa yang sedang Anda lakukan. Semakin banyak, semakin bagus!</string>
<string name="feedback_submit">Kirim dan luncurkan ulang</string>
<string name="please_feedback">Tolong bantu kami dengan memberitahu apa yang sedang Anda lakukan saat ini terjadi.</string>
<string name="sentry_dialogue_disabled">Laporan kesalahan dimatikan. Aktifkan untuk mengirim masukan.</string>
<string name="feedback_name">Nama (opsional)</string>
@ -259,7 +252,6 @@
<string name="background_update_check_excludes">Modul-modul dikecualikan dari cek pembaruan otomatis</string>
<string name="update_title">Perbarui aplikasi</string>
<string name="update_message">Harap menunggu sementara kami mengcek pembaruan dan memasang pembaruan AMM. Ini mungkin akan membutuhkan beberapa menit</string>
<string name="update_button">Mohon menunggu…</string>
<string name="error_no_extras">KESALAHAN: Data tidak valid diterima saat meluncur</string>
<string name="update_debug_warning">Anda sepertinya menjalankan sebuah build debug. Build debug harus diperbarui secara manual, dan tidak mendukung pembaruan dalam aplikasi</string>
<string name="error_no_action">KESALAHAN: Tindakan yang ditentukan tidak valid. Menolak dan tidak akan melanjutkan.</string>
@ -297,9 +289,7 @@
<string name="notification_channel_category_app_update_description">Beritahu jika ada pembaruan aplikasi</string>
<string name="notification_channel_background_update_app">Ada pembaruan aplikasi!</string>
<string name="notification_channel_background_update_app_description">Ada sebuah pembaruan untuk AMM. Ketuk disini untuk memperbarui.</string>
<string name="yer_a_wizard_harry">Kamu adalah penyihir, Harry!</string>
<string name="keep_tapping_to_enter_hogwarts">Ketuk terus untuk masuk ke dalam Hogwarts!</string>
<string name="modules">Modul</string>
<string name="no_browser">Anda tidak punya browser yang terpasang di perangkat Anda. Mohon memasang satu sebelum melanjutkan.</string>
<string name="setup_update_check_headline">Periksa pembaruan</string>
<string name="setup_background_update_check_require_wifi">Membutuhkan Wi-Fi untuk memeriksa pembaruan</string>
@ -349,13 +339,9 @@
<string name="upgrade_now">Tingkatkan</string>
<string name="app_name_v2">Manajer Modul Androidacy</string>
<string name="app_name_short_v2">AMM</string>
<string name="title_notifications">Notifikasi</string>
<string name="download_latest">Unduh versi terbaru</string>
<string name="expired">Versi Manager telah kedaluwarsa!</string>
<string name="expired_message">Versi yang Anda gunakan telah kedaluwarsa dan tidak dapat digunakan lagi. Mohon untuk segera perbarui ke versi stabil terbaru.</string>
<string name="title_home">Beranda</string>
<string name="title_dashboard">Pedoman</string>
<string name="title_activity_expired">Aktivitas telah Kedaluwarsa</string>
<string name="reinstall_warning">Sebagian besar modul harus dicopot pemasangannya, kemudian dipasang kembali dengan bersih. Karena alasan ini, AMM tidak mendukung pemasangan ulang modul saat modul tersebut terpasang pada perangkat.</string>
<string name="logs_saved">Log tersimpan</string>
<string name="error_opening_notes">Gagal membuka catatan modul. Mungkin log tahu alasannya.</string>
@ -369,7 +355,6 @@
<string name="debug_build_toast">AMM versi debug %1$s dibuat dari %2$s dengan sisa waktu %3$d hari</string>
<string name="remote_message">%s tersedia di repo online. <b>Kami sangat merekomendasikan Anda untuk memasang ulang modul secara bersih karena sebagian besar modul tidak menangani pemasangan ulang dengan baik,</b> tetapi Anda masih dapat memasang secara langsung dengan risiko ditanggung Anda.</string>
<string name="reboot_extra_warning">Anda akan memulai ulang ke mode EDL, mode khusus yang ditujukan untuk OEM untuk mem-flash dan memperbaiki masalah tingkat rendah. Kecuali jika Anda yakin ingin melakukan hal ini dan memiliki alat yang diperlukan untuk keluar dari mode ini atau menggunakannya, kami sangat menyarankan Anda untuk menggunakan opsi mulai ulang lainnya.</string>
<string name="reboot_system">Mulai ulang sistem</string>
<string name="no_sentry_id">Tidak dapat mengirim masukan - tidak ada ID event dari Sentry. Event terakhir mungkin tidak terkirim.</string>
<string name="build_expired">Versi ini telah kedaluwarsa. Mohon perbarui ke versi terbaru.</string>
<string name="eula_agree_v2_headline">EULA dan ketentuan</string>

@ -59,11 +59,9 @@
<string name="enable_monet">Abilita Monet</string>
<string name="pref_category_info">Informazioni</string>
<string name="show_licenses">Mostra licenze</string>
<string name="licenses">Licenze</string>
<string name="show_incompatible_pref">Mostra moduli incompatibili</string>
<string name="show_incompatible_desc">Mostra i moduli che in base ai metadati sono incompatibili con il tuo dispositivo</string>
<string name="magisk_outdated">Nuova versione di Magisk da installare!</string>
<string name="pref_category_repos">Repository</string>
<string name="pref_category_security">Sicurezza</string>
<string name="pref_category_appearance">Aspetto</string>
<string name="master_delete">Rimuovere i dati del modulo?</string>
@ -112,8 +110,6 @@
<!-- Imposta su true nel file di traduzione se la tua lingua è da destra a sinistra -->
<bool name="lang_support_rtl">false</bool>
<!-- Copia sempre language_support_level durante la traduzione -->
<integer name="language_support_level">1</integer>
<string name="language_support_outdated">Alcune traduzioni per la lingua corrente non sono aggiornate, ti preghiamo di considerare di contribuire alle traduzioni dell\'app su GitHub</string>
<!-- Sostituisci con il tuo nome utente durante la traduzione -->
<string name="language_translated_by">Traduzione di CRANKV2</string>
<string name="crash_reporting">Segnala automaticamente problemi e prestazioni agli sviluppatori</string>
@ -129,7 +125,6 @@
<string name="androidacy_test_mode_warning">Stai impostando l\'app in modo che utilizzi un endpoint non di produzione per Androidacy. Ciò potrebbe comportare l\'instabilità dell\'app e il mancato caricamento del repository online. NON segnalare bug se hai attivato questo interruttore. L\'app verrà riavviata per ricaricare i repository.</string>
<string name="api_key_restart">La chiave API è stata modificata. Riavvia l\'app per applicare le modifiche.</string>
<string name="permission_notification_message">Abbiamo bisogno dell\'autorizzazione per le notifiche per informarti sugli aggiornamenti di app e moduli. Se non concedi questa autorizzazione, i controlli automatici degli aggiornamenti non verranno eseguiti.</string>
<string name="androidacy_failed_to_parse_token">Impossibile recuperare il token da Androidacy. Per favore riprova più tardi.</string>
<string name="androidacy_failed_to_validate_token">Impossibile convalidare il token per Androidacy. Per favore riprova più tardi.</string>
<string name="androidacy_need_captcha">Aggiornamento Androidacy bloccato da Captcha</string>
<string name="link_copied">Collegamento copiato</string>
@ -210,7 +205,6 @@
<string name="zip_load_failed">Impossibile caricare il file zip</string>
<string name="source_code_summary"><b>Commit</b> %1$s @ %2$s</string>
<string name="no_file_provided">Nessun file fornito mentre si prova ad aprire il file zip.</string>
<string name="update_button">Perfavore aspetta…</string>
<string name="pref_category_contributors">Collaboratori</string>
<string name="fox2code_thanks_desc">Fox2Code è il creatore originale dell\'app. Senza di lui, questo non sarebbe mai stato possibile.</string>
<string name="fox2code_thanks">Creato da Fox2Code</string>
@ -223,7 +217,6 @@
<string name="reset_api_key">Reimposta le chiavi API</string>
<string name="upgrade_androidacy_promo">Passa a premium</string>
<string name="crash_text">Abbiamo avuto un problema!</string>
<string name="feedback_submit">Invia e riavvia</string>
<string name="please_feedback">Perfavore aiutaci dicendoci cosa stavi facendo quando è accaduto.</string>
<string name="sentry_dialogue_disabled">La segnalazione degli arresti anomali è disattivata. Abilitala per inviarci dei feedback.</string>
<string name="feedback_name">Nome (opzionale)</string>
@ -273,9 +266,7 @@
<string name="background_update_check_excludes">Moduli da escludere dal controllo automatica degli aggiornamenti</string>
<string name="update_message">Si prega di attendere mentre controlliamo e installiamo gli aggiornamenti di AMM. L\'operazione potrebbe richiedere alcuni minuti</string>
<string name="checking_for_update">Cercando aggiornamenti…</string>
<string name="yer_a_wizard_harry">Sei un mago, Harry!</string>
<string name="keep_tapping_to_enter_hogwarts">Continua a premere per essere ammesso ad Hogwarts!</string>
<string name="modules">Moduli</string>
<string name="no_browser">Non hai un browser installato. Installane uno per continuare.</string>
<string name="invalid_repo_url">L\'URL della repo non è corretto</string>
<string name="add_repo_message">Le repository devono essere su protocollo HTTPS, e devono seguire le specifiche della documentazione.</string>

@ -21,10 +21,8 @@
<string name="showcase_mode_desc">ロックダウンモードはモジュール処理の実行を禁止します</string>
<string name="pref_category_info">情報</string>
<string name="show_licenses">ライセンスを表示</string>
<string name="licenses">ライセンス</string>
<string name="show_incompatible_pref">互換性のないモジュールを表示</string>
<string name="show_incompatible_desc">メタデータを基に、このデバイスと互換性がないモジュールを表示します</string>
<string name="pref_category_repos">リポジトリ</string>
<string name="master_delete">モジュールファイルを削除しますか\?</string>
<string name="master_delete_no">保持</string>
<string name="master_delete_yes">削除</string>
@ -145,7 +143,6 @@
<string name="download_full_app">フルバージョンをダウンロード</string>
<string name="repo_update_failed">一部のリポジトリのアップデートに失敗しました</string>
<string name="crash_reporting_desc">これを無効にすると、開発者にバグ報告がされず、問題の解決が難しくなるかもしれません</string>
<string name="androidacy_failed_to_parse_token">Androidacy からトークンを受け取れませんでした。後ほど再度お試しください。</string>
<string name="androidacy_failed_to_validate_token">Androidacy に使用するトークンを検証できませんでした。後ほど再度お試しください。</string>
<string name="androidacy_need_captcha">Captcha によって Androidacy のアップデートがブロックされています</string>
<string name="link_copied">リンクをコピーしました</string>
@ -155,7 +152,6 @@
<string name="notification_update_pref">自動でモジュールのアップデートを確認</string>
<string name="notification_update_desc">バッテリーの減りが早くなる可能性があります</string>
<string name="notification_update_debug_pref">通知をテスト</string>
<string name="language_support_outdated">現在の言語の翻訳の一部は最新ではありません。GitHub からアプリの翻訳にご協力ください</string>
<string name="language_translated_by">翻訳: Fox2Code (Suu, Re*Index.)</string>
<string name="crash_reporting">自動でバグとパフォーマンスを開発者に送信する</string>
<string name="api_key">Androidacy API キー</string>
@ -206,7 +202,6 @@
<string name="reset_api_key">API キーをリセット</string>
<string name="upgrade_androidacy_promo">プレミアムにアップグレード</string>
<string name="upgrade_androidacy_promo_desc">プレミアムにアップグレードすると、Androidacy リポジトリでの広告とCaptchaの削除、モジュール作者のサポートなどができます。</string>
<string name="feedback_submit">送信して再起動</string>
<string name="feedback_name">名前(オプション)</string>
<string name="feedback_email">メールアドレス(オプション)</string>
<string name="sentry_dialogue_empty_message">追加のフィードバック情報が入力されていません。</string>
@ -246,7 +241,6 @@
<string name="background_update_check_excludes">自動アップデートチェックから除外するモジュール</string>
<string name="update_title">アプリをアップデート</string>
<string name="update_message">AMM のアップデートをインストールしています。数分かかることがあります</string>
<string name="update_button">お待ちください…</string>
<string name="error_no_extras">エラー: 起動中に無効なデータを受け取りました</string>
<string name="update_debug_warning">デバッグ用のビルドで実行しています。アプリが自動で更新されないため、手動で更新してください</string>
<string name="error_no_action">エラー: 無効なアクションが指定されました。このアクションを無視して続行します。</string>
@ -284,9 +278,7 @@
<string name="notification_channel_category_app_update_description">アプリのアップデートが利用可能な場合に通知します</string>
<string name="notification_channel_background_update_app">アプリのアップデートが利用可能です!</string>
<string name="notification_channel_background_update_app_description">AMM のアップデートが利用可能です。ここをタップしてアップデートします。</string>
<string name="modules">モジュール</string>
<string name="no_browser">ブラウザーがインストールされていません。続行するにはインストールしてください。</string>
<string name="yer_a_wizard_harry">あなたはハリーみたいな魔法使いだよ!</string>
<string name="keep_tapping_to_enter_hogwarts">ホグワーツに入学したいならタップを続けてね!</string>
<string name="setup_update_check_headline">アップデートチェック</string>
<string name="setup_background_update_check_require_wifi_summary">アップデートを確認するには Wi-Fi または容量無制限のネットワークに接続してください</string>
@ -323,10 +315,6 @@
<string name="eula_agree_v2">LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.en.html) と EULA (https://www.androidacy.com/foxmmm-eula/) ライセンスに同意することになります。第三者からの条件も含め、このアプリの開発者はアプリ使用時に発生した問題に関する一切の責任を負いません。また、それに関わる保証も提供しません。</string>
<string name="setup_scroll_down_v2">完了ボタンを有効化するには、下にスクロールして EULA とライセンスに同意する必要があります。</string>
<string name="reboot_extra_warning">EDL モードは OEM が低レベルの問題を修正するための特別な機能です。EDL モードを解除したり、必要なツールをお持ちでない限りは他の再起動オプションを使用する事を強く推奨します。</string>
<string name="title_activity_expired">ExpiredActivity</string>
<string name="title_home">ホーム</string>
<string name="title_dashboard">ダッシュボード</string>
<string name="title_notifications">通知</string>
<string name="expired">このビルドは期限切れです!</string>
<string name="expired_message">使用中のビルドは有効期限が切れており、実行はできません。最新の安定版ビルドに更新してください。</string>
<string name="download_latest">最新のダウンロード</string>
@ -358,7 +346,6 @@
<string name="notification_update_frequency_pref">バックグラウンドでの確認の頻度</string>
<string name="notification_update_frequency_desc">バックグラウンドでアップデートを確認する頻度です。値を低く設定し過ぎるとバッテリーの消耗が激しくなる可能性があります。</string>
<string name="reboot">再起動</string>
<string name="reboot_system">通常の再起動</string>
<string name="reboot_recovery">リカバリーで再起動</string>
<string name="reboot_bootloader">ブートローダーで再起動</string>
<string name="reboot_edl">EDL モードで再起動</string>

@ -6,7 +6,6 @@
<string name="prevent_reboot_pref">재시작 방지</string>
<string name="enable_monet">Monet 활성화</string>
<string name="show_licenses">라이센스 보기</string>
<string name="licenses">라이센스</string>
<string name="magisk_outdated">설치할 수 있는 새로운 버전의 Magisk가 있습니다!</string>
<string name="pref_category_security">보안</string>
<string name="master_delete_yes">삭제</string>
@ -58,7 +57,6 @@
<string name="manage_repos_pref">저장소 관리</string>
<string name="notification_update_desc">배터리 사용량을 늘릴 수 있습니다</string>
<string name="showcase_mode_pref">잠금 모드</string>
<string name="language_support_outdated">현재 언어의 몇몇 변역이 최신이 아닙니다. GitHub에서 변역에 기여해주세요</string>
<string name="showcase_mode_desc">잠금 모드는 관리자가 모듈 작업을 수행하지 못하도록 막습니다</string>
<string name="prevent_reboot_desc">예지치 않은 재시작 방지</string>
<string name="pref_category_info">정보</string>
@ -66,7 +64,6 @@
<string name="show_incompatible_pref">호환되지 않는 모듈 표시하기</string>
<string name="warning">경고!</string>
<string name="show_incompatible_desc">메타데이터 상 기기에서 작동하지 않을 수 있는 모듈 표시</string>
<string name="pref_category_repos">레포</string>
<string name="pref_category_appearance">외양</string>
<string name="androidacy_test_mode_warning">Androidacy 비 상용 엔드포인트를 사용하도록 앱을 설정하고 있습니다. 이로 인해 앱이 불안정해지고 온라인 저장소를 로드하지 못할 수 있습니다. 이 스위치가 켜져 있으면 버그를 보고하지 마십시오. 저장소를 다시 로드하기 위해 앱이 다시 시작됩니다.</string>
<string name="crash_reporting_restart_title">변경사항을 적용하기 위해 앱을 다시 시작하겠습니까\?</string>
@ -121,7 +118,6 @@
<string name="support">지원</string>
<string name="donate">기부</string>
<string name="require_android_12">안드로이드 12 이상 필요</string>
<string name="androidacy_failed_to_parse_token">Androidacy에서 토큰을 가져올 수 없습니다. 나중에 다시 시도해 주세요.</string>
<string name="androidacy_failed_to_validate_token">Androidacy 토큰을 검증할 수 없습니다. 나중에 다시 시도해 주세요.</string>
<string name="api_key_unchanged">입력한 API 키가 지금 사용중인 것과 동일합니다.</string>
<string name="permission_notification_title">알림을 허용하시겠습니까\?</string>
@ -225,7 +221,6 @@
<string name="upgrade_androidacy_promo_desc">프리미엄으로 업그레이드하면 Androidacy 저장소에 대한 광고, 보안 문자 및 다운로드가 제거되고 Androidacy 및 모듈 제작자가 지원됩니다.</string>
<string name="crash_text">어-오, 뭔가 이상해 졌어요!</string>
<string name="feedback_message">이 일이 발생했을 때에 대해 자세히 알려주십시오. 자세할수록 더 편리해질 것입니다!</string>
<string name="feedback_submit">제출 및 다시 시작</string>
<string name="crash_details_copied">스택트레이스를 클립보드에 복사했습니다!</string>
<string name="crash_full_stacktrace">스택트레이스:
\n%1$s</string>
@ -238,7 +233,6 @@
<string name="notification_update_ignore_desc">업데이트에서 제외될 모듈 목록</string>
<string name="update_title">앱 업데이트</string>
<string name="update_message">AMM에 대한 업데이트를 확인하고 설치하는 동안 잠시 기다려 주십시오. 이 작업은 몇 분 정도 걸릴 수 있습니다</string>
<string name="update_button">잠시 가다려주세요…</string>
<string name="error_no_extras">에러: 실행중 옳지 않은 정보를 받았습니다</string>
<string name="update_debug_warning">디버그 빌드를 실행 중인 것 같습니다. 디버그 빌드는 수동으로 업데이트해야 하며 인앱 업데이트를 지원하지 않습니다</string>
<string name="update_available">업데이트 발견</string>

@ -2,4 +2,4 @@
~ Copyright (c) 2023 to present Androidacy and contributors. Names, logos, icons, and the Androidacy name are all trademarks of Androidacy and may not be used without license. See LICENSE for more information.
-->
<resources></resources>
<resources />

@ -36,11 +36,9 @@
<string name="showcase_mode_desc">Nedlåst modus forhendrer behandleren fra å utføre handlinger på moduler</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Vis lisenser</string>
<string name="licenses">Lisenser</string>
<string name="show_incompatible_pref">Vis inkompatible moduler</string>
<string name="show_incompatible_desc">Vis moduler som antagelig ikke virker basert på deres metadata</string>
<string name="magisk_outdated">Magisk er utdatert!</string>
<string name="pref_category_repos">Pakkebrønner</string>
<string name="master_delete">Slett modulfilene?</string>
<string name="master_delete_no">Behold</string>
<string name="master_delete_yes">Slett</string>
@ -57,7 +55,7 @@
<string name="file_picker_failure">Din nåværende filvelger kunne ikke innvilge tilgang til filen.</string>
<string name="remote_install_title">Fjerninstallering</string>
<string name="file_picker_wierd">Filvelgeren din returnerte et uventet svar.</string>
<string name="use_magisk_install_command_pref">Bruk «magisk --install-module»-kommandoen</string>
<string name="use_magisk_install_command_pref">Bruk «magisk install-module»-kommandoen</string>
<string name="use_magisk_install_command_desc">
Under testing forårsaket diagnoseverktøyet for modulinstallasjoner problemer,
så dette alternativet er skjult bak utviklingsmoduset. Bruk det på egen risiko!
@ -122,7 +120,6 @@
<string name="crash">Krasj programmet i testøyemed</string>
<string name="crash_reporting_restart_title">Start programmet på ny for å ta i bruk endringer\?</string>
<string name="notification_update_desc">Kan bruke mer batteri</string>
<string name="language_support_outdated">Noen oversettelser er ikke oppdatert. Bidra på Weblate fra https://translate.nift4.org</string>
<string name="crash_reporting">Rapporter feil og ytelse til utviklerne</string>
<string name="api_key_removed">API-nøkkel tilbakestilt</string>
<string name="crash_reporting_restart_message">Programmet må startes på ny for å bruke denne innstillingen</string>
@ -136,7 +133,6 @@
<string name="module_needs_ramdisk_desc">Denne modulen krever at ramdisk er isntallert</string>
<string name="androidacy_repo_info">Denne pakkebrønnen kan vise reklame for å dekke tjener- og utviklingskostnader.</string>
<string name="androidacy_failed_to_validate_token">Kunne ikke bekrefte symbol for Androidacy. Prøv igjen senere.</string>
<string name="androidacy_failed_to_parse_token">Kunne ikke hente symbol fra Androidacy. Prøv igjen senere.</string>
<string name="androidacy_need_captcha">Oppgradering til ny versjon av Androidacy blokkert av CAPTCHA</string>
<string name="link_copied">Lenke kopiert</string>
</resources>

@ -26,7 +26,6 @@
<string name="master_delete_no">राख्ने</string>
<string name="master_delete_yes">मेटाउने</string>
<string name="pref_category_appearance">हुलिया</string>
<string name="licenses">अनुमतिपत्रहरु</string>
<string name="module_min_magisk_chip">न्युनतम म्याजिस्क %s</string>
<string name="module_min_sdk_chip">न्युनतम एन्ड्रोइड</string>
<string name="module_max_sdk_chip">अधिकतम एन्ड्रोइड</string>
@ -51,7 +50,6 @@
<string name="androidacy_update_needed">यो एप पुरानो भएको छ ।</string>
<string name="androidacy_update_needed_message">कृपया यो एपलाई नयां संस्करणमा अध्यावधिक गर्नुहोस ।</string>
<string name="zip_security_warning">%s स्थापित गर्ने\?</string>
<string name="update_button">कृपया पर्खनुहोस् …</string>
<string name="sentry_dialogue_success">प्रतिकृया सफलतापूर्वक बुझाईयो । हामी छिटै पुनरावृति गर्नेछौं</string>
<string name="please_wait">कृपया पर्खनुहोस्</string>
<string name="warning">चेतावनि!</string>

@ -47,10 +47,8 @@
<string name="enable_monet">Activeer Monet</string>
<string name="pref_category_info">Info</string>
<string name="show_licenses">Toon licenties</string>
<string name="licenses">Licenties</string>
<string name="show_incompatible_desc">Toon modules die volgens de metadata mogelijk niet werken op je toestel</string>
<string name="magisk_outdated">Er is een nieuwe versie van Magisk beschikbaar!</string>
<string name="pref_category_repos">Pakketbeheerders</string>
<string name="pref_category_security">Beveiliging</string>
<string name="pref_category_appearance">Uiterlijk</string>
<string name="master_delete">Modulebestanden verwijderen\?</string>
@ -149,11 +147,9 @@
\nSchakel dit uit op eigen risico!</string>
<string name="androidacy_repo_info">Deze pakketbeheerder kan een aantal niet-intrusieve reclameboodschappen weergeven om de server- en ontwikkelingskosten te dekken. Beschikt over beoordelingen, automatische virusscans en meer.</string>
<string name="notification_update_pref">Automatische updatecontrole van modules</string>
<string name="language_support_outdated">Sommige vertalingen voor de huidige taal zijn niet up-to-date, overweeg om bij te dragen aan de app-vertalingen op GitHub</string>
<string name="crash_reporting_desc">Als je dit uitschakelt, krijgt de ontwikkelaar geen automatische bugrapporten en dit kan het oplossen van problemen bemoeilijken</string>
<string name="save_api_key">Valideren</string>
<string name="androidacy_test_mode_warning">Je stelt de app in om een niet-productie-eindpunt voor Androidacy te gebruiken. Dit kan leiden tot instabiliteit van de app en het niet laden van de online pakketbeheerder. Meld GEEN bugs als u deze schakelaar hebt ingeschakeld. De app wordt opnieuw gestart om pakketbeheerders opnieuw te laden.</string>
<string name="androidacy_failed_to_parse_token">Kan token niet ophalen van Androidacy. Probeer het later opnieuw.</string>
<string name="api_key_restart">API-sleutel is gewijzigd. Start de app opnieuw om wijzigingen toe te passen.</string>
<string name="permission_notification_message">We hebben de toestemming voor meldingen nodig om je op de hoogte te stellen van app- en module-updates. Als je deze machtiging niet verleent, worden er geen automatische controles van updates uitgevoerd.</string>
<string name="transparent_theme_dialogue_message">Transparante thema\'s kunnen enkele inconsistenties hebben en werken mogelijk niet op alle ROM\'s. Bovendien worden Monet en vervaging uitgeschakeld. Je kan dit op elk gewenst moment wijzigen.</string>
@ -171,7 +167,6 @@
<string name="background_update_check_excludes">Modules uitgesloten van automatische updatecontroles</string>
<string name="update_title">App bijwerken</string>
<string name="update_message">Even geduld terwijl we controleren op updates voor AMM en deze installeren. Dit kan enkele minuten duren</string>
<string name="update_button">Even geduld…</string>
<string name="clear_app_data">Verwijder app data</string>
<string name="clear_data_dialogue_title">App data verwijderen\?</string>
<string name="clear_data_dialogue_message">Je staat op het punt de app-gegevens te wissen. Bevestig deze actie.</string>
@ -189,9 +184,7 @@
<string name="error_saving_logs">Kan logboeken niet opslaan</string>
<string name="share_logs">AMM logs delen</string>
<string name="not_official_build">Deze app is geen officiële AMM build.</string>
<string name="yer_a_wizard_harry">Je bent een tovenaar, Harry!</string>
<string name="keep_tapping_to_enter_hogwarts">Blijf tikken om toegelaten te worden tot Zweinstein!</string>
<string name="modules">Modules</string>
<string name="no_browser">Je hebt geen browser geïnstalleerd. Installeer er een om door te gaan.</string>
<string name="theme">Thema</string>
<string name="theme_system">Systeem</string>
@ -207,7 +200,6 @@
<string name="upgrade_androidacy_promo_desc">Upgraden naar premium verwijdert advertenties, captcha\'s en downloads voor de Androidacy Repository en ondersteunt Androidacy en de auteurs van de module.</string>
<string name="crash_text">Oh-oh, er zit een addertje onder het gras!</string>
<string name="feedback_message">Geef ons meer details over wat je deed toen dit gebeurde. Hoe meer, hoe beter!</string>
<string name="feedback_submit">Verzenden en opnieuw opstarten</string>
<string name="please_feedback">Help ons alsjeblieft door ons te vertellen wat je probeerde te doen toen dit gebeurde.</string>
<string name="sentry_dialogue_disabled">Crashrapportage is uitgeschakeld. Schakel deze in om feedback te verzenden.</string>
<string name="feedback_name">Naam (optioneel)</string>

@ -56,11 +56,9 @@
<string name="enable_monet">Silnik motywu Monet</string>
<string name="pref_category_info">Informacje</string>
<string name="show_licenses">Pokaż licencje</string>
<string name="licenses">Licencje</string>
<string name="show_incompatible_pref">Pokaż niekompatybilne moduły</string>
<string name="show_incompatible_desc">Wyświetl moduły niezgodne z tym urządzeniem na podstawie ich metadanych</string>
<string name="magisk_outdated">Pojawiła się nowa wersja Magiska do zainstalowania!</string>
<string name="pref_category_repos">Repozytoria</string>
<string name="pref_category_security">Zabezpieczenia</string>
<string name="pref_category_appearance">Wygląd</string>
<string name="master_delete">Usunąć pliki modułu?</string>
@ -109,8 +107,6 @@
<!-- Set to true in translation file if your language is right to left -->
<bool name="lang_support_rtl">false</bool>
<!-- Always copy language_support_level when translating -->
<integer name="language_support_level">1</integer>
<string name="language_support_outdated">Niektóre tłumaczenia dla bieżącego języka nie są aktualne, prosimy o rozważenie wniesienia wkładu do tłumaczeń aplikacji na GitHubie</string>
<!-- Replace with your own username when translating -->
<string name="language_translated_by">Tłumaczenie: Daviteusz</string>
<string name="crash_reporting">Raportowanie błędów</string>
@ -130,7 +126,6 @@
<string name="androidacy_test_mode_disable_warning">Aby wyłączyć punkt końcowy staging, aplikacja zostanie ponownie uruchomiona</string>
<string name="crash_reporting_restart_message">Aby zastosować to ustawienie, aplikacja zostanie ponownie uruchomiona</string>
<string name="restart">Uruchom ponownie</string>
<string name="androidacy_failed_to_parse_token">Nie można pobrać tokena z Androidacy. Proszę spróbować ponownie później.</string>
<string name="androidacy_failed_to_validate_token">Nie można zweryfikować tokena dla Androidacy. Proszę spróbować ponownie później.</string>
<string name="androidacy_need_captcha">Aktualizacja Androidacy została zablokowana przez Captcha</string>
<string name="link_copied">Łącze zostało skopiowane</string>
@ -223,7 +218,6 @@
<string name="fox2code_thanks">Stworzone przez Fox2Code</string>
<string name="save_logs">Zapisz logi do pamięci wewnętrznej i udostępnij</string>
<string name="error_saving_logs">Nie można zapisać logów</string>
<string name="feedback_submit">Prześlij i restartuj</string>
<string name="please_feedback">Pomóż nam, informując o wykonywanych czynnościach, gdy to się stało.</string>
<string name="sentry_dialogue_disabled">Raportowanie błędów jest wyłączone. Włącz, aby przesłać informacje zwrotne.</string>
<string name="feedback_name">Imię (opcjonalnie)</string>
@ -246,7 +240,6 @@
<string name="background_update_check_excludes">Ignorowane moduły ze sprawdzania aktualizacji</string>
<string name="update_title">Zaktualizuj aplikację</string>
<string name="update_message">Proszę poczekać, aż sprawdzimy i zainstalujemy aktualizacje dla AMM. Może to potrwać kilka minut</string>
<string name="update_button">Proszę czekać…</string>
<string name="reset_app_confirmation">To całkowicie wyczyści dane aplikacji, lecz moduły pozostaną nietknięte.</string>
<string name="error_no_extras">BŁĄD: Nieprawidłowe dane otrzymane przy uruchomieniu</string>
<string name="update_debug_warning">Wygląda na to, że używasz wersji debugowanej. Kompilacje debugowe muszą być aktualizowane ręcznie i nie obsługują aktualizacji w aplikacji</string>
@ -290,9 +283,7 @@
<string name="notification_channel_category_app_update">Aktualizacje aplikacji</string>
<string name="notification_channel_background_update_app">Dostępna nowa wersja aplikacji!</string>
<string name="notification_channel_background_update_app_description">Dostępna jest nowa wersja AMM. Kliknij, aby zaktualizować.</string>
<string name="modules">Moduły</string>
<string name="no_browser">Nie masz zainstalowanej przeglądarki. Zainstaluj ją, aby kontynuować.</string>
<string name="yer_a_wizard_harry">Jesteś czarodziejem, Harry!</string>
<string name="keep_tapping_to_enter_hogwarts">Stukaj dalej, aby zostać przyjętym do Hogwartu!</string>
<string name="setup_background_update_check_require_wifi">Wymagaj sieci WiFi</string>
<string name="setup_app_analytics">Pozwól na analizę aplikacji</string>
@ -351,19 +342,14 @@
<string name="blur_performance_warning_summary">Urządzenie nie obsługuje blur</string>
<string name="reboot">Restartuj</string>
<string name="search">Szukaj</string>
<string name="reboot_system">Restartuj system</string>
<string name="reboot_recovery">Restartuj do recovery</string>
<string name="reboot_edl">Restartuj do trybu EDL</string>
<string name="install_terminal_reboot_prevented">Restart jest wyłączony w ustawieniach aplikacji</string>
<string name="androidacy_api_error">Błąd podczas komunikacji z API: %d</string>
<string name="title_home">Główna</string>
<string name="title_dashboard">Pulpit</string>
<string name="title_notifications">Powiadomienia</string>
<string name="expired">Ta wersja wygasła!</string>
<string name="download_latest">Pobierz najnowszą</string>
<string name="logs_saved">Pomyślnie zapisano logi</string>
<string name="reboot_extra_warning">Za chwilę nastąpi restart do trybu EDL, specjalnego trybu przeznaczonego dla producentów OEM do flashowania i naprawiania problemów niskiego poziomu. O ile nie jesteś pewien, że chcesz to zrobić i nie masz niezbędnych narzędzi, aby wyjść z tego trybu lub go użyć, zdecydowanie zachęcamy do korzystania z innych opcji restartu.</string>
<string name="title_activity_expired">ExpiredActivity</string>
<string name="expired_message">Używana wersja wygasła i nie będzie już działać. Prosimy o aktualizację do najnowszej stabilnej wersji.</string>
<string name="submit_feedback">Prześlij opinię</string>
<string name="ssl_error">Wykryto potencjalne przechwycenie SSL.</string>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save