legacy: remove fragment-based screens and sheets
Drops every Fragment, DialogFragment and BottomSheet in view/ui plus the flavoured SplashFragment variants. All call sites now route through compose/ui equivalents and ComposeActivity.
This commit is contained in:
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* 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.account
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.gplayapi.helpers.AuthHelper
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.event.AuthEvent
|
||||
import com.aurora.store.databinding.FragmentGoogleBinding
|
||||
import com.aurora.store.util.AC2DMUtil
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.viewmodel.auth.AuthViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AndroidEntryPoint
|
||||
class GoogleFragment : BaseFragment<FragmentGoogleBinding>() {
|
||||
|
||||
private val viewModel: AuthViewModel by activityViewModels()
|
||||
|
||||
companion object {
|
||||
const val EMBEDDED_SETUP_URL = "https://accounts.google.com/EmbeddedSetup"
|
||||
const val AUTH_TOKEN = "oauth_token"
|
||||
private const val JS_SCRIPT =
|
||||
"(function() { return document.getElementById('profileIdentifier').innerHTML; })();"
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
|
||||
binding.webview.apply {
|
||||
cookieManager.removeAllCookies(null)
|
||||
cookieManager.acceptThirdPartyCookies(this)
|
||||
cookieManager.setAcceptThirdPartyCookies(this, true)
|
||||
|
||||
webChromeClient = object : WebChromeClient() {
|
||||
override fun onProgressChanged(view: WebView?, newProgress: Int) {
|
||||
super.onProgressChanged(view, newProgress)
|
||||
|
||||
if (newProgress != 0) {
|
||||
binding.progressBar.also {
|
||||
it.isVisible = newProgress < 100
|
||||
it.isIndeterminate = false
|
||||
it.max = 100
|
||||
it.progress = newProgress
|
||||
}
|
||||
} else {
|
||||
binding.progressBar.isIndeterminate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
val cookies = CookieManager.getInstance().getCookie(url)
|
||||
// cookies can be null if there is an error
|
||||
if (cookies != null) {
|
||||
val cookieMap = AC2DMUtil.parseCookieString(cookies)
|
||||
if (cookieMap.isNotEmpty() && cookieMap[AUTH_TOKEN] != null) {
|
||||
val oauthToken = cookieMap[AUTH_TOKEN]
|
||||
evaluateJavascript(JS_SCRIPT) {
|
||||
val email = it.replace("\"".toRegex(), "")
|
||||
viewModel.buildAuthData(view.context, email, oauthToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
settings.apply {
|
||||
allowContentAccess = true
|
||||
domStorageEnabled = true
|
||||
javaScriptEnabled = true
|
||||
cacheMode = WebSettings.LOAD_DEFAULT
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) safeBrowsingEnabled = false
|
||||
}
|
||||
loadUrl(EMBEDDED_SETUP_URL)
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
AuroraApp.events.authEvent.collect { event ->
|
||||
if (event is AuthEvent.GoogleLogin) onEventReceived(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onEventReceived(event: AuthEvent.GoogleLogin) {
|
||||
if (event.success) {
|
||||
viewModel.buildGoogleAuthData(event.email, event.token, AuthHelper.Token.AAS)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
getString(R.string.toast_aas_token_failed),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
|
||||
findNavController().navigate(
|
||||
GoogleFragmentDirections.actionGoogleFragmentToSplashFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* 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.apps
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.store.MobileNavigationDirections
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.databinding.FragmentAppsGamesBinding
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.view.ui.commons.CategoryFragment
|
||||
import com.aurora.store.view.ui.commons.ForYouFragment
|
||||
import com.aurora.store.view.ui.commons.TopChartContainerFragment
|
||||
import com.aurora.store.viewmodel.apps.AppsContainerViewModel
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AppsContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
||||
|
||||
private val viewModel: AppsContainerViewModel by viewModels()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Adjust FAB margins for edgeToEdge display
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.searchFab) { _, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
binding.searchFab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = insets.bottom + resources.getDimensionPixelSize(R.dimen.margin_large)
|
||||
}
|
||||
WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
title = getString(R.string.title_apps)
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_download_manager -> {
|
||||
requireContext().navigate(Screen.Downloads)
|
||||
}
|
||||
|
||||
R.id.menu_more -> {
|
||||
findNavController().navigate(
|
||||
MobileNavigationDirections.actionGlobalMoreDialogFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// ViewPager
|
||||
val isForYouEnabled = Preferences.getBoolean(
|
||||
requireContext(),
|
||||
Preferences.PREFERENCE_FOR_YOU
|
||||
)
|
||||
|
||||
binding.pager.adapter = ViewPagerAdapter(
|
||||
childFragmentManager,
|
||||
viewLifecycleOwner.lifecycle,
|
||||
!viewModel.authProvider.isAnonymous,
|
||||
isForYouEnabled
|
||||
)
|
||||
|
||||
binding.pager.isUserInputEnabled =
|
||||
false // Disable viewpager scroll to avoid scroll conflicts
|
||||
|
||||
val tabTitles: MutableList<String> = mutableListOf<String>().apply {
|
||||
if (isForYouEnabled) {
|
||||
add(getString(R.string.tab_for_you))
|
||||
}
|
||||
|
||||
add(getString(R.string.tab_top_charts))
|
||||
add(getString(R.string.tab_categories))
|
||||
}
|
||||
|
||||
TabLayoutMediator(
|
||||
binding.tabLayout,
|
||||
binding.pager,
|
||||
true
|
||||
) { tab: TabLayout.Tab, position: Int ->
|
||||
tab.text = tabTitles[position]
|
||||
}.attach()
|
||||
|
||||
binding.searchFab.setOnClickListener {
|
||||
requireContext().navigate(Screen.Search)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
binding.pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
internal class ViewPagerAdapter(
|
||||
fragment: FragmentManager,
|
||||
lifecycle: Lifecycle,
|
||||
private val isGoogleAccount: Boolean,
|
||||
private val isForYouEnabled: Boolean
|
||||
) :
|
||||
FragmentStateAdapter(fragment, lifecycle) {
|
||||
|
||||
private val tabFragments: MutableList<Fragment> = mutableListOf<Fragment>().apply {
|
||||
if (isForYouEnabled) {
|
||||
add(ForYouFragment.newInstance(0))
|
||||
}
|
||||
add(TopChartContainerFragment.newInstance(0))
|
||||
add(CategoryFragment.newInstance(0))
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment = tabFragments[position]
|
||||
|
||||
override fun getItemCount(): Int = tabFragments.size
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* 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.commons
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.airbnb.epoxy.EpoxyRecyclerView
|
||||
import com.aurora.extensions.TAG
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.gplayapi.data.models.Category
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.store.MobileNavigationDirections
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.data.model.MinimalApp
|
||||
import com.aurora.store.data.providers.PermissionProvider
|
||||
import java.lang.reflect.ParameterizedType
|
||||
|
||||
abstract class BaseFragment<ViewBindingType : ViewBinding> : Fragment() {
|
||||
|
||||
lateinit var permissionProvider: PermissionProvider
|
||||
|
||||
protected open var viewBindingType: ViewBindingType? = null
|
||||
protected val binding get() = viewBindingType!!
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
permissionProvider = PermissionProvider(this)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val type = (javaClass.genericSuperclass as ParameterizedType)
|
||||
.actualTypeArguments[0] as Class<ViewBindingType>
|
||||
val method = type.getMethod(
|
||||
"inflate",
|
||||
LayoutInflater::class.java,
|
||||
ViewGroup::class.java,
|
||||
Boolean::class.java
|
||||
)
|
||||
viewBindingType = method.invoke(null, inflater, container, false) as ViewBindingType
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
permissionProvider.unregister()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
cleanupRecyclerViews(findAllRecyclerViews(requireView()))
|
||||
viewBindingType = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
fun openDetailsFragment(packageName: String) {
|
||||
requireContext().navigate(
|
||||
Screen.AppDetails(packageName)
|
||||
)
|
||||
}
|
||||
|
||||
fun openCategoryBrowseFragment(category: Category) {
|
||||
findNavController().navigate(
|
||||
MobileNavigationDirections.actionGlobalCategoryBrowseFragment(
|
||||
category.title,
|
||||
category.browseUrl
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun openStreamBrowseFragment(browseUrl: String, title: String = "") {
|
||||
if (browseUrl.lowercase().contains("expanded")) {
|
||||
findNavController().navigate(
|
||||
MobileNavigationDirections.actionGlobalExpandedStreamBrowseFragment(
|
||||
title,
|
||||
browseUrl
|
||||
)
|
||||
)
|
||||
} else if (browseUrl.lowercase().contains("developer")) {
|
||||
findNavController().navigate(
|
||||
MobileNavigationDirections.actionGlobalDevProfileFragment(
|
||||
browseUrl.substringAfter("developer-"),
|
||||
title
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun openStreamBrowseFragment(streamCluster: StreamCluster) {
|
||||
requireContext().navigate(Screen.StreamBrowse(streamCluster))
|
||||
}
|
||||
|
||||
fun openAppMenuSheet(app: MinimalApp) {
|
||||
findNavController().navigate(MobileNavigationDirections.actionGlobalAppMenuSheet(app))
|
||||
}
|
||||
|
||||
fun openGMSWarningFragment() {
|
||||
// TODO: FIX ME
|
||||
}
|
||||
|
||||
private fun cleanupRecyclerViews(recyclerViews: List<EpoxyRecyclerView>) {
|
||||
recyclerViews.forEach { recyclerView ->
|
||||
runCatching {
|
||||
recyclerView.adapter?.let {
|
||||
recyclerView.swapAdapter(it, true)
|
||||
}
|
||||
}.onFailure {
|
||||
Log.e(TAG, "Failed to cleanup RecyclerView", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findAllRecyclerViews(view: View): List<EpoxyRecyclerView> {
|
||||
val recyclerViews = mutableListOf<EpoxyRecyclerView>()
|
||||
|
||||
if (view is EpoxyRecyclerView) {
|
||||
recyclerViews.add(view)
|
||||
} else if (view is ViewGroup) {
|
||||
for (i in 0 until view.childCount) {
|
||||
recyclerViews.addAll(findAllRecyclerViews(view.getChildAt(i)))
|
||||
}
|
||||
}
|
||||
|
||||
return recyclerViews
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* 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.commons
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.StreamBundle
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
||||
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||
import com.aurora.store.view.epoxy.controller.CategoryCarouselController
|
||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||
import com.aurora.store.viewmodel.subcategory.CategoryStreamViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class CategoryBrowseFragment :
|
||||
BaseFragment<FragmentGenericWithToolbarBinding>(),
|
||||
GenericCarouselController.Callbacks {
|
||||
private val args: CategoryBrowseFragmentArgs by navArgs()
|
||||
private val viewModel: CategoryStreamViewModel by activityViewModels()
|
||||
|
||||
private var streamBundle: StreamBundle? = StreamBundle()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val genericCarouselController = CategoryCarouselController(this)
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
title = args.title
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
|
||||
// RecyclerView
|
||||
binding.recycler.setController(genericCarouselController)
|
||||
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
|
||||
override fun onLoadMore(currentPage: Int) {
|
||||
viewModel.observe(args.browseUrl)
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.getStreamBundle(args.browseUrl)
|
||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
is ViewState.Loading -> {
|
||||
genericCarouselController.setData(null)
|
||||
}
|
||||
|
||||
is ViewState.Success<*> -> {
|
||||
val stash = it.getDataAs<Map<String, StreamBundle>>()
|
||||
streamBundle = stash[args.browseUrl]
|
||||
|
||||
genericCarouselController.setData(streamBundle)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||
openStreamBrowseFragment(streamCluster)
|
||||
}
|
||||
|
||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||
viewModel.observeCluster(args.browseUrl, streamCluster)
|
||||
}
|
||||
|
||||
override fun onAppClick(app: App) {
|
||||
openDetailsFragment(app.packageName)
|
||||
}
|
||||
|
||||
override fun onAppLongClick(app: App) {
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* 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.commons
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.aurora.Constants
|
||||
import com.aurora.gplayapi.data.models.Category
|
||||
import com.aurora.store.CategoryStash
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.data.model.ViewState.Empty.getDataAs
|
||||
import com.aurora.store.databinding.FragmentGenericRecyclerBinding
|
||||
import com.aurora.store.view.epoxy.views.CategoryViewModel_
|
||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
||||
import com.aurora.store.viewmodel.category.CategoryViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class CategoryFragment : BaseFragment<FragmentGenericRecyclerBinding>() {
|
||||
private val viewModel: CategoryViewModel by viewModels()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(pageType: Int): CategoryFragment = CategoryFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(Constants.PAGE_TYPE, pageType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
var pageType = 0
|
||||
val bundle = arguments
|
||||
if (bundle != null) {
|
||||
pageType = bundle.getInt(Constants.PAGE_TYPE, 0)
|
||||
}
|
||||
|
||||
when (pageType) {
|
||||
0 -> viewModel.getCategoryList(Category.Type.APPLICATION)
|
||||
1 -> viewModel.getCategoryList(Category.Type.GAME)
|
||||
}
|
||||
|
||||
val categoryType = when (pageType) {
|
||||
1 -> Category.Type.GAME
|
||||
else -> Category.Type.APPLICATION
|
||||
}
|
||||
|
||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
is ViewState.Success<*> -> {
|
||||
val stash = it.getDataAs<CategoryStash>()
|
||||
updateController(stash[categoryType])
|
||||
}
|
||||
|
||||
else -> {
|
||||
updateController(emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateController(categories: List<Category>?) {
|
||||
binding.recycler.withModels {
|
||||
if (categories == null) {
|
||||
for (i in 1..10) {
|
||||
add(
|
||||
AppListViewShimmerModel_()
|
||||
.id(i)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setFilterDuplicates(true)
|
||||
categories.forEach {
|
||||
add(
|
||||
CategoryViewModel_()
|
||||
.id(it.title)
|
||||
.category(it)
|
||||
.click { _ -> openCategoryBrowseFragment(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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.commons
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
|
||||
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
|
||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
||||
import com.aurora.store.viewmodel.browse.ExpandedStreamBrowseViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ExpandedStreamBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>() {
|
||||
private val args: ExpandedStreamBrowseFragmentArgs by navArgs()
|
||||
private val viewModel: ExpandedStreamBrowseViewModel by viewModels()
|
||||
|
||||
private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
|
||||
private lateinit var cluster: StreamCluster
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
title = args.title
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
|
||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||
if (!::cluster.isInitialized) attachRecycler()
|
||||
cluster = it
|
||||
|
||||
updateController(cluster)
|
||||
updateTitle(cluster)
|
||||
}
|
||||
|
||||
viewModel.getInitialCluster(args.expandedStreamUrl)
|
||||
updateController(null)
|
||||
}
|
||||
|
||||
private fun updateTitle(streamCluster: StreamCluster) {
|
||||
if (streamCluster.clusterTitle.isNotEmpty()) {
|
||||
binding.toolbar.title = streamCluster.clusterTitle
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachRecycler() {
|
||||
endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
|
||||
override fun onLoadMore(currentPage: Int) {
|
||||
viewModel.next()
|
||||
}
|
||||
}
|
||||
binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
|
||||
}
|
||||
|
||||
private fun updateController(streamCluster: StreamCluster?) {
|
||||
binding.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
if (streamCluster == null) {
|
||||
for (i in 1..6) {
|
||||
add(
|
||||
AppListViewShimmerModel_()
|
||||
.id(i)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
streamCluster.clusterAppList.forEach {
|
||||
add(
|
||||
AppListViewModel_()
|
||||
.id(it.packageName.hashCode())
|
||||
.app(it)
|
||||
.click { _ -> openDetailsFragment(it.packageName) }
|
||||
)
|
||||
}
|
||||
|
||||
if (streamCluster.hasNext()) {
|
||||
add(
|
||||
AppProgressViewModel_()
|
||||
.id("progress")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* 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.commons
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.aurora.Constants
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.StreamBundle
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.gplayapi.helpers.contracts.StreamContract.Category
|
||||
import com.aurora.gplayapi.helpers.contracts.StreamContract.Type
|
||||
import com.aurora.store.HomeStash
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
||||
import com.aurora.store.databinding.FragmentForYouBinding
|
||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||
import com.aurora.store.viewmodel.homestream.StreamViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ForYouFragment :
|
||||
BaseFragment<FragmentForYouBinding>(),
|
||||
GenericCarouselController.Callbacks {
|
||||
private val viewModel: StreamViewModel by activityViewModels()
|
||||
|
||||
private var category: Category = Category.APPLICATION
|
||||
private var streamBundle: StreamBundle? = StreamBundle()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(pageType: Int): ForYouFragment = ForYouFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(Constants.PAGE_TYPE, pageType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val genericCarouselController = GenericCarouselController(this)
|
||||
|
||||
var pageType = 0
|
||||
val bundle = arguments
|
||||
if (bundle != null) {
|
||||
pageType = bundle.getInt(Constants.PAGE_TYPE, 0)
|
||||
}
|
||||
|
||||
category = if (pageType == 0) Category.APPLICATION else Category.GAME
|
||||
|
||||
binding.recycler.setController(genericCarouselController)
|
||||
binding.recycler.addOnScrollListener(
|
||||
object : EndlessRecyclerOnScrollListener(visibleThreshold = 4) {
|
||||
override fun onLoadMore(currentPage: Int) {
|
||||
viewModel.observe(category, Type.HOME)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.getStreamBundle(category, Type.HOME)
|
||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
is ViewState.Loading -> {
|
||||
genericCarouselController.setData(null)
|
||||
}
|
||||
|
||||
is ViewState.Success<*> -> {
|
||||
val stash = it.getDataAs<HomeStash>()
|
||||
streamBundle = stash[category]
|
||||
|
||||
genericCarouselController.setData(streamBundle)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||
openStreamBrowseFragment(streamCluster)
|
||||
}
|
||||
|
||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||
viewModel.observeCluster(category, streamCluster)
|
||||
}
|
||||
|
||||
override fun onAppClick(app: App) {
|
||||
openDetailsFragment(app.packageName)
|
||||
}
|
||||
|
||||
override fun onAppLongClick(app: App) {
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.aurora.store.view.ui.commons
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.aurora.store.R
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jakewharton.processphoenix.ProcessPhoenix
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ForceRestartDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.force_restart_title)
|
||||
.setMessage(R.string.force_restart_summary)
|
||||
.setPositiveButton(getString(R.string.action_restart)) { _, _ ->
|
||||
ProcessPhoenix.triggerRebirth(requireContext())
|
||||
}
|
||||
.create()
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
dialog?.setCancelable(false)
|
||||
}
|
||||
}
|
||||
@@ -1,460 +0,0 @@
|
||||
package com.aurora.store.view.ui.commons
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.crossfade
|
||||
import com.aurora.Constants
|
||||
import com.aurora.Constants.URL_TOS
|
||||
import com.aurora.extensions.browse
|
||||
import com.aurora.extensions.getStyledAttributeColor
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.extensions.setAppTheme
|
||||
import com.aurora.store.MR
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.compose.theme.AuroraTheme
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.viewmodel.commons.MoreViewModel
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MoreDialogFragment : DialogFragment() {
|
||||
|
||||
private val viewModel: MoreViewModel by viewModels()
|
||||
|
||||
private var primaryColor: Color = Color.White
|
||||
private var onPrimaryColor: Color = Color.Black
|
||||
private var secondaryColor: Color = Color.White
|
||||
private var onSecondaryColor: Color = Color.Black
|
||||
|
||||
private abstract class Option(
|
||||
@StringRes open val title: Int,
|
||||
@DrawableRes open val icon: Int
|
||||
)
|
||||
|
||||
private data class ViewOption(
|
||||
override val title: Int,
|
||||
override val icon: Int,
|
||||
@IdRes val destinationID: Int
|
||||
) : Option(title, icon)
|
||||
|
||||
private data class ComposeOption(
|
||||
override val title: Int,
|
||||
override val icon: Int,
|
||||
val screen: Screen
|
||||
) : Option(title, icon)
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setView(customDialogView(requireContext()))
|
||||
.create()
|
||||
|
||||
private fun customDialogView(context: Context): ComposeView = ComposeView(context).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
AuroraTheme {
|
||||
primaryColor =
|
||||
Color(requireContext().getStyledAttributeColor(MR.colorSurface))
|
||||
onPrimaryColor =
|
||||
Color(requireContext().getStyledAttributeColor(MR.colorOnSurface))
|
||||
secondaryColor =
|
||||
Color(requireContext().getStyledAttributeColor(MR.colorSecondaryContainer))
|
||||
onSecondaryColor =
|
||||
Color(
|
||||
requireContext().getStyledAttributeColor(MR.colorOnSecondaryContainer)
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = primaryColor)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
4.dp,
|
||||
Alignment.CenterVertically
|
||||
)
|
||||
) {
|
||||
AppBar(onBackgroundColor = onPrimaryColor)
|
||||
AccountHeader(
|
||||
backgroundColor = secondaryColor,
|
||||
onBackgroundColor = onSecondaryColor
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clip(
|
||||
RoundedCornerShape(
|
||||
topStart = 2.dp,
|
||||
topEnd = 2.dp,
|
||||
bottomStart = 25.dp,
|
||||
bottomEnd = 25.dp
|
||||
)
|
||||
)
|
||||
.background(color = secondaryColor)
|
||||
) {
|
||||
getOptions().fastForEach { option ->
|
||||
OptionItem(
|
||||
option = option,
|
||||
tintColor = onPrimaryColor,
|
||||
textColor = onSecondaryColor,
|
||||
onClick = {
|
||||
when (option) {
|
||||
is ViewOption -> {
|
||||
findNavController().navigate(option.destinationID)
|
||||
}
|
||||
|
||||
is ComposeOption -> context.navigate(option.screen)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
getExtraOptions().fastForEach { option ->
|
||||
OptionItem(
|
||||
option = option,
|
||||
tintColor = onPrimaryColor,
|
||||
textColor = onPrimaryColor,
|
||||
onClick = {
|
||||
when (option) {
|
||||
is ViewOption -> {
|
||||
findNavController().navigate(option.destinationID)
|
||||
}
|
||||
|
||||
is ComposeOption -> context.navigate(option.screen)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Footer(onPrimaryColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppBar(onBackgroundColor: Color) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(4.dp, 10.dp, 4.dp, 10.dp)
|
||||
) {
|
||||
ThreeStateIconButton(tint = onBackgroundColor)
|
||||
Text(
|
||||
modifier = Modifier.wrapContentWidth(),
|
||||
text = stringResource(id = R.string.app_name),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = onBackgroundColor,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_cancel),
|
||||
contentDescription = stringResource(id = R.string.action_cancel),
|
||||
modifier = Modifier.clickable {
|
||||
findNavController().navigateUp()
|
||||
},
|
||||
colorFilter = ColorFilter.tint(onBackgroundColor)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Footer(tintColor: Color) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterHorizontally)
|
||||
) {
|
||||
TextButton(onClick = { requireContext().browse(Constants.URL_POLICY) }) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.privacy_policy_title),
|
||||
fontWeight = FontWeight.Light,
|
||||
color = tintColor,
|
||||
fontSize = 12.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Text(text = "•", color = tintColor)
|
||||
TextButton(onClick = { requireContext().browse(URL_TOS) }) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.menu_terms),
|
||||
fontWeight = FontWeight.Light,
|
||||
color = tintColor,
|
||||
fontSize = 12.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountHeader(backgroundColor: Color, onBackgroundColor: Color = Color.White) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(
|
||||
RoundedCornerShape(
|
||||
topStart = 25.dp,
|
||||
topEnd = 25.dp,
|
||||
bottomStart = 2.dp,
|
||||
bottomEnd = 2.dp
|
||||
)
|
||||
)
|
||||
.background(color = backgroundColor)
|
||||
.padding(20.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(15.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(15.dp, Alignment.Start)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(
|
||||
if (viewModel.authProvider.isAnonymous) {
|
||||
R.mipmap.ic_launcher
|
||||
} else {
|
||||
viewModel.authProvider.authData?.userProfile?.artwork?.url
|
||||
}
|
||||
)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = stringResource(id = R.string.title_account_manager),
|
||||
placeholder = painterResource(R.drawable.ic_account),
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.requiredSize(36.dp)
|
||||
.clip(CircleShape)
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
Text(
|
||||
text = if (viewModel.authProvider.isAnonymous) {
|
||||
stringResource(R.string.account_anonymous)
|
||||
} else {
|
||||
viewModel.authProvider.authData?.userProfile?.name
|
||||
?: stringResource(R.string.status_unavailable)
|
||||
},
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = onBackgroundColor,
|
||||
fontSize = 15.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = if (viewModel.authProvider.isAnonymous) {
|
||||
stringResource(R.string.account_anonymous_email)
|
||||
} else {
|
||||
viewModel.authProvider.authData?.userProfile?.email
|
||||
?: stringResource(R.string.status_unavailable)
|
||||
},
|
||||
fontWeight = FontWeight.Light,
|
||||
color = onBackgroundColor,
|
||||
fontSize = 14.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
OutlinedButton(
|
||||
onClick = { requireContext().navigate(Screen.Accounts) },
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
border = BorderStroke(
|
||||
1.dp,
|
||||
Color(
|
||||
requireContext().getStyledAttributeColor(
|
||||
androidx.appcompat.R.attr.colorControlHighlight
|
||||
)
|
||||
)
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.manage_account),
|
||||
color = onBackgroundColor,
|
||||
fontWeight = FontWeight.Normal,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionItem(
|
||||
option: Option,
|
||||
tintColor: Color = Color.Blue,
|
||||
textColor: Color = Color.Black,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onClick() }
|
||||
.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = option.icon),
|
||||
contentDescription = stringResource(id = option.title),
|
||||
colorFilter = ColorFilter.tint(color = tintColor),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 10.dp)
|
||||
.requiredSize(23.dp)
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = option.title),
|
||||
color = textColor,
|
||||
fontSize = 14.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ThreeStateIconButton(tint: Color = Color.White) {
|
||||
var currentState by remember {
|
||||
mutableStateOf(
|
||||
Preferences.getInteger(
|
||||
requireContext(),
|
||||
Preferences.PREFERENCE_THEME_STYLE
|
||||
).let {
|
||||
State.entries.getOrNull(it)
|
||||
} ?: State.Auto
|
||||
)
|
||||
}
|
||||
|
||||
val iconRes = when (currentState) {
|
||||
State.Light -> R.drawable.ic_light
|
||||
State.Dark -> R.drawable.ic_dark
|
||||
else -> R.drawable.ic_auto
|
||||
}
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = iconRes),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.clickable {
|
||||
currentState = when (currentState) {
|
||||
State.Light -> State.Dark
|
||||
State.Dark -> State.Auto
|
||||
State.Auto -> State.Light
|
||||
}
|
||||
|
||||
Preferences.putInteger(
|
||||
requireContext(),
|
||||
Preferences.PREFERENCE_THEME_STYLE,
|
||||
currentState.value
|
||||
)
|
||||
|
||||
setAppTheme(currentState.value)
|
||||
findNavController().navigateUp()
|
||||
},
|
||||
colorFilter = ColorFilter.tint(tint)
|
||||
)
|
||||
}
|
||||
|
||||
enum class State(val value: Int) {
|
||||
Auto(0),
|
||||
Light(1),
|
||||
Dark(2)
|
||||
}
|
||||
|
||||
private fun getOptions(): List<Option> = listOf(
|
||||
ComposeOption(
|
||||
title = R.string.title_apps_games,
|
||||
icon = R.drawable.ic_apps,
|
||||
screen = Screen.Installed
|
||||
),
|
||||
ComposeOption(
|
||||
title = R.string.title_blacklist_manager,
|
||||
icon = R.drawable.ic_blacklist,
|
||||
screen = Screen.Blacklist
|
||||
),
|
||||
ComposeOption(
|
||||
title = R.string.title_favourites_manager,
|
||||
icon = R.drawable.ic_favorite_unchecked,
|
||||
screen = Screen.Favourite
|
||||
),
|
||||
ComposeOption(
|
||||
title = R.string.title_spoof_manager,
|
||||
icon = R.drawable.ic_spoof,
|
||||
screen = Screen.Spoof
|
||||
)
|
||||
)
|
||||
|
||||
private fun getExtraOptions(): List<Option> = listOf(
|
||||
ViewOption(
|
||||
title = R.string.title_settings,
|
||||
icon = R.drawable.ic_menu_settings,
|
||||
destinationID = R.id.settingsFragment
|
||||
),
|
||||
ComposeOption(
|
||||
title = R.string.title_about,
|
||||
icon = R.drawable.ic_menu_about,
|
||||
screen = Screen.About
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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.commons
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.aurora.Constants
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.FragmentTopChartBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TopChartContainerFragment : BaseFragment<FragmentTopChartBinding>() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(chartType: Int): TopChartContainerFragment =
|
||||
TopChartContainerFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(Constants.TOP_CHART_TYPE, chartType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
var chartType = 0
|
||||
val bundle = arguments
|
||||
if (bundle != null) {
|
||||
chartType = bundle.getInt(Constants.TOP_CHART_TYPE, 0)
|
||||
}
|
||||
|
||||
// ViewPager
|
||||
binding.pager.adapter =
|
||||
ViewPagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle, chartType)
|
||||
binding.topTabGroup.setOnCheckedStateChangeListener { _, checkedIds ->
|
||||
when (checkedIds[0]) {
|
||||
R.id.tab_top_free -> binding.pager.setCurrentItem(0, true)
|
||||
R.id.tab_top_grossing -> binding.pager.setCurrentItem(1, true)
|
||||
R.id.tab_trending -> binding.pager.setCurrentItem(2, true)
|
||||
R.id.tab_top_paid -> binding.pager.setCurrentItem(3, true)
|
||||
}
|
||||
}
|
||||
|
||||
binding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
when (position) {
|
||||
0 -> binding.topTabGroup.check(R.id.tab_top_free)
|
||||
1 -> binding.topTabGroup.check(R.id.tab_top_grossing)
|
||||
2 -> binding.topTabGroup.check(R.id.tab_trending)
|
||||
3 -> binding.topTabGroup.check(R.id.tab_top_paid)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
binding.pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
internal class ViewPagerAdapter(
|
||||
fragment: FragmentManager,
|
||||
lifecycle: Lifecycle,
|
||||
chartType: Int
|
||||
) :
|
||||
FragmentStateAdapter(fragment, lifecycle) {
|
||||
private val tabFragments: MutableList<TopChartFragment> = mutableListOf(
|
||||
TopChartFragment.newInstance(chartType, 0),
|
||||
TopChartFragment.newInstance(chartType, 1),
|
||||
TopChartFragment.newInstance(chartType, 2),
|
||||
TopChartFragment.newInstance(chartType, 3)
|
||||
)
|
||||
|
||||
override fun createFragment(position: Int): Fragment = tabFragments[position]
|
||||
|
||||
override fun getItemCount(): Int = tabFragments.size
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* 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.commons
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.aurora.Constants
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Chart
|
||||
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Type
|
||||
import com.aurora.store.TopChartStash
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.data.model.ViewState.Empty.getDataAs
|
||||
import com.aurora.store.databinding.FragmentTopContainerBinding
|
||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
|
||||
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
|
||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
||||
import com.aurora.store.viewmodel.topchart.TopChartViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TopChartFragment : BaseFragment<FragmentTopContainerBinding>() {
|
||||
|
||||
private val viewModel: TopChartViewModel by activityViewModels()
|
||||
|
||||
private var streamCluster: StreamCluster? = StreamCluster()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(chartType: Int, chartCategory: Int): TopChartFragment =
|
||||
TopChartFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(Constants.TOP_CHART_TYPE, chartType)
|
||||
putInt(Constants.TOP_CHART_CATEGORY, chartCategory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
var type = 0
|
||||
var category = 0
|
||||
val bundle = arguments
|
||||
|
||||
if (bundle != null) {
|
||||
type = bundle.getInt(Constants.TOP_CHART_TYPE, 0)
|
||||
category = bundle.getInt(Constants.TOP_CHART_CATEGORY, 0)
|
||||
}
|
||||
|
||||
val chartType = when (type) {
|
||||
1 -> Type.GAME
|
||||
else -> Type.APPLICATION
|
||||
}
|
||||
|
||||
val chartCategory = when (category) {
|
||||
1 -> Chart.TOP_GROSSING
|
||||
2 -> Chart.MOVERS_SHAKERS
|
||||
3 -> Chart.TOP_SELLING_PAID
|
||||
else -> Chart.TOP_SELLING_FREE
|
||||
}
|
||||
|
||||
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
|
||||
override fun onLoadMore(currentPage: Int) {
|
||||
viewModel.nextCluster(chartType, chartCategory)
|
||||
}
|
||||
})
|
||||
|
||||
updateController(null)
|
||||
|
||||
viewModel.getStreamCluster(chartType, chartCategory)
|
||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
is ViewState.Loading, is ViewState.Error -> {
|
||||
updateController(null)
|
||||
}
|
||||
|
||||
is ViewState.Success<*> -> {
|
||||
val stash = it.getDataAs<TopChartStash>()
|
||||
streamCluster = stash[chartType]?.get(chartCategory)
|
||||
|
||||
updateController(streamCluster)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateController(streamCluster: StreamCluster?) {
|
||||
binding.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
if (streamCluster == null) {
|
||||
for (i in 1..6) {
|
||||
add(
|
||||
AppListViewShimmerModel_()
|
||||
.id(i)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
streamCluster.clusterAppList.forEach { app ->
|
||||
add(
|
||||
AppListViewModel_()
|
||||
.id(app.id)
|
||||
.app(app)
|
||||
.click { _ -> openDetailsFragment(app.packageName) }
|
||||
)
|
||||
}
|
||||
|
||||
if (streamCluster.hasNext()) {
|
||||
add(
|
||||
AppProgressViewModel_()
|
||||
.id("progress")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* 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.details
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import coil3.load
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.gplayapi.data.models.details.DevStream
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.databinding.FragmentDevProfileBinding
|
||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.viewmodel.details.DevProfileViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DevProfileFragment :
|
||||
BaseFragment<FragmentDevProfileBinding>(),
|
||||
GenericCarouselController.Callbacks {
|
||||
|
||||
private val args: DevProfileFragmentArgs by navArgs()
|
||||
private val viewModel: DevProfileViewModel by viewModels()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
title =
|
||||
if (args.title.isNullOrBlank()) {
|
||||
getString(R.string.details_dev_profile)
|
||||
} else {
|
||||
args.title
|
||||
}
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
|
||||
// RecyclerView
|
||||
|
||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
is ViewState.Empty -> {
|
||||
}
|
||||
|
||||
is ViewState.Loading -> {
|
||||
}
|
||||
|
||||
is ViewState.Error -> {
|
||||
}
|
||||
|
||||
is ViewState.Status -> {
|
||||
}
|
||||
|
||||
is ViewState.Success<*> -> {
|
||||
(it.data as DevStream).apply {
|
||||
binding.toolbar.title = title
|
||||
binding.txtDevName.text = title
|
||||
binding.txtDevDescription.text = description
|
||||
binding.imgIcon.load(imgUrl)
|
||||
binding.viewFlipper.displayedChild = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.viewFlipper.displayedChild = 1
|
||||
viewModel.getStreamBundle(args.devId)
|
||||
}
|
||||
|
||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||
openStreamBrowseFragment(streamCluster.clusterBrowseUrl, streamCluster.clusterTitle)
|
||||
}
|
||||
|
||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||
viewModel.observeCluster(streamCluster)
|
||||
}
|
||||
|
||||
override fun onAppClick(app: App) {
|
||||
openDetailsFragment(app.packageName)
|
||||
}
|
||||
|
||||
override fun onAppLongClick(app: App) {
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* 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.games
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.store.MobileNavigationDirections
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.databinding.FragmentAppsGamesBinding
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.view.ui.commons.CategoryFragment
|
||||
import com.aurora.store.view.ui.commons.ForYouFragment
|
||||
import com.aurora.store.view.ui.commons.TopChartContainerFragment
|
||||
import com.aurora.store.viewmodel.games.GamesContainerViewModel
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class GamesContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
||||
|
||||
private val viewModel: GamesContainerViewModel by viewModels()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Adjust FAB margins for edgeToEdge display
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.searchFab) { _, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
binding.searchFab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = insets.bottom + resources.getDimensionPixelSize(R.dimen.margin_large)
|
||||
}
|
||||
WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
title = getString(R.string.title_games)
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_download_manager -> {
|
||||
requireContext().navigate(Screen.Downloads)
|
||||
}
|
||||
|
||||
R.id.menu_more -> {
|
||||
findNavController().navigate(
|
||||
MobileNavigationDirections.actionGlobalMoreDialogFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// ViewPager
|
||||
val isForYouEnabled = Preferences.getBoolean(
|
||||
requireContext(),
|
||||
Preferences.PREFERENCE_FOR_YOU
|
||||
)
|
||||
|
||||
binding.pager.adapter = ViewPagerAdapter(
|
||||
childFragmentManager,
|
||||
viewLifecycleOwner.lifecycle,
|
||||
!viewModel.authProvider.isAnonymous,
|
||||
isForYouEnabled
|
||||
)
|
||||
|
||||
binding.pager.isUserInputEnabled = false
|
||||
|
||||
val tabTitles: MutableList<String> = mutableListOf<String>().apply {
|
||||
if (isForYouEnabled) {
|
||||
add(getString(R.string.tab_for_you))
|
||||
}
|
||||
|
||||
add(getString(R.string.tab_top_charts))
|
||||
add(getString(R.string.tab_categories))
|
||||
}
|
||||
|
||||
TabLayoutMediator(
|
||||
binding.tabLayout,
|
||||
binding.pager,
|
||||
true
|
||||
) { tab: TabLayout.Tab, position: Int ->
|
||||
tab.text = tabTitles[position]
|
||||
}.attach()
|
||||
|
||||
binding.searchFab.setOnClickListener {
|
||||
requireContext().navigate(Screen.Search)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
binding.pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
internal class ViewPagerAdapter(
|
||||
fragment: FragmentManager,
|
||||
lifecycle: Lifecycle,
|
||||
private val isGoogleAccount: Boolean,
|
||||
private val isForYouEnabled: Boolean
|
||||
) :
|
||||
FragmentStateAdapter(fragment, lifecycle) {
|
||||
private val tabFragments: MutableList<Fragment> = mutableListOf<Fragment>().apply {
|
||||
if (isForYouEnabled) {
|
||||
add(ForYouFragment.newInstance(1))
|
||||
}
|
||||
|
||||
add(TopChartContainerFragment.newInstance(1))
|
||||
add(CategoryFragment.newInstance(1))
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment = tabFragments[position]
|
||||
|
||||
override fun getItemCount(): Int = tabFragments.size
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.aurora.store.view.ui.preferences
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.aurora.store.data.providers.PermissionProvider
|
||||
import com.aurora.store.view.custom.preference.M3EditTextPreference
|
||||
import com.aurora.store.view.custom.preference.M3ListPreference
|
||||
|
||||
abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
|
||||
|
||||
lateinit var permissionProvider: PermissionProvider
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
permissionProvider = PermissionProvider(this)
|
||||
}
|
||||
|
||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||
when (preference) {
|
||||
is EditTextPreference -> {
|
||||
val dialogFragment = M3EditTextPreference.newInstance(preference.getKey())
|
||||
dialogFragment.setTargetFragment(this, 0)
|
||||
dialogFragment.show(
|
||||
parentFragmentManager,
|
||||
M3EditTextPreference.PREFERENCE_DIALOG_FRAGMENT_TAG
|
||||
)
|
||||
}
|
||||
|
||||
is ListPreference -> {
|
||||
val dialogFragment = M3ListPreference.newInstance(preference.getKey())
|
||||
dialogFragment.setTargetFragment(this, 0)
|
||||
dialogFragment.show(
|
||||
parentFragmentManager,
|
||||
M3ListPreference.PREFERENCE_DIALOG_FRAGMENT_TAG
|
||||
)
|
||||
}
|
||||
|
||||
else -> super.onDisplayPreferenceDialog(preference)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
permissionProvider.unregister()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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.preferences
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences_settings, rootKey)
|
||||
|
||||
findPreference<Preference>("pref_perms")?.setOnPreferenceClickListener {
|
||||
requireContext().navigate(
|
||||
Screen.PermissionRationale(requiredPermissions = PermissionType.entries.toSet())
|
||||
)
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("pref_install")?.setOnPreferenceClickListener {
|
||||
findNavController().navigate(R.id.installationPreference)
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("pref_ui")?.setOnPreferenceClickListener {
|
||||
findNavController().navigate(R.id.UIPreference)
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("pref_network")?.setOnPreferenceClickListener {
|
||||
findNavController().navigate(R.id.networkPreference)
|
||||
true
|
||||
}
|
||||
findPreference<Preference>("pref_updates")?.setOnPreferenceClickListener {
|
||||
findNavController().navigate(R.id.updatesPreference)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
view.findViewById<Toolbar>(R.id.toolbar)?.apply {
|
||||
title = getString(R.string.title_settings)
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.preferences
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.net.toUri
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.store.R
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Locale
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UIPreference : BasePreferenceFragment() {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences_ui, rootKey)
|
||||
|
||||
findPreference<Preference>("PREFERENCE_APP_LANGUAGE")?.apply {
|
||||
if (isTAndAbove) {
|
||||
summary = Locale.getDefault().displayName
|
||||
setOnPreferenceClickListener {
|
||||
startActivity(
|
||||
Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply {
|
||||
data = ("package:" + requireContext().packageName).toUri()
|
||||
}
|
||||
)
|
||||
true
|
||||
}
|
||||
} else {
|
||||
isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
view.findViewById<Toolbar>(R.id.toolbar)?.apply {
|
||||
title = getString(R.string.pref_ui_title)
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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.preferences.installation
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.extensions.showDialog
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLATION_DEVICE_OWNER
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class InstallationPreference : PreferenceFragmentCompat() {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences_installation, rootKey)
|
||||
|
||||
findPreference<Preference>(PREFERENCE_INSTALLER_ID)?.apply {
|
||||
setOnPreferenceClickListener {
|
||||
requireContext().navigate(Screen.Installer)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<Preference>(PREFERENCE_INSTALLATION_DEVICE_OWNER)?.apply {
|
||||
val packageName = context.packageName
|
||||
val devicePolicyManager = context.getSystemService<DevicePolicyManager>()
|
||||
|
||||
isVisible = devicePolicyManager?.isDeviceOwnerApp(packageName) ?: false
|
||||
setOnPreferenceClickListener {
|
||||
context.showDialog(
|
||||
context.getString(R.string.pref_clear_device_owner_title),
|
||||
context.getString(R.string.pref_clear_device_owner_desc),
|
||||
{ _: DialogInterface, _: Int ->
|
||||
@Suppress("DEPRECATION")
|
||||
devicePolicyManager!!.clearDeviceOwnerApp(packageName)
|
||||
activity?.recreate()
|
||||
},
|
||||
{ dialog: DialogInterface, _: Int -> dialog.dismiss() }
|
||||
)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
view.findViewById<Toolbar>(R.id.toolbar)?.apply {
|
||||
title = getString(R.string.title_installation)
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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.preferences.network
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.extensions.runOnUiThread
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_MICROG_AUTH
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_URL
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
|
||||
import com.aurora.store.util.save
|
||||
import com.aurora.store.view.ui.preferences.BasePreferenceFragment
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class NetworkPreference : BasePreferenceFragment() {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences_network, rootKey)
|
||||
|
||||
findPreference<Preference>(Preferences.PREFERENCE_DISPENSER_URLS)?.apply {
|
||||
setOnPreferenceClickListener {
|
||||
requireContext().navigate(Screen.Dispenser)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<Preference>(PREFERENCE_PROXY_URL)?.setOnPreferenceClickListener { _ ->
|
||||
findNavController().navigate(R.id.proxyURLDialog)
|
||||
false
|
||||
}
|
||||
|
||||
findPreference<Preference>(PREFERENCE_VENDING_VERSION)?.let {
|
||||
it.setOnPreferenceChangeListener { _, newValue ->
|
||||
save(PREFERENCE_VENDING_VERSION, Integer.parseInt(newValue.toString()))
|
||||
runOnUiThread {
|
||||
requireContext().toast(R.string.insecure_anonymous_apply)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<SwitchPreferenceCompat>(PREFERENCE_MICROG_AUTH)?.isVisible =
|
||||
PackageUtil.hasSupportedMicroGVariant(requireContext())
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
view.findViewById<Toolbar>(R.id.toolbar)?.apply {
|
||||
title = getString(R.string.pref_network_title)
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.aurora.store.view.ui.preferences.network
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.extensions.showKeyboard
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.util.CommonUtil
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_INFO
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_URL
|
||||
import com.aurora.store.util.remove
|
||||
import com.aurora.store.util.save
|
||||
import com.aurora.store.viewmodel.preferences.ProxyURLViewModel
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ProxyURLDialog : DialogFragment() {
|
||||
|
||||
private val viewModel: ProxyURLViewModel by viewModels()
|
||||
|
||||
private val currentProxyUrl: String
|
||||
get() = Preferences.getString(requireContext(), PREFERENCE_PROXY_URL)
|
||||
private val textInputLayout: TextInputLayout?
|
||||
get() = dialog?.findViewById(R.id.textInputLayout)
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val view = layoutInflater.inflate(R.layout.dialog_text_input_edit_text, null)
|
||||
val alertDialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.pref_network_proxy_url)
|
||||
.setMessage(R.string.pref_network_proxy_url_message)
|
||||
.setView(view)
|
||||
.setPositiveButton(getString(R.string.set), null)
|
||||
.setNeutralButton(getString(R.string.disable), null)
|
||||
.setNegativeButton(getString(android.R.string.cancel)) { _, _ -> dialog?.dismiss() }
|
||||
.create()
|
||||
|
||||
alertDialog.setOnShowListener {
|
||||
val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).apply {
|
||||
isEnabled = currentProxyUrl.isNotBlank()
|
||||
setOnClickListener { saveProxyUrl() }
|
||||
}
|
||||
textInputLayout?.editText?.doOnTextChanged { text, _, _, _ ->
|
||||
positiveButton.isEnabled = !text.isNullOrBlank()
|
||||
}
|
||||
|
||||
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).apply {
|
||||
isEnabled = currentProxyUrl.isNotBlank()
|
||||
setOnClickListener { deleteProxyUrl() }
|
||||
}
|
||||
}
|
||||
return alertDialog
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
textInputLayout?.editText?.apply {
|
||||
setText(currentProxyUrl)
|
||||
showKeyboard()
|
||||
}
|
||||
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
}
|
||||
|
||||
private fun saveProxyUrl() {
|
||||
val url = textInputLayout?.editText?.text?.toString()?.trim()
|
||||
if (url.isNullOrEmpty()) {
|
||||
toast(R.string.toast_proxy_invalid)
|
||||
return
|
||||
}
|
||||
|
||||
val proxyInfo = CommonUtil.parseProxyUrl(url)
|
||||
if (proxyInfo != null) {
|
||||
save(PREFERENCE_PROXY_URL, url)
|
||||
save(PREFERENCE_PROXY_INFO, viewModel.json.encodeToString(proxyInfo))
|
||||
toast(R.string.toast_proxy_success)
|
||||
findNavController().navigate(R.id.forceRestartDialog)
|
||||
return
|
||||
} else {
|
||||
toast(R.string.toast_proxy_failed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteProxyUrl() {
|
||||
remove(PREFERENCE_PROXY_URL)
|
||||
remove(PREFERENCE_PROXY_INFO)
|
||||
toast(R.string.toast_proxy_disabled)
|
||||
findNavController().navigate(R.id.forceRestartDialog)
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* 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.preferences.updates
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SeekBarPreference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.model.UpdateMode
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isGranted
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS
|
||||
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_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.save
|
||||
import com.aurora.store.view.ui.preferences.BasePreferenceFragment
|
||||
import com.aurora.store.viewmodel.all.UpdatesViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UpdatesPreference : BasePreferenceFragment() {
|
||||
|
||||
private val viewModel: UpdatesViewModel by viewModels()
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences_updates, rootKey)
|
||||
|
||||
val updatesEnabled = Preferences.getInteger(requireContext(), PREFERENCE_UPDATES_AUTO) != 0
|
||||
|
||||
findPreference<ListPreference>(PREFERENCE_UPDATES_AUTO)?.setOnPreferenceChangeListener {
|
||||
_,
|
||||
newValue
|
||||
->
|
||||
when (UpdateMode.entries[newValue.toString().toInt()]) {
|
||||
UpdateMode.DISABLED -> {
|
||||
handleAutoUpdateDependentPrefs(false)
|
||||
viewModel.updateHelper.cancelAutomatedCheck()
|
||||
requireContext().save(PREFERENCE_UPDATES_AUTO, 0)
|
||||
true
|
||||
}
|
||||
|
||||
UpdateMode.CHECK_AND_NOTIFY -> {
|
||||
if (isGranted(requireContext(), PermissionType.POST_NOTIFICATIONS)) {
|
||||
handleAutoUpdateDependentPrefs(true)
|
||||
viewModel.updateHelper.scheduleAutomatedCheck()
|
||||
true
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.POST_NOTIFICATIONS) {
|
||||
if (it) {
|
||||
handleAutoUpdateDependentPrefs(true)
|
||||
requireContext().save(PREFERENCE_UPDATES_AUTO, 1)
|
||||
viewModel.updateHelper.scheduleAutomatedCheck()
|
||||
activity?.recreate()
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
UpdateMode.CHECK_AND_INSTALL -> {
|
||||
if (isGranted(requireContext(), PermissionType.DOZE_WHITELIST)) {
|
||||
handleAutoUpdateDependentPrefs(true)
|
||||
viewModel.updateHelper.scheduleAutomatedCheck()
|
||||
true
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.DOZE_WHITELIST) {
|
||||
if (it) {
|
||||
handleAutoUpdateDependentPrefs(true)
|
||||
requireContext().save(PREFERENCE_UPDATES_AUTO, 2)
|
||||
viewModel.updateHelper.scheduleAutomatedCheck()
|
||||
activity?.recreate()
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
handleAutoUpdateDependentPrefs(updatesEnabled)
|
||||
|
||||
findPreference<SwitchPreferenceCompat>(PREFERENCE_FILTER_AURORA_ONLY)
|
||||
?.setOnPreferenceChangeListener { _, newValue ->
|
||||
findPreference<SwitchPreferenceCompat>(PREFERENCE_FILTER_FDROID)?.isEnabled =
|
||||
!newValue.toString().toBoolean()
|
||||
viewModel.updateHelper.checkUpdatesNow()
|
||||
true
|
||||
}
|
||||
|
||||
findPreference<SwitchPreferenceCompat>(PREFERENCE_FILTER_FDROID)?.apply {
|
||||
isEnabled = !Preferences.getBoolean(requireContext(), PREFERENCE_FILTER_AURORA_ONLY)
|
||||
setOnPreferenceChangeListener { _, _ ->
|
||||
viewModel.updateHelper.checkUpdatesNow()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<SwitchPreferenceCompat>(PREFERENCE_UPDATES_EXTENDED)
|
||||
?.setOnPreferenceChangeListener { _, _ ->
|
||||
viewModel.updateHelper.checkUpdatesNow()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
view.findViewById<Toolbar>(R.id.toolbar)?.apply {
|
||||
title = getString(R.string.title_updates)
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAutoUpdateDependentPrefs(enabled: Boolean) {
|
||||
findPreference<Preference>(PREFERENCES_UPDATES_RESTRICTIONS)?.apply {
|
||||
isEnabled = enabled
|
||||
isVisible = enabled
|
||||
setOnPreferenceClickListener {
|
||||
findNavController().navigate(R.id.updatesRestrictionsDialog)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<SeekBarPreference>(PREFERENCE_UPDATES_CHECK_INTERVAL)?.apply {
|
||||
isEnabled = enabled
|
||||
isVisible = enabled
|
||||
setOnPreferenceChangeListener { _, _ ->
|
||||
viewModel.updateHelper.updateAutomatedCheck()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.ui.preferences.updates
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_BATTERY
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_IDLE
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_METERED
|
||||
import com.aurora.store.viewmodel.preferences.UpdatesRestrictionsViewModel
|
||||
import com.google.android.material.checkbox.MaterialCheckBox
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UpdatesRestrictionsDialog : DialogFragment() {
|
||||
|
||||
private val viewModel: UpdatesRestrictionsViewModel by viewModels()
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val view = layoutInflater.inflate(R.layout.dialog_auto_updates_restrictions, null)
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.pref_updates_restrictions_title)
|
||||
.setMessage(R.string.pref_updates_restrictions_desc)
|
||||
.setView(view)
|
||||
.setPositiveButton(getString(android.R.string.ok)) { _, _ -> dialog?.dismiss() }
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
context?.let { setupRestrictions(it) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
viewModel.updateHelper.updateAutomatedCheck()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupRestrictions(context: Context) {
|
||||
dialog?.findViewById<MaterialCheckBox>(R.id.checkboxMetered)?.apply {
|
||||
isChecked =
|
||||
Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_METERED, true)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.putBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_METERED, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
dialog?.findViewById<MaterialCheckBox>(R.id.checkboxIdle)?.apply {
|
||||
isChecked = Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_IDLE, true)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.putBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_IDLE, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
dialog?.findViewById<MaterialCheckBox>(R.id.checkboxBattery)?.apply {
|
||||
isChecked =
|
||||
Preferences.getBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_BATTERY, true)
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.putBoolean(context, PREFERENCES_UPDATES_RESTRICTIONS_BATTERY, isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
* Copyright (C) 2022, The Calyx Institute
|
||||
*
|
||||
* 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.sheets
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.aurora.extensions.openInfo
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.event.BusEvent
|
||||
import com.aurora.store.data.installer.AppInstaller
|
||||
import com.aurora.store.databinding.SheetAppMenuBinding
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.viewmodel.sheets.AppMenuViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AppMenuSheet : BaseDialogSheet<SheetAppMenuBinding>() {
|
||||
|
||||
companion object {
|
||||
const val TAG = "APP_MENU_SHEET"
|
||||
}
|
||||
|
||||
private val viewModel: AppMenuViewModel by viewModels()
|
||||
private val args: AppMenuSheetArgs by navArgs()
|
||||
|
||||
private val exportMimeType = "application/zip"
|
||||
|
||||
private val requestDocumentCreation =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument(exportMimeType)) {
|
||||
if (it != null) {
|
||||
viewModel.copyInstalledApp(requireContext(), args.app, it)
|
||||
} else {
|
||||
toast(R.string.failed_apk_export)
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val isBlacklisted: Boolean = viewModel.blacklistProvider.isBlacklisted(args.app.packageName)
|
||||
|
||||
with(binding.navigationView) {
|
||||
// Switch strings for Add/Remove Blacklist
|
||||
val blackListMenu: MenuItem = menu.findItem(R.id.action_blacklist)
|
||||
blackListMenu.setTitle(
|
||||
if (isBlacklisted) {
|
||||
R.string.action_whitelist
|
||||
} else {
|
||||
R.string.action_blacklist_add
|
||||
}
|
||||
)
|
||||
|
||||
// Show/Hide actions based on installed status
|
||||
val installed = PackageUtil.isInstalled(requireContext(), args.app.packageName)
|
||||
menu.findItem(R.id.action_uninstall).isVisible = installed
|
||||
menu.findItem(R.id.action_local).isVisible = installed
|
||||
|
||||
setNavigationItemSelectedListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.action_blacklist -> {
|
||||
if (isBlacklisted) {
|
||||
viewModel.blacklistProvider.whitelist(args.app.packageName)
|
||||
requireContext().toast(R.string.toast_apk_whitelisted)
|
||||
} else {
|
||||
viewModel.blacklistProvider.blacklist(args.app.packageName)
|
||||
requireContext().toast(R.string.toast_apk_blacklisted)
|
||||
}
|
||||
|
||||
dismissAllowingStateLoss()
|
||||
AuroraApp.events.send(
|
||||
BusEvent.Blacklisted(args.app.packageName)
|
||||
)
|
||||
}
|
||||
|
||||
R.id.action_local -> {
|
||||
requestDocumentCreation.launch("${args.app.packageName}.zip")
|
||||
}
|
||||
|
||||
R.id.action_uninstall -> {
|
||||
AppInstaller.uninstall(requireContext(), args.app.packageName)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
R.id.action_info -> {
|
||||
requireContext().openInfo(args.app.packageName)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
* Copyright (C) 2022, The Calyx Institute
|
||||
*
|
||||
* 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.sheets
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.aurora.store.data.providers.PermissionProvider
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import java.lang.reflect.ParameterizedType
|
||||
|
||||
abstract class BaseDialogSheet<ViewBindingType : ViewBinding> : BottomSheetDialogFragment() {
|
||||
|
||||
private var _binding: ViewBindingType? = null
|
||||
protected val binding get() = _binding!!
|
||||
|
||||
lateinit var permissionProvider: PermissionProvider
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
permissionProvider = PermissionProvider(this)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val type = (javaClass.genericSuperclass as ParameterizedType)
|
||||
.actualTypeArguments[0] as Class<ViewBindingType>
|
||||
|
||||
val method = type.getMethod(
|
||||
"inflate",
|
||||
LayoutInflater::class.java,
|
||||
ViewGroup::class.java,
|
||||
Boolean::class.java
|
||||
)
|
||||
|
||||
_binding = method.invoke(null, inflater, container, false) as ViewBindingType
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
permissionProvider.unregister()
|
||||
_binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.sheets
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import coil3.load
|
||||
import coil3.request.transformations
|
||||
import coil3.transform.CircleCropTransformation
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.SheetDeviceMiuiBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DeviceMiuiSheet : BaseDialogSheet<SheetDeviceMiuiBinding>() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.imgIcon.load(R.drawable.ic_xiaomi_logo) {
|
||||
transformations(CircleCropTransformation())
|
||||
}
|
||||
|
||||
binding.btnPrimary.setOnClickListener {
|
||||
try {
|
||||
startActivity(Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS))
|
||||
} catch (_: Exception) {
|
||||
toast(R.string.toast_developer_setting_failed)
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnSecondary.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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.sheets
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import com.aurora.extensions.TAG
|
||||
import com.aurora.extensions.isQAndAbove
|
||||
import com.aurora.store.databinding.SheetNetworkBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class NetworkDialogSheet : BaseDialogSheet<SheetNetworkBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
const val TAG = "NetworkDialogSheet"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): NetworkDialogSheet = NetworkDialogSheet().apply {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.btnAction.setOnClickListener {
|
||||
if (isQAndAbove) {
|
||||
startActivity(Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY))
|
||||
} else {
|
||||
try {
|
||||
startActivity(Intent(Settings.ACTION_WIRELESS_SETTINGS))
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
Log.i(TAG, "Unable to launch wireless settings")
|
||||
startActivity(Intent(Settings.ACTION_SETTINGS))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
package com.aurora.store.view.ui.splash
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
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.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.Constants.PACKAGE_NAME_PLAY_STORE
|
||||
import com.aurora.extensions.TAG
|
||||
import com.aurora.extensions.getPackageName
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.gplayapi.helpers.AuthHelper
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.data.model.AuthState
|
||||
import com.aurora.store.databinding.FragmentSplashBinding
|
||||
import com.aurora.store.util.CertUtil.GOOGLE_ACCOUNT_TYPE
|
||||
import com.aurora.store.util.CertUtil.GOOGLE_PLAY_AUTH_TOKEN_TYPE
|
||||
import com.aurora.store.util.CertUtil.GOOGLE_PLAY_CERT
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
|
||||
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 kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseFlavouredSplashFragment : BaseFragment<FragmentSplashBinding>() {
|
||||
|
||||
val viewModel: AuthViewModel by activityViewModels()
|
||||
|
||||
val canLoginWithMicroG: Boolean
|
||||
get() = PackageUtil.hasSupportedMicroGVariant(requireContext()) &&
|
||||
Preferences.getBoolean(requireContext(), PREFERENCE_MICROG_AUTH, true)
|
||||
|
||||
val startForAccount =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val accountName = it.data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)
|
||||
if (!accountName.isNullOrBlank()) {
|
||||
requestAuthTokenForGoogle(accountName)
|
||||
} else {
|
||||
resetActions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
viewBindingType = FragmentSplashBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
if (!Preferences.getBoolean(requireContext(), PREFERENCE_INTRO)) {
|
||||
requireContext().navigate(Screen.Onboarding)
|
||||
activity?.finish()
|
||||
return
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_blacklist_manager -> {
|
||||
requireContext().navigate(Screen.Blacklist)
|
||||
}
|
||||
|
||||
R.id.menu_spoof_manager -> requireContext().navigate(Screen.Spoof)
|
||||
|
||||
R.id.menu_settings -> {
|
||||
findNavController().navigate(R.id.settingsFragment)
|
||||
}
|
||||
|
||||
R.id.menu_about -> requireContext().navigate(Screen.About)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
attachActions()
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.authState.collectLatest {
|
||||
when (it) {
|
||||
AuthState.Init -> updateStatus(getString(R.string.session_init))
|
||||
|
||||
AuthState.Fetching -> {
|
||||
updateStatus(getString(R.string.requesting_new_session))
|
||||
}
|
||||
|
||||
AuthState.Valid -> {
|
||||
val packageName =
|
||||
requireActivity().intent.getPackageName(requireArguments())
|
||||
if (packageName.isNullOrBlank()) {
|
||||
navigateToDefaultTab()
|
||||
} else {
|
||||
requireArguments().remove("packageName")
|
||||
openDetailsFragment(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
AuthState.Available -> {
|
||||
updateStatus(getString(R.string.session_verifying))
|
||||
updateActionLayout(false)
|
||||
}
|
||||
|
||||
AuthState.Unavailable -> {
|
||||
updateStatus(getString(R.string.session_login))
|
||||
updateActionLayout(true)
|
||||
}
|
||||
|
||||
AuthState.SignedIn -> {
|
||||
val packageName =
|
||||
requireActivity().intent.getPackageName(requireArguments())
|
||||
if (packageName.isNullOrBlank()) {
|
||||
navigateToDefaultTab()
|
||||
} else {
|
||||
requireArguments().remove("packageName")
|
||||
openDetailsFragment(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
AuthState.SignedOut -> {
|
||||
updateStatus(getString(R.string.session_scrapped))
|
||||
updateActionLayout(true)
|
||||
}
|
||||
|
||||
AuthState.Verifying -> {
|
||||
updateStatus(getString(R.string.verifying_new_session))
|
||||
}
|
||||
|
||||
is AuthState.PendingAccountManager -> {
|
||||
requestAuthTokenForGoogle(it.email, it.token)
|
||||
}
|
||||
|
||||
is AuthState.Failed -> {
|
||||
updateStatus(it.status)
|
||||
updateActionLayout(true)
|
||||
resetActions()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateStatus(string: String?) {
|
||||
activity?.runOnUiThread { binding.txtStatus.text = string }
|
||||
}
|
||||
|
||||
private fun updateActionLayout(isVisible: Boolean) {
|
||||
binding.layoutAction.isVisible = isVisible
|
||||
binding.toolbar.isVisible = isVisible
|
||||
}
|
||||
|
||||
private fun navigateToDefaultTab() {
|
||||
val defaultDestination =
|
||||
Preferences.getInteger(requireContext(), PREFERENCE_DEFAULT_SELECTED_TAB)
|
||||
val directions =
|
||||
when (requireArguments().getInt("destinationId", defaultDestination)) {
|
||||
R.id.updatesFragment -> {
|
||||
requireArguments().remove("destinationId")
|
||||
SplashFragmentDirections.actionSplashFragmentToUpdatesFragment()
|
||||
}
|
||||
|
||||
1 -> SplashFragmentDirections.actionSplashFragmentToGamesContainerFragment()
|
||||
|
||||
2 -> SplashFragmentDirections.actionSplashFragmentToUpdatesFragment()
|
||||
|
||||
else -> SplashFragmentDirections.actionSplashFragmentToNavigationApps()
|
||||
}
|
||||
requireActivity().viewModelStore.clear() // Clear ViewModelStore to avoid bugs with logout
|
||||
findNavController().navigate(directions)
|
||||
}
|
||||
|
||||
private fun requestAuthTokenForGoogle(accountName: String, oldToken: String? = null) {
|
||||
try {
|
||||
if (oldToken != null) {
|
||||
// Invalidate the old token before requesting a new one
|
||||
AccountManager.get(requireContext()).invalidateAuthToken(
|
||||
GOOGLE_ACCOUNT_TYPE,
|
||||
oldToken
|
||||
)
|
||||
}
|
||||
|
||||
AccountManager.get(requireContext())
|
||||
.getAuthToken(
|
||||
Account(accountName, GOOGLE_ACCOUNT_TYPE),
|
||||
GOOGLE_PLAY_AUTH_TOKEN_TYPE,
|
||||
Bundle().apply {
|
||||
putString("overridePackage", PACKAGE_NAME_PLAY_STORE)
|
||||
putByteArray(
|
||||
"overrideCertificate",
|
||||
Base64.decode(GOOGLE_PLAY_CERT, Base64.DEFAULT)
|
||||
)
|
||||
},
|
||||
requireActivity(),
|
||||
{
|
||||
viewModel.buildGoogleAuthData(
|
||||
accountName,
|
||||
it.result.getString(AccountManager.KEY_AUTHTOKEN)!!,
|
||||
AuthHelper.Token.AUTH
|
||||
)
|
||||
},
|
||||
Handler(Looper.getMainLooper())
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* 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.updates
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.Constants
|
||||
import com.aurora.extensions.browse
|
||||
import com.aurora.extensions.navigate
|
||||
import com.aurora.extensions.requiresObbDir
|
||||
import com.aurora.store.MobileNavigationDirections
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.data.model.MinimalApp
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isGranted
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.data.room.update.Update
|
||||
import com.aurora.store.databinding.FragmentUpdatesBinding
|
||||
import com.aurora.store.view.epoxy.views.UpdateHeaderViewModel_
|
||||
import com.aurora.store.view.epoxy.views.app.AppUpdateViewModel_
|
||||
import com.aurora.store.view.epoxy.views.app.NoAppViewModel_
|
||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.viewmodel.all.UpdatesViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AndroidEntryPoint
|
||||
class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||
|
||||
private val viewModel: UpdatesViewModel by viewModels()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Adjust FAB margins for edgeToEdge display
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.searchFab) { _, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
binding.searchFab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = insets.bottom + resources.getDimensionPixelSize(R.dimen.margin_large)
|
||||
}
|
||||
WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_download_manager -> {
|
||||
requireContext().navigate(Screen.Downloads)
|
||||
}
|
||||
|
||||
R.id.menu_more -> {
|
||||
findNavController().navigate(
|
||||
MobileNavigationDirections.actionGlobalMoreDialogFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.updates
|
||||
.combine(viewModel.downloadsList) { uList, dList ->
|
||||
uList?.associateWith { a ->
|
||||
dList.find {
|
||||
it.packageName == a.packageName && it.versionCode == a.versionCode
|
||||
}
|
||||
}
|
||||
}.collectLatest { map ->
|
||||
updateController(map)
|
||||
viewModel.updateAllEnqueued = map?.values?.all {
|
||||
it?.isRunning == true
|
||||
} ?: false
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.fetchingUpdates.collect {
|
||||
binding.swipeRefreshLayout.isRefreshing = it
|
||||
if (it && viewModel.updates.value.isNullOrEmpty()) {
|
||||
updateController(emptyMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
viewModel.fetchUpdates()
|
||||
}
|
||||
|
||||
binding.searchFab.setOnClickListener {
|
||||
requireContext().navigate(Screen.Search)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateController(appList: Map<Update, Download?>?) {
|
||||
binding.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
if (appList == null) {
|
||||
for (i in 1..10) {
|
||||
add(
|
||||
AppListViewShimmerModel_()
|
||||
.id(i)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (appList.isEmpty()) {
|
||||
add(
|
||||
NoAppViewModel_()
|
||||
.id("no_update")
|
||||
.icon(R.drawable.ic_updates)
|
||||
.message(R.string.details_no_updates)
|
||||
.showAction(true)
|
||||
.actionMessage(R.string.check_updates)
|
||||
.actionCallback { _ -> viewModel.fetchUpdates() }
|
||||
)
|
||||
} else {
|
||||
add(
|
||||
UpdateHeaderViewModel_()
|
||||
.id("header_all")
|
||||
.title(
|
||||
"${appList.size} " +
|
||||
if (appList.size == 1) {
|
||||
getString(R.string.update_available)
|
||||
} else {
|
||||
getString(R.string.updates_available)
|
||||
}
|
||||
)
|
||||
.action(
|
||||
if (viewModel.updateAllEnqueued) {
|
||||
getString(R.string.action_cancel)
|
||||
} else {
|
||||
getString(R.string.action_update_all)
|
||||
}
|
||||
)
|
||||
.click { _ ->
|
||||
if (viewModel.updateAllEnqueued) {
|
||||
cancelAll()
|
||||
} else {
|
||||
updateAll()
|
||||
}
|
||||
requestModelBuild()
|
||||
}
|
||||
)
|
||||
|
||||
for ((update, download) in appList) {
|
||||
add(
|
||||
AppUpdateViewModel_()
|
||||
.id(update.packageName)
|
||||
.update(update)
|
||||
.download(download)
|
||||
.click { _ ->
|
||||
if (update.packageName == requireContext().packageName) {
|
||||
requireContext().browse(Constants.GITLAB_URL)
|
||||
} else {
|
||||
openDetailsFragment(update.packageName)
|
||||
}
|
||||
}
|
||||
.longClick { _ ->
|
||||
openAppMenuSheet(MinimalApp.fromUpdate(update))
|
||||
false
|
||||
}
|
||||
.positiveAction { _ -> updateSingle(update) }
|
||||
.negativeAction { _ -> cancelSingle(update) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSingle(update: Update) {
|
||||
if (update.fileList.requiresObbDir()) {
|
||||
if (isGranted(requireContext(), PermissionType.STORAGE_MANAGER)) {
|
||||
viewModel.download(update)
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
|
||||
if (it) viewModel.download(update)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viewModel.download(update)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAll() {
|
||||
viewModel.updateAllEnqueued = true
|
||||
if (viewModel.updates.value?.any { it.fileList.requiresObbDir() } == true) {
|
||||
if (isGranted(requireContext(), PermissionType.STORAGE_MANAGER)) {
|
||||
viewModel.downloadAll()
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
|
||||
if (it) viewModel.downloadAll()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viewModel.downloadAll()
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelSingle(update: Update) {
|
||||
viewModel.cancelDownload(update.packageName)
|
||||
}
|
||||
|
||||
private fun cancelAll() {
|
||||
viewModel.cancelAll()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user