diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8ce81cc44..25f04a5d6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,8 +18,9 @@ android { targetSdk = 35 versionCode = 24 versionName = "7.2.0" - setProperty("archivesBaseName", "Iconify v${defaultConfig.versionName}") + setProperty("archivesBaseName", "Iconify Vivo Safe v${defaultConfig.versionName}") buildConfigField("int", "MIN_SDK_VERSION", "$minSdk") + buildConfigField("boolean", "VIVO_SAFE_MODE", "true") ksp { arg("room.schemaLocation", "$projectDir/schemas") @@ -51,7 +52,7 @@ android { isCrunchPngs = false proguardFiles("proguard-android-optimize.txt", "proguard.pro", "proguard-rules.pro") applicationIdSuffix = ".debug" - resValue("string", "derived_app_name", "Iconify (Debug)") + resValue("string", "derived_app_name", "Iconify Vivo Safe") signingConfig = releaseSigning } @@ -60,7 +61,7 @@ android { isShrinkResources = true isCrunchPngs = false proguardFiles("proguard-android-optimize.txt", "proguard.pro", "proguard-rules.pro") - resValue("string", "derived_app_name", "Iconify") + resValue("string", "derived_app_name", "Iconify Vivo Safe") signingConfig = releaseSigning } } @@ -71,13 +72,13 @@ android { create("standard") { isDefault = true dimension = "distribution" - resValue("string", "derived_app_name", "Iconify") + resValue("string", "derived_app_name", "Iconify Vivo Safe") } create("foss") { dimension = "distribution" applicationIdSuffix = ".foss" - resValue("string", "derived_app_name", "Iconify (FOSS)") + resValue("string", "derived_app_name", "Iconify Vivo Safe") } } @@ -279,4 +280,4 @@ dependencies { tasks.register("printVersionName") { println(android.defaultConfig.versionName?.replace("-(Stable|Beta)".toRegex(), "")) -} \ No newline at end of file +} diff --git a/app/src/foss/java/com/drdisagree/iconify/SplashActivity.kt b/app/src/foss/java/com/drdisagree/iconify/SplashActivity.kt index 21bb6fdc9..b29733b32 100644 --- a/app/src/foss/java/com/drdisagree/iconify/SplashActivity.kt +++ b/app/src/foss/java/com/drdisagree/iconify/SplashActivity.kt @@ -42,7 +42,10 @@ class SplashActivity : AppCompatActivity() { (isOverlayInstalled || isXposedOnlyMode) val intent: Intent = - if (SKIP_TO_HOMEPAGE_FOR_TESTING || + if (BuildConfig.VIVO_SAFE_MODE && !isRooted) { + keepShowing = false + Intent(this@SplashActivity, MainActivity::class.java) + } else if (SKIP_TO_HOMEPAGE_FOR_TESTING || (isRooted && isModuleProperlyInstalled && isVersionCodeCorrect) diff --git a/app/src/main/java/com/drdisagree/iconify/ui/activities/MainActivity.kt b/app/src/main/java/com/drdisagree/iconify/ui/activities/MainActivity.kt index ffefe07a8..16bedee38 100644 --- a/app/src/main/java/com/drdisagree/iconify/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/drdisagree/iconify/ui/activities/MainActivity.kt @@ -13,6 +13,7 @@ import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.airbnb.lottie.LottieCompositionFactory +import com.drdisagree.iconify.BuildConfig import com.drdisagree.iconify.R import com.drdisagree.iconify.data.common.Dynamic import com.drdisagree.iconify.data.common.Preferences @@ -37,9 +38,11 @@ import com.drdisagree.iconify.ui.preferences.preferencesearch.SearchPreferenceRe import com.drdisagree.iconify.ui.utils.FragmentGroup import com.drdisagree.iconify.ui.utils.isInGroup import com.drdisagree.iconify.utils.HapticUtils.weakVibrate +import com.drdisagree.iconify.utils.RootUtils import com.drdisagree.iconify.utils.SystemUtils import com.drdisagree.iconify.utils.overlay.FabricatedUtils import com.drdisagree.iconify.utils.overlay.OverlayUtils +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.jaredrummler.android.colorpicker.ColorPickerDialog import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import kotlinx.coroutines.CoroutineScope @@ -79,11 +82,14 @@ class MainActivity : BaseActivity(), } initData() + showVivoSafeNoticeIfNeeded() setupFloatingActionButtons() } private fun initData() { + if (BuildConfig.VIVO_SAFE_MODE && !RootUtils.deviceProperlyRooted()) return + CoroutineScope(Dispatchers.IO).launch { // Clear lottie cache LottieCompositionFactory.clearCache(this@MainActivity) @@ -136,6 +142,8 @@ class MainActivity : BaseActivity(), binding.restartSystemui.setOnClickListener { binding.restartSystemui.weakVibrate() + if (showVivoSafeBlockedIfNeeded()) return@setOnClickListener + Dynamic.requiresSystemUiRestart = false showOrHidePendingActionButton( @@ -152,6 +160,8 @@ class MainActivity : BaseActivity(), binding.restartDevice.setOnClickListener { binding.restartDevice.weakVibrate() + if (showVivoSafeBlockedIfNeeded()) return@setOnClickListener + Dynamic.requiresDeviceRestart = false showOrHidePendingActionButton( @@ -166,6 +176,28 @@ class MainActivity : BaseActivity(), } } + private fun showVivoSafeNoticeIfNeeded() { + if (!BuildConfig.VIVO_SAFE_MODE) return + + MaterialAlertDialogBuilder(this) + .setTitle(R.string.vivo_safe_notice_title) + .setMessage(R.string.vivo_safe_notice_desc) + .setPositiveButton(android.R.string.ok, null) + .show() + } + + private fun showVivoSafeBlockedIfNeeded(): Boolean { + if (!BuildConfig.VIVO_SAFE_MODE || RootUtils.deviceProperlyRooted()) return false + + MaterialAlertDialogBuilder(this) + .setTitle(R.string.vivo_safe_blocked_title) + .setMessage(R.string.vivo_safe_blocked_desc) + .setPositiveButton(android.R.string.ok, null) + .show() + + return true + } + private fun setupNavigation() { if (Preferences.isXposedOnlyMode) { binding.bottomNavigationView.menu.clear() @@ -550,4 +582,4 @@ class MainActivity : BaseActivity(), } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/drdisagree/iconify/ui/views/OnboardingView.kt b/app/src/main/java/com/drdisagree/iconify/ui/views/OnboardingView.kt index f95f46691..a963c630a 100644 --- a/app/src/main/java/com/drdisagree/iconify/ui/views/OnboardingView.kt +++ b/app/src/main/java/com/drdisagree/iconify/ui/views/OnboardingView.kt @@ -180,6 +180,11 @@ class OnboardingView : FrameLayout { hasErroredOut = false Shell.getShell { + if (BuildConfig.VIVO_SAFE_MODE && !deviceProperlyRooted()) { + openVivoSafePreview() + return@getShell + } + if (!isDeviceRooted) { ErrorDialog(context).show( R.string.root_not_found_title, @@ -255,6 +260,11 @@ class OnboardingView : FrameLayout { hasErroredOut = false Shell.getShell { + if (BuildConfig.VIVO_SAFE_MODE && !deviceProperlyRooted()) { + openVivoSafePreview() + return@getShell + } + if (!isDeviceRooted) { ErrorDialog(context).show( R.string.root_not_found_title, @@ -319,6 +329,21 @@ class OnboardingView : FrameLayout { return true } + private fun openVivoSafePreview() { + putBoolean(XPOSED_ONLY_MODE, false) + + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + animateSlideLeft(context) + + Toast.makeText( + context, + R.string.vivo_safe_preview_toast, + Toast.LENGTH_LONG + ).show() + } + private fun handleInstallation() { LottieCompositionFactory.fromRawRes(context, R.raw.loading_anim) .addListener { @@ -729,4 +754,4 @@ class OnboardingView : FrameLayout { binding.slider.setCurrentItem(numberOfPages - 1, true) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/drdisagree/iconify/utils/ModuleUtils.kt b/app/src/main/java/com/drdisagree/iconify/utils/ModuleUtils.kt index 7a4bf1c5c..13dac2ad8 100644 --- a/app/src/main/java/com/drdisagree/iconify/utils/ModuleUtils.kt +++ b/app/src/main/java/com/drdisagree/iconify/utils/ModuleUtils.kt @@ -38,6 +38,11 @@ object ModuleUtils { private val TAG = ModuleUtils::class.java.getSimpleName() fun handleModule() { + if (BuildConfig.VIVO_SAFE_MODE && !RootUtils.deviceProperlyRooted()) { + Log.w(TAG, "Vivo Safe Mode blocked module handling without compatible root") + return + } + if (moduleExists()) { // Clean temporary directory Shell.cmd("rm -rf $TEMP_DIR").exec() @@ -223,6 +228,10 @@ object ModuleUtils { fun flashModule(modulePath: String): Boolean { var result: Shell.Result? = null + if (BuildConfig.VIVO_SAFE_MODE && !RootUtils.deviceProperlyRooted()) { + throw Exception("Vivo Safe Mode blocked module flashing without compatible root") + } + if (RootUtils.isMagiskInstalled) { result = Shell.cmd("magisk --install-module $modulePath").exec() } else if (RootUtils.isKSUInstalled) { diff --git a/app/src/main/java/com/drdisagree/iconify/utils/SystemUtils.kt b/app/src/main/java/com/drdisagree/iconify/utils/SystemUtils.kt index 00265b76a..c65f9012d 100644 --- a/app/src/main/java/com/drdisagree/iconify/utils/SystemUtils.kt +++ b/app/src/main/java/com/drdisagree/iconify/utils/SystemUtils.kt @@ -53,6 +53,8 @@ object SystemUtils { get() = appContext.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES == Configuration.UI_MODE_NIGHT_YES fun restartSystemUI() { + if (blockVivoSafeRootAction()) return + val loadTimeKey = String.format("%s%s", LOAD_TIME_KEY_KEY, SYSTEMUI_PACKAGE) val strikeKey = String.format("%s%s", PACKAGE_STRIKE_KEY_KEY, SYSTEMUI_PACKAGE) val currentTime = Calendar.getInstance().time.time @@ -63,6 +65,8 @@ object SystemUtils { } private fun forceReloadUI() { + if (blockVivoSafeRootAction()) return + val state = RPrefs.getBoolean(FORCE_RELOAD_OVERLAY_STATE, false) val pkgName: String = FORCE_RELOAD_PACKAGE_NAME @@ -94,9 +98,23 @@ object SystemUtils { } fun restartDevice() { + if (blockVivoSafeRootAction()) return + Shell.cmd("am start -a android.intent.action.REBOOT").exec() } + private fun blockVivoSafeRootAction(): Boolean { + if (!BuildConfig.VIVO_SAFE_MODE || RootUtils.deviceProperlyRooted()) return false + + Toast.makeText( + appContext, + appContext.resources.getString(R.string.vivo_safe_blocked_desc), + Toast.LENGTH_LONG + ).show() + + return true + } + fun disableBlur(force: Boolean) { Shell.cmd( if (!force) "mv " + MODULE_DIR + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b6bf49cfc..e82c27bba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,11 @@ - Iconify - Customize Boring Android UI + Iconify Vivo Safe + Safe preview build for Vivo + Vivo Safe Mode + This custom build is installed for Vivo V2111 stock Android 12 testing. Without Magisk, KernelSU, or APatch root, system overlays and module actions are blocked. You can browse the app, but do not expect system UI changes to apply. + Root action blocked + Vivo Safe Mode blocked this action because this device does not have compatible root. No module, overlay, reboot, or SystemUI command was run. + Opened in Vivo Safe preview mode. Initializing setup @@ -16,8 +21,8 @@ Let\'s customize everything to our likings. Ditch the stock UI monotony – personalize every tech detail to match your style and preferences. Is My Device Supported ? - Iconify supports all Pixel and AOSP ROMs. - Compatible with all Pixel and AOSP-based ROMs, ensuring broad support for Android devices. + This safe build is only for preview on Vivo stock ROM. + Root-only module installation is blocked unless compatible Magisk, KernelSU, or APatch root is available. Shall We Begin Now ? It is time for iconify to do some internal work. Starting internal tasks, marking the initiation of essential work. Ready to begin?