mirror of
https://github.com/YTVanced/VancedManager
synced 2024-11-25 12:45:12 +00:00
switch to github as backend
This commit is contained in:
parent
73ee8d4f7a
commit
6960cb67b1
45 changed files with 757 additions and 744 deletions
|
@ -5,6 +5,7 @@ plugins {
|
|||
id("com.android.application")
|
||||
kotlin("android")
|
||||
id("kotlin-parcelize")
|
||||
kotlin("plugin.serialization")
|
||||
}
|
||||
|
||||
val composeVersion = "1.1.1"
|
||||
|
@ -93,6 +94,7 @@ val languages: String get() {
|
|||
|
||||
dependencies {
|
||||
implementation(kotlin("reflect"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
|
||||
|
||||
// AndroidX
|
||||
implementation("androidx.core:core-ktx:1.7.0")
|
||||
|
@ -146,6 +148,8 @@ dependencies {
|
|||
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
|
||||
implementation("com.squareup.retrofit2:converter-gson:$retrofitVersion")
|
||||
|
||||
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||
|
|
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
|
@ -12,11 +12,11 @@
|
|||
# public *;
|
||||
#}
|
||||
|
||||
-keep class com.vanced.manager.network.model.AppDto {
|
||||
-keep class com.vanced.manager.network.dto.GithubReleaseDto {
|
||||
*;
|
||||
}
|
||||
|
||||
-keep class com.vanced.manager.network.model.DataDto {
|
||||
-keep class com.vanced.manager.network.dto.GithubReleaseAssetDto {
|
||||
*;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,12 +15,11 @@ class ManagerApplication : Application() {
|
|||
|
||||
modules(
|
||||
apiModule,
|
||||
customTabsModule,
|
||||
datasourceModule,
|
||||
downloaderModule,
|
||||
installerModule,
|
||||
mapperModule,
|
||||
networkModule,
|
||||
packageManagerModule,
|
||||
preferenceModule,
|
||||
repositoryModule,
|
||||
serviceModule,
|
||||
viewModelModule,
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package com.vanced.manager.core.preferences
|
||||
|
||||
data class CheckboxPreference(
|
||||
val title: String,
|
||||
val key: String,
|
||||
)
|
|
@ -1,6 +0,0 @@
|
|||
package com.vanced.manager.core.preferences
|
||||
|
||||
data class RadioButtonPreference(
|
||||
val title: String,
|
||||
val key: String
|
||||
)
|
|
@ -1,20 +1,13 @@
|
|||
package com.vanced.manager.core.preferences.holder
|
||||
|
||||
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
|
||||
|
||||
var useCustomTabsPref by managerBooleanPreference(USE_CUSTOM_TABS_KEY)
|
||||
var managerVariantPref by managerStringPreference(
|
||||
MANAGER_VARIANT_KEY,
|
||||
MANAGER_VARIANT_DEFAULT_VALUE
|
||||
)
|
||||
|
||||
var managerThemePref by managerStringPreference(MANAGER_THEME_KEY, MANAGER_THEME_DEFAULT_VALUE)
|
||||
var managerAccentColorPref by managerLongPreference(MANAGER_ACCENT_COLOR_KEY, defAccentColor)
|
||||
|
||||
var vancedThemePref by managerStringPreference(APP_VANCED_THEME_KEY, VANCED_THEME_DEFAULT_VALUE)
|
||||
var vancedVersionPref by managerStringPreference(APP_VANCED_VERSION_KEY, APP_VERSION_DEFAULT_VALUE)
|
||||
var vancedLanguagesPref by managerStringSetPreference(
|
||||
|
@ -23,6 +16,3 @@ var vancedLanguagesPref by managerStringSetPreference(
|
|||
)
|
||||
|
||||
var musicVersionPref by managerStringPreference(APP_MUSIC_VERSION_KEY, APP_VERSION_DEFAULT_VALUE)
|
||||
|
||||
var vancedEnabled by managerBooleanPreference(VANCED_ENABLED_KEY, APP_ENABLED_DEFAULT_VALUE)
|
||||
var musicEnabled by managerBooleanPreference(MUSIC_ENABLED_KEY, APP_ENABLED_DEFAULT_VALUE)
|
|
@ -1,7 +0,0 @@
|
|||
package com.vanced.manager.core.util
|
||||
|
||||
enum class Variant {
|
||||
|
||||
Root, Nonroot
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package com.vanced.manager.datasource
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
|
||||
interface PkgInfoDatasource {
|
||||
|
||||
fun getVersionCode(packageName: String): Int?
|
||||
|
||||
fun getVersionName(packageName: String): String?
|
||||
|
||||
}
|
||||
|
||||
class PkgInfoDatasourceImpl(
|
||||
private val packageManager: PackageManager
|
||||
) : PkgInfoDatasource {
|
||||
|
||||
private companion object {
|
||||
const val FLAG_NOTHING = 0
|
||||
const val VERSION_IGNORE_MAJOR = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
override fun getVersionCode(packageName: String): Int? {
|
||||
return try {
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, FLAG_NOTHING)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
packageInfo.longVersionCode.and(VERSION_IGNORE_MAJOR).toInt()
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
packageInfo.versionCode
|
||||
}
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@SuppressLint("WrongConstant")
|
||||
override fun getVersionName(packageName: String): String? {
|
||||
return try {
|
||||
packageManager
|
||||
.getPackageInfo(packageName, FLAG_NOTHING)
|
||||
.versionName
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package com.vanced.manager.datasource
|
||||
|
||||
import android.content.SharedPreferences
|
||||
|
||||
interface PreferenceDatasource {
|
||||
|
||||
var managerUseCustomTabs: Boolean
|
||||
var managerMode: String
|
||||
var managerTheme: String
|
||||
|
||||
}
|
||||
|
||||
class PreferenceDatasourceImpl(
|
||||
private val sharedPreferences: SharedPreferences
|
||||
) : PreferenceDatasource {
|
||||
|
||||
override var managerUseCustomTabs: Boolean
|
||||
get() = getBoolean(PreferenceData.MANAGER_USE_CUSTOM_TABS_KEY, PreferenceData.MANAGER_USE_CUSTOM_TABS_DEFAULT_VALUE)
|
||||
set(value) {
|
||||
putBoolean(PreferenceData.MANAGER_USE_CUSTOM_TABS_KEY, value)
|
||||
}
|
||||
|
||||
override var managerMode: String
|
||||
get() = getString(PreferenceData.MANAGER_MODE_KEY, PreferenceData.MANAGER_MODE_DEFAULT_VALUE)
|
||||
set(value) {
|
||||
putString(PreferenceData.MANAGER_MODE_KEY, value)
|
||||
}
|
||||
|
||||
override var managerTheme: String
|
||||
get() = getString(PreferenceData.MANAGER_THEME_KEY, PreferenceData.MANAGER_THEME_DEFAULT_VALUE)
|
||||
set(value) {
|
||||
putString(PreferenceData.MANAGER_THEME_KEY, value)
|
||||
}
|
||||
|
||||
private fun getString(key: String, defaultValue: String): String {
|
||||
return sharedPreferences.getString(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
private fun getBoolean(key: String, defaultValue: Boolean): Boolean {
|
||||
return sharedPreferences.getBoolean(key, defaultValue)
|
||||
}
|
||||
|
||||
private fun putString(key: String, value: String) {
|
||||
sharedPreferences.edit().putString(key, value).apply()
|
||||
}
|
||||
|
||||
private fun putBoolean(key: String, value: Boolean) {
|
||||
sharedPreferences.edit().putBoolean(key, value).apply()
|
||||
}
|
||||
}
|
||||
|
||||
object PreferenceData {
|
||||
|
||||
const val MANAGER_USE_CUSTOM_TABS_KEY = "manager_behaviour_use_custom_tabs"
|
||||
const val MANAGER_USE_CUSTOM_TABS_DEFAULT_VALUE = true
|
||||
|
||||
const val MANAGER_MODE_KEY = "manager_behaviour_mode"
|
||||
const val MANAGER_MODE_VALUE_ROOT = "root"
|
||||
const val MANAGER_MODE_VALUE_NONROOT= "nonroot"
|
||||
const val MANAGER_MODE_DEFAULT_VALUE = MANAGER_MODE_VALUE_NONROOT
|
||||
|
||||
const val MANAGER_THEME_KEY = "manager_appearance_theme"
|
||||
const val MANAGER_THEME_VALUE_LIGHT = "light"
|
||||
const val MANAGER_THEME_VALUE_DARK = "dark"
|
||||
const val MANAGER_THEME_VALUE_SYSTEM_DEFAULT = "system_default"
|
||||
const val MANAGER_THEME_DEFAULT_VALUE = MANAGER_THEME_VALUE_SYSTEM_DEFAULT
|
||||
|
||||
}
|
13
app/src/main/java/com/vanced/manager/di/CustomTabsModule.kt
Normal file
13
app/src/main/java/com/vanced/manager/di/CustomTabsModule.kt
Normal file
|
@ -0,0 +1,13 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import org.koin.dsl.module
|
||||
|
||||
val customTabsModule = module {
|
||||
fun provideChromeCustomTabs(): CustomTabsIntent {
|
||||
return CustomTabsIntent.Builder()
|
||||
.build()
|
||||
}
|
||||
|
||||
single { provideChromeCustomTabs() }
|
||||
}
|
31
app/src/main/java/com/vanced/manager/di/DatasourceModule.kt
Normal file
31
app/src/main/java/com/vanced/manager/di/DatasourceModule.kt
Normal file
|
@ -0,0 +1,31 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import android.content.Context
|
||||
import com.vanced.manager.datasource.PkgInfoDatasource
|
||||
import com.vanced.manager.datasource.PkgInfoDatasourceImpl
|
||||
import com.vanced.manager.datasource.PreferenceDatasource
|
||||
import com.vanced.manager.datasource.PreferenceDatasourceImpl
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
|
||||
val datasourceModule = module {
|
||||
|
||||
fun providePkgInfoDatasource(
|
||||
context: Context
|
||||
): PkgInfoDatasource {
|
||||
return PkgInfoDatasourceImpl(
|
||||
packageManager = context.packageManager
|
||||
)
|
||||
}
|
||||
|
||||
fun providePreferenceDatasource(
|
||||
context: Context
|
||||
): PreferenceDatasource {
|
||||
return PreferenceDatasourceImpl(
|
||||
sharedPreferences = context.getSharedPreferences("manager_settings", Context.MODE_PRIVATE)
|
||||
)
|
||||
}
|
||||
|
||||
single { providePkgInfoDatasource(androidContext()) }
|
||||
single { providePreferenceDatasource(androidContext()) }
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
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.DataDtoMapper
|
||||
import org.koin.dsl.module
|
||||
|
||||
val mapperModule = module {
|
||||
|
||||
fun provideAppMapper(
|
||||
packageInformationDataSource: PackageInformationDataSource,
|
||||
context: Context,
|
||||
) = AppDtoMapper(packageInformationDataSource, context)
|
||||
|
||||
fun provideJsonMapper(
|
||||
appDtoMapper: AppDtoMapper
|
||||
) = DataDtoMapper(appDtoMapper)
|
||||
|
||||
single { provideAppMapper(get(), get()) }
|
||||
single { provideJsonMapper(get()) }
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import android.content.Context
|
||||
import com.vanced.manager.domain.datasource.PackageInformationDataSource
|
||||
import com.vanced.manager.domain.datasource.PackageInformationDataSourceImpl
|
||||
import com.vanced.manager.domain.pkg.PkgManager
|
||||
import com.vanced.manager.domain.pkg.PkgManagerImpl
|
||||
import org.koin.dsl.module
|
||||
|
||||
val packageManagerModule = module {
|
||||
|
||||
fun providePackageManager(
|
||||
context: Context
|
||||
): PkgManager =
|
||||
PkgManagerImpl(
|
||||
packageManager = context.packageManager
|
||||
)
|
||||
|
||||
fun providePackageInformationDataSource(
|
||||
pkgManager: PkgManager
|
||||
): PackageInformationDataSource =
|
||||
PackageInformationDataSourceImpl(pkgManager)
|
||||
|
||||
single { providePackageManager(get()) }
|
||||
single { providePackageInformationDataSource(get()) }
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import android.content.Context
|
||||
import org.koin.dsl.module
|
||||
|
||||
val preferenceModule = module {
|
||||
|
||||
fun provideDatastore(
|
||||
context: Context
|
||||
) = context.getSharedPreferences("manager_settings", Context.MODE_PRIVATE)
|
||||
|
||||
single { provideDatastore(get()) }
|
||||
}
|
|
@ -1,25 +1,31 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import com.vanced.manager.network.DataService
|
||||
import com.vanced.manager.network.model.DataDtoMapper
|
||||
import com.vanced.manager.repository.DataRepository
|
||||
import com.vanced.manager.repository.MainRepository
|
||||
import com.vanced.manager.repository.MirrorRepository
|
||||
import org.koin.core.qualifier.named
|
||||
import com.vanced.manager.datasource.PkgInfoDatasource
|
||||
import com.vanced.manager.datasource.PreferenceDatasource
|
||||
import com.vanced.manager.network.GithubService
|
||||
import com.vanced.manager.repository.*
|
||||
import org.koin.dsl.module
|
||||
|
||||
val repositoryModule = module {
|
||||
|
||||
fun provideMainRepository(
|
||||
dataService: DataService,
|
||||
dataDtoMapper: DataDtoMapper
|
||||
) = MainRepository(dataService, dataDtoMapper)
|
||||
fun provideGithubRepository(
|
||||
githubService: GithubService,
|
||||
pkgInfoDatasource: PkgInfoDatasource
|
||||
): AppRepository {
|
||||
return AppRepositoryImpl(
|
||||
githubService = githubService,
|
||||
pkgInfoDatasource = pkgInfoDatasource
|
||||
)
|
||||
}
|
||||
|
||||
fun provideMirrorRepository(
|
||||
dataService: DataService,
|
||||
dataDtoMapper: DataDtoMapper
|
||||
) = MirrorRepository(dataService, dataDtoMapper)
|
||||
fun providePreferenceRepository(
|
||||
preferenceDatasource: PreferenceDatasource
|
||||
): PreferenceRepository {
|
||||
return PreferenceRepositoryImpl(
|
||||
preferenceDatasource = preferenceDatasource
|
||||
)
|
||||
}
|
||||
|
||||
single { provideMainRepository(get(named("main")), get()) }
|
||||
single { provideMirrorRepository(get(named("mirror")), get()) }
|
||||
single { provideGithubRepository(get(), get()) }
|
||||
single { providePreferenceRepository(get()) }
|
||||
}
|
|
@ -1,35 +1,31 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.vanced.manager.network.DataService
|
||||
import com.vanced.manager.network.util.BASE_GITHUB
|
||||
import com.vanced.manager.network.util.BASE_MIRROR
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import com.vanced.manager.network.GithubService
|
||||
import com.vanced.manager.network.util.GITHUB_API_BASE
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.create
|
||||
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
val serviceModule = module {
|
||||
|
||||
fun provideMainService(
|
||||
fun provideGithubService(
|
||||
okHttpClient: OkHttpClient
|
||||
) = Retrofit.Builder()
|
||||
.baseUrl(BASE_GITHUB)
|
||||
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
.create(DataService::class.java)
|
||||
): GithubService {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(GITHUB_API_BASE)
|
||||
.addConverterFactory(json.asConverterFactory(MediaType.get("application/json")))
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
.create()
|
||||
}
|
||||
|
||||
fun provideMirrorService(
|
||||
okHttpClient: OkHttpClient
|
||||
) = Retrofit.Builder()
|
||||
.baseUrl(BASE_MIRROR)
|
||||
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
.create(DataService::class.java)
|
||||
|
||||
single(named("main")) { provideMainService(get()) }
|
||||
single(named("mirror")) { provideMirrorService(get()) }
|
||||
single { provideGithubService(get()) }
|
||||
}
|
|
@ -1,16 +1,14 @@
|
|||
package com.vanced.manager.di
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
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.installer.impl.MicrogInstaller
|
||||
import com.vanced.manager.core.installer.impl.MusicInstaller
|
||||
import com.vanced.manager.core.installer.impl.VancedInstaller
|
||||
import com.vanced.manager.repository.DataRepository
|
||||
import com.vanced.manager.repository.MainRepository
|
||||
import com.vanced.manager.repository.MirrorRepository
|
||||
import com.vanced.manager.repository.AppRepository
|
||||
import com.vanced.manager.repository.PreferenceRepository
|
||||
import com.vanced.manager.ui.viewmodel.ConfigurationViewModel
|
||||
import com.vanced.manager.ui.viewmodel.InstallViewModel
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
|
@ -22,11 +20,16 @@ import org.koin.dsl.module
|
|||
val viewModelModule = module {
|
||||
|
||||
fun provideMainViewModel(
|
||||
mainRepository: MainRepository,
|
||||
mirrorRepository: MirrorRepository,
|
||||
preferences: SharedPreferences,
|
||||
appRepository: AppRepository,
|
||||
preferenceRepository: PreferenceRepository,
|
||||
app: Application,
|
||||
) = MainViewModel(mainRepository, mirrorRepository, preferences, app)
|
||||
): MainViewModel {
|
||||
return MainViewModel(
|
||||
appRepository = appRepository,
|
||||
preferenceRepository = preferenceRepository,
|
||||
app = app
|
||||
)
|
||||
}
|
||||
|
||||
fun provideInstallViewModel(
|
||||
vancedDownloader: VancedDownloader,
|
||||
|
@ -42,12 +45,16 @@ val viewModelModule = module {
|
|||
return ConfigurationViewModel()
|
||||
}
|
||||
|
||||
fun provideSettingsViewModel(): SettingsViewModel {
|
||||
return SettingsViewModel()
|
||||
fun provideSettingsViewModel(
|
||||
preferenceRepository: PreferenceRepository
|
||||
): SettingsViewModel {
|
||||
return SettingsViewModel(
|
||||
preferenceRepository = preferenceRepository
|
||||
)
|
||||
}
|
||||
|
||||
viewModel { provideMainViewModel(get(), get(), get(), androidApplication()) }
|
||||
viewModel { provideMainViewModel(get(), get(), androidApplication()) }
|
||||
viewModel { provideInstallViewModel(get(), get(), get(), get(), get(), get()) }
|
||||
viewModel { provideConfigurationViewModel() }
|
||||
viewModel { provideSettingsViewModel() }
|
||||
viewModel { provideSettingsViewModel(get()) }
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.vanced.manager.domain.datasource
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import com.vanced.manager.domain.pkg.PkgManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
interface PackageInformationDataSource {
|
||||
|
||||
suspend fun getVersionCode(packageName: String): Int?
|
||||
|
||||
suspend fun getVersionName(packageName: String): String?
|
||||
}
|
||||
|
||||
class PackageInformationDataSourceImpl(
|
||||
private val pkgManager: PkgManager
|
||||
) : PackageInformationDataSource {
|
||||
|
||||
override suspend fun getVersionCode(packageName: String): Int? =
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
pkgManager.getVersionCode(packageName)
|
||||
} catch (exception: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getVersionName(packageName: String): String? =
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
pkgManager.getVersionName(packageName)
|
||||
} catch (exception: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,58 @@
|
|||
package com.vanced.manager.domain.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.vanced.manager.R
|
||||
|
||||
data class App(
|
||||
val name: String,
|
||||
val remoteVersion: String,
|
||||
val remoteVersionCode: Int,
|
||||
val installedVersion: String?,
|
||||
val installedVersionCode: Int?,
|
||||
val iconUrl: String?,
|
||||
val appStatus: AppStatus,
|
||||
val packageName: String,
|
||||
@DrawableRes val iconResId: Int,
|
||||
val changelog: String,
|
||||
val url: String?,
|
||||
val versions: List<String>?,
|
||||
val themes: List<String>?,
|
||||
val languages: List<String>?,
|
||||
val installationOptions: List<InstallationOption>?
|
||||
val remoteVersionCode: Int,
|
||||
val remoteVersionName: String,
|
||||
val installedVersionCode: Int?,
|
||||
val installedVersionName: String?,
|
||||
val packageName: String,
|
||||
val launchActivity: String,
|
||||
val state: AppState,
|
||||
val app: AppType
|
||||
)
|
||||
|
||||
object AppData {
|
||||
const val NAME_VANCED_YOUTUBE = "YouTube Vanced"
|
||||
const val NAME_VANCED_YOUTUBE_MUSIC = "YouTube Vanced Music"
|
||||
const val NAME_VANCED_MICROG = "Vanced microG"
|
||||
const val NAME_VANCED_MANAGER = "Vanced Manager"
|
||||
|
||||
const val ICON_VANCED_YOUTUBE = R.drawable.ic_vanced
|
||||
const val ICON_VANCED_YOUTUBE_MUSIC = R.drawable.ic_music
|
||||
const val ICON_VANCED_MICROG = R.drawable.ic_microg
|
||||
const val ICON_VANCED_MANAGER = R.drawable.ic_manager
|
||||
|
||||
const val PACKAGE_VANCED_YOUTUBE = "com.vanced.android.youtube"
|
||||
const val PACKAGE_VANCED_YOUTUBE_MUSIC = "com.vanced.android.youtube.apps.music"
|
||||
const val PACKAGE_VANCED_MICROG = "com.mgoogle.android.gms"
|
||||
const val PACKAGE_VANCED_MANAGER = "com.vanced.manager"
|
||||
|
||||
const val PACKAGE_ROOT_VANCED_YOUTUBE = "com.google.android.youtube"
|
||||
const val PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC = "com.google.android.youtube.apps.music"
|
||||
|
||||
const val LAUNCH_ACTIVITY_VANCED_YOUTUBE = "com.google.android.youtube.HomeActivity"
|
||||
const val LAUNCH_ACTIVITY_VANCED_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music.activities.MusicActivity"
|
||||
const val LAUNCH_ACTIVITY_VANCED_MICROG = "org.microg.gms.ui.SettingsActivity"
|
||||
const val LAUNCH_ACTIVITY_VANCED_MANAGER = ""
|
||||
}
|
||||
|
||||
enum class AppType {
|
||||
VANCED_YOUTUBE,
|
||||
VANCED_YOUTUBE_MUSIC,
|
||||
VANCED_MICROG,
|
||||
VANCED_MANAGER,
|
||||
}
|
||||
|
||||
enum class AppState {
|
||||
NOT_INSTALLED,
|
||||
INSTALLED,
|
||||
NEEDS_UPDATE
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package com.vanced.manager.domain.model
|
||||
|
||||
enum class AppStatus {
|
||||
|
||||
Install,
|
||||
Reinstall,
|
||||
Update,
|
||||
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package com.vanced.manager.domain.model
|
||||
|
||||
data class Data(
|
||||
val manager: App,
|
||||
val apps: List<App>,
|
||||
)
|
|
@ -1,49 +0,0 @@
|
|||
package com.vanced.manager.domain.pkg
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
|
||||
interface PkgManager {
|
||||
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
suspend fun getVersionCode(packageName: String): Int
|
||||
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
suspend fun getVersionName(packageName: String): String
|
||||
}
|
||||
|
||||
class PkgManagerImpl(
|
||||
private val packageManager: PackageManager
|
||||
) : PkgManager {
|
||||
|
||||
private companion object {
|
||||
const val PACKAGE_FLAG_ALL_OFF = 0
|
||||
const val MAJOR_IGNORE = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
@Suppress("DEPRECATION")
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
override suspend fun getVersionCode(
|
||||
packageName: String
|
||||
) = with(packageManager) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
getPackageInfo(packageName, PACKAGE_FLAG_ALL_OFF)
|
||||
.longVersionCode
|
||||
.and(MAJOR_IGNORE)
|
||||
.toInt()
|
||||
} else {
|
||||
getPackageInfo(packageName, PACKAGE_FLAG_ALL_OFF)
|
||||
.versionCode
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
override suspend fun getVersionName(
|
||||
packageName: String
|
||||
): String = packageManager
|
||||
.getPackageInfo(packageName, PACKAGE_FLAG_ALL_OFF)
|
||||
.versionName
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package com.vanced.manager.domain.util
|
||||
|
||||
interface EntityMapper<T, Model> {
|
||||
|
||||
suspend fun mapToModel(entity: T): Model
|
||||
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package com.vanced.manager.network
|
||||
|
||||
import com.vanced.manager.network.model.DataDto
|
||||
import retrofit2.http.GET
|
||||
|
||||
interface DataService {
|
||||
|
||||
@GET("latest.json")
|
||||
suspend fun get(): DataDto
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.vanced.manager.network
|
||||
|
||||
import com.vanced.manager.network.dto.GithubReleaseDto
|
||||
import retrofit2.http.GET
|
||||
|
||||
private const val REPOS_VANCED = "repos/YTVanced"
|
||||
|
||||
interface GithubService {
|
||||
|
||||
@GET("$REPOS_VANCED/Vanced/releases/latest")
|
||||
suspend fun getVancedYoutubeRelease(): GithubReleaseDto
|
||||
|
||||
@GET("$REPOS_VANCED/VancedMusic/releases/latest")
|
||||
suspend fun getVancedYoutubeMusicRelease(): GithubReleaseDto
|
||||
|
||||
@GET("$REPOS_VANCED/VancedMicrog/releases/latest")
|
||||
suspend fun getVancedMicrogRelease(): GithubReleaseDto
|
||||
|
||||
@GET("$REPOS_VANCED/VancedManager/releases/latest")
|
||||
suspend fun getVancedManagerRelease(): GithubReleaseDto
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.vanced.manager.network.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GithubReleaseDto(
|
||||
@SerialName("tag_name")
|
||||
val tagName: String,
|
||||
|
||||
@SerialName("body")
|
||||
val body: String,
|
||||
|
||||
@SerialName("assets")
|
||||
val assets: List<GithubReleaseAssetDto>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GithubReleaseAssetDto(
|
||||
@SerialName("name")
|
||||
val name: String,
|
||||
|
||||
@SerialName("browser_download_url")
|
||||
val browserDownloadUrl: String
|
||||
)
|
|
@ -1,16 +0,0 @@
|
|||
package com.vanced.manager.network.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AppDto(
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("version") val version: String,
|
||||
@SerializedName("version_code") val versionCode: Int,
|
||||
@SerializedName("changelog") val changelog: String,
|
||||
@SerializedName("icon_url") val iconUrl: String,
|
||||
@SerializedName("package_name") val packageName: String,
|
||||
@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,
|
||||
)
|
|
@ -1,156 +0,0 @@
|
|||
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.network.util.MUSIC_NAME
|
||||
import com.vanced.manager.network.util.VANCED_NAME
|
||||
import java.util.*
|
||||
|
||||
class AppDtoMapper(
|
||||
private val pkgInfoDataSource: PackageInformationDataSource,
|
||||
private val context: Context
|
||||
) : EntityMapper<AppDto, App> {
|
||||
|
||||
private val latestVersionRadioButton =
|
||||
InstallationOptionItem(
|
||||
displayText = { context.getString(R.string.app_version_dialog_option_latest) },
|
||||
key = "latest"
|
||||
)
|
||||
|
||||
override suspend fun mapToModel(
|
||||
entity: AppDto
|
||||
) = with(entity) {
|
||||
val localVersionCode = pkgInfoDataSource.getVersionCode(packageName)
|
||||
App(
|
||||
name = name,
|
||||
remoteVersion = version,
|
||||
remoteVersionCode = versionCode,
|
||||
installedVersion = pkgInfoDataSource.getVersionName(packageName),
|
||||
installedVersionCode = localVersionCode,
|
||||
appStatus = compareVersionCodes(versionCode, localVersionCode),
|
||||
packageName = packageName,
|
||||
iconUrl = iconUrl,
|
||||
changelog = changelog,
|
||||
url = url,
|
||||
versions = versions,
|
||||
themes = themes,
|
||||
languages = languages,
|
||||
installationOptions = getInstallationOptions(name, themes, versions, languages)
|
||||
)
|
||||
}
|
||||
|
||||
private fun compareVersionCodes(
|
||||
remote: Int?,
|
||||
local: Int?
|
||||
) = if (local != null && remote != null) {
|
||||
when {
|
||||
remote > local -> AppStatus.Update
|
||||
remote <= local -> AppStatus.Reinstall
|
||||
else -> AppStatus.Install
|
||||
}
|
||||
} else {
|
||||
AppStatus.Install
|
||||
}
|
||||
|
||||
private fun getInstallationOptions(
|
||||
appName: String,
|
||||
appThemes: List<String>?,
|
||||
appVersions: List<String>?,
|
||||
appLanguages: List<String>?,
|
||||
) : List<InstallationOption>? = when (appName) {
|
||||
VANCED_NAME -> buildList {
|
||||
if (appThemes != null) {
|
||||
add(
|
||||
InstallationOption.SingleSelect(
|
||||
titleId = R.string.app_installation_options_theme,
|
||||
getOption = { vancedThemePref },
|
||||
setOption = {
|
||||
vancedThemePref = it
|
||||
},
|
||||
items = appThemes.map { theme ->
|
||||
InstallationOptionItem(
|
||||
displayText = {
|
||||
theme.replaceFirstChar {
|
||||
it.titlecase(Locale.getDefault())
|
||||
}
|
||||
},
|
||||
key = theme
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
if (appVersions != null) {
|
||||
add(
|
||||
InstallationOption.SingleSelect(
|
||||
titleId = R.string.app_installation_options_version,
|
||||
getOption = { vancedVersionPref },
|
||||
setOption = {
|
||||
vancedVersionPref = it
|
||||
},
|
||||
items = (appVersions.map { version ->
|
||||
InstallationOptionItem(
|
||||
displayText = { "v$version" },
|
||||
key = version
|
||||
)
|
||||
} + latestVersionRadioButton).reversed(),
|
||||
)
|
||||
)
|
||||
}
|
||||
if (appLanguages != null) {
|
||||
add(
|
||||
InstallationOption.MultiSelect(
|
||||
titleId = R.string.app_installation_options_language,
|
||||
getOption = { vancedLanguagesPref },
|
||||
addOption = {
|
||||
vancedLanguagesPref = vancedLanguagesPref + it
|
||||
},
|
||||
removeOption = {
|
||||
vancedLanguagesPref = vancedLanguagesPref - it
|
||||
},
|
||||
items = appLanguages.map { language ->
|
||||
InstallationOptionItem(
|
||||
displayText = {
|
||||
val locale = Locale(it)
|
||||
locale.getDisplayName(locale)
|
||||
},
|
||||
key = language
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
MUSIC_NAME -> buildList {
|
||||
if (appVersions != null) {
|
||||
add(
|
||||
InstallationOption.SingleSelect(
|
||||
titleId = R.string.app_installation_options_version,
|
||||
getOption = { musicVersionPref },
|
||||
setOption = {
|
||||
musicVersionPref = it
|
||||
},
|
||||
items = (appVersions.map { version ->
|
||||
InstallationOptionItem(
|
||||
displayText = { "v$version" },
|
||||
key = version
|
||||
)
|
||||
} + latestVersionRadioButton).reversed(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.vanced.manager.network.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DataDto(
|
||||
@SerializedName("manager") val manager: AppDto,
|
||||
@SerializedName("apps") val apps: DataAppDto
|
||||
)
|
||||
|
||||
data class DataAppDto(
|
||||
@SerializedName("nonroot") val nonroot: List<AppDto>,
|
||||
@SerializedName("root") val root: List<AppDto>,
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
package com.vanced.manager.network.model
|
||||
|
||||
import com.vanced.manager.core.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.domain.model.Data
|
||||
import com.vanced.manager.domain.util.EntityMapper
|
||||
|
||||
class DataDtoMapper(
|
||||
private val appDtoMapper: AppDtoMapper
|
||||
) : EntityMapper<DataDto, Data> {
|
||||
|
||||
override suspend fun mapToModel(
|
||||
entity: DataDto
|
||||
) = with(entity) {
|
||||
Data(
|
||||
manager = appDtoMapper.mapToModel(manager),
|
||||
apps =
|
||||
if (managerVariantPref == "root") {
|
||||
apps.root
|
||||
} else {
|
||||
apps.nonroot
|
||||
}.map { appDto ->
|
||||
appDtoMapper.mapToModel(appDto)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package com.vanced.manager.network.util
|
||||
|
||||
const val BASE = "https://api.vancedapp.com/api/v1/"
|
||||
const val BASE_MIRROR = "https://x1nto.github.io/VancedFiles/api/v2/" //TODO
|
||||
const val BASE_GITHUB = "https://x1nto.github.io/VancedFiles/api/v2/"
|
||||
|
||||
const val GITHUB_API_BASE = "https://api.github.com/"
|
||||
|
||||
const val VANCED_NAME = "YouTube Vanced"
|
||||
const val MUSIC_NAME = "YouTube Vanced Music"
|
||||
|
|
172
app/src/main/java/com/vanced/manager/repository/AppRepository.kt
Normal file
172
app/src/main/java/com/vanced/manager/repository/AppRepository.kt
Normal file
|
@ -0,0 +1,172 @@
|
|||
package com.vanced.manager.repository
|
||||
|
||||
import com.vanced.manager.datasource.PkgInfoDatasource
|
||||
import com.vanced.manager.domain.model.AppData
|
||||
import com.vanced.manager.domain.model.AppState
|
||||
import com.vanced.manager.domain.model.AppType
|
||||
import com.vanced.manager.domain.model.App
|
||||
import com.vanced.manager.network.GithubService
|
||||
import com.vanced.manager.network.dto.GithubReleaseDto
|
||||
|
||||
interface AppRepository {
|
||||
|
||||
suspend fun getVancedYoutubeNonroot(): App
|
||||
|
||||
suspend fun getVancedYoutubeRoot(): App
|
||||
|
||||
suspend fun getVancedYoutubeMusicNonroot(): App
|
||||
|
||||
suspend fun getVancedYoutubeMusicRoot(): App
|
||||
|
||||
suspend fun getVancedMicrog(): App
|
||||
|
||||
suspend fun getVancedManager(): App
|
||||
|
||||
}
|
||||
|
||||
class AppRepositoryImpl(
|
||||
private val githubService: GithubService,
|
||||
private val pkgInfoDatasource: PkgInfoDatasource,
|
||||
) : AppRepository {
|
||||
|
||||
override suspend fun getVancedYoutubeNonroot(): App {
|
||||
val githubRelease = githubService.getVancedYoutubeRelease()
|
||||
val remoteVersionCode = githubRelease.getVersionCode()
|
||||
val remoteVersionName = githubRelease.getVersionName()
|
||||
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE)
|
||||
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE)
|
||||
return App(
|
||||
name = AppData.NAME_VANCED_YOUTUBE,
|
||||
iconResId = AppData.ICON_VANCED_YOUTUBE,
|
||||
changelog = githubRelease.body,
|
||||
remoteVersionCode = remoteVersionCode,
|
||||
remoteVersionName = remoteVersionName,
|
||||
installedVersionCode = installedVersionCode,
|
||||
installedVersionName = installedVersionName,
|
||||
packageName = AppData.PACKAGE_VANCED_YOUTUBE,
|
||||
launchActivity = AppData.LAUNCH_ACTIVITY_VANCED_YOUTUBE,
|
||||
state = getNoonrotAppState(installedVersionCode, remoteVersionCode),
|
||||
app = AppType.VANCED_YOUTUBE,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getVancedYoutubeRoot(): App {
|
||||
val githubRelease = githubService.getVancedYoutubeRelease()
|
||||
val remoteVersionCode = githubRelease.getVersionCode()
|
||||
val remoteVersionName = githubRelease.getVersionName()
|
||||
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE)
|
||||
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE)
|
||||
return App(
|
||||
name = AppData.NAME_VANCED_YOUTUBE,
|
||||
iconResId = AppData.ICON_VANCED_YOUTUBE,
|
||||
changelog = githubRelease.body,
|
||||
remoteVersionCode = remoteVersionCode,
|
||||
remoteVersionName = remoteVersionName,
|
||||
installedVersionCode = installedVersionCode,
|
||||
installedVersionName = installedVersionName,
|
||||
packageName = AppData.PACKAGE_VANCED_YOUTUBE,
|
||||
launchActivity = AppData.LAUNCH_ACTIVITY_VANCED_YOUTUBE,
|
||||
state = getNoonrotAppState(installedVersionCode, remoteVersionCode),
|
||||
app = AppType.VANCED_YOUTUBE,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getVancedYoutubeMusicNonroot(): App {
|
||||
val githubRelease = githubService.getVancedYoutubeMusicRelease()
|
||||
val remoteVersionCode = githubRelease.getVersionCode()
|
||||
val remoteVersionName = githubRelease.getVersionName()
|
||||
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC)
|
||||
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC)
|
||||
return App(
|
||||
name = AppData.NAME_VANCED_YOUTUBE_MUSIC,
|
||||
iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC,
|
||||
changelog = githubRelease.body,
|
||||
remoteVersionCode = remoteVersionCode,
|
||||
remoteVersionName = remoteVersionName,
|
||||
installedVersionCode = installedVersionCode,
|
||||
installedVersionName = installedVersionName,
|
||||
packageName = AppData.PACKAGE_VANCED_YOUTUBE_MUSIC,
|
||||
launchActivity = AppData.LAUNCH_ACTIVITY_VANCED_YOUTUBE_MUSIC,
|
||||
state = getNoonrotAppState(installedVersionCode, remoteVersionCode),
|
||||
app = AppType.VANCED_YOUTUBE_MUSIC,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getVancedYoutubeMusicRoot(): App {
|
||||
val githubRelease = githubService.getVancedYoutubeMusicRelease()
|
||||
val remoteVersionCode = githubRelease.getVersionCode()
|
||||
val remoteVersionName = githubRelease.getVersionName()
|
||||
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC)
|
||||
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC)
|
||||
return App(
|
||||
name = AppData.NAME_VANCED_YOUTUBE_MUSIC,
|
||||
iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC,
|
||||
changelog = githubRelease.body,
|
||||
remoteVersionCode = remoteVersionCode,
|
||||
remoteVersionName = remoteVersionName,
|
||||
installedVersionCode = installedVersionCode,
|
||||
installedVersionName = installedVersionName,
|
||||
packageName = AppData.PACKAGE_VANCED_YOUTUBE_MUSIC,
|
||||
launchActivity = AppData.LAUNCH_ACTIVITY_VANCED_YOUTUBE_MUSIC,
|
||||
state = getNoonrotAppState(installedVersionCode, remoteVersionCode),
|
||||
app = AppType.VANCED_YOUTUBE_MUSIC,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getVancedMicrog(): App {
|
||||
val githubRelease = githubService.getVancedMicrogRelease()
|
||||
val remoteVersionCode = githubRelease.getVersionCode()
|
||||
val remoteVersionName = githubRelease.getVersionName()
|
||||
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_MICROG)
|
||||
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_MICROG)
|
||||
return App(
|
||||
name = AppData.NAME_VANCED_MICROG,
|
||||
iconResId = AppData.ICON_VANCED_MICROG,
|
||||
changelog = githubRelease.body,
|
||||
remoteVersionCode = remoteVersionCode,
|
||||
remoteVersionName = remoteVersionName,
|
||||
installedVersionCode = installedVersionCode,
|
||||
installedVersionName = installedVersionName,
|
||||
packageName = AppData.PACKAGE_VANCED_MICROG,
|
||||
launchActivity = AppData.LAUNCH_ACTIVITY_VANCED_MICROG,
|
||||
state = getNoonrotAppState(installedVersionCode, remoteVersionCode),
|
||||
app = AppType.VANCED_MICROG,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getVancedManager(): App {
|
||||
val githubRelease = githubService.getVancedManagerRelease()
|
||||
val remoteVersionCode = githubRelease.getVersionCode()
|
||||
val remoteVersionName = githubRelease.getVersionName()
|
||||
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_MANAGER)
|
||||
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_MANAGER)
|
||||
return App(
|
||||
name = AppData.NAME_VANCED_MANAGER,
|
||||
iconResId = AppData.ICON_VANCED_MANAGER,
|
||||
changelog = githubRelease.body,
|
||||
remoteVersionCode = remoteVersionCode,
|
||||
remoteVersionName = remoteVersionName,
|
||||
installedVersionCode = installedVersionCode,
|
||||
installedVersionName = installedVersionName,
|
||||
packageName = AppData.PACKAGE_VANCED_MANAGER,
|
||||
launchActivity = AppData.LAUNCH_ACTIVITY_VANCED_MANAGER,
|
||||
state = getNoonrotAppState(installedVersionCode, remoteVersionCode),
|
||||
app = AppType.VANCED_MANAGER,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getNoonrotAppState(
|
||||
installedVersionCode: Int?,
|
||||
remoteVersionCode: Int
|
||||
): AppState {
|
||||
return when {
|
||||
installedVersionCode == null -> AppState.NOT_INSTALLED
|
||||
installedVersionCode < remoteVersionCode -> AppState.NEEDS_UPDATE
|
||||
installedVersionCode >= remoteVersionCode -> AppState.INSTALLED
|
||||
else -> AppState.NOT_INSTALLED
|
||||
}
|
||||
}
|
||||
|
||||
private fun GithubReleaseDto.getVersionCode() = tagName.substringAfter("-").toInt()
|
||||
private fun GithubReleaseDto.getVersionName() = tagName.substringBefore("-")
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package com.vanced.manager.repository
|
||||
|
||||
import com.vanced.manager.domain.model.Data
|
||||
|
||||
interface DataRepository {
|
||||
|
||||
suspend fun fetch(): Data
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.vanced.manager.repository
|
||||
|
||||
import com.vanced.manager.network.DataService
|
||||
import com.vanced.manager.network.model.DataDtoMapper
|
||||
|
||||
class MainRepository(
|
||||
private val mainService: DataService,
|
||||
private val mapper: DataDtoMapper
|
||||
) : DataRepository {
|
||||
|
||||
override suspend fun fetch() =
|
||||
mapper.mapToModel(mainService.get())
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.vanced.manager.repository
|
||||
|
||||
import com.vanced.manager.network.DataService
|
||||
import com.vanced.manager.network.model.DataDtoMapper
|
||||
|
||||
class MirrorRepository(
|
||||
private val mirrorService: DataService,
|
||||
private val mapper: DataDtoMapper
|
||||
) : DataRepository {
|
||||
|
||||
override suspend fun fetch() =
|
||||
mapper.mapToModel(mirrorService.get())
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.vanced.manager.repository
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.vanced.manager.datasource.PreferenceData
|
||||
import com.vanced.manager.datasource.PreferenceDatasource
|
||||
|
||||
interface PreferenceRepository {
|
||||
|
||||
var managerUseCustomTabs: Boolean
|
||||
var managerMode: ManagerMode
|
||||
var managerTheme: ManagerTheme
|
||||
|
||||
}
|
||||
|
||||
class PreferenceRepositoryImpl(
|
||||
private val preferenceDatasource: PreferenceDatasource
|
||||
) : PreferenceRepository {
|
||||
|
||||
override var managerUseCustomTabs: Boolean
|
||||
get() = preferenceDatasource.managerUseCustomTabs
|
||||
set(value) {
|
||||
preferenceDatasource.managerUseCustomTabs = value
|
||||
}
|
||||
|
||||
override var managerMode: ManagerMode
|
||||
get() = ManagerMode.fromValue(preferenceDatasource.managerMode)
|
||||
set(value) {
|
||||
preferenceDatasource.managerMode = value.value
|
||||
}
|
||||
|
||||
override var managerTheme: ManagerTheme
|
||||
get() = ManagerTheme.fromValue(preferenceDatasource.managerTheme)
|
||||
set(value) {
|
||||
preferenceDatasource.managerTheme = value.value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
enum class ManagerTheme(val value: String) {
|
||||
LIGHT(PreferenceData.MANAGER_THEME_VALUE_LIGHT),
|
||||
DARK(PreferenceData.MANAGER_THEME_VALUE_DARK),
|
||||
SYSTEM_DEFAULT(PreferenceData.MANAGER_THEME_VALUE_SYSTEM_DEFAULT);
|
||||
|
||||
@Composable
|
||||
fun isDark() = when (this) {
|
||||
LIGHT -> false
|
||||
DARK -> true
|
||||
SYSTEM_DEFAULT -> isSystemInDarkTheme()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromValue(value: String?): ManagerTheme {
|
||||
return values().find {
|
||||
it.value == value
|
||||
} ?: SYSTEM_DEFAULT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ManagerMode(val value: String) {
|
||||
ROOT(PreferenceData.MANAGER_MODE_VALUE_ROOT),
|
||||
NONROOT(PreferenceData.MANAGER_MODE_VALUE_NONROOT);
|
||||
|
||||
val isRoot get() = this == ROOT
|
||||
val isNonroot get() = this == NONROOT
|
||||
|
||||
companion object {
|
||||
fun fromValue(value: String?): ManagerMode {
|
||||
return when (value) {
|
||||
"root" -> ROOT
|
||||
else -> NONROOT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -73,12 +73,15 @@ class MainActivity : ComponentActivity() {
|
|||
when (val screen = backStack.last()) {
|
||||
is Screen.Home -> {
|
||||
HomeScreen(
|
||||
viewModel = mainViewModel,
|
||||
managerState = mainViewModel.appState,
|
||||
onRefresh = {
|
||||
mainViewModel.fetch()
|
||||
},
|
||||
onToolbarScreenSelected = {
|
||||
backStack.push(it)
|
||||
},
|
||||
onAppDownloadClick = { appName, appVersions, installationOptions ->
|
||||
if (installationOptions != null) {
|
||||
onAppDownloadClick = { app ->
|
||||
/*if (installationOptions != null) {
|
||||
backStack.push(
|
||||
Screen.Configuration(
|
||||
appName,
|
||||
|
@ -88,13 +91,13 @@ class MainActivity : ComponentActivity() {
|
|||
)
|
||||
} else {
|
||||
backStack.push(Screen.Install(appName, appVersions))
|
||||
}
|
||||
}*/
|
||||
},
|
||||
onAppLaunchClick = { appName, packageName ->
|
||||
mainViewModel.launchApp(appName, packageName)
|
||||
onAppLaunchClick = { app ->
|
||||
mainViewModel.launchApp(app.packageName, app.launchActivity)
|
||||
},
|
||||
onAppUninstallClick = { packageName ->
|
||||
mainViewModel.uninstallApp(packageName)
|
||||
onAppUninstallClick = { app ->
|
||||
mainViewModel.uninstallApp(app.packageName)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -15,36 +15,30 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.domain.model.App
|
||||
import com.vanced.manager.domain.model.InstallationOption
|
||||
import com.vanced.manager.ui.component.*
|
||||
import com.vanced.manager.ui.resource.managerString
|
||||
import com.vanced.manager.ui.util.Screen
|
||||
import com.vanced.manager.ui.viewmodel.MainViewModel
|
||||
import com.vanced.manager.ui.viewmodel.ManagerState
|
||||
import com.vanced.manager.ui.widget.AppCard
|
||||
import com.vanced.manager.ui.widget.AppCardPlaceholder
|
||||
import com.vanced.manager.ui.widget.LinkCard
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
viewModel: MainViewModel,
|
||||
managerState: ManagerState,
|
||||
onRefresh: () -> Unit,
|
||||
onToolbarScreenSelected: (Screen) -> Unit,
|
||||
onAppDownloadClick: (
|
||||
appName: String,
|
||||
appVersions: List<String>?,
|
||||
installationOptions: List<InstallationOption>?
|
||||
) -> Unit,
|
||||
onAppUninstallClick: (appPackage: String) -> Unit,
|
||||
onAppLaunchClick: (appName: String, appPackage: String) -> Unit,
|
||||
onAppDownloadClick: (App) -> Unit,
|
||||
onAppUninstallClick: (App) -> Unit,
|
||||
onAppLaunchClick: (App) -> Unit,
|
||||
) {
|
||||
val refreshState =
|
||||
rememberSwipeRefreshState(isRefreshing = viewModel.appState.isFetching)
|
||||
rememberSwipeRefreshState(isRefreshing = managerState.isFetching)
|
||||
var menuExpanded by remember { mutableStateOf(false) }
|
||||
val dropdownScreens = remember { listOf(Screen.Settings, Screen.About) }
|
||||
|
||||
|
@ -68,24 +62,24 @@ fun HomeScreen(
|
|||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
swipeRefreshState = refreshState,
|
||||
onRefresh = { viewModel.fetch() }
|
||||
onRefresh = onRefresh
|
||||
) {
|
||||
AnimatedContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
targetState = viewModel.appState,
|
||||
targetState = managerState,
|
||||
transitionSpec = {
|
||||
scaleIn(initialScale = 0.9f) + fadeIn() with
|
||||
scaleOut(targetScale = 0.9f) + fadeOut()
|
||||
}
|
||||
) { animatedAppState ->
|
||||
when (animatedAppState) {
|
||||
is StateFetching -> {
|
||||
is ManagerState.Fetching -> {
|
||||
HomeScreenLoading(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
appsCount = animatedAppState.placeholderAppsCount
|
||||
)
|
||||
}
|
||||
is StateSuccess -> {
|
||||
is ManagerState.Success -> {
|
||||
HomeScreenLoaded(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
apps = animatedAppState.apps,
|
||||
|
@ -94,7 +88,7 @@ fun HomeScreen(
|
|||
onAppLaunchClick = onAppLaunchClick
|
||||
)
|
||||
}
|
||||
is StateError -> {
|
||||
is ManagerState.Error -> {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
@ -103,10 +97,6 @@ fun HomeScreen(
|
|||
}
|
||||
}
|
||||
|
||||
typealias StateFetching = MainViewModel.AppState.Fetching
|
||||
typealias StateSuccess = MainViewModel.AppState.Success
|
||||
typealias StateError = MainViewModel.AppState.Error
|
||||
|
||||
@Composable
|
||||
private fun HomeScreenTopBar(
|
||||
menuExpanded: Boolean,
|
||||
|
@ -150,49 +140,34 @@ private fun HomeScreenTopBar(
|
|||
private fun HomeScreenLoaded(
|
||||
modifier: Modifier = Modifier,
|
||||
apps: List<App>,
|
||||
onAppDownloadClick: (
|
||||
appName: String,
|
||||
appVersions: List<String>?,
|
||||
installationOptions: List<InstallationOption>?
|
||||
) -> Unit,
|
||||
onAppUninstallClick: (appPackage: String) -> Unit,
|
||||
onAppLaunchClick: (appName: String, appPackage: String) -> Unit,
|
||||
onAppDownloadClick: (App) -> Unit,
|
||||
onAppUninstallClick: (App) -> Unit,
|
||||
onAppLaunchClick: (App) -> Unit,
|
||||
) {
|
||||
HomeScreenBody(modifier = modifier) {
|
||||
managerCategory(categoryName = {
|
||||
managerString(R.string.home_category_apps)
|
||||
}) {
|
||||
items(apps) { app ->
|
||||
val appIcon = rememberImagePainter(app.iconUrl) {
|
||||
diskCachePolicy(CachePolicy.ENABLED)
|
||||
}
|
||||
val appIcon = painterResource(id = app.iconResId)
|
||||
|
||||
var showAppInfoDialog by remember { mutableStateOf(false) }
|
||||
|
||||
AppCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
appName = app.name,
|
||||
appIcon = appIcon,
|
||||
appInstalledVersion = app.installedVersion,
|
||||
appRemoteVersion = app.remoteVersion,
|
||||
appInstalledVersion = app.installedVersionName,
|
||||
appRemoteVersion = app.remoteVersionName,
|
||||
appState = app.state,
|
||||
onAppDownloadClick = {
|
||||
onAppDownloadClick(
|
||||
app.name,
|
||||
app.versions,
|
||||
app.installationOptions
|
||||
)
|
||||
onAppDownloadClick(app)
|
||||
},
|
||||
onAppUninstallClick = {
|
||||
onAppUninstallClick(
|
||||
app.packageName
|
||||
)
|
||||
onAppUninstallClick(app)
|
||||
},
|
||||
onAppLaunchClick = {
|
||||
onAppLaunchClick(
|
||||
app.name,
|
||||
app.packageName,
|
||||
)
|
||||
onAppLaunchClick(app)
|
||||
},
|
||||
onAppInfoClick = {
|
||||
showAppInfoDialog = true
|
||||
|
|
|
@ -11,12 +11,11 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.preferences.holder.managerThemePref
|
||||
import com.vanced.manager.core.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.core.util.isMagiskInstalled
|
||||
import com.vanced.manager.repository.ManagerMode
|
||||
import com.vanced.manager.repository.ManagerTheme
|
||||
import com.vanced.manager.ui.component.*
|
||||
import com.vanced.manager.ui.resource.managerString
|
||||
import com.vanced.manager.ui.theme.ManagerTheme
|
||||
import com.vanced.manager.ui.util.Screen
|
||||
import com.vanced.manager.ui.viewmodel.SettingsViewModel
|
||||
import org.koin.androidx.compose.viewModel
|
||||
|
@ -60,13 +59,13 @@ fun SettingsScreen(
|
|||
preferenceDescription = stringResource(id = R.string.settings_preference_use_custom_tabs_summary),
|
||||
isChecked = viewModel.managerUseCustomTabs,
|
||||
onCheckedChange = {
|
||||
viewModel.managerUseCustomTabs = it
|
||||
viewModel.saveManagerUseCustomTabs(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
item {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
var selectedMode by remember { mutableStateOf(EntryValue(viewModel.managerMode)) }
|
||||
var selectedMode by remember { mutableStateOf(EntryValue(viewModel.managerMode.value)) }
|
||||
ManagerSingleSelectDialogPreference(
|
||||
preferenceTitle = managerString(
|
||||
stringId = R.string.settings_preference_variant_title
|
||||
|
@ -83,7 +82,7 @@ fun SettingsScreen(
|
|||
},
|
||||
onDismissRequest = {
|
||||
showDialog = false
|
||||
selectedMode = EntryValue(managerVariantPref)
|
||||
selectedMode = EntryValue(viewModel.managerMode.value)
|
||||
},
|
||||
onEntrySelect = {
|
||||
if (it.value == "root" && !isMagiskInstalled)
|
||||
|
@ -92,7 +91,7 @@ fun SettingsScreen(
|
|||
selectedMode = it
|
||||
},
|
||||
onSave = {
|
||||
viewModel.managerMode = selectedMode.value
|
||||
viewModel.saveManagerMode(ManagerMode.fromValue(selectedMode.value))
|
||||
showDialog = false
|
||||
}
|
||||
)
|
||||
|
@ -103,36 +102,38 @@ fun SettingsScreen(
|
|||
}) {
|
||||
item {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
var selectedTheme by remember { mutableStateOf(EntryValue(managerThemePref)) }
|
||||
var selectedTheme by remember { mutableStateOf(EntryValue(viewModel.managerTheme.value)) }
|
||||
ManagerSingleSelectDialogPreference(
|
||||
preferenceTitle = managerString(stringId = R.string.settings_preference_theme_title),
|
||||
preferenceDescription = managerString(
|
||||
stringId = viewModel.getThemeStringIdByValue(selectedTheme.value)
|
||||
stringId = viewModel.getThemeStringId(
|
||||
ManagerTheme.fromValue(selectedTheme.value)
|
||||
)
|
||||
),
|
||||
showDialog = showDialog,
|
||||
selected = selectedTheme,
|
||||
entries = mapOf(
|
||||
EntryText(managerString(viewModel.getThemeStringIdByValue(SettingsViewModel.THEME_LIGHT_VALUE))) to
|
||||
EntryValue(SettingsViewModel.THEME_LIGHT_VALUE),
|
||||
EntryText(managerString(viewModel.getThemeStringIdByValue(SettingsViewModel.THEME_DARK_VALUE))) to
|
||||
EntryValue(SettingsViewModel.THEME_DARK_VALUE),
|
||||
EntryText(managerString(viewModel.getThemeStringIdByValue(SettingsViewModel.THEME_SYSTEM_DEFAULT_VALUE))) to
|
||||
EntryValue(SettingsViewModel.THEME_SYSTEM_DEFAULT_VALUE),
|
||||
EntryText(managerString(viewModel.getThemeStringId(ManagerTheme.LIGHT)))
|
||||
to EntryValue(ManagerTheme.LIGHT.value),
|
||||
EntryText(managerString(viewModel.getThemeStringId(ManagerTheme.DARK)))
|
||||
to EntryValue(ManagerTheme.DARK.value),
|
||||
EntryText(managerString(viewModel.getThemeStringId(ManagerTheme.SYSTEM_DEFAULT)))
|
||||
to EntryValue(ManagerTheme.SYSTEM_DEFAULT.value),
|
||||
),
|
||||
onClick = {
|
||||
showDialog = true
|
||||
},
|
||||
onDismissRequest = {
|
||||
showDialog = false
|
||||
selectedTheme = EntryValue(viewModel.managerTheme)
|
||||
selectedTheme = EntryValue(viewModel.managerTheme.value)
|
||||
},
|
||||
onEntrySelect = {
|
||||
selectedTheme = it
|
||||
},
|
||||
onSave = {
|
||||
viewModel.managerTheme = selectedTheme.value
|
||||
showDialog = false
|
||||
onThemeChange(ManagerTheme.fromKey(selectedTheme.value))
|
||||
viewModel.saveManagerTheme(ManagerTheme.fromValue(selectedTheme.value))
|
||||
onThemeChange(ManagerTheme.fromValue(selectedTheme.value))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import androidx.compose.material3.*
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vanced.manager.ui.viewmodel.SettingsViewModel
|
||||
|
||||
const val defAccentColor = 0xFF0477E1
|
||||
|
||||
|
@ -67,29 +66,6 @@ private val DarkThemeColors = darkColorScheme(
|
|||
inverseSurface = md_theme_dark_inverseSurface,
|
||||
)
|
||||
|
||||
enum class ManagerTheme {
|
||||
LIGHT,
|
||||
DARK,
|
||||
SYSTEM_DEFAULT;
|
||||
|
||||
@Composable
|
||||
fun isDark() = when (this) {
|
||||
LIGHT -> false
|
||||
DARK -> true
|
||||
SYSTEM_DEFAULT -> isSystemInDarkTheme()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromKey(key: String?): ManagerTheme {
|
||||
return when (key) {
|
||||
SettingsViewModel.THEME_DARK_VALUE -> DARK
|
||||
SettingsViewModel.THEME_LIGHT_VALUE -> LIGHT
|
||||
else -> SYSTEM_DEFAULT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
inline fun apiDependantColorScheme(
|
||||
dynamic: () -> ColorScheme,
|
||||
|
|
|
@ -1,95 +1,84 @@
|
|||
package com.vanced.manager.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vanced.manager.core.installer.util.PM
|
||||
import com.vanced.manager.core.preferences.holder.managerVariantPref
|
||||
import com.vanced.manager.domain.model.App
|
||||
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.repository.MainRepository
|
||||
import com.vanced.manager.repository.MirrorRepository
|
||||
import com.vanced.manager.ui.theme.ManagerTheme
|
||||
import com.vanced.manager.repository.*
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import retrofit2.HttpException
|
||||
|
||||
class MainViewModel(
|
||||
private val mainRepository: MainRepository,
|
||||
private val mirrorRepository: MirrorRepository,
|
||||
private val preferences: SharedPreferences,
|
||||
private val appRepository: AppRepository,
|
||||
private val preferenceRepository: PreferenceRepository,
|
||||
private val app: Application,
|
||||
) : AndroidViewModel(app) {
|
||||
|
||||
private val isRoot
|
||||
get() = managerVariantPref == "root"
|
||||
var appMode by mutableStateOf(preferenceRepository.managerMode)
|
||||
var appTheme by mutableStateOf(preferenceRepository.managerTheme)
|
||||
|
||||
private val appCount: Int
|
||||
get() = if (isRoot) 2 else 3
|
||||
private val appCount
|
||||
get() = when (appMode) {
|
||||
ManagerMode.ROOT -> 2
|
||||
ManagerMode.NONROOT -> 3
|
||||
}
|
||||
|
||||
sealed class AppState {
|
||||
data class Fetching(val placeholderAppsCount: Int) : AppState()
|
||||
data class Success(val apps: List<App>) : AppState()
|
||||
data class Error(val error: String) : AppState()
|
||||
|
||||
val isFetching get() = this is Fetching
|
||||
val isSuccess get() = this is Success
|
||||
val isError get() = this is Error
|
||||
}
|
||||
|
||||
var appState by mutableStateOf<AppState>(AppState.Fetching(appCount))
|
||||
var appState by mutableStateOf<ManagerState>(ManagerState.Fetching(appCount))
|
||||
private set
|
||||
|
||||
var appTheme by mutableStateOf(
|
||||
ManagerTheme.fromKey(
|
||||
preferences.getString(
|
||||
SettingsViewModel.MANAGER_THEME_KEY,
|
||||
SettingsViewModel.THEME_SYSTEM_DEFAULT_VALUE
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun fetch() {
|
||||
viewModelScope.launch {
|
||||
appState = AppState.Fetching(appCount)
|
||||
try {
|
||||
supervisorScope {
|
||||
appState = ManagerState.Fetching(appCount)
|
||||
|
||||
fetchData()
|
||||
when (appMode) {
|
||||
ManagerMode.ROOT -> {
|
||||
appState = ManagerState.Success(
|
||||
apps = listOf(
|
||||
async { appRepository.getVancedYoutubeRoot() },
|
||||
async { appRepository.getVancedYoutubeMusicRoot() }
|
||||
).awaitAll()
|
||||
)
|
||||
}
|
||||
ManagerMode.NONROOT -> {
|
||||
appState = ManagerState.Success(
|
||||
apps = listOf(
|
||||
async { appRepository.getVancedYoutubeNonroot() },
|
||||
async { appRepository.getVancedYoutubeMusicNonroot() },
|
||||
async { appRepository.getVancedMicrog() }
|
||||
).awaitAll()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: HttpException) {
|
||||
appState = ManagerState.Error(e.message())
|
||||
} catch (e: Exception) {
|
||||
appState = ManagerState.Error(e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun launchApp(
|
||||
appName: String,
|
||||
appPackage: String,
|
||||
packageName: String,
|
||||
launchActivity: String
|
||||
) {
|
||||
val component = ComponentName(
|
||||
/* pkg = */ appPackage,
|
||||
/* cls = */ when (appName) {
|
||||
VANCED_NAME -> "com.google.android.youtube.HomeActivity"
|
||||
MUSIC_NAME -> "com.google.android.apps.youtube.music.activities.MusicActivity"
|
||||
MICROG_NAME -> "org.microg.gms.ui.SettingsActivity"
|
||||
else -> throw IllegalArgumentException("$appName is not a valid app")
|
||||
}
|
||||
)
|
||||
|
||||
try {
|
||||
app.startActivity(
|
||||
Intent().apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
setComponent(component)
|
||||
}
|
||||
)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.d(TAG, "Unable to launch $appName")
|
||||
e.printStackTrace()
|
||||
val component = ComponentName(packageName, launchActivity)
|
||||
val intent = Intent().apply {
|
||||
setComponent(component)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
app.startActivity(intent)
|
||||
}
|
||||
|
||||
//TODO implement root uninstallation
|
||||
|
@ -99,28 +88,14 @@ class MainViewModel(
|
|||
PM.uninstallPackage(appPackage, app)
|
||||
}
|
||||
|
||||
private suspend fun fetchData(
|
||||
fromMirror: Boolean = false
|
||||
) {
|
||||
try {
|
||||
val repository = if (fromMirror) mirrorRepository else mainRepository
|
||||
with(repository.fetch()) {
|
||||
appState = AppState.Success(apps)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (!fromMirror) {
|
||||
fetchData(true)
|
||||
return
|
||||
}
|
||||
|
||||
val error = "failed to fetch: \n${e.stackTraceToString()}"
|
||||
appState = AppState.Error(error)
|
||||
Log.d(TAG, error)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "MainViewModel"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sealed class ManagerState {
|
||||
data class Fetching(val placeholderAppsCount: Int) : ManagerState()
|
||||
data class Success(val apps: List<App>) : ManagerState()
|
||||
data class Error(val error: String) : ManagerState()
|
||||
|
||||
val isFetching get() = this is Fetching
|
||||
val isSuccess get() = this is Success
|
||||
val isError get() = this is Error
|
||||
}
|
|
@ -1,29 +1,46 @@
|
|||
package com.vanced.manager.ui.viewmodel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.preferences.managerBooleanPreference
|
||||
import com.vanced.manager.core.preferences.managerStringPreference
|
||||
import com.vanced.manager.repository.ManagerMode
|
||||
import com.vanced.manager.repository.ManagerTheme
|
||||
import com.vanced.manager.repository.PreferenceRepository
|
||||
|
||||
class SettingsViewModel : ViewModel() {
|
||||
class SettingsViewModel(
|
||||
private val preferenceRepository: PreferenceRepository
|
||||
) : ViewModel() {
|
||||
|
||||
companion object {
|
||||
const val MANAGER_THEME_KEY = "manager_theme"
|
||||
const val MANAGER_MODE_KEY = "manager_mode"
|
||||
var managerUseCustomTabs by mutableStateOf(preferenceRepository.managerUseCustomTabs)
|
||||
private set
|
||||
var managerMode by mutableStateOf(preferenceRepository.managerMode)
|
||||
private set
|
||||
var managerTheme by mutableStateOf(preferenceRepository.managerTheme)
|
||||
private set
|
||||
|
||||
const val THEME_DARK_VALUE = "dark"
|
||||
const val THEME_LIGHT_VALUE = "light"
|
||||
const val THEME_SYSTEM_DEFAULT_VALUE = "system_default"
|
||||
fun saveManagerUseCustomTabs(value: Boolean) {
|
||||
managerUseCustomTabs = value
|
||||
preferenceRepository.managerUseCustomTabs = value
|
||||
}
|
||||
|
||||
var managerUseCustomTabs by managerBooleanPreference(key = "manager_use_custom_tabs")
|
||||
var managerMode by managerStringPreference(key = MANAGER_MODE_KEY, defaultValue = "nonroot")
|
||||
var managerTheme by managerStringPreference(MANAGER_THEME_KEY, THEME_SYSTEM_DEFAULT_VALUE)
|
||||
fun saveManagerMode(value: ManagerMode) {
|
||||
managerMode = value
|
||||
preferenceRepository.managerMode = value
|
||||
}
|
||||
|
||||
fun getThemeStringIdByValue(value: String): Int {
|
||||
fun saveManagerTheme(value: ManagerTheme) {
|
||||
managerTheme = value
|
||||
preferenceRepository.managerTheme = value
|
||||
}
|
||||
|
||||
fun getThemeStringId(value: ManagerTheme): Int {
|
||||
return when (value) {
|
||||
THEME_DARK_VALUE -> R.string.settings_preference_theme_dark
|
||||
THEME_LIGHT_VALUE -> R.string.settings_preference_theme_light
|
||||
ManagerTheme.DARK -> R.string.settings_preference_theme_dark
|
||||
ManagerTheme.LIGHT -> R.string.settings_preference_theme_light
|
||||
else -> R.string.settings_option_system_default
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,7 @@ import androidx.compose.foundation.layout.*
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.material.icons.rounded.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
@ -15,13 +13,14 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.ImagePainter
|
||||
import com.google.accompanist.placeholder.PlaceholderHighlight
|
||||
import com.google.accompanist.placeholder.placeholder
|
||||
import com.google.accompanist.placeholder.shimmer
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.domain.model.AppState
|
||||
import com.vanced.manager.ui.component.ManagerElevatedCard
|
||||
import com.vanced.manager.ui.component.ManagerListItem
|
||||
import com.vanced.manager.ui.component.ManagerText
|
||||
|
@ -34,9 +33,10 @@ import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
|||
@Composable
|
||||
fun AppCard(
|
||||
appName: String,
|
||||
appIcon: ImagePainter,
|
||||
appIcon: Painter,
|
||||
appInstalledVersion: String?,
|
||||
appRemoteVersion: String?,
|
||||
appState: AppState,
|
||||
onAppDownloadClick: () -> Unit,
|
||||
onAppUninstallClick: () -> Unit,
|
||||
onAppLaunchClick: () -> Unit,
|
||||
|
@ -101,10 +101,27 @@ fun AppCard(
|
|||
}
|
||||
}
|
||||
IconButton(onClick = onAppDownloadClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Download,
|
||||
contentDescription = "Install",
|
||||
)
|
||||
when (appState) {
|
||||
AppState.NOT_INSTALLED -> {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.GetApp,
|
||||
contentDescription = "Install",
|
||||
)
|
||||
}
|
||||
AppState.INSTALLED -> {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.GetApp,
|
||||
contentDescription = "Install",
|
||||
)
|
||||
}
|
||||
AppState.NEEDS_UPDATE -> {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Update,
|
||||
contentDescription = "Update",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.vanced.manager.ui.widget
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
@ -13,11 +12,11 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vanced.manager.core.preferences.holder.useCustomTabsPref
|
||||
import com.vanced.manager.ui.component.ManagerElevatedCard
|
||||
import com.vanced.manager.ui.component.ManagerText
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingHorizontal
|
||||
import com.vanced.manager.ui.util.DefaultContentPaddingVertical
|
||||
import org.koin.androidx.compose.inject
|
||||
|
||||
//TODO this composable should not handle opening links
|
||||
@Composable
|
||||
|
@ -28,19 +27,14 @@ fun LinkCard(
|
|||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val customTabs = remember { CustomTabsIntent.Builder().build() }
|
||||
val customTabs: CustomTabsIntent by inject()
|
||||
val uri = remember { Uri.parse(url) }
|
||||
val intent = remember { Intent(Intent.ACTION_VIEW, uri) }
|
||||
ManagerElevatedCard(
|
||||
modifier = modifier
|
||||
.height(100.dp)
|
||||
.widthIn(min = 100.dp),
|
||||
onClick = {
|
||||
if (useCustomTabsPref) {
|
||||
customTabs.launchUrl(context, uri)
|
||||
} else {
|
||||
context.startActivity(intent)
|
||||
}
|
||||
customTabs.launchUrl(context, uri)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
|
|
|
@ -7,7 +7,8 @@ buildscript {
|
|||
val kotlinVersion = "1.6.10"
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.1.2")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
||||
classpath(kotlin("gradle-plugin", version = kotlinVersion))
|
||||
classpath(kotlin("serialization", version = kotlinVersion))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue