commit 27f3d315cc102127a977260dfda5d525bc9d2a5a Author: Sculas Date: Sun Aug 21 23:13:58 2022 +0200 Initialize compose rewrite diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..786af17 --- /dev/null +++ b/.gitignore @@ -0,0 +1,81 @@ +*.class +*.log +*.ctxt +.mtj.tmp/ +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar +hs_err_pid* +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf +.idea/**/contentModel.xml +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml +.idea/**/gradle.xml +.idea/**/libraries +cmake-build-*/ +.idea/**/mongoSettings.xml +*.iws +out/ +.idea_modules/ +atlassian-ide-plugin.xml +.idea/replstate.xml +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +.idea/httpRequests +.idea/caches/build_file_checksums.ser +*.apk +*.aar +*.ap_ +*.aab +*.dex +bin/ +gen/ +.gradle/ +build/ +local.properties +proguard/ +.navigation/ +captures/ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +.idea/caches +.idea/modules.xml +.idea/misc.xml +.idea/navEditor.xml +.externalNativeBuild +.cxx/ +freeline.py +freeline/ +freeline_project_description.json +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md +vcs.xml +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +*.hprof +/.idea/deploymentTargetDropDown.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..897c96a --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +ReVanced Manager \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/git_toolbox_prj.xml b/.idea/git_toolbox_prj.xml new file mode 100644 index 0000000..02b915b --- /dev/null +++ b/.idea/git_toolbox_prj.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..ed76bea --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,37 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..cfc391f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..1292d98 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,83 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("kotlin-parcelize") + kotlin("plugin.serialization") version "1.7.10" +} + +android { + namespace = "app.revanced.manager" + compileSdk = 32 + + defaultConfig { + applicationId = "app.revanced.manager" + minSdk = 26 + targetSdk = 32 + versionCode = 1 + versionName = "0.0.1" + + vectorDrawables.useSupportLibrary = true + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = "11" + freeCompilerArgs = freeCompilerArgs + "-opt-in=kotlin.RequiresOptIn" + } + + buildFeatures.compose = true + composeOptions.kotlinCompilerExtensionVersion = "1.2.0" +} + +dependencies { + // AndroidX core + implementation("androidx.core:core-ktx:1.8.0") + implementation("androidx.core:core-splashscreen:1.0.0") + + // AndroidX activity + implementation("androidx.activity:activity-compose:1.6.0-alpha05") + + // Koin + val koinVersion = "3.2.0" + implementation("io.insert-koin:koin-android:$koinVersion") + implementation("io.insert-koin:koin-androidx-compose:$koinVersion") + + // Compose + val composeVersion = "1.3.0-alpha01" + implementation("androidx.compose.ui:ui:${composeVersion}") + debugImplementation("androidx.compose.ui:ui-tooling:${composeVersion}") + implementation("androidx.compose.material3:material3:1.0.0-alpha15") + implementation("androidx.compose.material:material-icons-extended:${composeVersion}") + + // Accompanist + val accompanistVersion = "0.26.0-alpha" + implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion") + implementation("com.google.accompanist:accompanist-placeholder-material:$accompanistVersion") + + // Coil (async image loading) + implementation("io.coil-kt:coil-compose:2.1.0") + + // KotlinX + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3") + + // Taxi (navigation) + implementation("com.github.X1nto:Taxi:1.0.0") + + // Ktor + val ktorVersion = "2.0.3" + implementation("io.ktor:ktor-client-core:$ktorVersion") + implementation("io.ktor:ktor-client-cio:$ktorVersion") + implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") + implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..ff59496 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..be63a29 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/MainActivity.kt b/app/src/main/java/app/revanced/manager/MainActivity.kt new file mode 100644 index 0000000..6097c61 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/MainActivity.kt @@ -0,0 +1,48 @@ +package app.revanced.manager + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.BackHandler +import androidx.activity.compose.setContent +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.with +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Modifier +import app.revanced.manager.preferences.PreferencesManager +import app.revanced.manager.ui.navigation.AppDestination +import app.revanced.manager.ui.screen.MainRootScreen +import app.revanced.manager.ui.screen.SettingsScreen +import app.revanced.manager.ui.theme.ReVancedManagerTheme +import com.xinto.taxi.Taxi +import com.xinto.taxi.rememberBackstackNavigator +import org.koin.android.ext.android.inject + +class MainActivity : ComponentActivity() { + private val prefs: PreferencesManager by inject() + + @OptIn(ExperimentalAnimationApi::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + ReVancedManagerTheme(dynamicColor = prefs.dynamicColor) { + val navigator = rememberBackstackNavigator(AppDestination.Dashboard) + + BackHandler { + if (!navigator.pop()) finish() + } + + Taxi( + modifier = Modifier.fillMaxSize(), + navigator = navigator, + transitionSpec = { fadeIn() with fadeOut() } + ) { destination -> + when (destination) { + is AppDestination.Dashboard -> MainRootScreen(navigator = navigator) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ManagerApplication.kt b/app/src/main/java/app/revanced/manager/ManagerApplication.kt new file mode 100644 index 0000000..eacc2aa --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ManagerApplication.kt @@ -0,0 +1,20 @@ +package app.revanced.manager + +import android.app.Application +import app.revanced.manager.di.httpModule +import app.revanced.manager.di.preferencesModule +import app.revanced.manager.di.repositoryModule +import app.revanced.manager.di.viewModelModule +import org.koin.android.ext.koin.androidContext +import org.koin.core.context.startKoin + +class ManagerApplication : Application() { + override fun onCreate() { + super.onCreate() + + startKoin { + androidContext(this@ManagerApplication) + modules(httpModule, preferencesModule, viewModelModule, repositoryModule) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/di/HttpModule.kt b/app/src/main/java/app/revanced/manager/di/HttpModule.kt new file mode 100644 index 0000000..60206d0 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/di/HttpModule.kt @@ -0,0 +1,25 @@ +package app.revanced.manager.di + +import io.ktor.client.* +import io.ktor.client.engine.cio.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.serialization.kotlinx.json.* +import kotlinx.serialization.json.Json +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val httpModule = module { + fun provideHttpClient() = HttpClient(CIO) { + BrowserUserAgent() + install(ContentNegotiation) { + json(Json { + encodeDefaults = true + isLenient = true + ignoreUnknownKeys = true + }) + } + } + + singleOf(::provideHttpClient) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/di/PreferencesModule.kt b/app/src/main/java/app/revanced/manager/di/PreferencesModule.kt new file mode 100644 index 0000000..0535224 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/di/PreferencesModule.kt @@ -0,0 +1,14 @@ +package app.revanced.manager.di + +import android.content.Context +import app.revanced.manager.preferences.PreferencesManager +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val preferencesModule = module { + fun providePreferences( + context: Context + ) = PreferencesManager(context.getSharedPreferences("preferences", Context.MODE_PRIVATE)) + + singleOf(::providePreferences) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/di/RepositoryModule.kt b/app/src/main/java/app/revanced/manager/di/RepositoryModule.kt new file mode 100644 index 0000000..6671f75 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/di/RepositoryModule.kt @@ -0,0 +1,9 @@ +package app.revanced.manager.di + +import app.revanced.manager.repository.GitHubRepository +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val repositoryModule = module { + singleOf(::GitHubRepository) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt new file mode 100644 index 0000000..d6698d1 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt @@ -0,0 +1,10 @@ +package app.revanced.manager.di + +import app.revanced.manager.ui.viewmodel.* +import org.koin.androidx.viewmodel.dsl.viewModelOf +import org.koin.dsl.module + +val viewModelModule = module { + viewModelOf(::SettingsViewModel) + viewModelOf(::DashboardViewModel) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/dto/github/ApiCommit.kt b/app/src/main/java/app/revanced/manager/dto/github/ApiCommit.kt new file mode 100644 index 0000000..7e1a34f --- /dev/null +++ b/app/src/main/java/app/revanced/manager/dto/github/ApiCommit.kt @@ -0,0 +1,22 @@ +package app.revanced.manager.dto.github + +import kotlinx.serialization.Serializable + +@Serializable +class ApiCommit( + val sha: String, + val commit: Object +) { + @Serializable + class Object( + val message: String, + val author: Author, + val committer: Author + ) + + @Serializable + class Author( + val name: String, + val date: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/dto/github/ApiContributor.kt b/app/src/main/java/app/revanced/manager/dto/github/ApiContributor.kt new file mode 100644 index 0000000..ab62670 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/dto/github/ApiContributor.kt @@ -0,0 +1,11 @@ +package app.revanced.manager.dto.github + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +class ApiContributor( + @SerialName("login") val username: String, + @SerialName("avatar_url") val avatarUrl: String, + @SerialName("html_url") val profileUrl: String, +) \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/dto/github/ApiRelease.kt b/app/src/main/java/app/revanced/manager/dto/github/ApiRelease.kt new file mode 100644 index 0000000..d25c7b7 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/dto/github/ApiRelease.kt @@ -0,0 +1,19 @@ +package app.revanced.manager.dto.github + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ApiRelease( + @SerialName("tag_name") val tagName: String, + @SerialName("published_at") val publishedAt: String, + val prerelease: Boolean, + val assets: List, + val body: String +) { + @Serializable + data class Asset( + @SerialName("browser_download_url") val downloadUrl: String, + val name: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/preferences/PreferencesManager.kt b/app/src/main/java/app/revanced/manager/preferences/PreferencesManager.kt new file mode 100644 index 0000000..f6bda4b --- /dev/null +++ b/app/src/main/java/app/revanced/manager/preferences/PreferencesManager.kt @@ -0,0 +1,110 @@ +package app.revanced.manager.preferences + +import android.content.SharedPreferences +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.core.content.edit +import app.revanced.manager.util.ghIntegrations +import app.revanced.manager.util.ghPatches +import kotlin.reflect.KProperty + +class PreferencesManager( + sharedPreferences: SharedPreferences +) : BasePreferenceManager(sharedPreferences) { + var dynamicColor by booleanPreference("dynamic_color", true) + + var srcPatches by stringPreference("src_patches", ghPatches) + var srcIntegrations by stringPreference("src_integrations", ghIntegrations) +} + +/** + * @author Hyperion Authors, zt64 + */ +@Suppress("unused") +abstract class BasePreferenceManager( + private val prefs: SharedPreferences +) { + protected fun getString(key: String, defaultValue: String?) = + prefs.getString(key, defaultValue)!! + + private fun getBoolean(key: String, defaultValue: Boolean) = prefs.getBoolean(key, defaultValue) + private fun getInt(key: String, defaultValue: Int) = prefs.getInt(key, defaultValue) + private fun getFloat(key: String, defaultValue: Float) = prefs.getFloat(key, defaultValue) + protected inline fun > getEnum(key: String, defaultValue: E) = + enumValueOf(getString(key, defaultValue.name)) + + protected fun putString(key: String, value: String?) = prefs.edit { putString(key, value) } + private fun putBoolean(key: String, value: Boolean) = prefs.edit { putBoolean(key, value) } + private fun putInt(key: String, value: Int) = prefs.edit { putInt(key, value) } + private fun putFloat(key: String, value: Float) = prefs.edit { putFloat(key, value) } + protected inline fun > putEnum(key: String, value: E) = + putString(key, value.name) + + protected class Preference( + private val key: String, + defaultValue: T, + getter: (key: String, defaultValue: T) -> T, + private val setter: (key: String, newValue: T) -> Unit + ) { + @Suppress("RedundantSetter") + var value by mutableStateOf(getter(key, defaultValue)) + private set + + operator fun getValue(thisRef: Any?, property: KProperty<*>) = value + operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) { + value = newValue + setter(key, newValue) + } + } + + protected fun stringPreference( + key: String, + defaultValue: String? + ) = Preference( + key = key, + defaultValue = defaultValue, + getter = ::getString, + setter = ::putString + ) + + protected fun booleanPreference( + key: String, + defaultValue: Boolean + ) = Preference( + key = key, + defaultValue = defaultValue, + getter = ::getBoolean, + setter = ::putBoolean + ) + + protected fun intPreference( + key: String, + defaultValue: Int + ) = Preference( + key = key, + defaultValue = defaultValue, + getter = ::getInt, + setter = ::putInt + ) + + protected fun floatPreference( + key: String, + defaultValue: Float + ) = Preference( + key = key, + defaultValue = defaultValue, + getter = ::getFloat, + setter = ::putFloat + ) + + protected inline fun > enumPreference( + key: String, + defaultValue: E + ) = Preference( + key = key, + defaultValue = defaultValue, + getter = ::getEnum, + setter = ::putEnum + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/repository/GitHubRepository.kt b/app/src/main/java/app/revanced/manager/repository/GitHubRepository.kt new file mode 100644 index 0000000..1bc6e0b --- /dev/null +++ b/app/src/main/java/app/revanced/manager/repository/GitHubRepository.kt @@ -0,0 +1,33 @@ +package app.revanced.manager.repository + +import app.revanced.manager.dto.github.ApiCommit +import app.revanced.manager.dto.github.ApiContributor +import app.revanced.manager.dto.github.ApiRelease +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.request.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class GitHubRepository(private val client: HttpClient) { + suspend fun getLatestRelease(repo: String) = withContext(Dispatchers.IO) { + val res: List = client.get("$baseUrl/$repo/releases") { + parameter("per_page", 1) + }.body() + res.first() + } + + suspend fun getLatestCommit(repo: String, ref: String) = withContext(Dispatchers.IO) { + client.get("$baseUrl/$repo/commits/$ref") { + parameter("per_page", 1) + }.body() as ApiCommit + } + + suspend fun getContributors(org: String, repo: String) = withContext(Dispatchers.IO) { + client.get("$baseUrl/$org/$repo/contributors").body() as List + } + + private companion object { + private const val baseUrl = "https://api.github.com/repos" + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/ApplicationItem.kt b/app/src/main/java/app/revanced/manager/ui/component/ApplicationItem.kt new file mode 100644 index 0000000..3b00cf3 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/ApplicationItem.kt @@ -0,0 +1,55 @@ +package app.revanced.manager.ui.component + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.revanced.manager.R + +@Composable +fun ApplicationItem( + name: String, + released: String, // TODO: temp + icon: @Composable () -> Unit, + expandedContent: @Composable () -> Unit +) { + ExpandableCard( + content = { arrowButton -> + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + icon() + Column(modifier = Modifier.padding(start = 8.dp)) { + Text(name) + Text( + text = released, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + Row(verticalAlignment = Alignment.CenterVertically) { + arrowButton() + } + } + }, + expandedContent = { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { expandedContent() } + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/ExpandableCard.kt b/app/src/main/java/app/revanced/manager/ui/component/ExpandableCard.kt new file mode 100644 index 0000000..577be95 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/ExpandableCard.kt @@ -0,0 +1,67 @@ +package app.revanced.manager.ui.component + +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.revanced.manager.R + +@Composable +fun ExpandableCard( + content: @Composable (arrowButton: @Composable () -> Unit) -> Unit, + expandedContent: @Composable () -> Unit, +) { + var expandedState by remember { mutableStateOf(false) } + val rotateState by animateFloatAsState(targetValue = if (expandedState) 180f else 0f) + + Card( + modifier = Modifier + .fillMaxWidth() + .animateContentSize( + animationSpec = tween( + durationMillis = 300, + easing = LinearOutSlowInEasing + ) + ), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant.copy( + alpha = 0.5f + ) + ), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 14.dp, vertical = 2.dp) + ) { + content { + IconButton( + modifier = Modifier.rotate(rotateState), + onClick = { expandedState = !expandedState }, + ) { + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = stringResource(R.string.expand) + ) + } + } + if (expandedState) { + Box(modifier = Modifier.padding(bottom = 8.dp)) { + expandedContent() + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/HeadlineWithCard.kt b/app/src/main/java/app/revanced/manager/ui/component/HeadlineWithCard.kt new file mode 100644 index 0000000..2595d82 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/HeadlineWithCard.kt @@ -0,0 +1,33 @@ +package app.revanced.manager.ui.component + +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HeadlineWithCard( + @StringRes headline: Int, + content: @Composable () -> Unit +) { + Column { + Text( + text = stringResource(headline), + style = MaterialTheme.typography.headlineSmall + ) + ElevatedCard( + modifier = Modifier + .padding(top = 12.dp) + .fillMaxWidth(), + ) { content() } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/SocialItem.kt b/app/src/main/java/app/revanced/manager/ui/component/SocialItem.kt new file mode 100644 index 0000000..5dc936d --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/SocialItem.kt @@ -0,0 +1,44 @@ +package app.revanced.manager.ui.component + +import androidx.annotation.StringRes +import androidx.compose.foundation.clickable +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Code +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import app.revanced.manager.R +import app.revanced.manager.util.ghOrganization +import app.revanced.manager.util.openUrl + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SocialItem(@StringRes label: Int, vec: ImageVector, fn: () -> Unit) { + val rsc = stringResource(label) + ListItem( + modifier = Modifier.clickable { fn() }, + leadingContent = { + Icon( + imageVector = vec, + contentDescription = rsc + ) + }, + headlineText = { Text(rsc) } + ) +} + +@Preview +@Composable +fun SocialItemPreview() { + val ctx = LocalContext.current.applicationContext + SocialItem(R.string.github, Icons.Default.Code) { + ctx.openUrl(ghOrganization) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt b/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt new file mode 100644 index 0000000..30f1b15 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt @@ -0,0 +1,28 @@ +package app.revanced.manager.ui.navigation + +import androidx.annotation.StringRes +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.Dashboard +import androidx.compose.material.icons.filled.Settings +import androidx.compose.ui.graphics.vector.ImageVector +import app.revanced.manager.R +import com.xinto.taxi.Destination +import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.RawValue + +@Parcelize +sealed interface AppDestination : Destination { + @Parcelize + object Dashboard : AppDestination +} + +@Parcelize +enum class DashboardDestination( + val icon: @RawValue ImageVector, + @StringRes val label: Int +) : Destination { + DASHBOARD(Icons.Default.Dashboard, R.string.dashboard), + PATCHER(Icons.Default.Build, R.string.patcher), + SETTINGS(Icons.Default.Settings, R.string.settings), +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt new file mode 100644 index 0000000..219e993 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt @@ -0,0 +1,189 @@ +package app.revanced.manager.ui.screen + +import android.widget.Toast +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.Dashboard +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.revanced.manager.R +import app.revanced.manager.ui.component.ApplicationItem +import app.revanced.manager.ui.component.HeadlineWithCard +import app.revanced.manager.ui.viewmodel.DashboardViewModel +import org.koin.androidx.compose.getViewModel + +@Composable +fun DashboardScreen(viewModel: DashboardViewModel = getViewModel()) { + val context = LocalContext.current + val padHoriz = 16.dp + val padVert = 10.dp + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 18.dp) + .verticalScroll(state = rememberScrollState()), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + HeadlineWithCard(R.string.updates) { + Row( + modifier = Modifier + .padding(horizontal = padHoriz, vertical = padVert) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Column { + CommitDate( + label = R.string.patcher, + date = viewModel.patcherCommitDate + ) + CommitDate( + label = R.string.manager, + date = viewModel.managerCommitDate + ) + } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Button( + enabled = false, // needs update + onClick = { + Toast.makeText(context, "Already up-to-date!", Toast.LENGTH_SHORT) + .show() + }, + ) { Text(stringResource(R.string.update_manager)) } + Text( + text = "No updates available", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 10.sp, + ) + } + } + } + + HeadlineWithCard(R.string.patched_apps) { + Row( + modifier = Modifier + .padding(horizontal = padHoriz, vertical = padVert) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Column { + val amount = 2 // TODO + Text( + text = "${stringResource(R.string.updates_available)}: $amount", + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Bold, + fontSize = 18.sp + ) + } + Button( + enabled = true, // needs update + onClick = { + Toast.makeText(context, "Already up-to-date!", Toast.LENGTH_SHORT).show() + } + ) { Text(stringResource(R.string.update_all)) } + } + Column( + modifier = Modifier + .padding(horizontal = padHoriz) + .padding(bottom = padVert) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + ApplicationItem( + name = "ReVanced", + released = "Released 2 days ago", + icon = { Icon(Icons.Default.Dashboard, "ReVanced") } + ) { + ChangelogText( + """ + fix: aaaaaa + fix: aaaaaa + fix: aaaaaa + fix: aaaaaa + fix: aaaaaa + """.trimIndent() + ) + } + ApplicationItem( + name = "ReReddit", + released = "Released 1 month ago", + icon = { Icon(Icons.Default.Build, "ReReddit") } + ) { + ChangelogText( + """ + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + fix: bbbbbb + """.trimIndent() + ) + } + } + } + } +} + +@Composable +fun CommitDate(@StringRes label: Int, date: String) { + Row { + Text( + text = "${stringResource(label)}: ", + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Bold + ) + Text( + text = date, + style = MaterialTheme.typography.bodyMedium + ) + } +} + +@Composable +fun ChangelogText(text: String) { + Column { + Text( + text = "Changelog", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontWeight = FontWeight.Bold + ) + Text( + text = text, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/MainRootScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/MainRootScreen.kt new file mode 100644 index 0000000..3ecba15 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/screen/MainRootScreen.kt @@ -0,0 +1,74 @@ +package app.revanced.manager.ui.screen + +import androidx.compose.animation.* +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import app.revanced.manager.ui.navigation.AppDestination +import app.revanced.manager.ui.navigation.DashboardDestination +import com.xinto.taxi.BackstackNavigator +import com.xinto.taxi.Taxi +import com.xinto.taxi.rememberNavigator + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class) +@Composable +fun MainRootScreen(navigator: BackstackNavigator) { + val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( + decayAnimationSpec = rememberSplineBasedDecay(), + state = rememberTopAppBarState() + ) + val mainRootNavigator = rememberNavigator(DashboardDestination.DASHBOARD) + val currentDestination = mainRootNavigator.currentDestination + + Scaffold( + modifier = Modifier + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + SmallTopAppBar( + title = { + Text( + text = stringResource(mainRootNavigator.currentDestination.label), + style = MaterialTheme.typography.headlineLarge + ) + }, + scrollBehavior = scrollBehavior + ) + }, + bottomBar = { + NavigationBar { + DashboardDestination.values().forEach { destination -> + NavigationBarItem( + selected = currentDestination == destination, + icon = { Icon(destination.icon, stringResource(destination.label)) }, + label = { Text(stringResource(destination.label)) }, + onClick = { mainRootNavigator.replace(destination) } + ) + } + } + } + ) { paddingValues -> + Row( + modifier = Modifier.padding(paddingValues) + ) { + Taxi( + modifier = Modifier.weight(1f, true), + navigator = mainRootNavigator, + transitionSpec = { fadeIn() with fadeOut() } + ) { destination -> + when (destination) { + DashboardDestination.DASHBOARD -> DashboardScreen() + DashboardDestination.PATCHER -> DashboardScreen() + DashboardDestination.SETTINGS -> SettingsScreen() + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt new file mode 100644 index 0000000..b57280f --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt @@ -0,0 +1,52 @@ +package app.revanced.manager.ui.screen + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Code +import androidx.compose.material.icons.filled.NavigateBefore +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.revanced.manager.R +import app.revanced.manager.ui.component.SocialItem +import app.revanced.manager.ui.navigation.AppDestination +import app.revanced.manager.ui.viewmodel.SettingsViewModel +import com.xinto.taxi.BackstackNavigator +import org.koin.androidx.compose.getViewModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsScreen(viewModel: SettingsViewModel = getViewModel()) { + val prefs = viewModel.prefs + + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 18.dp) + .verticalScroll(state = rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + ListItem( + modifier = Modifier.clickable { prefs.dynamicColor = !prefs.dynamicColor }, + headlineText = { Text(stringResource(R.string.dynamic_color)) }, + trailingContent = { + Switch( + checked = prefs.dynamicColor, + onCheckedChange = { prefs.dynamicColor = it } + ) + } + ) + + Divider() + SocialItem(R.string.github, Icons.Default.Code, viewModel::openGitHub) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/theme/Color.kt b/app/src/main/java/app/revanced/manager/ui/theme/Color.kt new file mode 100644 index 0000000..903d630 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package app.revanced.manager.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/theme/Theme.kt b/app/src/main/java/app/revanced/manager/ui/theme/Theme.kt new file mode 100644 index 0000000..c443757 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/theme/Theme.kt @@ -0,0 +1,51 @@ +package app.revanced.manager.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.platform.LocalContext +import com.google.accompanist.systemuicontroller.rememberSystemUiController + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 +) + +@Composable +fun ReVancedManagerTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + val systemUiController = rememberSystemUiController() + SideEffect { + systemUiController.setSystemBarsColor( + color = colorScheme.background, + darkIcons = !darkTheme + ) + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/theme/Type.kt b/app/src/main/java/app/revanced/manager/ui/theme/Type.kt new file mode 100644 index 0000000..0af039f --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package app.revanced.manager.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt new file mode 100644 index 0000000..a268101 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt @@ -0,0 +1,40 @@ +package app.revanced.manager.ui.viewmodel + +import android.text.format.DateUtils +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import app.revanced.manager.repository.GitHubRepository +import app.revanced.manager.util.ghManager +import app.revanced.manager.util.ghPatcher +import kotlinx.coroutines.runBlocking +import java.text.SimpleDateFormat +import java.util.* + +class DashboardViewModel(private val repository: GitHubRepository) : ViewModel() { + var patcherCommitDate by mutableStateOf("") + private set + var managerCommitDate by mutableStateOf("") + private set + + init { + runBlocking { + patcherCommitDate = commitDateOf(ghPatcher) + managerCommitDate = commitDateOf(ghManager) + } + } + + private suspend fun commitDateOf(repo: String, ref: String = "HEAD"): String { + val commit = repository.getLatestCommit(repo, ref).commit + return DateUtils.getRelativeTimeSpanString( + formatter.parse(commit.committer.date)!!.time, + Calendar.getInstance().timeInMillis, + DateUtils.MINUTE_IN_MILLIS + ).toString() + } + + private companion object { + val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/SettingsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/SettingsViewModel.kt new file mode 100644 index 0000000..b152ab0 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/SettingsViewModel.kt @@ -0,0 +1,14 @@ +package app.revanced.manager.ui.viewmodel + +import android.app.Application +import androidx.lifecycle.ViewModel +import app.revanced.manager.preferences.PreferencesManager +import app.revanced.manager.util.ghOrganization +import app.revanced.manager.util.openUrl + +class SettingsViewModel( + private val app: Application, + val prefs: PreferencesManager +) : ViewModel() { + fun openGitHub() = app.openUrl(ghOrganization) +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/util/Constants.kt b/app/src/main/java/app/revanced/manager/util/Constants.kt new file mode 100644 index 0000000..3ce234c --- /dev/null +++ b/app/src/main/java/app/revanced/manager/util/Constants.kt @@ -0,0 +1,8 @@ +package app.revanced.manager.util + +private const val team = "revanced" +const val ghOrganization = "https://github.com/$team" +const val ghPatches = "$team/revanced-patches" +const val ghPatcher = "$team/revanced-patcher" +const val ghManager = "$team/revanced-manager" +const val ghIntegrations = "$team/revanced-integrations" \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/util/Util.kt b/app/src/main/java/app/revanced/manager/util/Util.kt new file mode 100644 index 0000000..3712694 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/util/Util.kt @@ -0,0 +1,11 @@ +package app.revanced.manager.util + +import android.content.Context +import android.content.Intent +import androidx.core.net.toUri + +fun Context.openUrl(url: String) { + startActivity(Intent(Intent.ACTION_VIEW, url.toUri()).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..6e7840e --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,16 @@ + + ReVanced Manager + Dashboard + Patcher + Dynamic Color + GitHub + Settings + Updates + Manager + Update Manager + Installed + Update All + Available updates + Expand + Update + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..66a08e1 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +