installer adjustments
This commit is contained in:
parent
493b6d3963
commit
0a50882412
|
@ -120,7 +120,7 @@ dependencies {
|
|||
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
|
||||
implementation("com.squareup.retrofit2:converter-gson:$retrofitVersion")
|
||||
|
||||
implementation("com.github.x1nto:apkhelper:1.1.0")
|
||||
implementation("com.github.x1nto:apkhelper:1.1.2")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.2")
|
||||
|
|
|
@ -3,6 +3,18 @@
|
|||
package="com.vanced.manager">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
|
||||
<queries>
|
||||
<package android:name="com.vanced.android.youtube" />
|
||||
<package android:name="com.google.android.youtube" />
|
||||
<package android:name="com.vanced.android.apps.youtube.music" />
|
||||
<package android:name="com.google.android.apps.youtube.music" />
|
||||
<package android:name="com.mgoogle.android.gms" />
|
||||
<package android:name="com.vanced.faq" />
|
||||
<package android:name="com.android.vending" />
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:name=".ManagerApplication"
|
||||
|
@ -29,6 +41,8 @@
|
|||
android:theme="@style/Theme.MaterialComponents.NoActionBar"
|
||||
android:label="@string/app_name"/>
|
||||
|
||||
<service android:name="com.xinto.apkhelper.services.PackageManagerService" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -16,6 +16,7 @@ class ManagerApplication : Application() {
|
|||
modules(
|
||||
apiModule,
|
||||
downloaderModule,
|
||||
installerModule,
|
||||
mapperModule,
|
||||
networkModule,
|
||||
packageManagerModule,
|
||||
|
|
|
@ -2,16 +2,28 @@ package com.vanced.manager.di
|
|||
|
||||
import com.vanced.manager.downloader.api.VancedAPI
|
||||
import com.vanced.manager.downloader.impl.VancedDownloader
|
||||
import com.vanced.manager.installer.VancedInstaller
|
||||
import org.koin.dsl.module
|
||||
|
||||
val downloaderModule = module {
|
||||
|
||||
fun provideVancedDownloader(
|
||||
vancedAPI: VancedAPI,
|
||||
): VancedDownloader = VancedDownloader(vancedAPI)
|
||||
vancedInstaller: VancedInstaller
|
||||
): VancedDownloader = VancedDownloader(vancedAPI, vancedInstaller)
|
||||
|
||||
single {
|
||||
provideVancedDownloader(get())
|
||||
}
|
||||
fun provideMusicDownloader(
|
||||
vancedAPI: VancedAPI,
|
||||
vancedInstaller: VancedInstaller
|
||||
): VancedDownloader = VancedDownloader(vancedAPI, vancedInstaller)
|
||||
|
||||
fun provideMicrogDownloader(
|
||||
vancedAPI: VancedAPI,
|
||||
vancedInstaller: VancedInstaller
|
||||
): VancedDownloader = VancedDownloader(vancedAPI, vancedInstaller)
|
||||
|
||||
single { provideVancedDownloader(get(), get()) }
|
||||
single { provideMusicDownloader(get(), get()) }
|
||||
single { provideMicrogDownloader(get(), get()) }
|
||||
|
||||
}
|
|
@ -1,15 +1,19 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import android.content.Context
|
||||
import com.vanced.manager.installer.AppInstaller
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
import com.vanced.manager.installer.MicrogInstaller
|
||||
import com.vanced.manager.installer.MusicInstaller
|
||||
import com.vanced.manager.installer.VancedInstaller
|
||||
import org.koin.dsl.module
|
||||
|
||||
val installerModule = module {
|
||||
fun provideAppInstaller(
|
||||
context: Context,
|
||||
homeViewModel: HomeViewModel
|
||||
) = AppInstaller(context, homeViewModel)
|
||||
|
||||
single { provideAppInstaller(get(), get()) }
|
||||
fun provideVancedInstaller() = VancedInstaller()
|
||||
|
||||
fun provideMusicInstaller() = MusicInstaller()
|
||||
|
||||
fun provideMicrogInstaller() = MicrogInstaller()
|
||||
|
||||
single { provideVancedInstaller() }
|
||||
single { provideMusicInstaller() }
|
||||
single { provideMicrogInstaller() }
|
||||
}
|
|
@ -10,10 +10,8 @@ val mapperModule = module {
|
|||
|
||||
fun provideAppMapper(
|
||||
packageInformationDataSource: PackageInformationDataSource,
|
||||
vancedDownloader: VancedDownloader,
|
||||
): AppDtoMapper = AppDtoMapper(
|
||||
packageInformationDataSource = packageInformationDataSource,
|
||||
vancedDownloader = vancedDownloader
|
||||
packageInformationDataSource = packageInformationDataSource
|
||||
)
|
||||
|
||||
fun provideJsonMapper(
|
||||
|
@ -22,7 +20,7 @@ val mapperModule = module {
|
|||
appDtoMapper = appDtoMapper
|
||||
)
|
||||
|
||||
single { provideAppMapper(get(), get()) }
|
||||
single { provideAppMapper(get()) }
|
||||
single { provideJsonMapper(get()) }
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.vanced.manager.domain.model
|
||||
|
||||
import com.vanced.manager.downloader.base.BaseDownloader
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.ui.widgets.home.installation.InstallationOption
|
||||
|
||||
data class App(
|
||||
|
@ -21,6 +21,6 @@ data class App(
|
|||
val versions: List<String>? = null,
|
||||
val themes: List<String>? = null,
|
||||
val languages: List<String>? = null,
|
||||
val downloader: BaseDownloader? = null,
|
||||
val downloader: AppDownloader? = null,
|
||||
val installationOptions: List<InstallationOption>? = null
|
||||
)
|
|
@ -8,7 +8,7 @@ import retrofit2.http.Streaming
|
|||
|
||||
interface VancedAPI {
|
||||
|
||||
@GET("apks/{version}/{variant}/{type}/{apkName}.apk")
|
||||
@GET("apks/v{version}/{variant}/{type}/{apkName}")
|
||||
@Streaming
|
||||
fun getApk(
|
||||
@Path("version") version: String,
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
package com.vanced.manager.downloader.base
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
import com.vanced.manager.util.log
|
||||
import okhttp3.ResponseBody
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import retrofit2.Call
|
||||
import retrofit2.awaitResponse
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
abstract class AppDownloader(
|
||||
val appName: String,
|
||||
val appInstaller: AppInstaller
|
||||
) : KoinComponent {
|
||||
|
||||
data class File(
|
||||
val call: Call<ResponseBody>,
|
||||
val fileName: String
|
||||
)
|
||||
|
||||
var downloadProgress by mutableStateOf(0f)
|
||||
private set
|
||||
|
||||
var downloadFile by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var installing by mutableStateOf(false)
|
||||
var error by mutableStateOf(false)
|
||||
|
||||
val showDownloadScreen = mutableStateOf(false)
|
||||
|
||||
private var canceled = false
|
||||
|
||||
private lateinit var call: Call<ResponseBody>
|
||||
|
||||
private val tag = this::class.simpleName!!
|
||||
|
||||
private fun resetValues() {
|
||||
showDownloadScreen.value = false
|
||||
downloadProgress = 0f
|
||||
downloadFile = ""
|
||||
installing = false
|
||||
error = false
|
||||
canceled = false
|
||||
}
|
||||
|
||||
val context: Context by inject()
|
||||
|
||||
abstract suspend fun download(
|
||||
app: App,
|
||||
viewModel: HomeViewModel
|
||||
)
|
||||
|
||||
private fun install(viewModel: HomeViewModel) {
|
||||
installing = true
|
||||
appInstaller.install {
|
||||
viewModel.fetch()
|
||||
resetValues()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun downloadFile(
|
||||
file: File,
|
||||
viewModel: HomeViewModel,
|
||||
folderStructure: String,
|
||||
onError: (error: String) -> Unit = {},
|
||||
) {
|
||||
downloadFiles(
|
||||
files = listOf(file),
|
||||
viewModel = viewModel,
|
||||
folderStructure = folderStructure,
|
||||
onError = onError
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun downloadFiles(
|
||||
files: List<File>,
|
||||
viewModel: HomeViewModel,
|
||||
folderStructure: String,
|
||||
onError: (error: String) -> Unit = {},
|
||||
) {
|
||||
showDownloadScreen.value = true
|
||||
fun error(errorBody: String) {
|
||||
error = true
|
||||
onError(errorBody)
|
||||
log(tag, errorBody)
|
||||
}
|
||||
try {
|
||||
for (file in files) {
|
||||
if (canceled) {
|
||||
break
|
||||
}
|
||||
|
||||
val fileName = file.fileName
|
||||
downloadFile = fileName
|
||||
this.call = file.call
|
||||
val response = call.awaitResponse()
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body()
|
||||
if (body != null) {
|
||||
if (!writeFile(body, fileName, folderStructure)) {
|
||||
error("Failed to write file data")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val error = response.errorBody()?.toString()
|
||||
if (error != null) {
|
||||
error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
error(e.stackTraceToString())
|
||||
}
|
||||
|
||||
if (canceled) {
|
||||
resetValues()
|
||||
return
|
||||
}
|
||||
|
||||
install(viewModel)
|
||||
}
|
||||
|
||||
fun cancelDownload() {
|
||||
canceled = true
|
||||
call.cancel()
|
||||
}
|
||||
|
||||
private fun writeFile(
|
||||
body: ResponseBody,
|
||||
fileName: String,
|
||||
folderStructure: String
|
||||
): Boolean {
|
||||
val folder = File("${context.getExternalFilesDir(appName)?.path}/$folderStructure")
|
||||
folder.mkdirs()
|
||||
val file = File("${folder.path}/$fileName")
|
||||
val inputStream = body.byteStream()
|
||||
val outputStream = FileOutputStream(file)
|
||||
return try {
|
||||
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
|
||||
downloadProgress = (downloadedBytes * 100 / totalBytes).toFloat()
|
||||
}
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
} finally {
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package com.vanced.manager.downloader.base
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.vanced.manager.installer.AppInstaller
|
||||
import com.vanced.manager.util.log
|
||||
import okhttp3.ResponseBody
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import retrofit2.Call
|
||||
import retrofit2.awaitResponse
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
abstract class BaseDownloader(
|
||||
val appName: String
|
||||
) : KoinComponent {
|
||||
|
||||
var downloadProgress by mutableStateOf(0f)
|
||||
private set
|
||||
|
||||
var downloadFile by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var installing by mutableStateOf(false)
|
||||
|
||||
private lateinit var call: Call<ResponseBody>
|
||||
|
||||
private val tag = this::class.simpleName!!
|
||||
|
||||
val context: Context by inject()
|
||||
val appInstaller: AppInstaller by inject()
|
||||
|
||||
abstract suspend fun download()
|
||||
|
||||
suspend fun downloadFile(
|
||||
call: Call<ResponseBody>,
|
||||
folderStructure: String,
|
||||
fileName: String,
|
||||
onError: (error: String) -> Unit = {},
|
||||
onComplete: suspend () -> Unit = {},
|
||||
) {
|
||||
fun error(errorBody: String) {
|
||||
downloadFile = "Error downloading $fileName"
|
||||
onError(errorBody)
|
||||
log(tag, errorBody)
|
||||
}
|
||||
try {
|
||||
this.call = call
|
||||
val response = call.awaitResponse()
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body()
|
||||
if (body != null) {
|
||||
if (writeFile(body, fileName, folderStructure)) {
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val error = response.errorBody()?.toString()
|
||||
if (error != null) {
|
||||
error(error)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
error(e.stackTraceToString())
|
||||
}
|
||||
downloadProgress = 0f
|
||||
}
|
||||
|
||||
fun cancelDownload() {
|
||||
call.cancel()
|
||||
}
|
||||
|
||||
private fun writeFile(
|
||||
body: ResponseBody,
|
||||
fileName: String,
|
||||
folderStructure: String
|
||||
): Boolean {
|
||||
val file = File("${context.getExternalFilesDir(appName)?.path}/$folderStructure/$fileName")
|
||||
val inputStream = body.byteStream()
|
||||
val outputStream = FileOutputStream(file)
|
||||
return try {
|
||||
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
|
||||
downloadProgress = (downloadedBytes * 100 / totalBytes).toFloat()
|
||||
}
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
} finally {
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
package com.vanced.manager.downloader.impl
|
||||
|
||||
import com.vanced.manager.downloader.base.BaseDownloader
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
|
||||
object MicrogDownloader : BaseDownloader("") {
|
||||
|
||||
override suspend fun download() {
|
||||
class MicrogDownloader : AppDownloader("") {
|
||||
|
||||
override suspend fun download(app: App, viewModel: HomeViewModel) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
package com.vanced.manager.downloader.impl
|
||||
|
||||
import com.vanced.manager.downloader.base.BaseDownloader
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
|
||||
object MusicDownloader : BaseDownloader("") {
|
||||
|
||||
override suspend fun download() {
|
||||
class MusicDownloader : AppDownloader("") {
|
||||
|
||||
override suspend fun download(app: App, viewModel: HomeViewModel) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
|
@ -1,73 +1,119 @@
|
|||
package com.vanced.manager.downloader.impl
|
||||
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.downloader.api.VancedAPI
|
||||
import com.vanced.manager.downloader.base.BaseDownloader
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.installer.VancedInstaller
|
||||
import com.vanced.manager.ui.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.ui.preferences.holder.vancedLanguagesPref
|
||||
import com.vanced.manager.ui.preferences.holder.vancedThemePref
|
||||
import com.vanced.manager.ui.preferences.holder.vancedVersionPref
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
import com.vanced.manager.util.arch
|
||||
import com.vanced.manager.util.log
|
||||
|
||||
class VancedDownloader(
|
||||
private val vancedAPI: VancedAPI,
|
||||
) : BaseDownloader(
|
||||
appName = "vanced"
|
||||
vancedInstaller: VancedInstaller
|
||||
) : AppDownloader(
|
||||
appName = "vanced",
|
||||
appInstaller = vancedInstaller
|
||||
) {
|
||||
|
||||
private lateinit var version: String
|
||||
private lateinit var variant: String
|
||||
private val theme by vancedThemePref
|
||||
private val version by vancedVersionPref
|
||||
private val variant by managerVariantPref
|
||||
private val languages by vancedLanguagesPref
|
||||
|
||||
private lateinit var folderStructure: String
|
||||
|
||||
override suspend fun download() {
|
||||
version = "v16.16.38"
|
||||
variant = "nonroot"
|
||||
folderStructure = "$appName/$version/$variant"
|
||||
downloadTheme()
|
||||
}
|
||||
|
||||
private suspend fun downloadTheme() {
|
||||
downloadVancedApk(
|
||||
type = "Theme",
|
||||
apkName = "black.apk"
|
||||
) {
|
||||
downloadArch()
|
||||
override suspend fun download(app: App, viewModel: HomeViewModel) {
|
||||
val files = listOf(
|
||||
getFile(
|
||||
type = "Theme",
|
||||
apkName = "$theme.apk"
|
||||
),
|
||||
getFile(
|
||||
type = "Arch",
|
||||
apkName = "split_config.$arch.apk"
|
||||
)
|
||||
) + languages.map { language ->
|
||||
getFile("Language", "split_config.$language.apk")
|
||||
}
|
||||
downloadFiles(
|
||||
files = files,
|
||||
viewModel,
|
||||
folderStructure = "$version/$variant",
|
||||
onError = {
|
||||
log("error", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun downloadArch() {
|
||||
downloadVancedApk(
|
||||
type = "Arch",
|
||||
apkName = "split_config.x86.apk"
|
||||
) {
|
||||
downloadLanguage()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun downloadLanguage() {
|
||||
downloadVancedApk(
|
||||
type = "Language",
|
||||
apkName = "split_config.en.apk"
|
||||
) {
|
||||
appInstaller.installVanced(version)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun downloadVancedApk(
|
||||
private fun getFile(
|
||||
type: String,
|
||||
apkName: String,
|
||||
onDownload: suspend () -> Unit,
|
||||
) {
|
||||
downloadFile(
|
||||
vancedAPI.getApk(
|
||||
version = version,
|
||||
variant = variant,
|
||||
type = type,
|
||||
apkName = apkName
|
||||
),
|
||||
folderStructure = folderStructure,
|
||||
fileName = apkName,
|
||||
onError = {
|
||||
) = File(
|
||||
call = vancedAPI.getApk(
|
||||
version = version,
|
||||
variant = variant,
|
||||
type = type,
|
||||
apkName = apkName
|
||||
),
|
||||
fileName = apkName
|
||||
)
|
||||
|
||||
}
|
||||
) {
|
||||
onDownload()
|
||||
}
|
||||
}
|
||||
// private suspend fun downloadTheme() {
|
||||
// val theme by vancedThemePref
|
||||
// downloadVancedApk(
|
||||
// type = "Theme",
|
||||
// apkName = "$theme.apk"
|
||||
// ) {
|
||||
// downloadArch()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private suspend fun downloadArch() {
|
||||
// downloadVancedApk(
|
||||
// type = "Arch",
|
||||
// apkName = "split_config.x86.apk"
|
||||
// ) {
|
||||
// downloadLanguage()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private suspend fun downloadLanguage() {
|
||||
// val languages by vancedLanguagesPref
|
||||
// languages.forEach { language ->
|
||||
// downloadVancedApk(
|
||||
// type = "Language",
|
||||
// apkName = "split_config.$language.apk"
|
||||
// ) {}
|
||||
// }
|
||||
// install()
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// private suspend fun downloadVancedApk(
|
||||
// type: String,
|
||||
// apkName: String,
|
||||
// onDownload: suspend () -> Unit,
|
||||
// ) {
|
||||
// downloadFile(
|
||||
// vancedAPI.getApk(
|
||||
// version = version,
|
||||
// variant = variant,
|
||||
// type = type,
|
||||
// apkName = apkName
|
||||
// ),
|
||||
// folderStructure = "$version/$variant",
|
||||
// fileName = apkName,
|
||||
// onError = {
|
||||
// log("error", it)
|
||||
// }
|
||||
// ) {
|
||||
// onDownload()
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package com.vanced.manager.installer
|
||||
|
||||
import android.content.Context
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
import com.xinto.apkhelper.installSplitApks
|
||||
import com.xinto.apkhelper.statusCallback
|
||||
import com.xinto.apkhelper.statusCallbackBuilder
|
||||
|
||||
class AppInstaller(
|
||||
private val context: Context,
|
||||
private val viewModel: HomeViewModel
|
||||
) {
|
||||
|
||||
fun installVanced(version: String) {
|
||||
installSplitApks(context.getExternalFilesDir("vanced/$version")!!.path, context)
|
||||
}
|
||||
|
||||
fun installMusic() {
|
||||
|
||||
}
|
||||
|
||||
fun installMicrog() {
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
statusCallback = statusCallbackBuilder(
|
||||
onAction = {
|
||||
viewModel.fetch()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.vanced.manager.installer
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.ui.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.ui.preferences.holder.vancedVersionPref
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
import com.xinto.apkhelper.installSplitApks
|
||||
|
||||
class MicrogInstaller : AppInstaller() {
|
||||
|
||||
override fun install(
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
super.install(onDone)
|
||||
|
||||
val version by vancedVersionPref
|
||||
val variant by managerVariantPref
|
||||
|
||||
installSplitApks(
|
||||
apksPath = context.getExternalFilesDir("vanced/$version/$variant")!!.path,
|
||||
context = context
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.vanced.manager.installer
|
||||
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.ui.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.ui.preferences.holder.vancedVersionPref
|
||||
import com.xinto.apkhelper.installSplitApks
|
||||
|
||||
class MusicInstaller : AppInstaller() {
|
||||
|
||||
override fun install(
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
super.install(onDone)
|
||||
|
||||
val version by vancedVersionPref
|
||||
val variant by managerVariantPref
|
||||
|
||||
installSplitApks(
|
||||
apksPath = context.getExternalFilesDir("vanced/$version/$variant")!!.path,
|
||||
context = context
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.vanced.manager.installer
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.ui.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.ui.preferences.holder.vancedVersionPref
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
import com.xinto.apkhelper.installSplitApks
|
||||
|
||||
class VancedInstaller : AppInstaller() {
|
||||
|
||||
override fun install(
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
super.install(onDone)
|
||||
|
||||
val version by vancedVersionPref
|
||||
val variant by managerVariantPref
|
||||
|
||||
installSplitApks(
|
||||
apksPath = context.getExternalFilesDir("vanced/$version/$variant")!!.path,
|
||||
context = context
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.vanced.manager.installer.base
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.CallSuper
|
||||
import com.vanced.manager.util.log
|
||||
import com.xinto.apkhelper.statusCallback
|
||||
import com.xinto.apkhelper.statusCallbackBuilder
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
open class AppInstaller : KoinComponent {
|
||||
|
||||
val context: Context by inject()
|
||||
|
||||
@CallSuper
|
||||
open fun install(
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
setStatusCallback(onDone = onDone)
|
||||
}
|
||||
|
||||
private fun setStatusCallback(
|
||||
onDone: () -> Unit
|
||||
) {
|
||||
statusCallback = statusCallbackBuilder(
|
||||
onInstall = { _, _ ->
|
||||
onDone()
|
||||
},
|
||||
onInstallFailed = { error, _, _ ->
|
||||
onDone()
|
||||
log("install", error)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -5,11 +5,6 @@ import com.vanced.manager.domain.datasource.PackageInformationDataSource
|
|||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.domain.model.AppStatus
|
||||
import com.vanced.manager.domain.util.EntityMapper
|
||||
import com.vanced.manager.downloader.base.BaseDownloader
|
||||
import com.vanced.manager.downloader.impl.MicrogDownloader
|
||||
import com.vanced.manager.downloader.impl.MusicDownloader
|
||||
import com.vanced.manager.downloader.impl.VancedDownloader
|
||||
import com.vanced.manager.network.util.MICROG_NAME
|
||||
import com.vanced.manager.network.util.MUSIC_NAME
|
||||
import com.vanced.manager.network.util.VANCED_NAME
|
||||
import com.vanced.manager.ui.preferences.CheckboxPreference
|
||||
|
@ -24,8 +19,7 @@ import com.vanced.manager.ui.widgets.home.installation.RadiobuttonInstallationOp
|
|||
import java.util.*
|
||||
|
||||
class AppDtoMapper(
|
||||
private val packageInformationDataSource: PackageInformationDataSource,
|
||||
private val vancedDownloader: VancedDownloader
|
||||
private val packageInformationDataSource: PackageInformationDataSource
|
||||
) : EntityMapper<AppDto, App> {
|
||||
|
||||
override suspend fun mapToModel(entity: AppDto): App =
|
||||
|
@ -52,7 +46,6 @@ class AppDtoMapper(
|
|||
versions = versions,
|
||||
themes = themes,
|
||||
languages = languages,
|
||||
downloader = getDownloader(name),
|
||||
installationOptions = getInstallationOptions(entity)
|
||||
)
|
||||
}
|
||||
|
@ -68,14 +61,6 @@ class AppDtoMapper(
|
|||
AppStatus.Install
|
||||
}
|
||||
|
||||
private fun getDownloader(app: String?): BaseDownloader? =
|
||||
when (app) {
|
||||
VANCED_NAME -> vancedDownloader
|
||||
MUSIC_NAME -> MusicDownloader
|
||||
MICROG_NAME -> MicrogDownloader
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getInstallationOptions(app: AppDto): List<InstallationOption>? =
|
||||
when (app.name) {
|
||||
VANCED_NAME -> listOf(
|
||||
|
|
|
@ -38,7 +38,7 @@ fun HomeLayout() {
|
|||
),
|
||||
contentPaddingHorizontal = 0.dp
|
||||
) {
|
||||
HomeAppsItem(viewModel, isFetching)
|
||||
HomeAppsItem(viewModel)
|
||||
}
|
||||
CategoryLayout(
|
||||
categoryName = managerString(
|
||||
|
|
|
@ -4,7 +4,7 @@ const val MANAGER_VARIANT_DEFAULT_VALUE = "nonroot"
|
|||
|
||||
const val MANAGER_THEME_DEFAULT_VALUE = "System Default"
|
||||
|
||||
const val VANCED_THEME_DEFAULT_VALUE = "Dark"
|
||||
const val VANCED_THEME_DEFAULT_VALUE = "dark"
|
||||
val VANCED_LANGUAGE_DEFAULT_VALUE = setOf("en")
|
||||
|
||||
const val APP_VERSION_DEFAULT_VALUE = "latest"
|
||||
|
|
|
@ -13,15 +13,14 @@ import com.vanced.manager.ui.widgets.home.apps.card.AppCard
|
|||
|
||||
@Composable
|
||||
fun HomeAppsItem(
|
||||
viewModel: HomeViewModel,
|
||||
isFetching: Boolean
|
||||
viewModel: HomeViewModel
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
viewModel.apps.fastForEach { app ->
|
||||
val observedApp by app.observeAsState(initial = App())
|
||||
AppCard(observedApp, isFetching)
|
||||
AppCard(observedApp, viewModel)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
package com.vanced.manager.ui.widgets.home.apps.card
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -18,21 +16,25 @@ import com.vanced.manager.ui.components.card.ManagerThemedCard
|
|||
import com.vanced.manager.ui.components.layout.ManagerButtonColumn
|
||||
import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.utils.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.viewmodel.HomeViewModel
|
||||
import com.vanced.manager.ui.widgets.button.ManagerCancelButton
|
||||
import com.vanced.manager.ui.widgets.button.ManagerDownloadButton
|
||||
import com.vanced.manager.ui.widgets.home.apps.dialog.AppChangelogDialog
|
||||
import com.vanced.manager.ui.widgets.home.apps.dialog.AppDownloadDialog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun AppCard(
|
||||
app: App,
|
||||
fetching: Boolean
|
||||
viewModel: HomeViewModel
|
||||
) {
|
||||
var showDownloadDialog by remember { mutableStateOf(false) }
|
||||
var showAppInfoDialog by remember { mutableStateOf(false) }
|
||||
var showAppInfoDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showInstallationOptions by remember { mutableStateOf(false) }
|
||||
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
|
||||
val icon = rememberGlidePainter(
|
||||
request = app.iconUrl ?: "",
|
||||
fadeIn = true
|
||||
|
@ -41,17 +43,21 @@ fun AppCard(
|
|||
val hasInstallationOptions = app.installationOptions != null
|
||||
val animationSpec = tween<IntSize>(400)
|
||||
|
||||
fun download() {
|
||||
val downloader = app.downloader
|
||||
|
||||
val download: () -> Unit = {
|
||||
showInstallationOptions = false
|
||||
coroutineScope.launch {
|
||||
downloader!!.download(app, viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
ManagerThemedCard {
|
||||
Column {
|
||||
AppInfoCard(
|
||||
appName = app.name ?: "",
|
||||
appName = app.name,
|
||||
icon = icon,
|
||||
fetching = fetching
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = defaultContentPaddingVertical)
|
||||
|
@ -63,7 +69,7 @@ fun AppCard(
|
|||
if (hasInstallationOptions) {
|
||||
showInstallationOptions = true
|
||||
} else {
|
||||
showDownloadDialog = true
|
||||
download()
|
||||
}
|
||||
},
|
||||
onInfoClick = {
|
||||
|
@ -97,9 +103,9 @@ fun AppCard(
|
|||
.padding(horizontal = defaultContentPaddingHorizontal)
|
||||
.padding(top = defaultContentPaddingVertical)
|
||||
) {
|
||||
ManagerDownloadButton {
|
||||
showDownloadDialog = true
|
||||
}
|
||||
ManagerDownloadButton(
|
||||
onClick = download
|
||||
)
|
||||
ManagerCancelButton {
|
||||
showInstallationOptions = false
|
||||
}
|
||||
|
@ -109,12 +115,12 @@ fun AppCard(
|
|||
}
|
||||
}
|
||||
|
||||
if (app.name != null && app.downloader != null && showDownloadDialog) {
|
||||
if (app.name != null && downloader != null && downloader.showDownloadScreen.value) {
|
||||
AppDownloadDialog(
|
||||
app = app.name,
|
||||
downloader = app.downloader,
|
||||
downloader = downloader,
|
||||
onCancelClick = {
|
||||
showDownloadDialog = false
|
||||
downloader.cancelDownload()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,9 +17,8 @@ import com.vanced.manager.ui.utils.defaultContentPaddingHorizontal
|
|||
|
||||
@Composable
|
||||
fun AppInfoCard(
|
||||
appName: String,
|
||||
appName: String?,
|
||||
icon: LoadPainter<Any>,
|
||||
fetching: Boolean
|
||||
) {
|
||||
ManagerCard {
|
||||
ManagerListItem(
|
||||
|
@ -29,8 +28,8 @@ fun AppInfoCard(
|
|||
),
|
||||
title = {
|
||||
ManagerText(
|
||||
modifier = Modifier.managerPlaceholder(fetching),
|
||||
text = appName,
|
||||
modifier = Modifier.managerPlaceholder(appName == null),
|
||||
text = appName ?: "",
|
||||
textStyle = MaterialTheme.typography.h5
|
||||
)
|
||||
},
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package com.vanced.manager.ui.widgets.home.apps.dialog
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vanced.manager.downloader.base.BaseDownloader
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.ui.components.dialog.ManagerDialog
|
||||
import com.vanced.manager.ui.widgets.button.ManagerCancelButton
|
||||
import com.vanced.manager.ui.widgets.home.download.AppDownloadDialogProgress
|
||||
|
@ -10,13 +9,9 @@ import com.vanced.manager.ui.widgets.home.download.AppDownloadDialogProgress
|
|||
@Composable
|
||||
fun AppDownloadDialog(
|
||||
app: String,
|
||||
downloader: BaseDownloader,
|
||||
downloader: AppDownloader,
|
||||
onCancelClick: () -> Unit,
|
||||
) {
|
||||
val rememberProgress = remember { downloader.downloadProgress }
|
||||
val rememberFile = remember { downloader.downloadFile }
|
||||
val rememberInstalling = remember { downloader.installing }
|
||||
|
||||
ManagerDialog(
|
||||
title = app,
|
||||
onDismissRequest = {},
|
||||
|
@ -25,9 +20,9 @@ fun AppDownloadDialog(
|
|||
}
|
||||
) {
|
||||
AppDownloadDialogProgress(
|
||||
progress = rememberProgress,
|
||||
file = rememberFile,
|
||||
installing = rememberInstalling
|
||||
progress = downloader.downloadProgress,
|
||||
file = downloader.downloadFile,
|
||||
installing = downloader.installing
|
||||
)
|
||||
}
|
||||
}
|
|
@ -2,12 +2,14 @@ package com.vanced.manager.ui.widgets.home.download
|
|||
|
||||
import androidx.compose.animation.core.animateIntAsState
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.components.progressindicator.ManagerProgressIndicator
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
|
@ -20,7 +22,7 @@ fun AppDownloadDialogProgress(
|
|||
) {
|
||||
when (installing) {
|
||||
true -> ManagerProgressIndicator()
|
||||
false -> ManagerProgressIndicator(progress = progress)
|
||||
false -> ManagerProgressIndicator(progress = progress / 100f)
|
||||
}
|
||||
val animatedProgress by animateIntAsState(targetValue = progress.toInt())
|
||||
Row {
|
||||
|
@ -35,9 +37,9 @@ fun AppDownloadDialogProgress(
|
|||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 4.dp)
|
||||
.wrapContentWidth(Alignment.End),
|
||||
text = "%$animatedProgress"
|
||||
text = "$animatedProgress%"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.vanced.manager.util
|
||||
|
||||
import android.os.Build
|
||||
|
||||
val arch = when {
|
||||
Build.SUPPORTED_ABIS.contains("x86") -> "x86"
|
||||
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
|
||||
else -> "armeabi_v7a"
|
||||
}
|
Loading…
Reference in New Issue