Merge branch 'dev'

This commit is contained in:
Aayush Gupta
2025-05-28 20:52:48 +08:00
33 changed files with 654 additions and 448 deletions

5
.gitignore vendored
View File

@@ -36,9 +36,10 @@ gradle-app.setting
*.zip
app/release/*
app/beta/*
app/alpha/*
app/nightly/*
app/vanilla/*
app/huawei/*
app/preload/*
#Exclude signing configurations & keystore

View File

@@ -58,6 +58,8 @@ android {
testInstrumentationRunnerArguments["disableAnalytics"] = "true"
buildConfigField("String", "EXODUS_API_KEY", "\"bbe6ebae4ad45a9cbacb17d69739799b8df2c7ae\"")
missingDimensionStrategy("device", "vanilla")
}
signingConfigs {
@@ -108,12 +110,32 @@ android {
}
}
flavorDimensions += "device"
productFlavors {
create("vanilla") {
dimension = "device"
}
create("huawei") {
dimension = "device"
versionNameSuffix = "-hw"
}
// This flavor is only for preloaded devices / users who push the app to system
create("preload") {
dimension = "device"
versionNameSuffix = "-preload"
}
}
buildFeatures {
buildConfig = true
viewBinding = true
aidl = true
compose = true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_21.toString()
}
@@ -137,6 +159,15 @@ android {
}
}
androidComponents {
beforeVariants(selector().all()) { variant ->
val flavour = variant.flavorName
if ((flavour == "huawei" || flavour == "preload") && variant.buildType == "nightly") {
variant.enable = false
}
}
}
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="MissingTranslation" severity="ignore" />
<issue id="ProtectedPermissions" severity="ignore" />
<issue id="QueryAllPackagesPermission" severity="ignore" />
<issue id="ScopedStorage" severity="ignore" />
</lint>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
</manifest>

View File

@@ -0,0 +1,83 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.onboarding
import androidx.fragment.app.Fragment
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR
import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import com.aurora.store.util.save
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class OnboardingFragment : BaseFlavouredOnboardingFragment() {
override fun loadDefaultPreferences() {
/*Filters*/
save(PREFERENCE_FILTER_AURORA_ONLY, false)
save(PREFERENCE_FILTER_FDROID, true)
/*Network*/
save(PREFERENCE_DISPENSER_URLS, setOf())
save(PREFERENCE_VENDING_VERSION, 0)
/*Customization*/
save(PREFERENCE_THEME_STYLE, 0)
save(PREFERENCE_DEFAULT_SELECTED_TAB, 0)
save(PREFERENCE_FOR_YOU, true)
save(PREFERENCE_SIMILAR, true)
/*Installer*/
save(PREFERENCE_AUTO_DELETE, true)
save(PREFERENCE_INSTALLER_ID, 0)
/*Updates*/
save(PREFERENCE_UPDATES_EXTENDED, false)
save(PREFERENCE_UPDATES_CHECK_INTERVAL, 3)
}
override fun onboardingPages(): List<Fragment> {
return listOf(
WelcomeFragment(),
PermissionsFragment.newInstance()
)
}
override fun setupAutoUpdates() {
super.setupAutoUpdates()
// Remove super & implement variant logic here
}
override fun finishOnboarding() {
super.finishOnboarding()
// Remove super & implement variant logic here
}
}

View File

@@ -0,0 +1,38 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.splash
import com.aurora.extensions.hide
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class SplashFragment : BaseFlavouredSplashFragment() {
override fun attachActions() {
super.attachActions()
binding.btnAnonymous.hide()
}
override fun resetActions() {
super.resetActions()
binding.btnAnonymous.hide()
}
}

View File

@@ -21,6 +21,7 @@ package com.aurora.extensions
import android.content.Context
import android.content.Intent
import android.net.UrlQuerySanitizer
import android.os.Bundle
inline fun <reified T : Context> Context.newIntent(): Intent =
@@ -40,3 +41,22 @@ inline fun <reified T : Context> Context.newIntent(flags: Int, extras: Bundle):
intent.putExtras(extras)
return intent
}
fun Intent.getPackageName(fallbackBundle: Bundle? = null): String? {
return when (action) {
Intent.ACTION_VIEW -> {
data?.getQueryParameter("id")
}
Intent.ACTION_SEND -> {
val clipData = getStringExtra(Intent.EXTRA_TEXT).orEmpty()
UrlQuerySanitizer(clipData).getValue("id")
}
Intent.ACTION_SHOW_APP_INFO -> {
extras?.getString(Intent.EXTRA_PACKAGE_NAME)
}
else -> {
extras?.getString("packageName") ?: fallbackBundle?.getString("packageName")
}
}
}

View File

@@ -1,56 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.data.serializers
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.encoding.encodeStructure
import java.util.Locale
/**
* Serializer for [Locale] for working with Kotlin's Serialization library
*/
object LocaleSerializer : KSerializer<Locale> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Locale") {
element<String>("language")
element<String>("region")
}
override fun serialize(encoder: Encoder, value: Locale) {
return encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, value.language)
encodeStringElement(descriptor, 1, value.country)
}
}
override fun deserialize(decoder: Decoder): Locale {
return decoder.decodeStructure(descriptor) {
var language: String? = null
var region: String? = null
while (true) {
when (val index = decodeElementIndex(descriptor)) {
0 -> language = decodeStringElement(descriptor, 0)
1 -> region = decodeStringElement(descriptor, 1)
CompositeDecoder.DECODE_DONE -> break
else -> error("Unexpected index: $index")
}
}
require(!language.isNullOrBlank() && !region.isNullOrBlank())
Locale.Builder()
.setLanguage(language)
.setRegion(region)
.build()
}
}
}

View File

@@ -1,32 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.data.serializers
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.util.Properties
/**
* Serializer for [Properties] for working with Kotlin's Serialization library
*/
object PropertiesSerializer : KSerializer<Properties> {
override val descriptor: SerialDescriptor =
MapSerializer(String.serializer(), String.serializer()).descriptor
override fun serialize(encoder: Encoder, value: Properties) {
val map = value.stringPropertyNames().associateWith { value.getProperty(it) }
MapSerializer(String.serializer(), String.serializer()).serialize(encoder, map)
}
override fun deserialize(decoder: Decoder): Properties {
val map = MapSerializer(String.serializer(), String.serializer()).deserialize(decoder)
return Properties().apply { putAll(map) }
}
}

View File

@@ -1,7 +1,7 @@
package com.aurora.store.module
import com.aurora.store.data.serializers.LocaleSerializer
import com.aurora.store.data.serializers.PropertiesSerializer
import com.aurora.gplayapi.data.serializers.LocaleSerializer
import com.aurora.gplayapi.data.serializers.PropertiesSerializer
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn

View File

@@ -24,6 +24,7 @@ import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import com.aurora.store.BuildConfig
object Preferences {
@@ -65,10 +66,17 @@ object Preferences {
private var prefs: SharedPreferences? = null
fun getPrefs(context: Context): SharedPreferences {
if (prefs == null) {
prefs = PreferenceManager.getDefaultSharedPreferences(context)
return when (BuildConfig.FLAVOR) {
"vanilla" -> {
prefs ?: PreferenceManager.getDefaultSharedPreferences(context).also { prefs = it }
}
else -> {
val prefName = "${context.packageName}_${BuildConfig.FLAVOR}_preferences"
prefs ?: context.getSharedPreferences(prefName, Context.MODE_PRIVATE)
.also { prefs = it }
}
}
return prefs!!
}
fun remove(context: Context, key: String) {

View File

@@ -21,13 +21,14 @@ package com.aurora.store.view.epoxy.views.details
import android.content.Context
import android.util.AttributeSet
import androidx.core.content.ContextCompat
import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView
import com.airbnb.epoxy.OnViewRecycled
import com.aurora.store.R
import com.aurora.store.databinding.ViewInfoBinding
import com.aurora.store.view.epoxy.views.BaseModel
import com.aurora.store.view.epoxy.views.BaseView
import java.util.Locale
@ModelView(
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
@@ -41,16 +42,14 @@ class InfoView @JvmOverloads constructor(
@ModelProp(options = [ModelProp.Option.IgnoreRequireHashCode])
fun badge(info: Map.Entry<String, String>) {
binding.txtTitle.text = info.key
.replace("_", " ")
.lowercase(Locale.getDefault())
.replaceFirstChar {
if (it.isLowerCase()) {
it.titlecase(Locale.getDefault())
} else {
it.toString()
}
}
binding.txtTitle.text = when (info.key) {
"DOWNLOAD" -> ContextCompat.getString(context, R.string.app_info_downloads)
"UPDATED_ON" -> ContextCompat.getString(context, R.string.app_info_updated_on)
"REQUIRES" -> ContextCompat.getString(context, R.string.app_info_min_android)
"TARGET" -> ContextCompat.getString(context, R.string.app_info_target_android)
else -> info.key
}
binding.txtSubtitle.text = info.value
}

View File

@@ -42,7 +42,7 @@ abstract class BaseFragment<ViewBindingType : ViewBinding> : Fragment() {
lateinit var permissionProvider: PermissionProvider
private var _binding: ViewBindingType? = null
protected open var _binding: ViewBindingType? = null
protected val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -58,9 +58,9 @@ class StreamBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>() {
}
})
viewModel.initCluster(streamCluster)
viewModel.seedCluster(streamCluster)
viewModel.liveData.observe(viewLifecycleOwner) {
updateController(streamCluster)
updateController(it)
}
}

View File

@@ -164,7 +164,7 @@ class DetailsMoreFragment : BaseFragment<FragmentDetailsMoreBinding>() {
add(
InfoViewModel_()
.id(UUID.randomUUID().toString())
.badge(mapOf("targets" to "API ${app.targetSdk}").entries.first())
.badge(mapOf("TARGET" to "${app.targetSdk}").entries.first())
)
}
}

View File

@@ -1,15 +0,0 @@
package com.aurora.store.view.ui.onboarding
import android.os.Bundle
import android.view.View
import com.aurora.store.databinding.FragmentAppLinksBinding
import com.aurora.store.view.ui.commons.BaseFragment
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AppLinksFragment : BaseFragment<FragmentAppLinksBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}

View File

@@ -1,26 +1,9 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.onboarding
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
@@ -29,59 +12,36 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.aurora.Constants
import com.aurora.extensions.areNotificationsEnabled
import com.aurora.extensions.isIgnoringBatteryOptimizations
import com.aurora.store.R
import com.aurora.store.data.model.UpdateMode
import com.aurora.store.data.work.CacheWorker
import com.aurora.store.databinding.FragmentOnboardingBinding
import com.aurora.store.util.CertUtil
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.Preferences.PREFERENCE_INTRO
import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR
import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import com.aurora.store.util.save
import com.aurora.store.view.ui.commons.BaseFragment
import com.aurora.store.viewmodel.onboarding.OnboardingViewModel
import com.google.android.material.tabs.TabLayoutMediator
import com.jakewharton.processphoenix.ProcessPhoenix
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
abstract class BaseFlavouredOnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
private val viewModel: OnboardingViewModel by viewModels()
val viewModel: OnboardingViewModel by viewModels()
private var lastPosition = 0
var lastPosition = 0
internal class PagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) {
override fun createFragment(position: Int): Fragment {
when (position) {
0 -> return WelcomeFragment()
1 -> return PermissionsFragment.newInstance()
2 -> return AppLinksFragment()
}
return Fragment()
}
override fun getItemCount(): Int {
return 2
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentOnboardingBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -95,6 +55,7 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
}
val isDefaultPrefLoaded = Preferences.getBoolean(requireContext(), PREFERENCE_DEFAULT)
if (!isDefaultPrefLoaded) {
save(PREFERENCE_DEFAULT, true)
loadDefaultPreferences()
@@ -105,7 +66,11 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
// ViewPager2
binding.viewpager2.apply {
adapter = PagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
adapter = PagerAdapter(
childFragmentManager,
viewLifecycleOwner.lifecycle,
onboardingPages()
)
isUserInputEnabled = false
setCurrentItem(0, true)
registerOnPageChangeCallback(object : OnPageChangeCallback() {
@@ -149,7 +114,11 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
}
}
private fun finishOnboarding() {
abstract fun loadDefaultPreferences()
abstract fun onboardingPages(): List<Fragment>
open fun finishOnboarding() {
setupAutoUpdates()
CacheWorker.scheduleAutomatedCacheCleanup(requireContext())
Preferences.putBooleanNow(requireContext(), PREFERENCE_INTRO, true)
@@ -158,40 +127,29 @@ class OnboardingFragment : BaseFragment<FragmentOnboardingBinding>() {
ProcessPhoenix.triggerRebirth(context)
}
private fun loadDefaultPreferences() {
/*Filters*/
save(PREFERENCE_FILTER_AURORA_ONLY, false)
save(PREFERENCE_FILTER_FDROID, true)
/*Network*/
// TODO: Gather feedback and drop setting default dispenser for all builds
if (!CertUtil.isAppGalleryApp(requireContext(), requireContext().packageName)) {
save(PREFERENCE_DISPENSER_URLS, setOf(Constants.URL_DISPENSER))
}
save(PREFERENCE_VENDING_VERSION, 0)
/*Customization*/
save(PREFERENCE_THEME_STYLE, 0)
save(PREFERENCE_DEFAULT_SELECTED_TAB, 0)
save(PREFERENCE_FOR_YOU, true)
save(PREFERENCE_SIMILAR, false)
/*Installer*/
save(PREFERENCE_AUTO_DELETE, true)
save(PREFERENCE_INSTALLER_ID, 0)
/*Updates*/
save(PREFERENCE_UPDATES_EXTENDED, false)
save(PREFERENCE_UPDATES_CHECK_INTERVAL, 3)
}
private fun setupAutoUpdates() {
open fun setupAutoUpdates() {
val updateMode = when {
requireContext().isIgnoringBatteryOptimizations() -> UpdateMode.CHECK_AND_INSTALL
requireContext().areNotificationsEnabled() -> UpdateMode.CHECK_AND_NOTIFY
else -> UpdateMode.DISABLED
}
save(PREFERENCE_UPDATES_AUTO, updateMode.ordinal)
viewModel.updateHelper.scheduleAutomatedCheck()
}
internal class PagerAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle,
var items: List<Fragment>
) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun createFragment(position: Int): Fragment {
return items[position]
}
override fun getItemCount(): Int {
return items.size
}
}
}

View File

@@ -1,45 +1,24 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.splash
import android.accounts.Account
import android.accounts.AccountManager
import android.content.Intent
import android.net.UrlQuerySanitizer
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Base64
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.aurora.extensions.hide
import com.aurora.extensions.getPackageName
import com.aurora.extensions.isMAndAbove
import com.aurora.extensions.isNAndAbove
import com.aurora.extensions.navigate
import com.aurora.extensions.show
import com.aurora.gplayapi.helpers.AuthHelper
import com.aurora.store.R
import com.aurora.store.compose.navigation.Screen
@@ -56,22 +35,20 @@ import com.aurora.store.util.Preferences.PREFERENCE_INTRO
import com.aurora.store.util.Preferences.PREFERENCE_MICROG_AUTH
import com.aurora.store.view.ui.commons.BaseFragment
import com.aurora.store.viewmodel.auth.AuthViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@AndroidEntryPoint
class SplashFragment : BaseFragment<FragmentSplashBinding>() {
abstract class BaseFlavouredSplashFragment : BaseFragment<FragmentSplashBinding>() {
private val TAG = SplashFragment::class.java.simpleName
private val TAG = BaseFlavouredSplashFragment::class.java.simpleName
private val viewModel: AuthViewModel by activityViewModels()
val viewModel: AuthViewModel by activityViewModels()
private val canLoginWithMicroG: Boolean
val canLoginWithMicroG: Boolean
get() = isMAndAbove && PackageUtil.hasSupportedMicroG(requireContext()) &&
Preferences.getBoolean(requireContext(), PREFERENCE_MICROG_AUTH, true)
private val startForAccount =
val startForAccount =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val accountName = it.data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)
if (!accountName.isNullOrBlank()) {
@@ -81,6 +58,15 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSplashBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -115,9 +101,6 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
attachActions()
// Show anonymous logins if we have dispenser URL
binding.btnAnonymous.isVisible = !viewModel.authProvider.dispenserURL.isNullOrBlank()
viewLifecycleOwner.lifecycleScope.launch {
viewModel.authState.collectLatest {
when (it) {
@@ -128,7 +111,8 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
}
AuthState.Valid -> {
val packageName = getPackageName(requireActivity().intent)
val packageName =
requireActivity().intent.getPackageName(requireArguments())
if (packageName.isNullOrBlank()) {
navigateToDefaultTab()
} else {
@@ -152,7 +136,8 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
}
AuthState.SignedIn -> {
val packageName = getPackageName(requireActivity().intent)
val packageName =
requireActivity().intent.getPackageName(requireArguments())
if (packageName.isNullOrBlank()) {
navigateToDefaultTab()
} else {
@@ -193,55 +178,8 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
}
private fun updateActionLayout(isVisible: Boolean) {
if (isVisible) {
binding.layoutAction.show()
binding.toolbar.visibility = View.VISIBLE
} else {
binding.layoutAction.hide()
binding.toolbar.visibility = View.GONE
}
}
private fun attachActions() {
binding.btnAnonymous.addOnClickListener {
if (viewModel.authState.value != AuthState.Fetching) {
binding.btnAnonymous.updateProgress(true)
viewModel.buildAnonymousAuthData()
}
}
binding.btnGoogle.addOnClickListener {
if (viewModel.authState.value != AuthState.Fetching) {
binding.btnGoogle.updateProgress(true)
if (canLoginWithMicroG) {
Log.i(TAG, "Found supported microG, trying to request credentials")
val accountIntent = AccountManager.newChooseAccountIntent(
null,
null,
arrayOf(GOOGLE_ACCOUNT_TYPE),
null,
null,
null,
null
)
startForAccount.launch(accountIntent)
} else {
findNavController().navigate(R.id.googleFragment)
}
}
}
}
private fun resetActions() {
binding.btnGoogle.apply {
updateProgress(false)
isEnabled = true
}
binding.btnAnonymous.apply {
updateProgress(false)
isEnabled = true
}
binding.layoutAction.isVisible = isVisible
binding.toolbar.isVisible = isVisible
}
private fun navigateToDefaultTab() {
@@ -262,29 +200,6 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
findNavController().navigate(directions)
}
private fun getPackageName(intent: Intent): String? {
return when {
intent.action == Intent.ACTION_VIEW -> {
intent.data!!.getQueryParameter("id")
}
intent.action == Intent.ACTION_SEND -> {
val clipData = intent.getStringExtra(Intent.EXTRA_TEXT) ?: ""
UrlQuerySanitizer(clipData).getValue("id")
}
isNAndAbove && intent.action == Intent.ACTION_SHOW_APP_INFO -> {
intent.extras?.getString(Intent.EXTRA_PACKAGE_NAME)
}
intent.extras != null -> {
intent.extras?.getString("packageName")
}
else -> requireArguments().getString("packageName")
}
}
private fun requestAuthTokenForGoogle(accountName: String, oldToken: String? = null) {
try {
if (oldToken != null) {
@@ -317,4 +232,46 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
Log.e(TAG, "Failed to get authToken for Google login")
}
}
open fun attachActions() {
binding.btnAnonymous.addOnClickListener {
if (viewModel.authState.value != AuthState.Fetching) {
binding.btnAnonymous.updateProgress(true)
viewModel.buildAnonymousAuthData()
}
}
binding.btnGoogle.addOnClickListener {
if (viewModel.authState.value != AuthState.Fetching) {
binding.btnGoogle.updateProgress(true)
if (canLoginWithMicroG) {
Log.i(TAG, "Found supported microG, trying to request credentials")
val accountIntent = AccountManager.newChooseAccountIntent(
null,
null,
arrayOf(GOOGLE_ACCOUNT_TYPE),
null,
null,
null,
null
)
startForAccount.launch(accountIntent)
} else {
findNavController().navigate(R.id.googleFragment)
}
}
}
}
open fun resetActions() {
binding.btnGoogle.apply {
updateProgress(false)
isEnabled = true
}
binding.btnAnonymous.apply {
updateProgress(false)
isEnabled = true
}
}
}

View File

@@ -28,7 +28,6 @@ import com.aurora.gplayapi.helpers.web.WebStreamHelper
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import javax.inject.Inject
@HiltViewModel
@@ -42,32 +41,36 @@ class StreamBrowseViewModel @Inject constructor(
private lateinit var streamCluster: StreamCluster
fun initCluster(cluster: StreamCluster) {
fun seedCluster(cluster: StreamCluster) {
streamCluster = cluster
liveData.postValue(streamCluster)
}
fun nextCluster() {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
if (streamCluster.hasNext()) {
val nextCluster = streamHelper.nextStreamCluster(
streamCluster.clusterNextPageUrl
)
try {
if (streamCluster.hasNext()) {
val next = streamHelper.nextStreamCluster(streamCluster.clusterNextPageUrl)
streamCluster = streamCluster.copy(
clusterNextPageUrl = nextCluster.clusterNextPageUrl,
clusterAppList = streamCluster.clusterAppList + nextCluster.clusterAppList
)
streamCluster = streamCluster.copy(
clusterNextPageUrl = next.clusterNextPageUrl,
clusterAppList = streamCluster.clusterAppList + next.clusterAppList
)
liveData.postValue(streamCluster)
} else {
Log.i(TAG, "End of Cluster")
}
} catch (_: Exception) {
liveData.postValue(streamCluster)
} else {
Log.i(TAG, "End of Cluster")
postClusterEnd()
}
} catch (_: Exception) {
}
}
}
fun postClusterEnd() {
streamCluster = streamCluster.copy(
clusterNextPageUrl = ""
)
liveData.postValue(streamCluster)
}
}

View File

@@ -107,9 +107,9 @@ class DetailsClusterViewModel @Inject constructor(
clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
)
val newStreamClusters = bundle.streamClusters.toMutableMap().also {
it.remove(clusterID)
it[clusterID] = mergedCluster
val newStreamClusters = bundle.streamClusters.toMutableMap().apply {
this[clusterID] = mergedCluster
}
stash.put(url, bundle.copy(streamClusters = newStreamClusters))

View File

@@ -90,9 +90,9 @@ class DevProfileViewModel @Inject constructor(
clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
)
val newStreamClusters = streamBundle.streamClusters.toMutableMap().also {
it.remove(newCluster.id)
it[newCluster.id] = mergedCluster
val newStreamClusters = streamBundle.streamClusters.toMutableMap().apply {
this[newCluster.id] = mergedCluster
}
streamBundle = streamBundle.copy(streamClusters = newStreamClusters)

View File

@@ -32,7 +32,8 @@ import com.aurora.store.data.model.ViewState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
@HiltViewModel
@@ -44,11 +45,14 @@ class StreamViewModel @Inject constructor(
val liveData: MutableLiveData<ViewState> = MutableLiveData()
private var stash: HomeStash = mutableMapOf()
private val stash: HomeStash = mutableMapOf()
private val streamContract: StreamContract
get() = webStreamHelper
// Mutex to protect stash access for thread safety
private val stashMutex = Mutex()
fun getStreamBundle(category: StreamContract.Category, type: StreamContract.Type) {
liveData.postValue(ViewState.Loading)
observe(category, type)
@@ -56,16 +60,18 @@ class StreamViewModel @Inject constructor(
fun observe(category: StreamContract.Category, type: StreamContract.Type) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
val bundle = targetBundle(category)
if (bundle.hasCluster()) {
liveData.postValue(ViewState.Success(stash))
}
try {
stashMutex.withLock {
val bundle = targetBundle(category)
// Post existing data if any clusters exist
if (bundle.hasCluster()) {
liveData.postValue(ViewState.Success(stash.toMap()))
}
try {
if (!bundle.hasCluster() || bundle.hasNext()) {
//Fetch new stream bundle
// Fetch new stream bundle
val newBundle = if (bundle.hasCluster()) {
streamContract.nextStreamBundle(
category,
@@ -75,41 +81,47 @@ class StreamViewModel @Inject constructor(
streamContract.fetch(type, category)
}
//Update old bundle
// Update old bundle
val mergedBundle = bundle.copy(
streamClusters = bundle.streamClusters + newBundle.streamClusters,
streamNextPageUrl = newBundle.streamNextPageUrl
)
stash[category] = mergedBundle
//Post updated to UI
liveData.postValue(ViewState.Success(stash))
// Post updated to UI
liveData.postValue(ViewState.Success(stash.toMap()))
} else {
Log.i(TAG, "End of Bundle")
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
}
}
}
fun observeCluster(category: StreamContract.Category, streamCluster: StreamCluster) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
if (streamCluster.hasNext()) {
val newCluster = streamContract.nextStreamCluster(
streamCluster.clusterNextPageUrl
)
try {
if (streamCluster.hasNext()) {
val newCluster = streamContract.nextStreamCluster(
streamCluster.clusterNextPageUrl
)
stashMutex.withLock {
updateCluster(category, streamCluster.id, newCluster)
liveData.postValue(ViewState.Success(stash))
} else {
Log.i(TAG, "End of cluster")
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
liveData.postValue(ViewState.Success(stash.toMap()))
} else {
stashMutex.withLock {
postClusterEnd(category, streamCluster.id)
}
liveData.postValue(ViewState.Success(stash.toMap()))
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
}
}
}
@@ -119,26 +131,34 @@ class StreamViewModel @Inject constructor(
clusterID: Int,
newCluster: StreamCluster
) {
val bundle = targetBundle(category)
bundle.streamClusters[clusterID]?.let { oldCluster ->
val mergedCluster = oldCluster.copy(
clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
)
val newStreamClusters = bundle.streamClusters.toMutableMap().also {
it.remove(clusterID)
it[clusterID] = mergedCluster
}
val bundle = stash[category] ?: return
val oldCluster = bundle.streamClusters[clusterID] ?: return
stash.put(category, bundle.copy(streamClusters = newStreamClusters))
val mergedCluster = oldCluster.copy(
clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
)
val updatedClusters = bundle.streamClusters.toMutableMap().apply {
this[clusterID] = mergedCluster
}
stash[category] = bundle.copy(streamClusters = updatedClusters)
}
private fun postClusterEnd(category: StreamContract.Category, clusterID: Int) {
val bundle = stash[category] ?: return
val oldCluster = bundle.streamClusters[clusterID] ?: return
val updatedCluster = oldCluster.copy(clusterNextPageUrl = "")
val updatedClusters = bundle.streamClusters.toMutableMap().apply {
this[clusterID] = updatedCluster
}
stash[category] = bundle.copy(streamClusters = updatedClusters)
}
private fun targetBundle(category: StreamContract.Category): StreamBundle {
val streamBundle = stash.getOrPut(category) {
StreamBundle()
}
return streamBundle
return stash.getOrPut(category) { StreamBundle() }
}
}

View File

@@ -19,6 +19,7 @@
package com.aurora.store.viewmodel.review
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@@ -28,13 +29,13 @@ import com.aurora.gplayapi.helpers.ReviewsHelper
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import javax.inject.Inject
@HiltViewModel
class ReviewViewModel @Inject constructor(
private val reviewsHelper: ReviewsHelper
) : ViewModel() {
val TAG = javaClass.simpleName
val liveData: MutableLiveData<ReviewCluster> = MutableLiveData()
@@ -42,29 +43,29 @@ class ReviewViewModel @Inject constructor(
fun fetchReview(packageName: String, filter: Review.Filter) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
reviewsCluster = reviewsHelper.getReviews(packageName, filter)
liveData.postValue(reviewsCluster)
} catch (_: Exception) {
}
try {
reviewsCluster = reviewsHelper.getReviews(packageName, filter)
liveData.postValue(reviewsCluster)
} catch (e: Exception) {
Log.e(TAG, "Failed to fetch reviews", e)
}
}
}
fun next(nextReviewPageUrl: String) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
val nextReviewCluster = reviewsHelper.next(nextReviewPageUrl)
reviewsCluster = reviewsCluster.copy(
nextPageUrl = nextReviewCluster.nextPageUrl,
reviewList = nextReviewCluster.reviewList + nextReviewCluster.reviewList
)
try {
val currentCluster = reviewsCluster
val nextReviewCluster = reviewsHelper.next(nextReviewPageUrl)
liveData.postValue(reviewsCluster)
} catch (_: Exception) {
}
reviewsCluster = currentCluster.copy(
nextPageUrl = nextReviewCluster.nextPageUrl,
reviewList = currentCluster.reviewList + nextReviewCluster.reviewList
)
liveData.postValue(reviewsCluster)
} catch (e: Exception) {
Log.e(TAG, "Failed to fetch next reviews $nextReviewPageUrl", e)
}
}
}

View File

@@ -121,9 +121,9 @@ class CategoryStreamViewModel @Inject constructor(
clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
)
val newStreamClusters = bundle.streamClusters.toMutableMap().also {
it.remove(clusterID)
it[clusterID] = mergedCluster
val newStreamClusters = bundle.streamClusters.toMutableMap().apply {
this[clusterID] = mergedCluster
}
stash.put(browseUrl, bundle.copy(streamClusters = newStreamClusters))

View File

@@ -91,7 +91,7 @@ class TopChartViewModel @Inject constructor(
clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList
)
stash[type] = mutableMapOf(chart to mergedCluster)
stash[type]?.set(chart, mergedCluster)
}
private fun targetCluster(

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".view.ui.onboarding.AppLinksFragment">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/appCompatTextView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:includeFontPadding="true"
android:paddingStart="@dimen/padding_normal"
android:paddingEnd="@dimen/padding_normal"
android:text="@string/app_links_title"
android:textAlignment="textStart"
android:textColor="?colorAccent"
android:textSize="42sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/appCompatTextView"
style="@style/AuroraTextStyle.Subtitle.Alt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/margin_small"
android:paddingStart="@dimen/padding_normal"
android:paddingEnd="@dimen/padding_normal"
android:text="@string/app_links_desc"
android:textAlignment="textStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appCompatTextView2" />
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/epoxyRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:overScrollMode="never"
android:padding="@dimen/padding_medium"
tools:listitem="@layout/view_permission_switch" />
</LinearLayout>

View File

@@ -357,6 +357,10 @@
<string name="android_market_desc">Android Market was an online store offering software applications designed for Android devices, retired in 2017.</string>
<string name="failed_apk_export">Failed to export APKs</string>
<string name="no_apps_available">No apps available</string>
<string name="app_info_downloads">"Downloads"</string>
<string name="app_info_updated_on">"Last updated"</string>
<string name="app_info_min_android">"Minimum Android Version"</string>
<string name="app_info_target_android">"Target API Level"</string>
<!-- InstallerFragment -->
<string name="session_installer_subtitle">Session based installer for bundled/split APKs</string>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
</manifest>

View File

@@ -0,0 +1,84 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.onboarding
import androidx.fragment.app.Fragment
import com.aurora.Constants
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR
import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import com.aurora.store.util.save
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class OnboardingFragment : BaseFlavouredOnboardingFragment() {
override fun loadDefaultPreferences() {
/*Filters*/
save(PREFERENCE_FILTER_AURORA_ONLY, false)
save(PREFERENCE_FILTER_FDROID, true)
/*Network*/
save(PREFERENCE_DISPENSER_URLS, setOf(Constants.URL_DISPENSER))
save(PREFERENCE_VENDING_VERSION, 0)
/*Customization*/
save(PREFERENCE_THEME_STYLE, 0)
save(PREFERENCE_DEFAULT_SELECTED_TAB, 0)
save(PREFERENCE_FOR_YOU, true)
save(PREFERENCE_SIMILAR, false)
/*Installer*/
save(PREFERENCE_AUTO_DELETE, true)
save(PREFERENCE_INSTALLER_ID, 0)
/*Updates*/
save(PREFERENCE_UPDATES_EXTENDED, false)
save(PREFERENCE_UPDATES_CHECK_INTERVAL, 3)
}
override fun onboardingPages(): List<Fragment> {
return listOf(
WelcomeFragment(),
PermissionsFragment.newInstance()
)
}
override fun setupAutoUpdates() {
super.setupAutoUpdates()
// Remove super & implement variant logic here
}
override fun finishOnboarding() {
super.finishOnboarding()
// Remove super & implement variant logic here
}
}

View File

@@ -0,0 +1,25 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.splash
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class SplashFragment : BaseFlavouredSplashFragment()

View File

@@ -0,0 +1,84 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.onboarding
import androidx.fragment.app.Fragment
import com.aurora.Constants
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR
import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import com.aurora.store.util.save
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class OnboardingFragment : BaseFlavouredOnboardingFragment() {
override fun loadDefaultPreferences() {
/*Filters*/
save(PREFERENCE_FILTER_AURORA_ONLY, false)
save(PREFERENCE_FILTER_FDROID, true)
/*Network*/
save(PREFERENCE_DISPENSER_URLS, setOf(Constants.URL_DISPENSER))
save(PREFERENCE_VENDING_VERSION, 0)
/*Customization*/
save(PREFERENCE_THEME_STYLE, 0)
save(PREFERENCE_DEFAULT_SELECTED_TAB, 0)
save(PREFERENCE_FOR_YOU, true)
save(PREFERENCE_SIMILAR, false)
/*Installer*/
save(PREFERENCE_AUTO_DELETE, true)
save(PREFERENCE_INSTALLER_ID, 0)
/*Updates*/
save(PREFERENCE_UPDATES_EXTENDED, false)
save(PREFERENCE_UPDATES_CHECK_INTERVAL, 3)
}
override fun onboardingPages(): List<Fragment> {
return listOf(
WelcomeFragment(),
PermissionsFragment.newInstance()
)
}
override fun setupAutoUpdates() {
super.setupAutoUpdates()
// Remove super & implement variant logic here
}
override fun finishOnboarding() {
super.finishOnboarding()
// Remove super & implement variant logic here
}
}

View File

@@ -0,0 +1,25 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.splash
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class SplashFragment : BaseFlavouredSplashFragment()

View File

@@ -9,7 +9,7 @@ composeBom = "2025.05.01"
core = "1.16.0"
epoxy = "5.1.4"
espresso = "3.6.1"
gplayapi = "3.5.2"
gplayapi = "3.5.3"
hiddenapibypass = "6.1"
hilt = "2.56.2"
hiltWork = "1.2.0"