diff --git a/app/src/main/java/com/vanced/manager/core/downloader/base/AppDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/base/AppDownloader.kt index 7901506f..efeb9eb0 100644 --- a/app/src/main/java/com/vanced/manager/core/downloader/base/AppDownloader.kt +++ b/app/src/main/java/com/vanced/manager/core/downloader/base/AppDownloader.kt @@ -1,83 +1,67 @@ package com.vanced.manager.core.downloader.base -import com.vanced.manager.core.downloader.util.DownloadStatus +import com.vanced.manager.core.io.writeFile import okhttp3.ResponseBody import retrofit2.Call import retrofit2.awaitResponse -import java.io.FileOutputStream - -typealias DownloadCall = Call abstract class AppDownloader { data class DownloadFile( val fileName: String, - val call: DownloadCall, + val call: Call, ) - private lateinit var call: DownloadCall + sealed class DownloadStatus { + object Success : DownloadStatus() + data class Error(val error: String, val fileName: String) : DownloadStatus() + + val isSuccess + get() = this is Success + + val isError + get() = this is Error + } abstract suspend fun download( appVersions: List?, - onStatus: (DownloadStatus) -> Unit - ) + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus + + abstract suspend fun downloadRoot( + appVersions: List?, + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus abstract fun getSavedFilePath(): String - suspend fun downloadFiles( - downloadFiles: Array, - onFile: (String) -> Unit, + suspend inline fun downloadFiles( + files: Array, onProgress: (Float) -> Unit, - onError: (error: String, fileName: String) -> Unit, - onSuccess: () -> Unit - ) { - for (downloadFile in downloadFiles) { + onFile: (String) -> Unit + ): DownloadStatus { + for (file in files) { try { - this.call = downloadFile.call + onFile(file.fileName) - onFile(downloadFile.fileName) - - val response = call.awaitResponse() + val response = file.call.awaitResponse() if (response.isSuccessful) { - val body = response.body() - if (body != null) { - writeFile(body, downloadFile.fileName, onProgress) - } + response.body()?.writeFile(getSavedFilePath() + "/${file.fileName}", onProgress) continue } val error = response.errorBody()?.toString() if (error != null) { - onError(error, downloadFile.fileName) - return + return DownloadStatus.Error(error, file.fileName) } } catch (e: Exception) { - onError(e.stackTraceToString(), downloadFile.fileName) - return + return DownloadStatus.Error(e.stackTraceToString(), file.fileName) } } - onSuccess() - } - - private inline fun writeFile( - body: ResponseBody, - fileName: String, - onProgress: (Float) -> Unit - ) { - val inputStream = body.byteStream() - val outputStream = FileOutputStream(getSavedFilePath() + "/$fileName") - val totalBytes = body.contentLength() - val fileReader = ByteArray(4096) - var downloadedBytes = 0L - var read: Int - while (inputStream.read(fileReader).also { read = it } != -1) { - outputStream.write(fileReader, 0, read) - downloadedBytes += read - onProgress((downloadedBytes * 100 / totalBytes).toFloat()) - } - inputStream.close() - outputStream.close() + return DownloadStatus.Success } } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/downloader/impl/MicrogDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/impl/MicrogDownloader.kt index 99581ce3..ca810c8e 100644 --- a/app/src/main/java/com/vanced/manager/core/downloader/impl/MicrogDownloader.kt +++ b/app/src/main/java/com/vanced/manager/core/downloader/impl/MicrogDownloader.kt @@ -3,7 +3,6 @@ package com.vanced.manager.core.downloader.impl import android.content.Context import com.vanced.manager.core.downloader.api.MicrogAPI import com.vanced.manager.core.downloader.base.AppDownloader -import com.vanced.manager.core.downloader.util.DownloadStatus import com.vanced.manager.core.downloader.util.getMicrogPath import java.io.File @@ -14,33 +13,31 @@ class MicrogDownloader( override suspend fun download( appVersions: List?, - onStatus: (DownloadStatus) -> Unit - ) { - downloadFiles( - downloadFiles = arrayOf( + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus { + val downloadStatus = downloadFiles( + files = arrayOf( DownloadFile( call = microgAPI.getFile(), fileName = "microg.apk" ) ), - onProgress = { progress -> - onStatus(DownloadStatus.Progress(progress)) - }, - onFile = { fileName -> - onStatus(DownloadStatus.File(fileName)) - }, - onSuccess = { - onStatus(DownloadStatus.StartInstall) - }, - onError = { error, fileName -> - onStatus( - DownloadStatus.Error( - displayError = "Failed to download $fileName", - stacktrace = error - ) - ) - } + onProgress = onProgress, + onFile = onFile ) + if (downloadStatus.isError) + return downloadStatus + + return DownloadStatus.Success + } + + override suspend fun downloadRoot( + appVersions: List?, + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus { + throw IllegalAccessException("Vanced microG does not have a root downloader") } override fun getSavedFilePath(): String { diff --git a/app/src/main/java/com/vanced/manager/core/downloader/impl/MusicDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/impl/MusicDownloader.kt index b715b76e..09477052 100644 --- a/app/src/main/java/com/vanced/manager/core/downloader/impl/MusicDownloader.kt +++ b/app/src/main/java/com/vanced/manager/core/downloader/impl/MusicDownloader.kt @@ -3,8 +3,7 @@ package com.vanced.manager.core.downloader.impl import android.content.Context import com.vanced.manager.core.downloader.api.MusicAPI import com.vanced.manager.core.downloader.base.AppDownloader -import com.vanced.manager.core.downloader.util.DownloadStatus -import com.vanced.manager.core.downloader.util.getVancedMusicPath +import com.vanced.manager.core.downloader.util.getVancedYoutubeMusicPath import com.vanced.manager.core.preferences.holder.managerVariantPref import com.vanced.manager.core.preferences.holder.musicVersionPref import com.vanced.manager.core.util.getLatestOrProvidedAppVersion @@ -19,12 +18,13 @@ class MusicDownloader( override suspend fun download( appVersions: List?, - onStatus: (DownloadStatus) -> Unit - ) { + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus { absoluteVersion = getLatestOrProvidedAppVersion(musicVersionPref, appVersions) - downloadFiles( - downloadFiles = arrayOf( + val downloadStatus = downloadFiles( + files = arrayOf( DownloadFile( call = musicAPI.getFiles( version = absoluteVersion, @@ -33,28 +33,25 @@ class MusicDownloader( fileName = "music.apk" ) ), - onProgress = { progress -> - onStatus(DownloadStatus.Progress(progress)) - }, - onFile = { fileName -> - onStatus(DownloadStatus.File(fileName)) - }, - onSuccess = { - onStatus(DownloadStatus.StartInstall) - }, - onError = { error, fileName -> - onStatus( - DownloadStatus.Error( - displayError = "Failed to download $fileName", - stacktrace = error - ) - ) - } + onProgress = onProgress, + onFile = onFile ) + if (downloadStatus.isError) + return downloadStatus + + return DownloadStatus.Success + } + + override suspend fun downloadRoot( + appVersions: List?, + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus { + return DownloadStatus.Success } override fun getSavedFilePath(): String { - val directory = File(getVancedMusicPath(absoluteVersion, managerVariantPref, context)) + val directory = File(getVancedYoutubeMusicPath(absoluteVersion, managerVariantPref, context)) if (!directory.exists()) directory.mkdirs() diff --git a/app/src/main/java/com/vanced/manager/core/downloader/impl/VancedDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/impl/VancedDownloader.kt index adb6ae3b..364c9187 100644 --- a/app/src/main/java/com/vanced/manager/core/downloader/impl/VancedDownloader.kt +++ b/app/src/main/java/com/vanced/manager/core/downloader/impl/VancedDownloader.kt @@ -3,7 +3,6 @@ package com.vanced.manager.core.downloader.impl import android.content.Context import com.vanced.manager.core.downloader.api.VancedAPI import com.vanced.manager.core.downloader.base.AppDownloader -import com.vanced.manager.core.downloader.util.DownloadStatus import com.vanced.manager.core.downloader.util.getVancedYoutubePath import com.vanced.manager.core.preferences.holder.managerVariantPref import com.vanced.manager.core.preferences.holder.vancedLanguagesPref @@ -22,8 +21,9 @@ class VancedDownloader( override suspend fun download( appVersions: List?, - onStatus: (DownloadStatus) -> Unit - ) { + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus { absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions) val files = arrayOf( @@ -42,26 +42,23 @@ class VancedDownloader( ) } - downloadFiles( - downloadFiles = files, - onProgress = { progress -> - onStatus(DownloadStatus.Progress(progress)) - }, - onFile = { fileName -> - onStatus(DownloadStatus.File(fileName)) - }, - onSuccess = { - onStatus(DownloadStatus.StartInstall) - }, - onError = { error, fileName -> - onStatus( - DownloadStatus.Error( - displayError = "Failed to download $fileName", - stacktrace = error - ) - ) - } + val downloadStatus = downloadFiles( + files = files, + onProgress = onProgress, + onFile = onFile, ) + if (downloadStatus.isError) + return downloadStatus + + return DownloadStatus.Success + } + + override suspend fun downloadRoot( + appVersions: List?, + onProgress: (Float) -> Unit, + onFile: (String) -> Unit + ): DownloadStatus { + return DownloadStatus.Success } override fun getSavedFilePath(): String { diff --git a/app/src/main/java/com/vanced/manager/core/downloader/util/DownloadPath.kt b/app/src/main/java/com/vanced/manager/core/downloader/util/DownloadPath.kt index 76511b77..746e4b59 100644 --- a/app/src/main/java/com/vanced/manager/core/downloader/util/DownloadPath.kt +++ b/app/src/main/java/com/vanced/manager/core/downloader/util/DownloadPath.kt @@ -6,19 +6,34 @@ fun getVancedYoutubePath( version: String, variant: String, context: Context -) = context.getExternalFilesDir("vanced_youtube")!!.path + "/$version/$variant" +) = context.getExternalFilesDirPath("vanced_youtube") + "/$version/$variant" + +fun getVancedYoutubeMusicPath( + version: String, + variant: String, + context: Context +) = context.getExternalFilesDirPath("vanced_music") + "/$version/$variant" + +fun getMicrogPath( + context: Context +) = context.getExternalFilesDirPath("microg") fun getStockYoutubePath( version: String, context: Context -) = context.getExternalFilesDir("stock_youtube")!!.path + "/$version" +) = context.getExternalFilesDirPath("stock_youtube") + "/$version" -fun getVancedMusicPath( +fun getStockYoutubeMusicPath( version: String, - variant: String, context: Context -) = context.getExternalFilesDir("vanced_music")!!.path + "/$version/$variant" +) = context.getExternalFilesDirPath("stock_youtube_music") + "/$version" -fun getMicrogPath( - context: Context -) = context.getExternalFilesDir("microg")!!.path \ No newline at end of file +private fun Context.getExternalFilesDirPath( + type: String +): String { + val filesDir = getExternalFilesDir(type)!! //fuck null safety, amirite? + if (!filesDir.exists()) + filesDir.mkdirs() + + return filesDir.path +} diff --git a/app/src/main/java/com/vanced/manager/core/downloader/util/DownloadStatus.kt b/app/src/main/java/com/vanced/manager/core/downloader/util/DownloadStatus.kt deleted file mode 100644 index ee01c5d0..00000000 --- a/app/src/main/java/com/vanced/manager/core/downloader/util/DownloadStatus.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.vanced.manager.core.downloader.util - -sealed class DownloadStatus { - - object StartInstall : DownloadStatus() - - data class File(val fileName: String) : DownloadStatus() - - data class Progress(val progress: Float) : DownloadStatus() - - data class Error( - val displayError: String, - val stacktrace: String, - ) : DownloadStatus() - -} diff --git a/app/src/main/java/com/vanced/manager/core/installer/base/AppInstaller.kt b/app/src/main/java/com/vanced/manager/core/installer/base/AppInstaller.kt index 592290a9..61e5b38e 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/base/AppInstaller.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/base/AppInstaller.kt @@ -1,9 +1,11 @@ package com.vanced.manager.core.installer.base +import com.vanced.manager.core.installer.util.PMRootResult + abstract class AppInstaller { abstract fun install(appVersions: List?) - abstract fun installRoot(appVersions: List?) + abstract fun installRoot(appVersions: List?): PMRootResult } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/impl/MicrogInstaller.kt b/app/src/main/java/com/vanced/manager/core/installer/impl/MicrogInstaller.kt index 993cce40..55642b25 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/impl/MicrogInstaller.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/impl/MicrogInstaller.kt @@ -4,6 +4,7 @@ import android.content.Context import com.vanced.manager.core.downloader.util.getMicrogPath import com.vanced.manager.core.installer.base.AppInstaller import com.vanced.manager.core.installer.util.PM +import com.vanced.manager.core.installer.util.PMRootResult import java.io.File class MicrogInstaller( @@ -18,7 +19,7 @@ class MicrogInstaller( PM.installApp(musicApk, context) } - override fun installRoot(appVersions: List?) { + override fun installRoot(appVersions: List?): PMRootResult { throw IllegalAccessException("Vanced microG does not have a root installer") } diff --git a/app/src/main/java/com/vanced/manager/core/installer/impl/MusicInstaller.kt b/app/src/main/java/com/vanced/manager/core/installer/impl/MusicInstaller.kt index 2880a6a4..46ee003b 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/impl/MusicInstaller.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/impl/MusicInstaller.kt @@ -1,11 +1,14 @@ package com.vanced.manager.core.installer.impl import android.content.Context -import com.vanced.manager.core.downloader.util.getVancedMusicPath +import com.vanced.manager.core.downloader.util.getStockYoutubeMusicPath +import com.vanced.manager.core.downloader.util.getVancedYoutubeMusicPath import com.vanced.manager.core.installer.base.AppInstaller -import com.vanced.manager.core.installer.util.PM +import com.vanced.manager.core.installer.util.* import com.vanced.manager.core.preferences.holder.managerVariantPref import com.vanced.manager.core.preferences.holder.musicVersionPref +import com.vanced.manager.core.preferences.holder.vancedVersionPref +import com.vanced.manager.core.util.VANCED_MUSIC_PACKAGE_ROOT import com.vanced.manager.core.util.getLatestOrProvidedAppVersion import java.io.File @@ -18,13 +21,39 @@ class MusicInstaller( ) { val absoluteVersion = getLatestOrProvidedAppVersion(musicVersionPref, appVersions) - val musicApk = File(getVancedMusicPath(absoluteVersion, managerVariantPref, context) + "/music.apk") + val musicApk = File(getVancedYoutubeMusicPath(absoluteVersion, managerVariantPref, context) + "/music.apk") PM.installApp(musicApk, context) } - override fun installRoot(appVersions: List?) { + override fun installRoot(appVersions: List?): PMRootResult { + val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions) + val stockPath = getStockYoutubeMusicPath(absoluteVersion, context) + "/base.apk" + val vancedPath = getVancedYoutubeMusicPath(absoluteVersion, "root", context) + "/base.apk" + + val prepareStock = RootPatchHelper.prepareStock( + stockPackage = VANCED_MUSIC_PACKAGE_ROOT, + stockVersion = absoluteVersion + ) { + PMRoot.installApp(stockPath) + } + if (prepareStock.isError) + return prepareStock + + val patchStock = RootPatchHelper.patchStock( + patchPath = vancedPath, + stockPackage = VANCED_MUSIC_PACKAGE_ROOT, + app = APP_KEY + ) + if (patchStock.isError) + return patchStock + + return PMRootResult.Success() + } + + companion object { + const val APP_KEY = "youtube_music_vanced" } } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/impl/VancedInstaller.kt b/app/src/main/java/com/vanced/manager/core/installer/impl/VancedInstaller.kt index 08de16fd..bdcbf866 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/impl/VancedInstaller.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/impl/VancedInstaller.kt @@ -1,13 +1,12 @@ package com.vanced.manager.core.installer.impl import android.content.Context -import android.content.Intent import com.vanced.manager.core.downloader.util.getStockYoutubePath import com.vanced.manager.core.downloader.util.getVancedYoutubePath import com.vanced.manager.core.installer.base.AppInstaller import com.vanced.manager.core.installer.util.* import com.vanced.manager.core.preferences.holder.vancedVersionPref -import com.vanced.manager.core.util.Message +import com.vanced.manager.core.util.VANCED_YOUTUBE_PACKAGE_ROOT import com.vanced.manager.core.util.getLatestOrProvidedAppVersion import java.io.File @@ -26,8 +25,34 @@ class VancedInstaller( PM.installSplitApp(apks!!, context) } - override fun installRoot(appVersions: List?) { + override fun installRoot(appVersions: List?): PMRootResult { + val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions) + val stockApks = File(getStockYoutubePath(absoluteVersion, context)) + .listFiles()?.map { it.absolutePath } + val vancedBaseApk = getVancedYoutubePath(absoluteVersion, "root", context) + "/base.apk" + + val prepareStock = RootPatchHelper.prepareStock( + stockPackage = VANCED_YOUTUBE_PACKAGE_ROOT, + stockVersion = absoluteVersion, + ) { + PMRoot.installSplitApp(stockApks!!) + } + if (prepareStock.isError) + return prepareStock + + val patchStock = RootPatchHelper.patchStock( + patchPath = vancedBaseApk, + stockPackage = VANCED_YOUTUBE_PACKAGE_ROOT, + app = APP_KEY + ) + if (patchStock.isError) + return patchStock + + return PMRootResult.Success() } + companion object { + const val APP_KEY = "youtube_vanced" + } } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt b/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt index 285cefc9..45412f45 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt @@ -9,7 +9,6 @@ import android.os.Build import com.vanced.manager.core.installer.service.AppInstallService import com.vanced.manager.core.installer.service.AppUninstallService import java.io.File -import java.io.FileInputStream private const val byteArraySize = 1024 * 1024 // Because 1,048,576 is not readable @@ -19,7 +18,7 @@ object PM { val packageInstaller = context.packageManager.packageInstaller val session = packageInstaller.openSession(packageInstaller.createSession(sessionParams)) - writeApkToSession(apk, session) + session.writeApk(apk) session.commit(context.installIntentSender) session.close() } @@ -29,7 +28,7 @@ object PM { val session = packageInstaller.openSession(packageInstaller.createSession(sessionParams)) for (apk in apks) { - writeApkToSession(apk, session) + session.writeApk(apk) } session.commit(context.installIntentSender) session.close() @@ -41,17 +40,13 @@ object PM { } } -private fun writeApkToSession( - apk: File, - session: PackageInstaller.Session -) { - val inputStream = FileInputStream(apk) - val outputStream = session.openWrite(apk.name, 0, apk.length()) - inputStream.copyTo(outputStream, byteArraySize) - session.fsync(outputStream) - inputStream.close() - outputStream.flush() - outputStream.close() +private fun PackageInstaller.Session.writeApk(apk: File) { + apk.inputStream().use { inputStream -> + openWrite(apk.name, 0, apk.length()).use { outputStream -> + inputStream.copyTo(outputStream, byteArraySize) + fsync(outputStream) + } + } } private val intentFlags diff --git a/app/src/main/java/com/vanced/manager/core/installer/util/PMRoot.kt b/app/src/main/java/com/vanced/manager/core/installer/util/PMRoot.kt index 9e35096f..8cea3a28 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/util/PMRoot.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/util/PMRoot.kt @@ -10,10 +10,10 @@ import java.io.IOException object PMRoot { - fun installApp(apkPath: String): PMRootStatus { + fun installApp(apkPath: String): PMRootResult { val apk = File(apkPath) - val tmpApk = copyApkToTemp(apk) { error -> - return PMRootStatus.Error(PMRootStatusType.SESSION_FAILED_COPY, error) + val tmpApk = copyApkToTemp(apk).getOrElse { exception -> + return PMRootResult.Error(PMRootStatus.SESSION_FAILED_COPY, exception.stackTraceToString()) } val install = Shell.su("pm", "install", "-r", tmpApk.absolutePath).exec() @@ -22,27 +22,27 @@ object PMRoot { if (!install.isSuccess) { val errString = install.errString - return PMRootStatus.Error(getEnumForInstallFailed(errString), errString) + return PMRootResult.Error(getEnumForInstallFailed(errString), errString) } - return PMRootStatus.Success() + return PMRootResult.Success() } - fun installSplitApp(apkPaths: List): PMRootStatus { + fun installSplitApp(apkPaths: List): PMRootResult { val installCreate = Shell.su("pm", "install-create", "-r").exec() if (!installCreate.isSuccess) - return PMRootStatus.Error(PMRootStatusType.SESSION_FAILED_CREATE, installCreate.errString) + return PMRootResult.Error(PMRootStatus.SESSION_FAILED_CREATE, installCreate.errString) val sessionId = installCreate.outString if (sessionId.toIntOrNull() == null) - return PMRootStatus.Error(PMRootStatusType.SESSION_INVALID_ID, installCreate.errString) + return PMRootResult.Error(PMRootStatus.SESSION_INVALID_ID, installCreate.errString) for (apkPath in apkPaths) { val apk = File(apkPath) - val tmpApk = copyApkToTemp(apk) { error -> - return PMRootStatus.Error(PMRootStatusType.SESSION_FAILED_COPY, error) + val tmpApk = copyApkToTemp(apk).getOrElse { exception -> + return PMRootResult.Error(PMRootStatus.SESSION_FAILED_COPY, exception.stackTraceToString()) } val installWrite = @@ -52,62 +52,82 @@ object PMRoot { tmpApk.delete() if (!installWrite.isSuccess) - return PMRootStatus.Error(PMRootStatusType.SESSION_FAILED_WRITE, installWrite.errString) + return PMRootResult.Error(PMRootStatus.SESSION_FAILED_WRITE, installWrite.errString) } val installCommit = Shell.su("pm", "install-commit", sessionId).exec() if (!installCommit.isSuccess) { val errString = installCommit.errString - return PMRootStatus.Error(getEnumForInstallFailed(errString), errString) + return PMRootResult.Error(getEnumForInstallFailed(errString), errString) } - return PMRootStatus.Success() + return PMRootResult.Success() } - fun uninstallApp(pkg: String): PMRootStatus { + fun uninstallApp(pkg: String): PMRootResult { val uninstall = Shell.su("pm", "uninstall", pkg).exec() if (!uninstall.isSuccess) - return PMRootStatus.Error(PMRootStatusType.UNINSTALL_FAILED, uninstall.errString) + return PMRootResult.Error(PMRootStatus.UNINSTALL_FAILED, uninstall.errString) - return PMRootStatus.Success() + return PMRootResult.Success() } - fun setInstallerPackage(targetPkg: String, installerPkg: String): PMRootStatus { + fun setInstallerPackage(targetPkg: String, installerPkg: String): PMRootResult { val setInstaller = Shell.su("pm", "set-installer", targetPkg, installerPkg) .exec() if (!setInstaller.isSuccess) - return PMRootStatus.Error(PMRootStatusType.ACTION_FAILED_SET_INSTALLER, setInstaller.errString) + return PMRootResult.Error(PMRootStatus.ACTION_FAILED_SET_INSTALLER, setInstaller.errString) - return PMRootStatus.Success() + return PMRootResult.Success() } - fun forceStopApp(pkg: String): PMRootStatus { + fun forceStopApp(pkg: String): PMRootResult { val stopApp = Shell.su("am", "force-stop", pkg).exec() if (!stopApp.isSuccess) - return PMRootStatus.Error(PMRootStatusType.ACTION_FAILED_FORCE_STOP_APP, stopApp.errString) + return PMRootResult.Error(PMRootStatus.ACTION_FAILED_FORCE_STOP_APP, stopApp.errString) - return PMRootStatus.Success() + return PMRootResult.Success() } - fun getPackageDir(pkg: String): PMRootStatus { - val delimeter = "path: " - val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", delimeter).exec() + fun getPackageVersionName(pkg: String): PMRootResult { + val keyword = "versionName=" + val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec() if (!dumpsys.isSuccess) - return PMRootStatus.Error(PMRootStatusType.ACTION_FAILED_GET_PACKAGE_DIR, dumpsys.errString) + return PMRootResult.Error(PMRootStatus.ACTION_FAILED_GET_PACKAGE_VERSION_NAME, + dumpsys.errString) - return PMRootStatus.Success(dumpsys.outString.removePrefix(delimeter)) + return PMRootResult.Success(dumpsys.outString.removePrefix(keyword)) + } + + fun getPackageVersionCode(pkg: String): PMRootResult { + val keyword = "versionCode=" + val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec() + + if (!dumpsys.isSuccess) + return PMRootResult.Error(PMRootStatus.ACTION_FAILED_GET_PACKAGE_VERSION_CODE, + dumpsys.errString) + + return PMRootResult.Success(dumpsys.outString.removePrefix(keyword).substringAfter("minSdk") + .toLong()) + } + + fun getPackageDir(pkg: String): PMRootResult { + val keyword = "path: " + val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec() + + if (!dumpsys.isSuccess) + return PMRootResult.Error(PMRootStatus.ACTION_FAILED_GET_PACKAGE_DIR, dumpsys.errString) + + return PMRootResult.Success(dumpsys.outString.removePrefix(keyword)) } } -private inline fun copyApkToTemp( - apk: File, - onError: (String) -> Unit -): SuFile { +private fun copyApkToTemp(apk: File, ): Result { val tmpPath = "/data/local/tmp/${apk.name}" val tmpApk = SuFile(tmpPath).apply { @@ -120,20 +140,20 @@ private inline fun copyApkToTemp( it.flush() } } catch (e: IOException) { - onError(e.stackTraceToString()) + return Result.failure(e) } - return tmpApk + return Result.success(tmpApk) } private fun getEnumForInstallFailed(outString: String) = when { - outString.contains("INSTALL_FAILED_ABORTED") -> PMRootStatusType.INSTALL_FAILED_ABORTED - outString.contains("INSTALL_FAILED_ALREADY_EXISTS") -> PMRootStatusType.INSTALL_FAILED_ALREADY_EXISTS - outString.contains("INSTALL_FAILED_CPU_ABI_INCOMPATIBLE") -> PMRootStatusType.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE - outString.contains("INSTALL_FAILED_INSUFFICIENT_STORAGE") -> PMRootStatusType.INSTALL_FAILED_INSUFFICIENT_STORAGE - outString.contains("INSTALL_FAILED_INVALID_APK") -> PMRootStatusType.INSTALL_FAILED_INVALID_APK - outString.contains("INSTALL_FAILED_VERSION_DOWNGRADE") -> PMRootStatusType.INSTALL_FAILED_VERSION_DOWNGRADE - outString.contains("INSTALL_PARSE_FAILED_NO_CERTIFICATES") -> PMRootStatusType.INSTALL_FAILED_PARSE_NO_CERTIFICATES - else -> PMRootStatusType.INSTALL_FAILED_UNKNOWN + outString.contains("INSTALL_FAILED_ABORTED") -> PMRootStatus.INSTALL_FAILED_ABORTED + outString.contains("INSTALL_FAILED_ALREADY_EXISTS") -> PMRootStatus.INSTALL_FAILED_ALREADY_EXISTS + outString.contains("INSTALL_FAILED_CPU_ABI_INCOMPATIBLE") -> PMRootStatus.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE + outString.contains("INSTALL_FAILED_INSUFFICIENT_STORAGE") -> PMRootStatus.INSTALL_FAILED_INSUFFICIENT_STORAGE + outString.contains("INSTALL_FAILED_INVALID_APK") -> PMRootStatus.INSTALL_FAILED_INVALID_APK + outString.contains("INSTALL_FAILED_VERSION_DOWNGRADE") -> PMRootStatus.INSTALL_FAILED_VERSION_DOWNGRADE + outString.contains("INSTALL_PARSE_FAILED_NO_CERTIFICATES") -> PMRootStatus.INSTALL_FAILED_PARSE_NO_CERTIFICATES + else -> PMRootStatus.INSTALL_FAILED_UNKNOWN } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/util/PMRootStatus.kt b/app/src/main/java/com/vanced/manager/core/installer/util/PMRootResult.kt similarity index 51% rename from app/src/main/java/com/vanced/manager/core/installer/util/PMRootStatus.kt rename to app/src/main/java/com/vanced/manager/core/installer/util/PMRootResult.kt index 8da73942..13a51780 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/util/PMRootStatus.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/util/PMRootResult.kt @@ -1,8 +1,10 @@ package com.vanced.manager.core.installer.util -enum class PMRootStatusType { +enum class PMRootStatus { ACTION_FAILED_SET_INSTALLER, ACTION_FAILED_GET_PACKAGE_DIR, + ACTION_FAILED_GET_PACKAGE_VERSION_NAME, + ACTION_FAILED_GET_PACKAGE_VERSION_CODE, ACTION_FAILED_FORCE_STOP_APP, INSTALL_SUCCESSFUL, @@ -38,35 +40,20 @@ enum class PMRootStatusType { UNINSTALL_FAILED, } -sealed class PMRootStatus { - data class Success(val value: V? = null) : PMRootStatus() - data class Error(val error: PMRootStatusType, val message: String) : PMRootStatus() +sealed class PMRootResult { + data class Success(val value: V? = null) : PMRootResult() + data class Error(val error: PMRootStatus, val message: String) : PMRootResult() + + val isError + get() = this is Error + + val isSuccess + get() = this is Success } -inline fun > S.exec( - onError: (PMRootStatusType, String) -> Unit, - onSuccess: () -> Unit -) { - when (this) { - is PMRootStatus.Success<*> -> { - onSuccess() - } - is PMRootStatus.Error -> { - onError(error, message) - } - } -} - -inline fun > S.execWithValue( - onError: (PMRootStatusType, String) -> Unit, - onSuccess: (value: V?) -> Unit -) { - when (this) { - is PMRootStatus.Success<*> -> { - onSuccess(value as V?) - } - is PMRootStatus.Error -> { - onError(error, message) - } +inline fun PMRootResult.getOrElse(onError: (PMRootResult.Error) -> R): R? { + return when (this) { + is PMRootResult.Error -> onError(this) + is PMRootResult.Success -> return this.value } } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/util/Patcher.kt b/app/src/main/java/com/vanced/manager/core/installer/util/Patcher.kt index dca7fc9c..99e5a965 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/util/Patcher.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/util/Patcher.kt @@ -13,27 +13,29 @@ object Patcher { fun setupScript( app: String, - pkg: String, + stockPackage: String, stockPath: String, - ): PMRootStatus { + ): PMRootResult { val postFsDataScriptPath = getAppPostFsScriptPath(app) val serviceDScriptPath = getAppServiceDScriptPath(app) - val postFsDataScript = getPostFsDataScript(pkg) + val postFsDataScript = getPostFsDataScript(stockPackage) val serviceDScript = getServiceDScript(getAppPatchPath(app), stockPath) - copyScriptToDestination(postFsDataScriptPath, postFsDataScript) { error -> - return PMRootStatus.Error(PMRootStatusType.SCRIPT_FAILED_SETUP_POST_FS, error) - } + val copyServiceDScript = copyScriptToDestination(postFsDataScript, postFsDataScriptPath) + if (copyServiceDScript.isFailure) + return PMRootResult.Error(PMRootStatus.SCRIPT_FAILED_SETUP_POST_FS, + copyServiceDScript.exceptionOrNull()!!.stackTraceToString()) - copyScriptToDestination(serviceDScriptPath, serviceDScript) { error -> - return PMRootStatus.Error(PMRootStatusType.SCRIPT_FAILED_SETUP_SERVICE_D, error) - } + val copyPostFsDataScript = copyScriptToDestination(serviceDScript, serviceDScriptPath) + if (copyPostFsDataScript.isFailure) + return PMRootResult.Error(PMRootStatus.SCRIPT_FAILED_SETUP_SERVICE_D, + copyPostFsDataScript.exceptionOrNull()!!.stackTraceToString()) - return PMRootStatus.Success() + return PMRootResult.Success() } - fun movePatchToDataAdb(patchPath: String, app: String): PMRootStatus { + fun movePatchToDataAdb(patchPath: String, app: String): PMRootResult { val newPatchPath = getAppPatchPath(app) val patchApk = File(patchPath) @@ -47,38 +49,38 @@ object Patcher { try { patchApk.copyTo(newPatchApk) } catch (e: IOException) { - return PMRootStatus.Error(PMRootStatusType.PATCH_FAILED_COPY, e.stackTraceToString()) + return PMRootResult.Error(PMRootStatus.PATCH_FAILED_COPY, e.stackTraceToString()) } val chmod = Shell.su("chmod", "644", newPatchPath).exec() if (!chmod.isSuccess) - return PMRootStatus.Error(PMRootStatusType.PATCH_FAILED_CHMOD, chmod.errString) + return PMRootResult.Error(PMRootStatus.PATCH_FAILED_CHMOD, chmod.errString) val chown = Shell.su("chown", "system:system", newPatchPath).exec() if (!chmod.isSuccess) - return PMRootStatus.Error(PMRootStatusType.PATCH_FAILED_CHOWN, chown.errString) + return PMRootResult.Error(PMRootStatus.PATCH_FAILED_CHOWN, chown.errString) - return PMRootStatus.Success() + return PMRootResult.Success() } - fun chconPatch(app: String): PMRootStatus { + fun chconPatch(app: String): PMRootResult { val chcon = Shell.su("chcon u:object_r:apk_data_file:s0 ${getAppPatchPath(app)}").exec() if (!chcon.isSuccess) - return PMRootStatus.Error(PMRootStatusType.PATCH_FAILED_CHCON, chcon.errString) + return PMRootResult.Error(PMRootStatus.PATCH_FAILED_CHCON, chcon.errString) - return PMRootStatus.Success() + return PMRootResult.Success() } - fun linkPatch(app: String, pkg: String, stockPath: String): PMRootStatus { - val umount = Shell.su("""for i in ${'$'}(ls /data/app/ | grep $pkg | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """).exec() + fun linkPatch(app: String, stockPackage: String, stockPath: String): PMRootResult { + val umount = Shell.su("""for i in ${'$'}(ls /data/app/ | grep $stockPackage | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """).exec() if (!umount.isSuccess) - return PMRootStatus.Error(PMRootStatusType.LINK_FAILED_UNMOUNT, umount.errString) + return PMRootResult.Error(PMRootStatus.LINK_FAILED_UNMOUNT, umount.errString) val mount = Shell.su("su", "-mm", "-c", """"mount -o bind ${getAppPatchPath(app)} $stockPath"""").exec() if (!mount.isSuccess) - return PMRootStatus.Error(PMRootStatusType.LINK_FAILED_MOUNT, mount.errString) + return PMRootResult.Error(PMRootStatus.LINK_FAILED_MOUNT, mount.errString) - return PMRootStatus.Success() + return PMRootResult.Success() } fun destroyPatch(app: String) = @@ -112,42 +114,41 @@ private fun getServiceDScript(patchPath: String, stockPath: String) = mount -o bind $patchPath $stockPath """.trimIndent() -private fun getPostFsDataScript(pkg: String) = +private fun getPostFsDataScript(stockPackage: String) = """ #!/system/bin/sh - while read line; do echo \${'$'}{line} | grep $pkg | awk '{print \${'$'}2}' | xargs umount -l; done< /proc/mounts + while read line; do echo \${'$'}{line} | grep $stockPackage | awk '{print \${'$'}2}' | xargs umount -l; done< /proc/mounts """.trimIndent() private fun cleanPatchFiles( postFsPath: String, serviceDPath: String, patchPath: String, -): PMRootStatus { +): PMRootResult { val files = mapOf( - postFsPath to PMRootStatusType.SCRIPT_FAILED_DESTROY_POST_FS, - serviceDPath to PMRootStatusType.SCRIPT_FAILED_DESTROY_SERVICE_D, - patchPath to PMRootStatusType.PATCH_FAILED_DESTROY, + postFsPath to PMRootStatus.SCRIPT_FAILED_DESTROY_POST_FS, + serviceDPath to PMRootStatus.SCRIPT_FAILED_DESTROY_SERVICE_D, + patchPath to PMRootStatus.PATCH_FAILED_DESTROY, ) - for ((filePath, statusType) in files) { + for ((filePath, errorStatusType) in files) { try { with(ManagerSuFile(filePath)) { if (exists()) delete() } } catch (e: SUIOException) { - return PMRootStatus.Error(statusType, e.stackTraceToString()) + return PMRootResult.Error(errorStatusType, e.stackTraceToString()) } } - return PMRootStatus.Success() + return PMRootResult.Success() } -private inline fun copyScriptToDestination( - scriptDestination: String, +private fun copyScriptToDestination( script: String, - onError: (String) -> Unit -) { - val scriptFile = SuFile(scriptDestination) + destination: String, +): Result { + val scriptFile = SuFile(destination) .apply { if (!exists()) createNewFile() } @@ -159,9 +160,11 @@ private inline fun copyScriptToDestination( } val chmod = Shell.su("chmod", "744", scriptFile.absolutePath).exec() if (!chmod.isSuccess) { - onError(chmod.errString) + return Result.failure(Exception(chmod.errString)) } } catch (e: IOException) { - onError(e.stackTraceToString()) + return Result.failure(e) } + + return Result.success(null) } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/util/RootPatchHelper.kt b/app/src/main/java/com/vanced/manager/core/installer/util/RootPatchHelper.kt new file mode 100644 index 00000000..bf23db01 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/installer/util/RootPatchHelper.kt @@ -0,0 +1,64 @@ +package com.vanced.manager.core.installer.util + +object RootPatchHelper { + + fun cleanPatches(app: String): PMRootResult { + val cleanOldPatches = Patcher.destroyOldPatch(app) + if (cleanOldPatches.isError) + return cleanOldPatches + + val cleanPatches = Patcher.destroyPatch(app) + if (cleanOldPatches.isError) + return cleanPatches + + return PMRootResult.Success() + } + + inline fun prepareStock( + stockPackage: String, + stockVersion: String, + install: () -> PMRootResult + ): PMRootResult { + val stockYoutubeVersion = PMRoot.getPackageVersionName(stockPackage) + .getOrElse { null } + if (stockYoutubeVersion != stockVersion) { + val uninstallStock = PMRoot.uninstallApp(stockPackage) + if (uninstallStock.isError) + return uninstallStock + + val installStock = install() + if (installStock.isError) + return installStock + } + + return PMRootResult.Success() + } + + fun patchStock( + patchPath: String, + stockPackage: String, + app: String + ): PMRootResult { + val movePatch = Patcher.movePatchToDataAdb(patchPath, app) + if (movePatch.isError) + return movePatch + + val chconPatch = Patcher.chconPatch(app) + if (chconPatch.isError) + return chconPatch + + val stockPackageDir = PMRoot.getPackageDir(stockPackage) + .getOrElse { error -> return error }!! + + val setupScript = Patcher.setupScript(app, stockPackage, stockPackageDir) + if (setupScript is PMRootResult.Error) + return setupScript + + val linkPatch = Patcher.linkPatch(app, stockPackage, stockPackageDir) + if (linkPatch is PMRootResult.Error) + return linkPatch + + return PMRootResult.Success() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/io/Util.kt b/app/src/main/java/com/vanced/manager/core/io/Util.kt new file mode 100644 index 00000000..df387453 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/io/Util.kt @@ -0,0 +1,36 @@ +package com.vanced.manager.core.io + +import okhttp3.ResponseBody +import java.io.FileOutputStream +import java.io.InputStream +import java.io.OutputStream + +inline fun ResponseBody.writeFile( + filePath: String, + onProgress: (Float) -> Unit +) { + byteStream().use { inputStream -> + FileOutputStream(filePath).use { outputStream -> + val totalBytes = contentLength() + inputStream.copyTo(outputStream, 8192) { bytes -> + onProgress((bytes * 100 / totalBytes).toFloat()) + } + } + } +} + +inline fun InputStream.copyTo( + outputStream: OutputStream, + bufferSize: Int, + onProgress: (Long) -> Unit +) { + val buffer = ByteArray(bufferSize) + var bytesCopied: Long = 0 + var bytes = read(buffer) + while (bytes >= 0) { + outputStream.write(buffer, 0, bytes) + bytesCopied += bytes + bytes = read(buffer) + onProgress(bytesCopied) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/util/AppPackages.kt b/app/src/main/java/com/vanced/manager/core/util/AppPackages.kt new file mode 100644 index 00000000..7e976287 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/util/AppPackages.kt @@ -0,0 +1,7 @@ +package com.vanced.manager.core.util + +const val VANCED_YOUTUBE_PACKAGE = "com.vanced.android.youtube" +const val VANCED_YOUTUBE_PACKAGE_ROOT = "com.google.android.youtube" + +const val VANCED_MUSIC_PACKAGE = "com.vanced.android.apps.youtube.music" +const val VANCED_MUSIC_PACKAGE_ROOT = "com.google.android.apps.youtube.music" \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/util/Coroutines.kt b/app/src/main/java/com/vanced/manager/core/util/Coroutines.kt new file mode 100644 index 00000000..6339e3f9 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/util/Coroutines.kt @@ -0,0 +1,14 @@ +package com.vanced.manager.core.util + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import java.util.concurrent.Executor +import kotlin.coroutines.CoroutineContext + +fun CoroutineContext.asExecutor(): Executor = object : Executor { + private val scope = CoroutineScope(this@asExecutor) + + override fun execute(command: Runnable) { + scope.launch { command.run() } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/util/DownloadLog.kt b/app/src/main/java/com/vanced/manager/core/util/DownloadLog.kt deleted file mode 100644 index fdfa563a..00000000 --- a/app/src/main/java/com/vanced/manager/core/util/DownloadLog.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.vanced.manager.core.util - -sealed class Message { - data class Success(val message: String) : Message() - data class Warning(val message: String) : Message() - data class Error(val message: String) : Message() -} - -val downloadLogs = mutableListOf() \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/util/Log.kt b/app/src/main/java/com/vanced/manager/core/util/Log.kt deleted file mode 100644 index 5476699f..00000000 --- a/app/src/main/java/com/vanced/manager/core/util/Log.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.vanced.manager.core.util - -import android.util.Log -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.withStyle - -data class LogContent( - val body: AnnotatedString, -) - -val logs = mutableListOf() - -fun log(tag: String, message: String) { - Log.i(tag, message) - logs.add( - LogContent( - body = buildAnnotatedString { - withStyle( - SpanStyle( - color = Color(0xFF2E73FF), - fontWeight = FontWeight.Bold - ) - ) { - append("$tag:") - } - append("") - withStyle(SpanStyle(color = Color.Magenta)) { - append(message) - } - } - ) - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/util/SuShell.kt b/app/src/main/java/com/vanced/manager/core/util/SuShell.kt index 7173aa05..6a6324d4 100644 --- a/app/src/main/java/com/vanced/manager/core/util/SuShell.kt +++ b/app/src/main/java/com/vanced/manager/core/util/SuShell.kt @@ -1,6 +1,8 @@ package com.vanced.manager.core.util import com.topjohnwu.superuser.Shell +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine val Shell.Result.outString get() = out.joinToString("\n") @@ -9,5 +11,10 @@ val Shell.Result.errString get() = err.joinToString("\n") val isMagiskInstalled - get() = Shell.su("magisk", "-c").exec().isSuccess + get() = Shell.rootAccess() && Shell.su("magisk", "-c").exec().isSuccess + +suspend fun Shell.Job.await() = + suspendCoroutine { continuation -> + submit(/*continuation.context.asExecutor(),*/ continuation::resume) + } diff --git a/app/src/main/java/com/vanced/manager/core/util/VancedAppPackages.kt b/app/src/main/java/com/vanced/manager/core/util/VancedAppPackages.kt deleted file mode 100644 index e9b5661c..00000000 --- a/app/src/main/java/com/vanced/manager/core/util/VancedAppPackages.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.vanced.manager.core.util - -const val VANCED_PACKAGE = "" \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/ui/screens/SettingsLayout.kt b/app/src/main/java/com/vanced/manager/ui/screens/SettingsLayout.kt index 97bd3ef1..ec5050f8 100644 --- a/app/src/main/java/com/vanced/manager/ui/screens/SettingsLayout.kt +++ b/app/src/main/java/com/vanced/manager/ui/screens/SettingsLayout.kt @@ -16,7 +16,7 @@ import com.vanced.manager.ui.component.topappbar.ManagerTopAppBar import com.vanced.manager.ui.resources.managerString import com.vanced.manager.ui.util.Screen import com.vanced.manager.ui.widget.layout.managerCategory -import com.vanced.manager.ui.widget.screens.settings.* +import com.vanced.manager.ui.widget.settings.* @ExperimentalMaterial3Api @Composable diff --git a/app/src/main/java/com/vanced/manager/ui/viewmodel/InstallViewModel.kt b/app/src/main/java/com/vanced/manager/ui/viewmodel/InstallViewModel.kt index f965d66e..201dc8e0 100644 --- a/app/src/main/java/com/vanced/manager/ui/viewmodel/InstallViewModel.kt +++ b/app/src/main/java/com/vanced/manager/ui/viewmodel/InstallViewModel.kt @@ -7,13 +7,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.vanced.manager.core.downloader.base.AppDownloader import com.vanced.manager.core.downloader.impl.MicrogDownloader import com.vanced.manager.core.downloader.impl.MusicDownloader import com.vanced.manager.core.downloader.impl.VancedDownloader -import com.vanced.manager.core.downloader.util.DownloadStatus import com.vanced.manager.core.installer.impl.MicrogInstaller import com.vanced.manager.core.installer.impl.MusicInstaller import com.vanced.manager.core.installer.impl.VancedInstaller +import com.vanced.manager.core.installer.util.PMRootResult +import com.vanced.manager.core.preferences.holder.managerVariantPref import com.vanced.manager.network.util.MICROG_NAME import com.vanced.manager.network.util.MUSIC_NAME import com.vanced.manager.network.util.VANCED_NAME @@ -30,6 +32,9 @@ class InstallViewModel( private val microgInstaller: MicrogInstaller, ) : ViewModel() { + private val isRoot + get() = managerVariantPref == "root" + sealed class Log { data class Info(val infoText: String) : Log() data class Success(val successText: String) : Log() @@ -72,16 +77,6 @@ class InstallViewModel( } } - fun postInstallStatusRoot(pmStatus: Int, extra: String) { - if (pmStatus == PackageInstaller.STATUS_SUCCESS) { - status = Status.Installed - log(Log.Success("Successfully installed")) - } else { - status = Status.Failure - log(Log.Error("Failed to install app", extra)) - } - } - fun clear() { logs.clear() status = Status.Idle @@ -93,21 +88,29 @@ class InstallViewModel( ) { val downloader = getDownloader(appName) - downloader.download(appVersions) { downloadStatus -> - when (downloadStatus) { - is DownloadStatus.File -> log(Log.Info("Downloading ${downloadStatus.fileName}")) - is DownloadStatus.Error -> log( - Log.Error( - displayText = downloadStatus.displayError, - stacktrace = downloadStatus.stacktrace - ) - ) - is DownloadStatus.Progress -> status = - Status.Progress(downloadStatus.progress / 100) - is DownloadStatus.StartInstall -> { - log(Log.Success("Successfully downloaded $appName")) - installApp(appName, appVersions) - } + val onProgress: (Float) -> Unit = { progress -> + status = Status.Progress(progress / 100) + } + val onFile: (String) -> Unit = { file -> + log(Log.Info("Downloading $file")) + } + + val download = + if (isRoot) + downloader.downloadRoot(appVersions, onProgress, onFile) + else + downloader.download(appVersions, onProgress, onFile) + + when (download) { + is AppDownloader.DownloadStatus.Success -> { + log(Log.Success("Successfully downloaded $appName")) + installApp(appName, appVersions) + } + is AppDownloader.DownloadStatus.Error -> { + log(Log.Error( + displayText = "Failed to download ${download.fileName}", + stacktrace = download.error + )) } } } @@ -120,7 +123,20 @@ class InstallViewModel( status = Status.Installing - installer.install(appVersions) + if (isRoot) { + when (val installStatus = installer.installRoot(appVersions)) { + is PMRootResult.Success -> { + status = Status.Installed + log(Log.Success("Successfully installed")) + } + is PMRootResult.Error -> { + status = Status.Failure + log(Log.Error("Failed to install app", installStatus.message)) + } + } + } else { + installer.install(appVersions) + } } private fun getDownloader( diff --git a/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt b/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt index 7732618a..1a7b7e65 100644 --- a/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt +++ b/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import com.vanced.manager.core.installer.util.uninstallPackage +import com.vanced.manager.core.installer.util.PM import com.vanced.manager.core.preferences.holder.managerVariantPref import com.vanced.manager.domain.model.App import com.vanced.manager.network.util.MICROG_NAME @@ -80,7 +80,7 @@ class MainViewModel( fun uninstallApp( appPackage: String, ) { - uninstallPackage(appPackage, app) + PM.uninstallPackage(appPackage, app) } private suspend fun fetchData( diff --git a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/AccentColorItem.kt b/app/src/main/java/com/vanced/manager/ui/widget/settings/AccentColorItem.kt similarity index 94% rename from app/src/main/java/com/vanced/manager/ui/widget/screens/settings/AccentColorItem.kt rename to app/src/main/java/com/vanced/manager/ui/widget/settings/AccentColorItem.kt index 71da7a94..996ee857 100644 --- a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/AccentColorItem.kt +++ b/app/src/main/java/com/vanced/manager/ui/widget/settings/AccentColorItem.kt @@ -1,4 +1,4 @@ -package com.vanced.manager.ui.widget.screens.settings +package com.vanced.manager.ui.widget.settings import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ClearFilesItem.kt b/app/src/main/java/com/vanced/manager/ui/widget/settings/ClearFilesItem.kt similarity index 88% rename from app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ClearFilesItem.kt rename to app/src/main/java/com/vanced/manager/ui/widget/settings/ClearFilesItem.kt index 57cb4ed4..7dedf13e 100644 --- a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ClearFilesItem.kt +++ b/app/src/main/java/com/vanced/manager/ui/widget/settings/ClearFilesItem.kt @@ -1,4 +1,4 @@ -package com.vanced.manager.ui.widget.screens.settings +package com.vanced.manager.ui.widget.settings import androidx.compose.runtime.Composable import com.vanced.manager.R diff --git a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/CustomTabsItem.kt b/app/src/main/java/com/vanced/manager/ui/widget/settings/CustomTabsItem.kt similarity index 92% rename from app/src/main/java/com/vanced/manager/ui/widget/screens/settings/CustomTabsItem.kt rename to app/src/main/java/com/vanced/manager/ui/widget/settings/CustomTabsItem.kt index bfe7d412..b71f0da3 100644 --- a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/CustomTabsItem.kt +++ b/app/src/main/java/com/vanced/manager/ui/widget/settings/CustomTabsItem.kt @@ -1,4 +1,4 @@ -package com.vanced.manager.ui.widget.screens.settings +package com.vanced.manager.ui.widget.settings import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource diff --git a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ManagerVariantItem.kt b/app/src/main/java/com/vanced/manager/ui/widget/settings/ManagerVariantItem.kt similarity index 87% rename from app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ManagerVariantItem.kt rename to app/src/main/java/com/vanced/manager/ui/widget/settings/ManagerVariantItem.kt index 121a30a1..dd553b5a 100644 --- a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ManagerVariantItem.kt +++ b/app/src/main/java/com/vanced/manager/ui/widget/settings/ManagerVariantItem.kt @@ -1,9 +1,10 @@ -package com.vanced.manager.ui.widget.screens.settings +package com.vanced.manager.ui.widget.settings import androidx.compose.runtime.* import com.vanced.manager.R import com.vanced.manager.core.preferences.RadioButtonPreference import com.vanced.manager.core.preferences.holder.managerVariantPref +import com.vanced.manager.core.util.isMagiskInstalled import com.vanced.manager.ui.component.preference.SingleSelectDialogPreference import com.vanced.manager.ui.resources.managerString @@ -36,6 +37,9 @@ fun SettingsManagerVariantItem() { selectedKey = managerVariantPref }, onItemClick = { + if (it == "root" && !isMagiskInstalled) + return@SingleSelectDialogPreference + selectedKey = it }, onSave = { diff --git a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/NotificationsItem.kt b/app/src/main/java/com/vanced/manager/ui/widget/settings/NotificationsItem.kt similarity index 94% rename from app/src/main/java/com/vanced/manager/ui/widget/screens/settings/NotificationsItem.kt rename to app/src/main/java/com/vanced/manager/ui/widget/settings/NotificationsItem.kt index 5e6393e6..8fb8d8c6 100644 --- a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/NotificationsItem.kt +++ b/app/src/main/java/com/vanced/manager/ui/widget/settings/NotificationsItem.kt @@ -1,4 +1,4 @@ -package com.vanced.manager.ui.widget.screens.settings +package com.vanced.manager.ui.widget.settings import androidx.compose.runtime.Composable import com.vanced.manager.core.preferences.managerBooleanPreference diff --git a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ThemeItem.kt b/app/src/main/java/com/vanced/manager/ui/widget/settings/ThemeItem.kt similarity index 96% rename from app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ThemeItem.kt rename to app/src/main/java/com/vanced/manager/ui/widget/settings/ThemeItem.kt index 45392cef..4ae1d778 100644 --- a/app/src/main/java/com/vanced/manager/ui/widget/screens/settings/ThemeItem.kt +++ b/app/src/main/java/com/vanced/manager/ui/widget/settings/ThemeItem.kt @@ -1,4 +1,4 @@ -package com.vanced.manager.ui.widget.screens.settings +package com.vanced.manager.ui.widget.settings import androidx.compose.runtime.* import com.vanced.manager.R