mirror of
https://github.com/YTVanced/VancedManager
synced 2024-11-26 05:05:15 +00:00
refactor
This commit is contained in:
parent
4075bd154f
commit
9d3529e8a5
91 changed files with 1305 additions and 1187 deletions
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -78,7 +79,6 @@ val languages: String get() {
|
|||
|
||||
dependencies {
|
||||
implementation(kotlin("reflect"))
|
||||
|
||||
implementation("androidx.core:core-ktx:1.6.0")
|
||||
implementation("androidx.appcompat:appcompat:1.3.1")
|
||||
implementation("com.google.android.material:material:1.4.0")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.downloader.api
|
||||
package com.vanced.manager.core.downloader.api
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Call
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.downloader.api
|
||||
package com.vanced.manager.core.downloader.api
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Call
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.downloader.api
|
||||
package com.vanced.manager.core.downloader.api
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Call
|
|
@ -0,0 +1,85 @@
|
|||
package com.vanced.manager.core.downloader.base
|
||||
|
||||
import com.vanced.manager.core.downloader.util.DownloadStatus
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Call
|
||||
import retrofit2.awaitResponse
|
||||
import java.io.FileOutputStream
|
||||
|
||||
typealias DownloadCall = Call<ResponseBody>
|
||||
|
||||
abstract class AppDownloader {
|
||||
|
||||
data class DownloadFile(
|
||||
val fileName: String,
|
||||
val call: DownloadCall,
|
||||
)
|
||||
|
||||
private lateinit var call: DownloadCall
|
||||
|
||||
abstract suspend fun download(
|
||||
appVersions: List<String>,
|
||||
onStatus: (DownloadStatus) -> Unit
|
||||
)
|
||||
|
||||
abstract fun getSavedFilePath(): String
|
||||
|
||||
suspend fun downloadFiles(
|
||||
downloadFiles: Array<DownloadFile>,
|
||||
onFile: (String) -> Unit,
|
||||
onProgress: (Float) -> Unit,
|
||||
onError: (error: String, fileName: String) -> Unit,
|
||||
onSuccess: () -> Unit
|
||||
) {
|
||||
for (downloadFile in downloadFiles) {
|
||||
try {
|
||||
this.call = downloadFile.call
|
||||
|
||||
onFile(downloadFile.fileName)
|
||||
|
||||
val response = call.awaitResponse()
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body()
|
||||
if (body != null) {
|
||||
writeFile(body, downloadFile.fileName) { progress ->
|
||||
onProgress(progress)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val error = response.errorBody()?.toString()
|
||||
if (error != null) {
|
||||
onError(error, downloadFile.fileName)
|
||||
return
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
onError(e.stackTraceToString(), downloadFile.fileName)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
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 java.io.File
|
||||
|
||||
class MicrogDownloader(
|
||||
private val microgAPI: MicrogAPI,
|
||||
private val context: Context,
|
||||
) : AppDownloader() {
|
||||
|
||||
override suspend fun download(
|
||||
appVersions: List<String>,
|
||||
onStatus: (DownloadStatus) -> Unit
|
||||
) {
|
||||
downloadFiles(
|
||||
downloadFiles = 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
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSavedFilePath(): String {
|
||||
val directory =
|
||||
File(context.getExternalFilesDir("microg")!!.path)
|
||||
|
||||
if (!directory.exists())
|
||||
directory.mkdirs()
|
||||
|
||||
return directory.path
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
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.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.core.preferences.holder.musicVersionPref
|
||||
import com.vanced.manager.core.util.getLatestOrProvidedAppVersion
|
||||
import java.io.File
|
||||
|
||||
class MusicDownloader(
|
||||
private val musicAPI: MusicAPI,
|
||||
private val context: Context,
|
||||
) : AppDownloader() {
|
||||
|
||||
private val version by musicVersionPref
|
||||
private val variant by managerVariantPref
|
||||
|
||||
private lateinit var correctVersion: String
|
||||
|
||||
override suspend fun download(
|
||||
appVersions: List<String>,
|
||||
onStatus: (DownloadStatus) -> Unit
|
||||
) {
|
||||
correctVersion = getLatestOrProvidedAppVersion(version, appVersions)
|
||||
|
||||
downloadFiles(
|
||||
downloadFiles = arrayOf(DownloadFile(
|
||||
call = musicAPI.getFiles(
|
||||
version = correctVersion,
|
||||
variant = variant,
|
||||
),
|
||||
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
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSavedFilePath(): String {
|
||||
val directory =
|
||||
File(context.getExternalFilesDir("vancedmusic")!!.path + "$correctVersion/$variant")
|
||||
|
||||
if (!directory.exists())
|
||||
directory.mkdirs()
|
||||
|
||||
return directory.path
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
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.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.core.preferences.holder.vancedLanguagesPref
|
||||
import com.vanced.manager.core.preferences.holder.vancedThemePref
|
||||
import com.vanced.manager.core.preferences.holder.vancedVersionPref
|
||||
import com.vanced.manager.core.util.arch
|
||||
import com.vanced.manager.core.util.getLatestOrProvidedAppVersion
|
||||
import java.io.File
|
||||
|
||||
class VancedDownloader(
|
||||
private val vancedAPI: VancedAPI,
|
||||
private val context: Context,
|
||||
) : AppDownloader() {
|
||||
|
||||
private val theme by vancedThemePref
|
||||
private val version by vancedVersionPref
|
||||
private val variant by managerVariantPref
|
||||
private val languages by vancedLanguagesPref
|
||||
|
||||
private lateinit var correctVersion: String
|
||||
|
||||
override suspend fun download(
|
||||
appVersions: List<String>,
|
||||
onStatus: (DownloadStatus) -> Unit
|
||||
) {
|
||||
correctVersion = getLatestOrProvidedAppVersion(version, appVersions)
|
||||
|
||||
val files = arrayOf(
|
||||
getFile(
|
||||
type = "Theme",
|
||||
apkName = "$theme.apk",
|
||||
),
|
||||
getFile(
|
||||
type = "Arch",
|
||||
apkName = "split_config.$arch.apk",
|
||||
)
|
||||
) + languages.map { language ->
|
||||
getFile(
|
||||
type = "Language",
|
||||
apkName = "split_config.$language.apk",
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSavedFilePath(): String {
|
||||
val directory =
|
||||
File(context.getExternalFilesDir("vanced")!!.path + "$correctVersion/$variant")
|
||||
|
||||
if (!directory.exists())
|
||||
directory.mkdirs()
|
||||
|
||||
return directory.path
|
||||
}
|
||||
|
||||
private fun getFile(
|
||||
type: String,
|
||||
apkName: String,
|
||||
) = DownloadFile(
|
||||
call = vancedAPI.getFiles(
|
||||
version = correctVersion,
|
||||
variant = variant,
|
||||
type = type,
|
||||
apkName = apkName
|
||||
),
|
||||
fileName = apkName
|
||||
)
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
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()
|
||||
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package com.vanced.manager.installer.base
|
||||
package com.vanced.manager.core.installer.base
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.CallSuper
|
||||
import com.vanced.manager.util.log
|
||||
import com.vanced.manager.core.util.log
|
||||
import com.xinto.apkhelper.statusCallback
|
||||
import com.xinto.apkhelper.statusCallbackBuilder
|
||||
import org.koin.core.component.KoinComponent
|
|
@ -1,6 +1,6 @@
|
|||
package com.vanced.manager.installer.impl
|
||||
package com.vanced.manager.core.installer.impl
|
||||
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.core.installer.base.AppInstaller
|
||||
import com.xinto.apkhelper.installApk
|
||||
|
||||
class MicrogInstaller : AppInstaller() {
|
|
@ -1,8 +1,8 @@
|
|||
package com.vanced.manager.installer.impl
|
||||
package com.vanced.manager.core.installer.impl
|
||||
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.preferences.holder.musicVersionPref
|
||||
import com.vanced.manager.core.installer.base.AppInstaller
|
||||
import com.vanced.manager.core.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.core.preferences.holder.musicVersionPref
|
||||
import com.xinto.apkhelper.installApk
|
||||
|
||||
class MusicInstaller : AppInstaller() {
|
|
@ -1,8 +1,8 @@
|
|||
package com.vanced.manager.installer.impl
|
||||
package com.vanced.manager.core.installer.impl
|
||||
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.preferences.holder.vancedVersionPref
|
||||
import com.vanced.manager.core.installer.base.AppInstaller
|
||||
import com.vanced.manager.core.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.core.preferences.holder.vancedVersionPref
|
||||
import com.xinto.apkhelper.installSplitApks
|
||||
|
||||
class VancedInstaller : AppInstaller() {
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.preferences
|
||||
package com.vanced.manager.core.preferences
|
||||
|
||||
data class CheckboxPreference(
|
||||
val title: String,
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.preferences
|
||||
package com.vanced.manager.core.preferences
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.compose.runtime.State
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.preferences
|
||||
package com.vanced.manager.core.preferences
|
||||
|
||||
data class RadioButtonPreference(
|
||||
val title: String,
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.preferences.holder
|
||||
package com.vanced.manager.core.preferences.holder
|
||||
|
||||
const val MANAGER_VARIANT_DEFAULT_VALUE = "nonroot"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
package com.vanced.manager.preferences.holder
|
||||
package com.vanced.manager.core.preferences.holder
|
||||
|
||||
import com.vanced.manager.preferences.managerBooleanPreference
|
||||
import com.vanced.manager.preferences.managerLongPreference
|
||||
import com.vanced.manager.preferences.managerStringPreference
|
||||
import com.vanced.manager.preferences.managerStringSetPreference
|
||||
import com.vanced.manager.core.preferences.managerBooleanPreference
|
||||
import com.vanced.manager.core.preferences.managerLongPreference
|
||||
import com.vanced.manager.core.preferences.managerStringPreference
|
||||
import com.vanced.manager.core.preferences.managerStringSetPreference
|
||||
import com.vanced.manager.ui.theme.defAccentColor
|
||||
|
||||
val useCustomTabsPref = managerBooleanPreference(USE_CUSTOM_TABS_KEY)
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.preferences.holder
|
||||
package com.vanced.manager.core.preferences.holder
|
||||
|
||||
const val USE_CUSTOM_TABS_KEY = "use_custom_tabs"
|
||||
const val MANAGER_VARIANT_KEY = "manager_variant"
|
11
app/src/main/java/com/vanced/manager/core/util/AppHelper.kt
Normal file
11
app/src/main/java/com/vanced/manager/core/util/AppHelper.kt
Normal file
|
@ -0,0 +1,11 @@
|
|||
package com.vanced.manager.core.util
|
||||
|
||||
fun getLatestOrProvidedAppVersion(
|
||||
version: String,
|
||||
appVersions: List<String>
|
||||
): String {
|
||||
if (appVersions.contains(version))
|
||||
return version
|
||||
|
||||
return appVersions.last()
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.util
|
||||
package com.vanced.manager.core.util
|
||||
|
||||
import com.vanced.manager.domain.model.NotificationPrefModel
|
||||
import com.vanced.manager.network.util.MICROG_NAME
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.util
|
||||
package com.vanced.manager.core.util
|
||||
|
||||
import android.os.Build
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
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<Message>()
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.util
|
||||
package com.vanced.manager.core.util
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.ui.graphics.Color
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.util
|
||||
package com.vanced.manager.core.util
|
||||
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.domain.model.Link
|
|
@ -1,4 +1,4 @@
|
|||
package com.vanced.manager.util
|
||||
package com.vanced.manager.core.util
|
||||
|
||||
enum class Variant {
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import com.vanced.manager.downloader.api.MicrogAPI
|
||||
import com.vanced.manager.downloader.api.MusicAPI
|
||||
import com.vanced.manager.downloader.api.VancedAPI
|
||||
import com.vanced.manager.core.downloader.api.MicrogAPI
|
||||
import com.vanced.manager.core.downloader.api.MusicAPI
|
||||
import com.vanced.manager.core.downloader.api.VancedAPI
|
||||
import com.vanced.manager.network.util.BASE
|
||||
import okhttp3.OkHttpClient
|
||||
import org.koin.dsl.module
|
||||
|
|
|
@ -1,34 +1,32 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import com.vanced.manager.downloader.api.MicrogAPI
|
||||
import com.vanced.manager.downloader.api.MusicAPI
|
||||
import com.vanced.manager.downloader.api.VancedAPI
|
||||
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.installer.impl.MicrogInstaller
|
||||
import com.vanced.manager.installer.impl.MusicInstaller
|
||||
import com.vanced.manager.installer.impl.VancedInstaller
|
||||
import android.content.Context
|
||||
import com.vanced.manager.core.downloader.api.MicrogAPI
|
||||
import com.vanced.manager.core.downloader.api.MusicAPI
|
||||
import com.vanced.manager.core.downloader.api.VancedAPI
|
||||
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 org.koin.dsl.module
|
||||
|
||||
val downloaderModule = module {
|
||||
|
||||
fun provideVancedDownloader(
|
||||
vancedInstaller: VancedInstaller,
|
||||
vancedAPI: VancedAPI,
|
||||
) = VancedDownloader(vancedInstaller, vancedAPI)
|
||||
context: Context,
|
||||
) = VancedDownloader(vancedAPI, context)
|
||||
|
||||
fun provideMusicDownloader(
|
||||
musicInstaller: MusicInstaller,
|
||||
musicAPI: MusicAPI,
|
||||
) = MusicDownloader(musicInstaller, musicAPI)
|
||||
context: Context,
|
||||
) = MusicDownloader(musicAPI, context)
|
||||
|
||||
fun provideMicrogDownloader(
|
||||
microgInstaller: MicrogInstaller,
|
||||
microgAPI: MicrogAPI,
|
||||
) = MicrogDownloader(microgInstaller, microgAPI)
|
||||
context: Context,
|
||||
) = MicrogDownloader(microgAPI, context)
|
||||
|
||||
single { provideVancedDownloader(get(), get()) }
|
||||
single { provideMusicDownloader(get(), get()) }
|
||||
single { provideMicrogDownloader(get(), get()) }
|
||||
factory { provideVancedDownloader(get(), get()) }
|
||||
factory { provideMusicDownloader(get(), get()) }
|
||||
factory { provideMicrogDownloader(get(), get()) }
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import com.vanced.manager.installer.impl.MicrogInstaller
|
||||
import com.vanced.manager.installer.impl.MusicInstaller
|
||||
import com.vanced.manager.installer.impl.VancedInstaller
|
||||
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 org.koin.dsl.module
|
||||
|
||||
val installerModule = module {
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import android.content.Context
|
||||
import com.vanced.manager.domain.datasource.PackageInformationDataSource
|
||||
import com.vanced.manager.network.model.AppDtoMapper
|
||||
import com.vanced.manager.network.model.JsonDtoMapper
|
||||
import org.koin.dsl.module
|
||||
|
||||
val mapperModule = module {
|
||||
|
||||
fun provideAppMapper(): AppDtoMapper = AppDtoMapper()
|
||||
fun provideAppMapper(
|
||||
packageInformationDataSource: PackageInformationDataSource,
|
||||
context: Context,
|
||||
) = AppDtoMapper(packageInformationDataSource, context)
|
||||
|
||||
fun provideJsonMapper(
|
||||
appDtoMapper: AppDtoMapper
|
||||
|
@ -14,6 +19,6 @@ val mapperModule = module {
|
|||
appDtoMapper = appDtoMapper
|
||||
)
|
||||
|
||||
single { provideAppMapper() }
|
||||
single { provideAppMapper(get(), get()) }
|
||||
single { provideJsonMapper(get()) }
|
||||
}
|
|
@ -1,26 +1,22 @@
|
|||
package com.vanced.manager.domain.model
|
||||
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.ui.widget.screens.home.installation.InstallationOption
|
||||
|
||||
data class App(
|
||||
val name: String? = null,
|
||||
val remoteVersion: String? = null,
|
||||
val remoteVersionCode: Int? = null,
|
||||
val installedVersion: String? = null,
|
||||
val installedVersionCode: Int? = null,
|
||||
val installedVersionRoot: String? = null,
|
||||
val installedVersionCodeRoot: Int? = null,
|
||||
val iconUrl: String? = "",
|
||||
val appStatus: AppStatus = AppStatus.Install,
|
||||
val appStatusRoot: AppStatus = AppStatus.Install,
|
||||
val packageName: String? = null,
|
||||
val packageNameRoot: String? = null,
|
||||
val changelog: String? = null,
|
||||
val url: String? = null,
|
||||
val versions: List<String>? = null,
|
||||
val themes: List<String>? = null,
|
||||
val languages: List<String>? = null,
|
||||
val downloader: AppDownloader? = null,
|
||||
val installationOptions: List<InstallationOption>? = null
|
||||
val name: String,
|
||||
val remoteVersion: String,
|
||||
val remoteVersionCode: Int,
|
||||
val installedVersion: String?,
|
||||
val installedVersionCode: Int?,
|
||||
val installedVersionRoot: String?,
|
||||
val installedVersionCodeRoot: Int?,
|
||||
val iconUrl: String?,
|
||||
val appStatus: AppStatus,
|
||||
val appStatusRoot: AppStatus,
|
||||
val packageName: String,
|
||||
val packageNameRoot: String?,
|
||||
val changelog: String,
|
||||
val url: String?,
|
||||
val versions: List<String>?,
|
||||
val themes: List<String>?,
|
||||
val languages: List<String>?,
|
||||
val installationOptions: List<InstallationOption>?
|
||||
)
|
|
@ -0,0 +1,34 @@
|
|||
package com.vanced.manager.domain.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.StringRes
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
sealed class InstallationOption(
|
||||
@StringRes val itemTitleId: Int,
|
||||
) : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data class MultiSelect(
|
||||
@StringRes val titleId: Int,
|
||||
val items: List<InstallationOptionItem>,
|
||||
val getOption: () -> Set<String>,
|
||||
val addOption: (String) -> Unit,
|
||||
val removeOption: (String) -> Unit
|
||||
) : InstallationOption(titleId)
|
||||
|
||||
@Parcelize
|
||||
data class SingleSelect(
|
||||
@StringRes val titleId: Int,
|
||||
val items: List<InstallationOptionItem>,
|
||||
val getOption: () -> String,
|
||||
val setOption: (String) -> Unit,
|
||||
) : InstallationOption(titleId)
|
||||
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class InstallationOptionItem(
|
||||
val displayText: String,
|
||||
val key: String,
|
||||
) : Parcelable
|
|
@ -1,5 +1,6 @@
|
|||
package com.vanced.manager.domain.pkg
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
|
||||
|
@ -21,6 +22,7 @@ class PkgManagerImpl(
|
|||
const val MAJOR_IGNORE = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
@Suppress("DEPRECATION")
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
override suspend fun getVersionCode(packageName: String): Int {
|
||||
|
@ -37,6 +39,7 @@ class PkgManagerImpl(
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
override suspend fun getVersionName(packageName: String): String =
|
||||
packageManager.getPackageInfo(packageName, PACKAGE_FLAG_ALL_OFF)
|
||||
|
|
|
@ -1,168 +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.domain.model.App
|
||||
import com.vanced.manager.installer.base.AppInstaller
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
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
|
||||
|
||||
//TODO remove showDownloadScreen and viewModel references
|
||||
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: MainViewModel
|
||||
)
|
||||
|
||||
private fun install(viewModel: MainViewModel) {
|
||||
installing = true
|
||||
appInstaller.install {
|
||||
viewModel.fetch()
|
||||
resetValues()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun downloadFile(
|
||||
file: File,
|
||||
viewModel: MainViewModel,
|
||||
folderStructure: String,
|
||||
onError: (error: String) -> Unit = {},
|
||||
) {
|
||||
downloadFiles(
|
||||
files = listOf(file),
|
||||
viewModel = viewModel,
|
||||
folderStructure = folderStructure,
|
||||
onError = onError
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun downloadFiles(
|
||||
files: List<File>,
|
||||
viewModel: MainViewModel,
|
||||
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,31 +0,0 @@
|
|||
package com.vanced.manager.downloader.impl
|
||||
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.downloader.api.MicrogAPI
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.installer.impl.MicrogInstaller
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
|
||||
class MicrogDownloader(
|
||||
microgInstaller: MicrogInstaller,
|
||||
private val microgAPI: MicrogAPI,
|
||||
) : AppDownloader(
|
||||
appName = "microg",
|
||||
appInstaller = microgInstaller
|
||||
) {
|
||||
|
||||
override suspend fun download(
|
||||
app: App,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
downloadFile(
|
||||
file = File(
|
||||
call = microgAPI.getFile(),
|
||||
fileName = "microg.apk"
|
||||
),
|
||||
viewModel = viewModel,
|
||||
folderStructure = ""
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package com.vanced.manager.downloader.impl
|
||||
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.downloader.api.MusicAPI
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.installer.impl.MusicInstaller
|
||||
import com.vanced.manager.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.preferences.holder.musicVersionPref
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
import com.vanced.manager.util.getLatestOrProvidedAppVersion
|
||||
|
||||
class MusicDownloader(
|
||||
musicInstaller: MusicInstaller,
|
||||
private val musicAPI: MusicAPI,
|
||||
) : AppDownloader(
|
||||
appName = "music",
|
||||
appInstaller = musicInstaller
|
||||
) {
|
||||
|
||||
private val version by musicVersionPref
|
||||
private val variant by managerVariantPref
|
||||
|
||||
override suspend fun download(
|
||||
app: App,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
val correctVersion = getLatestOrProvidedAppVersion(
|
||||
version = version,
|
||||
app = app
|
||||
)
|
||||
downloadFile(
|
||||
file = File(
|
||||
call = musicAPI.getFiles(
|
||||
version = correctVersion,
|
||||
variant = variant,
|
||||
),
|
||||
fileName = "music.apk"
|
||||
),
|
||||
viewModel = viewModel,
|
||||
folderStructure = "$correctVersion/$variant"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
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.AppDownloader
|
||||
import com.vanced.manager.installer.impl.VancedInstaller
|
||||
import com.vanced.manager.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.preferences.holder.vancedLanguagesPref
|
||||
import com.vanced.manager.preferences.holder.vancedThemePref
|
||||
import com.vanced.manager.preferences.holder.vancedVersionPref
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
import com.vanced.manager.util.arch
|
||||
import com.vanced.manager.util.getLatestOrProvidedAppVersion
|
||||
import com.vanced.manager.util.log
|
||||
|
||||
class VancedDownloader(
|
||||
vancedInstaller: VancedInstaller,
|
||||
private val vancedAPI: VancedAPI,
|
||||
) : AppDownloader(
|
||||
appName = "vanced",
|
||||
appInstaller = vancedInstaller
|
||||
) {
|
||||
|
||||
private val theme by vancedThemePref
|
||||
private val version by vancedVersionPref
|
||||
private val variant by managerVariantPref
|
||||
private val languages by vancedLanguagesPref
|
||||
|
||||
override suspend fun download(
|
||||
app: App,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
val correctVersion = getLatestOrProvidedAppVersion(
|
||||
version = version,
|
||||
app = app
|
||||
)
|
||||
val files = listOf(
|
||||
getFile(
|
||||
type = "Theme",
|
||||
apkName = "$theme.apk",
|
||||
version = correctVersion
|
||||
),
|
||||
getFile(
|
||||
type = "Arch",
|
||||
apkName = "split_config.$arch.apk",
|
||||
version = correctVersion
|
||||
)
|
||||
) + languages.map { language ->
|
||||
getFile(
|
||||
type = "Language",
|
||||
apkName = "split_config.$language.apk",
|
||||
version = correctVersion
|
||||
)
|
||||
}
|
||||
downloadFiles(
|
||||
files = files,
|
||||
viewModel,
|
||||
folderStructure = "$correctVersion/$variant",
|
||||
onError = {
|
||||
log("error", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFile(
|
||||
type: String,
|
||||
apkName: String,
|
||||
version: String,
|
||||
) = File(
|
||||
call = vancedAPI.getFiles(
|
||||
version = version,
|
||||
variant = variant,
|
||||
type = type,
|
||||
apkName = apkName
|
||||
),
|
||||
fileName = apkName
|
||||
)
|
||||
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package com.vanced.manager.ext
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
fun <T : Any> Call<T>.enqueue(
|
||||
onResponse: (call: Call<T>, response: Response<T>) -> Unit,
|
||||
onFailure: (call: Call<T>, t: Throwable) -> Unit
|
||||
) {
|
||||
enqueue(object : Callback<T> {
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<T>,
|
||||
response: Response<T>
|
||||
) = onResponse(call, response)
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<T>,
|
||||
t: Throwable
|
||||
) = onFailure(call, t)
|
||||
|
||||
})
|
||||
}
|
|
@ -3,15 +3,15 @@ package com.vanced.manager.network.model
|
|||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AppDto(
|
||||
@SerializedName("name") val name: String? = null,
|
||||
@SerializedName("version") val version: String? = null,
|
||||
@SerializedName("versionCode") val versionCode: Int? = null,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("version") val version: String,
|
||||
@SerializedName("versionCode") val versionCode: Int,
|
||||
@SerializedName("changelog") val changelog: String,
|
||||
@SerializedName("icon_url") val iconUrl: String? = null,
|
||||
@SerializedName("package_name") val packageName: String,
|
||||
@SerializedName("package_name_root") val packageNameRoot: String? = null,
|
||||
@SerializedName("url") val url: String? = null,
|
||||
@SerializedName("versions") val versions: List<String>? = null,
|
||||
@SerializedName("themes") val themes: List<String>? = null,
|
||||
@SerializedName("langs") val languages: List<String>? = null,
|
||||
@SerializedName("package_name") val packageName: String? = null,
|
||||
@SerializedName("package_name_root") val packageNameRoot: String? = null,
|
||||
@SerializedName("changelog") val changelog: String? = null,
|
||||
@SerializedName("icon_url") val iconUrl: String? = null,
|
||||
@SerializedName("url") val url: String? = null,
|
||||
)
|
||||
|
|
|
@ -2,48 +2,37 @@ package com.vanced.manager.network.model
|
|||
|
||||
import android.content.Context
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.preferences.holder.musicVersionPref
|
||||
import com.vanced.manager.core.preferences.holder.vancedLanguagesPref
|
||||
import com.vanced.manager.core.preferences.holder.vancedThemePref
|
||||
import com.vanced.manager.core.preferences.holder.vancedVersionPref
|
||||
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.model.InstallationOption
|
||||
import com.vanced.manager.domain.model.InstallationOptionItem
|
||||
import com.vanced.manager.domain.util.EntityMapper
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
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.preferences.CheckboxPreference
|
||||
import com.vanced.manager.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.preferences.holder.musicVersionPref
|
||||
import com.vanced.manager.preferences.holder.vancedLanguagesPref
|
||||
import com.vanced.manager.preferences.holder.vancedThemePref
|
||||
import com.vanced.manager.preferences.holder.vancedVersionPref
|
||||
import com.vanced.manager.ui.widget.screens.home.installation.CheckboxInstallationOption
|
||||
import com.vanced.manager.ui.widget.screens.home.installation.InstallationOption
|
||||
import com.vanced.manager.ui.widget.screens.home.installation.RadiobuttonInstallationOption
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
import org.koin.core.component.inject
|
||||
import java.util.*
|
||||
|
||||
class AppDtoMapper : EntityMapper<AppDto, App>, KoinComponent {
|
||||
|
||||
private val packageInformationDataSource: PackageInformationDataSource by inject()
|
||||
private val context: Context by inject()
|
||||
class AppDtoMapper(
|
||||
private val packageInformationDataSource: PackageInformationDataSource,
|
||||
context: Context
|
||||
) : EntityMapper<AppDto, App> {
|
||||
|
||||
private val latestVersionRadioButton =
|
||||
RadioButtonPreference(
|
||||
title = context.getString(R.string.app_version_dialog_option_latest),
|
||||
InstallationOptionItem(
|
||||
displayText = context.getString(R.string.app_version_dialog_option_latest),
|
||||
key = "latest"
|
||||
)
|
||||
|
||||
override suspend fun mapToModel(entity: AppDto): App =
|
||||
with(entity) {
|
||||
val localVersionCode = packageInformationDataSource.getVersionCode(packageName ?: "")
|
||||
val localVersionCode = packageInformationDataSource.getVersionCode(packageName)
|
||||
val localVersionCodeRoot =
|
||||
packageInformationDataSource.getVersionCode(packageNameRoot ?: "")
|
||||
val localVersionName = packageInformationDataSource.getVersionName(packageName ?: "")
|
||||
val localVersionName = packageInformationDataSource.getVersionName(packageName)
|
||||
val localVersionNameRoot =
|
||||
packageInformationDataSource.getVersionName(packageNameRoot ?: "")
|
||||
App(
|
||||
|
@ -64,12 +53,11 @@ class AppDtoMapper : EntityMapper<AppDto, App>, KoinComponent {
|
|||
versions = versions,
|
||||
themes = themes,
|
||||
languages = languages,
|
||||
downloader = getDownloader(name),
|
||||
installationOptions = getInstallationOptions(entity)
|
||||
installationOptions = getInstallationOptions(name, themes, versions, languages)
|
||||
)
|
||||
}
|
||||
|
||||
private fun compareVersionCodes(remote: Int?, local: Int?): AppStatus =
|
||||
private fun compareVersionCodes(remote: Int?, local: Int?) =
|
||||
if (local != null && remote != null) {
|
||||
when {
|
||||
remote > local -> AppStatus.Update
|
||||
|
@ -80,60 +68,73 @@ class AppDtoMapper : EntityMapper<AppDto, App>, KoinComponent {
|
|||
AppStatus.Install
|
||||
}
|
||||
|
||||
private fun getInstallationOptions(app: AppDto): List<InstallationOption>? =
|
||||
when (app.name) {
|
||||
private fun getInstallationOptions(
|
||||
appName: String,
|
||||
appThemes: List<String>?,
|
||||
appVersions: List<String>?,
|
||||
appLanguages: List<String>?,
|
||||
) = when (appName) {
|
||||
VANCED_NAME -> listOf(
|
||||
RadiobuttonInstallationOption(
|
||||
InstallationOption.SingleSelect(
|
||||
titleId = R.string.app_installation_options_theme,
|
||||
preference = vancedThemePref,
|
||||
buttons = app.themes?.map { version ->
|
||||
RadioButtonPreference(
|
||||
title = version.replaceFirstChar { it.titlecase(Locale.getDefault()) },
|
||||
getOption = { vancedThemePref.value.value },
|
||||
setOption = {
|
||||
vancedThemePref.save(it)
|
||||
},
|
||||
items = appThemes?.map { theme ->
|
||||
InstallationOptionItem(
|
||||
displayText = theme.replaceFirstChar {
|
||||
it.titlecase(Locale.getDefault())
|
||||
},
|
||||
key = theme
|
||||
)
|
||||
} ?: emptyList(),
|
||||
),
|
||||
InstallationOption.SingleSelect(
|
||||
titleId = R.string.app_installation_options_version,
|
||||
getOption = { vancedVersionPref.value.value },
|
||||
setOption = {
|
||||
vancedVersionPref.save(it)
|
||||
},
|
||||
items = appVersions?.map { version ->
|
||||
InstallationOptionItem(
|
||||
displayText = version,
|
||||
key = version
|
||||
)
|
||||
} ?: emptyList()
|
||||
}?.plus(latestVersionRadioButton)?.reversed() ?: emptyList(),
|
||||
),
|
||||
RadiobuttonInstallationOption(
|
||||
titleId = R.string.app_installation_options_version,
|
||||
preference = vancedVersionPref,
|
||||
buttons = app.versions?.map {
|
||||
RadioButtonPreference(
|
||||
title = it,
|
||||
key = it
|
||||
)
|
||||
}?.plus(latestVersionRadioButton)?.reversed() ?: emptyList()
|
||||
),
|
||||
CheckboxInstallationOption(
|
||||
InstallationOption.MultiSelect(
|
||||
titleId = R.string.app_installation_options_language,
|
||||
preference = vancedLanguagesPref,
|
||||
buttons = app.languages?.map {
|
||||
CheckboxPreference(
|
||||
title = Locale(it).displayName,
|
||||
key = it
|
||||
getOption = { vancedLanguagesPref.value.value },
|
||||
addOption = {
|
||||
vancedLanguagesPref.save(vancedLanguagesPref.value.value + it)
|
||||
},
|
||||
removeOption = {
|
||||
vancedLanguagesPref.save(vancedLanguagesPref.value.value - it)
|
||||
},
|
||||
items = appLanguages?.map { version ->
|
||||
InstallationOptionItem(
|
||||
displayText = version,
|
||||
key = version
|
||||
)
|
||||
} ?: emptyList()
|
||||
}?.plus(latestVersionRadioButton)?.reversed() ?: emptyList(),
|
||||
),
|
||||
)
|
||||
MUSIC_NAME -> listOf(
|
||||
RadiobuttonInstallationOption(
|
||||
InstallationOption.SingleSelect(
|
||||
titleId = R.string.app_installation_options_version,
|
||||
preference = musicVersionPref,
|
||||
buttons = app.versions?.map {
|
||||
RadioButtonPreference(
|
||||
title = it,
|
||||
key = it
|
||||
getOption = { musicVersionPref.value.value },
|
||||
setOption = {
|
||||
musicVersionPref.save(it)
|
||||
},
|
||||
items = appVersions?.map { version ->
|
||||
InstallationOptionItem(
|
||||
displayText = version,
|
||||
key = version
|
||||
)
|
||||
}?.plus(latestVersionRadioButton)?.reversed() ?: emptyList()
|
||||
),
|
||||
} ?: emptyList(),
|
||||
)
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun getDownloader(app: String?): AppDownloader? =
|
||||
when (app) {
|
||||
VANCED_NAME -> get<VancedDownloader>()
|
||||
MUSIC_NAME -> get<MusicDownloader>()
|
||||
MICROG_NAME -> get<MicrogDownloader>()
|
||||
else -> null
|
||||
}
|
||||
}
|
|
@ -10,26 +10,25 @@ import androidx.compose.material.*
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBackIos
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.vanced.manager.ui.component.color.managerAnimatedColor
|
||||
import com.vanced.manager.ui.component.color.managerSurfaceColor
|
||||
import com.vanced.manager.ui.component.color.managerTextColor
|
||||
import com.vanced.manager.ui.component.menu.ManagerDropdownMenuItem
|
||||
import com.vanced.manager.ui.component.text.ToolbarTitleText
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
import com.vanced.manager.ui.screens.*
|
||||
import com.vanced.manager.ui.theme.ManagerTheme
|
||||
import com.vanced.manager.ui.theme.isDark
|
||||
import com.vanced.manager.ui.util.Screen
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
@ -49,19 +48,23 @@ class MainActivity : ComponentActivity() {
|
|||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
|
||||
val surfaceColor = managerSurfaceColor()
|
||||
val navController = rememberAnimatedNavController()
|
||||
|
||||
val screens = listOf(
|
||||
Screen.Home,
|
||||
Screen.Settings,
|
||||
Screen.About,
|
||||
Screen.Logs
|
||||
)
|
||||
val isDark = isDark()
|
||||
|
||||
val navController = rememberAnimatedNavController()
|
||||
val systemUiController = rememberSystemUiController()
|
||||
|
||||
SideEffect {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = surfaceColor,
|
||||
darkIcons = !isDark
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
MainToolbar(
|
||||
navController = navController,
|
||||
screens = screens,
|
||||
isMenuExpanded = isMenuExpanded
|
||||
)
|
||||
},
|
||||
|
@ -91,12 +94,29 @@ class MainActivity : ComponentActivity() {
|
|||
)
|
||||
}
|
||||
) {
|
||||
screens.fastForEach { screen ->
|
||||
composable(
|
||||
route = screen.route,
|
||||
) {
|
||||
screen.content()
|
||||
}
|
||||
composable(Screen.Home.route) {
|
||||
HomeLayout(navController)
|
||||
}
|
||||
composable(Screen.Settings.route) {
|
||||
SettingsLayout()
|
||||
}
|
||||
composable(Screen.About.route) {
|
||||
AboutLayout()
|
||||
}
|
||||
composable(Screen.InstallPreferences.route) {
|
||||
val arguments = navController.previousBackStackEntry?.arguments
|
||||
|
||||
InstallPreferencesScreen(
|
||||
installationOptions = arguments?.getParcelableArrayList("app")!!
|
||||
)
|
||||
}
|
||||
composable(Screen.Install.route,) {
|
||||
val arguments = navController.previousBackStackEntry?.arguments
|
||||
|
||||
InstallScreen(
|
||||
appName = arguments?.getString("appName")!!,
|
||||
appVersions = arguments.getParcelableArrayList("appVersions")!!
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +125,6 @@ class MainActivity : ComponentActivity() {
|
|||
@Composable
|
||||
fun MainToolbar(
|
||||
navController: NavHostController,
|
||||
screens: List<Screen>,
|
||||
isMenuExpanded: MutableState<Boolean>
|
||||
) {
|
||||
val currentScreenRoute =
|
||||
|
@ -115,7 +134,7 @@ class MainActivity : ComponentActivity() {
|
|||
title = {
|
||||
ToolbarTitleText(
|
||||
text = managerString(
|
||||
stringId = screens.find { it.route == currentScreenRoute }?.displayName
|
||||
stringId = Screen.values().find { it.route == currentScreenRoute }?.displayName
|
||||
)
|
||||
)
|
||||
},
|
||||
|
@ -139,7 +158,7 @@ class MainActivity : ComponentActivity() {
|
|||
},
|
||||
modifier = Modifier.background(MaterialTheme.colors.surface),
|
||||
) {
|
||||
screens.filter { it.route != currentScreenRoute }.forEach { screen ->
|
||||
for (screen in Screen.values()) {
|
||||
ManagerDropdownMenuItem(
|
||||
title = stringResource(id = screen.displayName)
|
||||
) {
|
||||
|
|
|
@ -28,7 +28,7 @@ fun IconButton(
|
|||
onClick = onClick,
|
||||
role = Role.Button,
|
||||
interactionSource = interactionSource,
|
||||
indication = rememberRipple(bounded = false, radius = 18.dp)
|
||||
indication = rememberRipple(radius = buttonSize / 2)
|
||||
)
|
||||
.size(buttonSize),
|
||||
contentAlignment = Alignment.Center
|
||||
|
|
|
@ -7,7 +7,7 @@ import androidx.browser.customtabs.CustomTabsIntent
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vanced.manager.preferences.holder.useCustomTabsPref
|
||||
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
|
||||
|
||||
@Composable
|
||||
fun ManagerLinkCard(
|
||||
|
|
|
@ -6,7 +6,7 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import com.vanced.manager.preferences.holder.managerAccentColorPref
|
||||
import com.vanced.manager.core.preferences.holder.managerAccentColorPref
|
||||
|
||||
@Composable
|
||||
fun contentColorForColor(color: Color) =
|
||||
|
|
|
@ -8,8 +8,8 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.window.Dialog
|
||||
import com.vanced.manager.ui.component.card.ManagerCard
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
|
||||
@Composable
|
||||
fun ManagerDialog(
|
||||
|
@ -46,8 +46,8 @@ fun ManagerDialog(
|
|||
content = {
|
||||
ManagerCard {
|
||||
Column(
|
||||
modifier = Modifier.padding(defaultContentPaddingHorizontal),
|
||||
verticalArrangement = Arrangement.spacedBy(defaultContentPaddingVertical)
|
||||
modifier = Modifier.padding(DefaultContentPaddingHorizontal),
|
||||
verticalArrangement = Arrangement.spacedBy(DefaultContentPaddingVertical)
|
||||
) {
|
||||
title()
|
||||
content()
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
|
||||
@Composable
|
||||
fun ManagerButtonColumn(
|
||||
|
@ -14,7 +14,7 @@ fun ManagerButtonColumn(
|
|||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(defaultContentPaddingVertical),
|
||||
verticalArrangement = Arrangement.spacedBy(DefaultContentPaddingVertical),
|
||||
content = content
|
||||
)
|
||||
}
|
|
@ -5,11 +5,13 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ManagerLazyColumn(
|
||||
modifier: Modifier = Modifier,
|
||||
itemSpacing: Dp = 0.dp,
|
||||
content: LazyListScope.() -> Unit
|
||||
) {
|
||||
|
|
|
@ -11,17 +11,18 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
|
||||
@Composable
|
||||
fun ManagerScrollableColumn(
|
||||
contentPaddingVertical: Dp = defaultContentPaddingVertical,
|
||||
modifier: Modifier = Modifier,
|
||||
contentPaddingVertical: Dp = DefaultContentPaddingVertical,
|
||||
itemSpacing: Dp = 0.dp,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.verticalScroll(scrollState)
|
||||
.padding(vertical = contentPaddingVertical),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
|
|
|
@ -12,8 +12,8 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.preferences.CheckboxPreference
|
||||
import com.vanced.manager.preferences.ManagerPreference
|
||||
import com.vanced.manager.core.preferences.CheckboxPreference
|
||||
import com.vanced.manager.core.preferences.ManagerPreference
|
||||
import com.vanced.manager.ui.component.button.ManagerThemedTextButton
|
||||
import com.vanced.manager.ui.widget.list.CheckboxItem
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.vanced.manager.ui.component.preference
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.preferences.ManagerPreference
|
||||
import com.vanced.manager.core.preferences.ManagerPreference
|
||||
import com.vanced.manager.ui.widget.checkbox.ManagerAnimatedCheckbox
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import com.vanced.manager.ui.component.color.managerAnimatedColor
|
|||
import com.vanced.manager.ui.component.list.ManagerListItem
|
||||
import com.vanced.manager.ui.component.modifier.managerClickable
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
|
||||
|
||||
@Composable
|
||||
fun Preference(
|
||||
|
@ -43,7 +43,7 @@ fun Preference(
|
|||
ManagerListItem(
|
||||
modifier = Modifier
|
||||
.managerClickable(onClick = onClick)
|
||||
.padding(horizontal = defaultContentPaddingHorizontal),
|
||||
.padding(horizontal = DefaultContentPaddingHorizontal),
|
||||
title = {
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides color,
|
||||
|
|
|
@ -6,8 +6,8 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.preferences.ManagerPreference
|
||||
import com.vanced.manager.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.core.preferences.ManagerPreference
|
||||
import com.vanced.manager.core.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.ui.widget.button.ManagerSaveButton
|
||||
import com.vanced.manager.ui.widget.list.RadiobuttonItem
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
|
@ -2,12 +2,15 @@ package com.vanced.manager.ui.component.text
|
|||
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun AppVersionText(
|
||||
text: String
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ManagerText(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
textStyle = MaterialTheme.typography.body2,
|
||||
)
|
||||
|
|
|
@ -5,14 +5,14 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vanced.manager.ui.component.color.managerAnimatedColor
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
|
||||
|
||||
@Composable
|
||||
fun CategoryTitleText(
|
||||
text: String
|
||||
) {
|
||||
ManagerText(
|
||||
modifier = Modifier.padding(start = defaultContentPaddingHorizontal),
|
||||
modifier = Modifier.padding(start = DefaultContentPaddingHorizontal),
|
||||
text = text,
|
||||
textStyle = MaterialTheme.typography.h2,
|
||||
color = managerAnimatedColor(MaterialTheme.colors.onSurface)
|
||||
|
|
|
@ -11,11 +11,11 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
|
||||
@Composable
|
||||
fun ManagerText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
textAlign: TextAlign? = null,
|
||||
text: String,
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
|
@ -28,11 +28,11 @@ fun ManagerText(
|
|||
|
||||
@Composable
|
||||
fun ManagerText(
|
||||
text: AnnotatedString,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
textAlign: TextAlign? = null,
|
||||
text: AnnotatedString,
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
|
|
|
@ -24,7 +24,7 @@ import com.vanced.manager.ui.component.layout.ScrollableItemRow
|
|||
import com.vanced.manager.ui.component.list.ManagerListItem
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.widget.layout.CategoryLayout
|
||||
|
||||
data class Person(
|
||||
|
@ -171,7 +171,7 @@ fun AboutManagerCard() {
|
|||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = defaultContentPaddingVertical),
|
||||
modifier = Modifier.padding(vertical = DefaultContentPaddingVertical),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
|
|
|
@ -1,58 +1,145 @@
|
|||
package com.vanced.manager.ui.screens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import coil.compose.rememberImagePainter
|
||||
import coil.request.CachePolicy
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.util.socialMedia
|
||||
import com.vanced.manager.core.util.sponsors
|
||||
import com.vanced.manager.ui.component.card.ManagerLinkCard
|
||||
import com.vanced.manager.ui.component.dialog.ManagerDialog
|
||||
import com.vanced.manager.ui.component.layout.ManagerScrollableColumn
|
||||
import com.vanced.manager.ui.component.layout.ManagerSwipeRefresh
|
||||
import com.vanced.manager.ui.component.layout.ScrollableItemRow
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.Screen
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
import com.vanced.manager.ui.widget.app.AppCard
|
||||
import com.vanced.manager.ui.widget.app.AppCardPlaceholder
|
||||
import com.vanced.manager.ui.widget.button.ManagerCloseButton
|
||||
import com.vanced.manager.ui.widget.layout.CategoryLayout
|
||||
import com.vanced.manager.ui.widget.screens.home.apps.HomeAppsItem
|
||||
import com.vanced.manager.ui.widget.screens.home.socialmedia.HomeSocialMediaItem
|
||||
import com.vanced.manager.ui.widget.screens.home.sponsors.HomeSponsorsItem
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
@Preview
|
||||
fun HomeLayout() {
|
||||
fun HomeLayout(
|
||||
navController: NavController
|
||||
) {
|
||||
val viewModel: MainViewModel = getViewModel()
|
||||
val isFetching by viewModel.isFetching.collectAsState()
|
||||
val refreshState = rememberSwipeRefreshState(isRefreshing = isFetching)
|
||||
val appState by viewModel.appState.collectAsState()
|
||||
|
||||
val refreshState = rememberSwipeRefreshState(isRefreshing = appState is MainViewModel.AppState.Fetching)
|
||||
ManagerSwipeRefresh(
|
||||
refreshState = refreshState,
|
||||
onRefresh = { viewModel.fetch() }
|
||||
) {
|
||||
ManagerScrollableColumn(
|
||||
contentPaddingVertical = defaultContentPaddingVertical,
|
||||
contentPaddingVertical = DefaultContentPaddingVertical,
|
||||
itemSpacing = 18.dp
|
||||
) {
|
||||
CategoryLayout(
|
||||
categoryName = managerString(
|
||||
stringId = R.string.home_category_apps
|
||||
),
|
||||
categoryName = managerString(R.string.home_category_apps),
|
||||
contentPaddingHorizontal = 0.dp
|
||||
) {
|
||||
HomeAppsItem(viewModel)
|
||||
AnimatedContent(
|
||||
targetState = appState,
|
||||
transitionSpec = {
|
||||
scaleIn(initialScale = 0.9f) + fadeIn() with
|
||||
scaleOut(targetScale = 0.9f) + fadeOut()
|
||||
}
|
||||
) { animatedAppState ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
when (animatedAppState) {
|
||||
is MainViewModel.AppState.Success -> {
|
||||
for (app in animatedAppState.apps) {
|
||||
val appIcon = rememberImagePainter(app.iconUrl) {
|
||||
diskCachePolicy(CachePolicy.ENABLED)
|
||||
}
|
||||
|
||||
var showAppInfoDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
AppCard(
|
||||
appName = app.name,
|
||||
appIcon = appIcon,
|
||||
appInstalledVersion = app.installedVersion,
|
||||
appRemoteVersion = app.remoteVersion,
|
||||
onDownloadClick = {
|
||||
if (app.installationOptions != null) {
|
||||
navController.navigate(Screen.InstallPreferences.route)
|
||||
} else {
|
||||
navController.navigate(Screen.Install.route)
|
||||
}
|
||||
|
||||
},
|
||||
onUninstallClick = { /*TODO*/ },
|
||||
onLaunchClick = { /*TODO*/ },
|
||||
onInfoClick = {
|
||||
showAppInfoDialog = true
|
||||
}
|
||||
)
|
||||
|
||||
if (showAppInfoDialog) {
|
||||
ManagerDialog(
|
||||
title = managerString(R.string.app_info_title, app.name),
|
||||
onDismissRequest = { showAppInfoDialog = false },
|
||||
buttons = {
|
||||
ManagerCloseButton(onClick = {
|
||||
showAppInfoDialog = false
|
||||
})
|
||||
}
|
||||
) {
|
||||
ManagerText(
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
text = app.changelog,
|
||||
textStyle = MaterialTheme.typography.subtitle1
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is MainViewModel.AppState.Fetching -> {
|
||||
for (i in 0 until animatedAppState.placeholderAppsCount) {
|
||||
AppCardPlaceholder()
|
||||
}
|
||||
}
|
||||
is MainViewModel.AppState.Error -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CategoryLayout(
|
||||
categoryName = managerString(
|
||||
stringId = R.string.home_category_support_us
|
||||
)
|
||||
) {
|
||||
HomeSponsorsItem()
|
||||
CategoryLayout(managerString(R.string.home_category_support_us)) {
|
||||
ScrollableItemRow(items = sponsors) { sponsor ->
|
||||
ManagerLinkCard(
|
||||
icon = sponsor.icon,
|
||||
title = sponsor.title,
|
||||
link = sponsor.link
|
||||
)
|
||||
}
|
||||
}
|
||||
CategoryLayout(
|
||||
categoryName = managerString(
|
||||
stringId = R.string.home_category_social_media
|
||||
)
|
||||
) {
|
||||
HomeSocialMediaItem()
|
||||
CategoryLayout( managerString(R.string.home_category_social_media)) {
|
||||
ScrollableItemRow(items = socialMedia) { socialMedia ->
|
||||
ManagerLinkCard(
|
||||
icon = socialMedia.icon,
|
||||
title = socialMedia.title,
|
||||
link = socialMedia.link
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package com.vanced.manager.ui.screens
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.vanced.manager.domain.model.InstallationOption
|
||||
import com.vanced.manager.ui.component.card.ManagerCard
|
||||
import com.vanced.manager.ui.component.layout.ManagerLazyColumn
|
||||
import com.vanced.manager.ui.component.layout.ManagerScrollableColumn
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
import com.vanced.manager.ui.widget.list.CheckboxItem
|
||||
import com.vanced.manager.ui.widget.list.RadiobuttonItem
|
||||
|
||||
@Composable
|
||||
fun InstallPreferencesScreen(
|
||||
installationOptions: List<InstallationOption>
|
||||
) {
|
||||
var selectedOptionIndex by rememberSaveable { mutableStateOf(0) }
|
||||
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
|
||||
}
|
||||
) { paddingValues ->
|
||||
ManagerScrollableColumn(
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
) {
|
||||
installationOptions.fastForEachIndexed { index, installationOption ->
|
||||
ManagerCard(onClick = {
|
||||
selectedOptionIndex = index
|
||||
}) {
|
||||
Column {
|
||||
ManagerText(
|
||||
text = managerString(installationOption.itemTitleId),
|
||||
textStyle = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = index == selectedOptionIndex
|
||||
) {
|
||||
ManagerLazyColumn(
|
||||
modifier = Modifier.sizeIn(
|
||||
minHeight = 400.dp,
|
||||
maxHeight = 400.dp
|
||||
)
|
||||
) {
|
||||
when (installationOption) {
|
||||
is InstallationOption.MultiSelect -> {
|
||||
items(installationOption.items) { item ->
|
||||
val preference = installationOption.getOption()
|
||||
CheckboxItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = item.displayText,
|
||||
isChecked = preference.contains(item.key),
|
||||
onCheck = {
|
||||
if (it) {
|
||||
installationOption.addOption(item.key)
|
||||
} else {
|
||||
installationOption.removeOption(item.key)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
is InstallationOption.SingleSelect -> {
|
||||
items(installationOption.items) { item ->
|
||||
val preference = installationOption.getOption()
|
||||
RadiobuttonItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = item.displayText,
|
||||
isSelected = preference == item.key,
|
||||
onSelect = {
|
||||
installationOption.setOption(item.key)
|
||||
},
|
||||
tag = item.key
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
190
app/src/main/java/com/vanced/manager/ui/screens/InstallScreen.kt
Normal file
190
app/src/main/java/com/vanced/manager/ui/screens/InstallScreen.kt
Normal file
|
@ -0,0 +1,190 @@
|
|||
package com.vanced.manager.ui.screens
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.FloatingActionButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.ArrowDropDown
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
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.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.component.layout.ManagerLazyColumn
|
||||
import com.vanced.manager.ui.component.modifier.managerClickable
|
||||
import com.vanced.manager.ui.component.progressindicator.ManagerProgressIndicator
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
import org.koin.androidx.compose.get
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
sealed class Log {
|
||||
data class Info(val infoText: String) : Log()
|
||||
data class Success(val successText: String) : Log()
|
||||
data class Error(
|
||||
val displayText: String,
|
||||
val stacktrace: String,
|
||||
) : Log()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InstallScreen(
|
||||
appName: String,
|
||||
appVersions: List<String>
|
||||
) {
|
||||
val logs = rememberSaveable { mutableStateListOf<Log>() }
|
||||
|
||||
var progress by rememberSaveable { mutableStateOf(0f) }
|
||||
var installing by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val viewModel: MainViewModel = getViewModel()
|
||||
|
||||
val downloader = when (appName) {
|
||||
VANCED_NAME -> get<VancedDownloader>()
|
||||
MUSIC_NAME -> get<MusicDownloader>()
|
||||
MICROG_NAME -> get<MicrogDownloader>()
|
||||
else -> throw IllegalArgumentException("$appName is not a valid app")
|
||||
}
|
||||
|
||||
val installer = when (appName) {
|
||||
VANCED_NAME -> get<VancedInstaller>()
|
||||
MUSIC_NAME -> get<MusicInstaller>()
|
||||
MICROG_NAME -> get<MicrogInstaller>()
|
||||
else -> throw IllegalArgumentException("$appName is not a valid app")
|
||||
}
|
||||
|
||||
//FIXME this is absolutely bad, must move to WorkManager
|
||||
LaunchedEffect(true) {
|
||||
downloader.download(appVersions) { status ->
|
||||
when (status) {
|
||||
is DownloadStatus.File -> logs.add(Log.Info("Downloading ${status.fileName}"))
|
||||
is DownloadStatus.Error -> logs.add(Log.Error(
|
||||
displayText = status.displayError,
|
||||
stacktrace = status.stacktrace
|
||||
))
|
||||
is DownloadStatus.Progress -> progress = status.progress
|
||||
is DownloadStatus.StartInstall -> {
|
||||
installing = true
|
||||
installer.install {
|
||||
viewModel.fetch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
if (installing) {
|
||||
ManagerProgressIndicator()
|
||||
} else {
|
||||
ManagerProgressIndicator(progress)
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = { /*TODO*/ }) {
|
||||
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
ManagerLazyColumn(
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
) {
|
||||
items(logs) { log ->
|
||||
when (log) {
|
||||
is Log.Success -> {
|
||||
ManagerText(
|
||||
text = log.successText,
|
||||
textStyle = TextStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp,
|
||||
color = Color.Green
|
||||
),
|
||||
)
|
||||
}
|
||||
is Log.Info -> {
|
||||
ManagerText(
|
||||
text = log.infoText,
|
||||
textStyle = TextStyle(
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 14.sp,
|
||||
color = Color.Green
|
||||
),
|
||||
)
|
||||
}
|
||||
is Log.Error -> {
|
||||
var visible by remember { mutableStateOf(false) }
|
||||
val iconRotation by animateFloatAsState(if (visible) 0f else 90f)
|
||||
Row(
|
||||
modifier = Modifier.managerClickable {
|
||||
visible = !visible
|
||||
},
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.rotate(iconRotation),
|
||||
imageVector = Icons.Rounded.ArrowDropDown,
|
||||
contentDescription = "expand",
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
ManagerText(
|
||||
text = buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = MaterialTheme.colors.error)) {
|
||||
append(log.displayText)
|
||||
append(": ")
|
||||
}
|
||||
withStyle(SpanStyle(color = MaterialTheme.colors.error.copy(alpha = 0.7f))) {
|
||||
append(log.stacktrace)
|
||||
}
|
||||
},
|
||||
textStyle = TextStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp,
|
||||
color = Color.Green
|
||||
),
|
||||
)
|
||||
AnimatedVisibility(visible) {
|
||||
ManagerText(
|
||||
text = log.stacktrace,
|
||||
textStyle = TextStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp,
|
||||
color = Color.Green
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier
|
|||
import com.vanced.manager.ui.component.color.managerAccentColor
|
||||
import com.vanced.manager.ui.component.color.managerSurfaceColor
|
||||
import com.vanced.manager.ui.component.layout.ManagerLazyColumn
|
||||
import com.vanced.manager.util.logs
|
||||
import com.vanced.manager.core.util.logs
|
||||
|
||||
@Composable
|
||||
fun LogLayout() {
|
||||
|
|
|
@ -5,14 +5,14 @@ import androidx.compose.ui.unit.dp
|
|||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.component.layout.ManagerScrollableColumn
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.widget.layout.SettingsCategoryLayout
|
||||
import com.vanced.manager.ui.widget.screens.settings.*
|
||||
|
||||
@Composable
|
||||
fun SettingsLayout() {
|
||||
ManagerScrollableColumn(
|
||||
contentPaddingVertical = defaultContentPaddingVertical,
|
||||
contentPaddingVertical = DefaultContentPaddingVertical,
|
||||
itemSpacing = 12.dp
|
||||
) {
|
||||
SettingsCategoryLayout(
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.material.lightColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vanced.manager.preferences.holder.managerThemePref
|
||||
import com.vanced.manager.core.preferences.holder.managerThemePref
|
||||
|
||||
const val defAccentColor = 0xFF0477E1
|
||||
|
||||
|
|
|
@ -2,16 +2,5 @@ package com.vanced.manager.ui.util
|
|||
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val defaultContentPaddingHorizontal = 16.dp
|
||||
val defaultContentPaddingVertical = 12.dp
|
||||
|
||||
fun test(anotherFun: () -> Int) = anotherFun() + 5
|
||||
fun test(anotherNum: Int) = anotherNum + 5
|
||||
|
||||
fun test2() {
|
||||
test {
|
||||
print("haha jonathan you are banging my number")
|
||||
return@test 0
|
||||
}
|
||||
test(0)
|
||||
}
|
||||
val DefaultContentPaddingHorizontal = 16.dp
|
||||
val DefaultContentPaddingVertical = 12.dp
|
|
@ -8,41 +8,32 @@ import com.vanced.manager.ui.screens.HomeLayout
|
|||
import com.vanced.manager.ui.screens.LogLayout
|
||||
import com.vanced.manager.ui.screens.SettingsLayout
|
||||
|
||||
sealed class Screen(
|
||||
enum class Screen(
|
||||
val route: String,
|
||||
@StringRes val displayName: Int,
|
||||
val content: @Composable () -> Unit
|
||||
) {
|
||||
|
||||
object Home : Screen(
|
||||
Home(
|
||||
route = "home",
|
||||
displayName = R.string.app_name,
|
||||
content = {
|
||||
HomeLayout()
|
||||
}
|
||||
)
|
||||
|
||||
object Settings : Screen(
|
||||
displayName = R.string.app_name
|
||||
),
|
||||
Settings(
|
||||
route = "settings",
|
||||
displayName = R.string.toolbar_settings,
|
||||
content = {
|
||||
SettingsLayout()
|
||||
}
|
||||
)
|
||||
|
||||
object About : Screen(
|
||||
),
|
||||
About(
|
||||
route = "about",
|
||||
displayName = R.string.toolbar_about,
|
||||
content = {
|
||||
AboutLayout()
|
||||
}
|
||||
)
|
||||
|
||||
object Logs : Screen(
|
||||
),
|
||||
Logs(
|
||||
route = "logs",
|
||||
displayName = R.string.toolbar_logs,
|
||||
content = {
|
||||
LogLayout()
|
||||
}
|
||||
)
|
||||
),
|
||||
InstallPreferences(
|
||||
route = "installpreferences",
|
||||
displayName = R.string.toolbar_installation_preferences
|
||||
),
|
||||
Install(
|
||||
route = "install",
|
||||
displayName = R.string.toolbar_install
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package com.vanced.manager.ui.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.preferences.holder.musicEnabled
|
||||
import com.vanced.manager.preferences.holder.vancedEnabled
|
||||
import com.vanced.manager.core.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.core.preferences.holder.musicEnabled
|
||||
import com.vanced.manager.core.preferences.holder.vancedEnabled
|
||||
import com.vanced.manager.repository.JsonRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -18,40 +17,54 @@ class MainViewModel(
|
|||
private val repository: JsonRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val vanced = MutableStateFlow(App())
|
||||
private val music = MutableStateFlow(App())
|
||||
private val microg = MutableStateFlow(App())
|
||||
private val manager = MutableStateFlow(App())
|
||||
sealed class AppState {
|
||||
|
||||
private val _isFetching = MutableStateFlow(false)
|
||||
val isFetching: StateFlow<Boolean> = _isFetching
|
||||
data class Fetching(val placeholderAppsCount: Int) : AppState()
|
||||
|
||||
val apps = mutableListOf<StateFlow<App>>()
|
||||
data class Success(val apps: List<App>) : AppState()
|
||||
|
||||
data class Error(val error: String) : AppState()
|
||||
|
||||
}
|
||||
|
||||
private val _appState = MutableStateFlow<AppState>(AppState.Fetching(3))
|
||||
val appState: StateFlow<AppState> = _appState
|
||||
|
||||
fun fetch() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_isFetching.value = true
|
||||
val vancedEnabled = vancedEnabled.value.value
|
||||
val musicEnabled = musicEnabled.value.value
|
||||
val isNonroot = managerVariantPref.value.value == "nonroot"
|
||||
|
||||
var appsCount = 0
|
||||
|
||||
if (vancedEnabled) appsCount++
|
||||
if (musicEnabled) appsCount++
|
||||
if (isNonroot) appsCount++
|
||||
|
||||
_appState.value = AppState.Fetching(appsCount)
|
||||
|
||||
try {
|
||||
with(repository.fetch()) {
|
||||
this@MainViewModel.vanced.value = vanced
|
||||
this@MainViewModel.music.value = music
|
||||
this@MainViewModel.microg.value = microg
|
||||
val apps = mutableListOf<App>()
|
||||
|
||||
apps.apply {
|
||||
if (vancedEnabled) add(vanced)
|
||||
if (musicEnabled) add(music)
|
||||
if (isNonroot) add(microg)
|
||||
}
|
||||
|
||||
_appState.value = AppState.Success(apps)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.d("HomeViewModel", "failed to fetch: $e")
|
||||
val error = "failed to fetch: \n${e.stackTraceToString()}"
|
||||
_appState.value = AppState.Error(error)
|
||||
Log.d("MainViewModel", error)
|
||||
}
|
||||
|
||||
_isFetching.value = false
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
apps.apply {
|
||||
if (vancedEnabled.value.value) add(vanced)
|
||||
if (musicEnabled.value.value) add(music)
|
||||
if (managerVariantPref.value.value == "nonroot") add(microg)
|
||||
}
|
||||
|
||||
fetch()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package com.vanced.manager.ui.widget.app
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.rounded.DeleteForever
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.Launch
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.ImagePainter
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.component.button.IconButton
|
||||
import com.vanced.manager.ui.component.text.AppVersionText
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun AppCard(
|
||||
appName: String,
|
||||
appIcon: ImagePainter,
|
||||
appInstalledVersion: String?,
|
||||
appRemoteVersion: String?,
|
||||
onDownloadClick: () -> Unit,
|
||||
onUninstallClick: () -> Unit,
|
||||
onLaunchClick: () -> Unit,
|
||||
onInfoClick: () -> Unit,
|
||||
) {
|
||||
BaseAppCard(
|
||||
appTitle = {
|
||||
ManagerText(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
text = appName,
|
||||
textStyle = MaterialTheme.typography.h5
|
||||
)
|
||||
},
|
||||
appIcon = {
|
||||
Image(
|
||||
modifier = Modifier.size(48.dp),
|
||||
painter = appIcon,
|
||||
contentDescription = "App Icon",
|
||||
)
|
||||
},
|
||||
appVersionsColumn = {
|
||||
AppVersionText(
|
||||
text = stringResource(
|
||||
id = R.string.app_version_latest,
|
||||
appRemoteVersion ?: stringResource(
|
||||
id = R.string.app_content_unavailable
|
||||
)
|
||||
)
|
||||
)
|
||||
AppVersionText(
|
||||
text = stringResource(
|
||||
id = R.string.app_version_installed,
|
||||
appInstalledVersion ?: stringResource(
|
||||
id = R.string.app_content_unavailable
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
appActionsRow = {
|
||||
IconButton(
|
||||
icon = Icons.Outlined.Info,
|
||||
contentDescription = "App Info",
|
||||
onClick = onInfoClick
|
||||
)
|
||||
IconButton(
|
||||
icon = Icons.Rounded.DeleteForever,
|
||||
contentDescription = "Uninstall",
|
||||
onClick = onUninstallClick
|
||||
)
|
||||
IconButton(
|
||||
icon = Icons.Rounded.Launch,
|
||||
contentDescription = "Launch",
|
||||
onClick = onLaunchClick
|
||||
)
|
||||
IconButton(
|
||||
icon = Icons.Rounded.Download,
|
||||
contentDescription = "Install",
|
||||
onClick = onDownloadClick
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.vanced.manager.ui.widget.app
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.ui.component.modifier.managerPlaceholder
|
||||
import com.vanced.manager.ui.component.text.AppVersionText
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
|
||||
@Composable
|
||||
fun AppCardPlaceholder() {
|
||||
BaseAppCard(
|
||||
appTitle = {
|
||||
ManagerText(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.managerPlaceholder(true),
|
||||
text = " ".repeat(40),
|
||||
textStyle = MaterialTheme.typography.h5
|
||||
)
|
||||
},
|
||||
appIcon = {
|
||||
Box(
|
||||
Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.managerPlaceholder(true)
|
||||
.size(48.dp)
|
||||
)
|
||||
},
|
||||
appVersionsColumn = {
|
||||
AppVersionText(
|
||||
modifier = Modifier
|
||||
.managerPlaceholder(true)
|
||||
.clip(MaterialTheme.shapes.small),
|
||||
text = " ".repeat(30)
|
||||
)
|
||||
AppVersionText(
|
||||
modifier = Modifier
|
||||
.managerPlaceholder(true)
|
||||
.clip(MaterialTheme.shapes.small),
|
||||
text = " ".repeat(30)
|
||||
)
|
||||
},
|
||||
appActionsRow = {
|
||||
Box(
|
||||
Modifier
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.fillMaxWidth(0.8f)
|
||||
.height(36.dp)
|
||||
.managerPlaceholder(true)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.vanced.manager.ui.widget.app
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.component.card.ManagerCard
|
||||
import com.vanced.manager.ui.component.card.ManagerThemedCard
|
||||
import com.vanced.manager.ui.component.list.ManagerListItem
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
|
||||
@Composable
|
||||
fun BaseAppCard(
|
||||
appTitle: @Composable () -> Unit,
|
||||
appIcon: @Composable () -> Unit,
|
||||
appVersionsColumn: @Composable ColumnScope.() -> Unit,
|
||||
appActionsRow: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
ManagerThemedCard {
|
||||
Column {
|
||||
ManagerCard {
|
||||
ManagerListItem(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = DefaultContentPaddingHorizontal,
|
||||
vertical = DefaultContentPaddingVertical
|
||||
),
|
||||
title = appTitle,
|
||||
icon = appIcon
|
||||
)
|
||||
}
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides MaterialTheme.colors.onSurface
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = DefaultContentPaddingHorizontal,
|
||||
vertical = 8.dp
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.wrapContentWidth(Alignment.Start),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
ManagerText(stringResource(id = R.string.app_versions))
|
||||
appVersionsColumn()
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 4.dp)
|
||||
.wrapContentWidth(Alignment.End),
|
||||
content = appActionsRow
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,14 +5,14 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.vanced.manager.ui.component.text.CategoryTitleText
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
|
||||
@Composable
|
||||
fun CategoryLayout(
|
||||
categoryName: String,
|
||||
contentPaddingHorizontal: Dp = defaultContentPaddingHorizontal,
|
||||
categoryNameSpacing: Dp = defaultContentPaddingVertical,
|
||||
contentPaddingHorizontal: Dp = DefaultContentPaddingHorizontal,
|
||||
categoryNameSpacing: Dp = DefaultContentPaddingVertical,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.vanced.manager.ui.widget.list
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -16,13 +15,13 @@ import com.vanced.manager.ui.widget.checkbox.ManagerAnimatedCheckbox
|
|||
fun CheckboxItem(
|
||||
text: String,
|
||||
isChecked: Boolean,
|
||||
onCheck: (Boolean) -> Unit = {}
|
||||
onCheck: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val toggle = { onCheck(!isChecked) }
|
||||
|
||||
ManagerSelectableListItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
modifier = modifier
|
||||
.managerClickable(onClick = toggle),
|
||||
title = {
|
||||
Text(
|
||||
|
|
|
@ -17,12 +17,12 @@ fun <T> RadiobuttonItem(
|
|||
text: String,
|
||||
tag: T,
|
||||
isSelected: Boolean,
|
||||
onSelect: (tag: T) -> Unit
|
||||
onSelect: (tag: T) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val onClick = { onSelect(tag) }
|
||||
ManagerSelectableListItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.managerClickable(onClick = onClick),
|
||||
title = {
|
||||
Text(
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.apps
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
import com.vanced.manager.ui.widget.screens.home.apps.card.AppCard
|
||||
|
||||
@Composable
|
||||
fun HomeAppsItem(
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
viewModel.apps.fastForEach { app ->
|
||||
val observedApp by app.collectAsState()
|
||||
AppCard(observedApp, viewModel)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.apps.card
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.rounded.DeleteForever
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.Launch
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.component.button.IconButton
|
||||
import com.vanced.manager.ui.component.text.AppVersionText
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingHorizontal
|
||||
|
||||
@Composable
|
||||
fun AppActionCard(
|
||||
appRemoteVersion: String?,
|
||||
appInstalledVersion: String?,
|
||||
onInfoClick: () -> Unit,
|
||||
onUninstallClick: () -> Unit,
|
||||
onLaunchClick: () -> Unit,
|
||||
onDownloadClick: () -> Unit,
|
||||
) {
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colors.onSurface) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = defaultContentPaddingHorizontal
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.wrapContentWidth(Alignment.Start),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
ManagerText(
|
||||
text = stringResource(id = R.string.app_versions)
|
||||
)
|
||||
AppVersionText(
|
||||
text = stringResource(
|
||||
id = R.string.app_version_latest,
|
||||
appRemoteVersion ?: stringResource(
|
||||
id = R.string.app_content_unavailable
|
||||
)
|
||||
)
|
||||
)
|
||||
AppVersionText(
|
||||
text = stringResource(
|
||||
id = R.string.app_version_installed,
|
||||
appInstalledVersion ?: stringResource(
|
||||
id = R.string.app_content_unavailable
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 4.dp)
|
||||
.wrapContentWidth(Alignment.End)
|
||||
) {
|
||||
IconButton(
|
||||
icon = Icons.Outlined.Info,
|
||||
contentDescription = "App Info",
|
||||
onClick = onInfoClick
|
||||
)
|
||||
IconButton(
|
||||
icon = Icons.Rounded.DeleteForever,
|
||||
contentDescription = "Uninstall",
|
||||
onClick = onUninstallClick
|
||||
)
|
||||
IconButton(
|
||||
icon = Icons.Rounded.Launch,
|
||||
contentDescription = "Launch",
|
||||
onClick = onLaunchClick
|
||||
)
|
||||
IconButton(
|
||||
icon = Icons.Rounded.Download,
|
||||
contentDescription = "Install",
|
||||
onClick = onDownloadClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.apps.card
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
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
|
||||
import coil.compose.rememberImagePainter
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.ui.component.card.ManagerThemedCard
|
||||
import com.vanced.manager.ui.component.layout.ManagerButtonColumn
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingVertical
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
import com.vanced.manager.ui.widget.button.ManagerCancelButton
|
||||
import com.vanced.manager.ui.widget.button.ManagerDownloadButton
|
||||
import com.vanced.manager.ui.widget.screens.home.apps.dialog.AppChangelogDialog
|
||||
import com.vanced.manager.ui.widget.screens.home.apps.dialog.AppDownloadDialog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun AppCard(
|
||||
app: App,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
var showAppInfoDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showInstallationOptions by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val coroutineScope = rememberCoroutineScope { Dispatchers.IO }
|
||||
|
||||
val icon = rememberImagePainter(
|
||||
data = app.iconUrl
|
||||
)
|
||||
|
||||
val hasInstallationOptions = app.installationOptions != null
|
||||
val animationSpec = tween<IntSize>(400)
|
||||
|
||||
val downloader = app.downloader
|
||||
|
||||
val download: () -> Unit = {
|
||||
showInstallationOptions = false
|
||||
coroutineScope.launch {
|
||||
downloader!!.download(app, viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
ManagerThemedCard {
|
||||
Column {
|
||||
AppInfoCard(
|
||||
appName = app.name,
|
||||
icon = icon,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = defaultContentPaddingVertical)
|
||||
) {
|
||||
AppActionCard(
|
||||
appInstalledVersion = app.installedVersion,
|
||||
appRemoteVersion = app.remoteVersion,
|
||||
onDownloadClick = {
|
||||
if (hasInstallationOptions) {
|
||||
showInstallationOptions = true
|
||||
} else {
|
||||
download()
|
||||
}
|
||||
},
|
||||
onInfoClick = {
|
||||
showAppInfoDialog = true
|
||||
},
|
||||
onLaunchClick = {},
|
||||
onUninstallClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasInstallationOptions) {
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
visible = showInstallationOptions,
|
||||
enter = expandVertically(
|
||||
animationSpec = animationSpec
|
||||
),
|
||||
exit = shrinkVertically(
|
||||
animationSpec = animationSpec
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 4.dp)
|
||||
) {
|
||||
app.installationOptions?.forEach {
|
||||
it.item()
|
||||
}
|
||||
ManagerButtonColumn(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = defaultContentPaddingHorizontal)
|
||||
.padding(top = defaultContentPaddingVertical)
|
||||
) {
|
||||
ManagerDownloadButton(
|
||||
onClick = download
|
||||
)
|
||||
ManagerCancelButton {
|
||||
showInstallationOptions = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (app.name != null && downloader != null && downloader.showDownloadScreen.value) {
|
||||
AppDownloadDialog(
|
||||
app = app.name,
|
||||
downloader = downloader,
|
||||
onCancelClick = {
|
||||
downloader.cancelDownload()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (app.name != null && app.changelog != null && showAppInfoDialog) {
|
||||
AppChangelogDialog(
|
||||
appName = app.name,
|
||||
changelog = app.changelog,
|
||||
onDismissRequest = {
|
||||
showAppInfoDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.apps.card
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.annotation.ExperimentalCoilApi
|
||||
import coil.compose.ImagePainter
|
||||
import com.vanced.manager.ui.component.card.ManagerCard
|
||||
import com.vanced.manager.ui.component.list.ManagerListItem
|
||||
import com.vanced.manager.ui.component.modifier.managerPlaceholder
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.util.defaultContentPaddingHorizontal
|
||||
|
||||
@OptIn(ExperimentalCoilApi::class)
|
||||
@Composable
|
||||
fun AppInfoCard(
|
||||
appName: String?,
|
||||
icon: ImagePainter,
|
||||
) {
|
||||
ManagerCard {
|
||||
ManagerListItem(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = defaultContentPaddingHorizontal,
|
||||
vertical = 12.dp
|
||||
),
|
||||
title = {
|
||||
ManagerText(
|
||||
modifier = Modifier.managerPlaceholder(appName == null),
|
||||
text = appName ?: "",
|
||||
textStyle = MaterialTheme.typography.h5
|
||||
)
|
||||
},
|
||||
icon = {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.managerPlaceholder(icon.state is ImagePainter.State.Loading),
|
||||
painter = icon,
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.apps.dialog
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.component.dialog.ManagerDialog
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
import com.vanced.manager.ui.widget.button.ManagerCloseButton
|
||||
|
||||
@Composable
|
||||
fun AppChangelogDialog(
|
||||
appName: String,
|
||||
changelog: String,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
ManagerDialog(
|
||||
title = managerString(R.string.app_info_title, appName),
|
||||
onDismissRequest = onDismissRequest,
|
||||
buttons = {
|
||||
ManagerCloseButton(onClick = onDismissRequest)
|
||||
}
|
||||
) {
|
||||
ManagerText(
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
text = changelog,
|
||||
textStyle = MaterialTheme.typography.subtitle1
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.apps.dialog
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.downloader.base.AppDownloader
|
||||
import com.vanced.manager.ui.component.dialog.ManagerDialog
|
||||
import com.vanced.manager.ui.component.text.ManagerText
|
||||
import com.vanced.manager.ui.widget.button.ManagerCancelButton
|
||||
import com.vanced.manager.ui.widget.screens.home.download.AppDownloadDialogProgress
|
||||
|
||||
@Composable
|
||||
fun AppDownloadDialog(
|
||||
app: String,
|
||||
downloader: AppDownloader,
|
||||
onCancelClick: () -> Unit,
|
||||
) {
|
||||
ManagerDialog(
|
||||
title = app,
|
||||
onDismissRequest = {},
|
||||
buttons = {
|
||||
ManagerCancelButton(onClick = onCancelClick)
|
||||
}
|
||||
) {
|
||||
ManagerText(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.app_download_dialog_subtitle),
|
||||
textStyle = MaterialTheme.typography.subtitle2,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
AppDownloadDialogProgress(
|
||||
progress = downloader.downloadProgress,
|
||||
file = downloader.downloadFile,
|
||||
installing = downloader.installing
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.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.component.progressindicator.ManagerProgressIndicator
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
|
||||
@Composable
|
||||
fun AppDownloadDialogProgress(
|
||||
progress: Float,
|
||||
file: String,
|
||||
installing: Boolean
|
||||
) {
|
||||
when (installing) {
|
||||
true -> ManagerProgressIndicator()
|
||||
false -> ManagerProgressIndicator(progress = progress / 100f)
|
||||
}
|
||||
val animatedProgress by animateIntAsState(targetValue = progress.toInt())
|
||||
Row {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.wrapContentWidth(Alignment.Start),
|
||||
text = managerString(
|
||||
stringId = R.string.app_download_dialog_downloading_file,
|
||||
file
|
||||
)
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = 4.dp)
|
||||
.wrapContentWidth(Alignment.End),
|
||||
text = "$animatedProgress%"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.installation
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.vanced.manager.preferences.CheckboxPreference
|
||||
import com.vanced.manager.preferences.ManagerPreference
|
||||
import com.vanced.manager.ui.component.preference.CheckboxDialogPreference
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
|
||||
data class CheckboxInstallationOption(
|
||||
@StringRes val titleId: Int,
|
||||
val preference: ManagerPreference<Set<String>>,
|
||||
val buttons: List<CheckboxPreference>
|
||||
) : InstallationOption(
|
||||
item = {
|
||||
CheckboxDialogPreference(
|
||||
preferenceTitle = managerString(stringId = titleId),
|
||||
preference = preference,
|
||||
buttons = buttons
|
||||
)
|
||||
}
|
||||
)
|
|
@ -1,7 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.installation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
open class InstallationOption(
|
||||
val item: @Composable () -> Unit
|
||||
)
|
|
@ -1,21 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.installation
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.vanced.manager.preferences.ManagerPreference
|
||||
import com.vanced.manager.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.ui.component.preference.RadiobuttonDialogPreference
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
|
||||
data class RadiobuttonInstallationOption(
|
||||
@StringRes val titleId: Int,
|
||||
val preference: ManagerPreference<String>,
|
||||
val buttons: List<RadioButtonPreference>
|
||||
) : InstallationOption(
|
||||
item = {
|
||||
RadiobuttonDialogPreference(
|
||||
preferenceTitle = managerString(stringId = titleId),
|
||||
preference = preference,
|
||||
buttons = buttons
|
||||
)
|
||||
}
|
||||
)
|
|
@ -1,17 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.socialmedia
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vanced.manager.ui.component.card.ManagerLinkCard
|
||||
import com.vanced.manager.ui.component.layout.ScrollableItemRow
|
||||
import com.vanced.manager.util.socialMedia
|
||||
|
||||
@Composable
|
||||
fun HomeSocialMediaItem() {
|
||||
ScrollableItemRow(items = socialMedia) { socialMedia ->
|
||||
ManagerLinkCard(
|
||||
icon = socialMedia.icon,
|
||||
title = socialMedia.title,
|
||||
link = socialMedia.link
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package com.vanced.manager.ui.widget.screens.home.sponsors
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vanced.manager.ui.component.card.ManagerLinkCard
|
||||
import com.vanced.manager.ui.component.layout.ScrollableItemRow
|
||||
import com.vanced.manager.util.sponsors
|
||||
|
||||
@Composable
|
||||
fun HomeSponsorsItem() {
|
||||
ScrollableItemRow(items = sponsors) { sponsor ->
|
||||
ManagerLinkCard(
|
||||
icon = sponsor.icon,
|
||||
title = sponsor.title,
|
||||
link = sponsor.link
|
||||
)
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ package com.vanced.manager.ui.widget.screens.settings
|
|||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.preferences.holder.managerAccentColorPref
|
||||
import com.vanced.manager.core.preferences.holder.managerAccentColorPref
|
||||
import com.vanced.manager.ui.component.color.ManagerColorPicker
|
||||
import com.vanced.manager.ui.component.preference.DialogPreference
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.vanced.manager.ui.widget.screens.settings
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.preferences.holder.useCustomTabsPref
|
||||
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
|
||||
import com.vanced.manager.ui.component.preference.CheckboxPreference
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -2,8 +2,8 @@ package com.vanced.manager.ui.widget.screens.settings
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.core.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.core.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.ui.component.preference.RadiobuttonDialogPreference
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package com.vanced.manager.ui.widget.screens.settings
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vanced.manager.preferences.managerBooleanPreference
|
||||
import com.vanced.manager.core.preferences.managerBooleanPreference
|
||||
import com.vanced.manager.core.util.notificationApps
|
||||
import com.vanced.manager.ui.component.preference.CheckboxPreference
|
||||
import com.vanced.manager.util.notificationApps
|
||||
|
||||
@Composable
|
||||
fun SettingsNotificationsItem() {
|
||||
|
|
|
@ -2,8 +2,8 @@ package com.vanced.manager.ui.widget.screens.settings
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.preferences.holder.managerThemePref
|
||||
import com.vanced.manager.core.preferences.RadioButtonPreference
|
||||
import com.vanced.manager.core.preferences.holder.managerThemePref
|
||||
import com.vanced.manager.ui.component.preference.RadiobuttonDialogPreference
|
||||
import com.vanced.manager.ui.resources.managerString
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package com.vanced.manager.util
|
||||
|
||||
import com.vanced.manager.domain.model.App
|
||||
|
||||
fun getLatestOrProvidedAppVersion(
|
||||
version: String,
|
||||
app: App
|
||||
): String {
|
||||
if (version == "latest") {
|
||||
return app.versions?.last() ?: version
|
||||
}
|
||||
return version
|
||||
}
|
|
@ -71,6 +71,8 @@
|
|||
<string name="toolbar_home">Manager</string>
|
||||
<string name="toolbar_logs">Logs</string>
|
||||
<string name="toolbar_settings">Settings</string>
|
||||
<string name="toolbar_install">Install</string>
|
||||
<string name="toolbar_installation_preferences">Installation Preferences</string>
|
||||
<string name="toolbar_update_manager">Update Manager</string>
|
||||
|
||||
<!-- App Card -->
|
||||
|
|
|
@ -4,9 +4,10 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
|
||||
val kotlinVersion = "1.5.31"
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.0.3")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue