compose: preferences: Migrate installer logic to compose

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2025-12-15 17:25:15 +08:00
parent b308535a8c
commit fc656d4d43
12 changed files with 329 additions and 420 deletions

View File

@@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.composable
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.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.aurora.store.R
import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.data.installer.SessionInstaller
import com.aurora.store.data.model.InstallerInfo
/**
* Composable to display installer details in a list
* @param modifier The modifier to be applied to the composable
* @param installerInfo A [InstallerInfo] object to display details
* @param isSelected Whether this installer is selected
* @param onClick Callback when this composable is clicked
*/
@Composable
fun InstallerListItem(
modifier: Modifier = Modifier,
installerInfo: InstallerInfo,
isSelected: Boolean = false,
onClick: () -> Unit = {}
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(dimensionResource(R.dimen.padding_small)),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1F)) {
Text(
text = stringResource(installerInfo.title),
style = MaterialTheme.typography.bodyLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(installerInfo.subtitle),
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(installerInfo.description),
style = MaterialTheme.typography.bodySmall
)
}
RadioButton(selected = isSelected, onClick = onClick)
}
}
@Preview(showBackground = true)
@Composable
private fun InstallerListItemPreview() {
PreviewTemplate {
InstallerListItem(installerInfo = SessionInstaller.installerInfo, isSelected = true)
}
}

View File

@@ -28,6 +28,7 @@ import com.aurora.store.compose.ui.dispenser.DispenserScreen
import com.aurora.store.compose.ui.downloads.DownloadsScreen
import com.aurora.store.compose.ui.favourite.FavouriteScreen
import com.aurora.store.compose.ui.onboarding.OnboardingScreen
import com.aurora.store.compose.ui.preferences.installation.InstallerScreen
import com.aurora.store.compose.ui.search.SearchScreen
import com.aurora.store.compose.ui.spoof.SpoofScreen
@@ -140,6 +141,10 @@ fun NavDisplay(startDestination: NavKey) {
entry<Screen.Dispenser> {
DispenserScreen(onNavigateUp = ::onNavigateUp)
}
entry<Screen.Installer> {
InstallerScreen(onNavigateUp = ::onNavigateUp)
}
}
)
}

View File

@@ -57,4 +57,7 @@ sealed class Screen : NavKey, Parcelable {
@Serializable
data object Dispenser : Screen()
@Serializable
data object Installer : Screen()
}

View File

@@ -0,0 +1,102 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.ui.preferences.installation
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.store.R
import com.aurora.store.compose.composable.InstallerListItem
import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.installer.SessionInstaller
import com.aurora.store.data.model.Installer
import com.aurora.store.data.model.InstallerInfo
import com.aurora.store.viewmodel.preferences.InstallerViewModel
@Composable
fun InstallerScreen(onNavigateUp: () -> Unit, viewModel: InstallerViewModel = hiltViewModel()) {
val currentInstallerId by viewModel.currentInstaller.collectAsStateWithLifecycle()
val snackBarHostState = remember { SnackbarHostState() }
LaunchedEffect(key1 = Unit) {
viewModel.error.collect { error ->
snackBarHostState.showSnackbar(message = error)
}
}
ScreenContent(
onNavigateUp = onNavigateUp,
snackBarHostState = snackBarHostState,
currentInstaller = Installer.entries[currentInstallerId],
availableInstallers = AppInstaller.getAvailableInstallersInfo(LocalContext.current),
onInstallerSelected = { installer -> viewModel.save(installer) }
)
}
@Composable
private fun ScreenContent(
onNavigateUp: () -> Unit = {},
snackBarHostState: SnackbarHostState = SnackbarHostState(),
currentInstaller: Installer = Installer.SESSION,
availableInstallers: List<InstallerInfo> = emptyList(),
onInstallerSelected: (installer: Installer) -> Unit = {}
) {
val snackBarHostState = remember { snackBarHostState }
Scaffold(
snackbarHost = {
SnackbarHost(hostState = snackBarHostState)
},
topBar = {
TopAppBar(
title = stringResource(R.string.pref_install_mode_title),
onNavigateUp = onNavigateUp
)
}
) { paddingValues ->
LazyColumn(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium))
) {
items(items = availableInstallers, key = { i -> i.id }) { installerInfo ->
InstallerListItem(
installerInfo = installerInfo,
isSelected = installerInfo.installer == currentInstaller,
onClick = { onInstallerSelected(installerInfo.installer) }
)
}
}
}
}
@Preview
@Composable
private fun InstallerScreenPreview() {
PreviewTemplate {
ScreenContent(
availableInstallers = listOf(SessionInstaller.installerInfo)
)
}
}

View File

@@ -1,64 +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.epoxy.views.preference
import android.content.Context
import android.util.AttributeSet
import android.widget.CompoundButton
import com.airbnb.epoxy.CallbackProp
import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView
import com.aurora.store.data.model.InstallerInfo
import com.aurora.store.databinding.ViewInstallerBinding
import com.aurora.store.view.epoxy.views.BaseModel
import com.aurora.store.view.epoxy.views.BaseView
@ModelView(
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseModel::class
)
class InstallerView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : BaseView<ViewInstallerBinding>(context, attrs, defStyleAttr) {
@ModelProp
fun installer(installer: InstallerInfo) {
binding.line1.text = context.getString(installer.title)
binding.line2.text = context.getString(installer.subtitle)
binding.line3.text = context.getString(installer.description)
}
@ModelProp
fun markChecked(isChecked: Boolean) {
binding.radiobutton.isChecked = isChecked
}
@CallbackProp
fun checked(onCheckedChangeListener: CompoundButton.OnCheckedChangeListener?) {
binding.radiobutton.setOnCheckedChangeListener(onCheckedChangeListener)
}
@CallbackProp
fun click(onClickListener: OnClickListener?) {
binding.root.setOnClickListener(onClickListener)
}
}

View File

@@ -28,8 +28,10 @@ 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
@@ -42,7 +44,7 @@ class InstallationPreference : PreferenceFragmentCompat() {
findPreference<Preference>(PREFERENCE_INSTALLER_ID)?.apply {
setOnPreferenceClickListener {
findNavController().navigate(R.id.installerFragment)
requireContext().navigate(Screen.Installer)
true
}
}

View File

@@ -1,192 +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.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.navigation.fragment.findNavController
import com.aurora.extensions.isMIUI
import com.aurora.extensions.isMiuiOptimizationDisabled
import com.aurora.extensions.isOAndAbove
import com.aurora.extensions.showDialog
import com.aurora.store.R
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.databinding.FragmentInstallerBinding
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.save
import com.aurora.store.view.epoxy.views.preference.InstallerViewModel_
import com.aurora.store.view.ui.commons.BaseFragment
import dagger.hilt.android.AndroidEntryPoint
import rikka.shizuku.Shizuku
import rikka.sui.Sui
@AndroidEntryPoint
class InstallerFragment : BaseFragment<FragmentInstallerBinding>() {
private val TAG = InstallerFragment::class.java.simpleName
private var installerId: Int = 0
private var shizukuAlive = Sui.isSui()
private val shizukuAliveListener = Shizuku.OnBinderReceivedListener {
Log.d(TAG, "ShizukuInstaller Alive!")
shizukuAlive = true
}
private val shizukuDeadListener = Shizuku.OnBinderDeadListener {
Log.d(TAG, "ShizukuInstaller Dead!")
shizukuAlive = false
}
private val shizukuResultListener =
Shizuku.OnRequestPermissionResultListener { _: Int, result: Int ->
if (result == PackageManager.PERMISSION_GRANTED) {
this.installerId = 5
save(PREFERENCE_INSTALLER_ID, 5)
binding.epoxyRecycler.requestModelBuild()
} else {
showDialog(
R.string.action_installations,
R.string.installer_shizuku_unavailable
)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Toolbar
binding.toolbar.setNavigationOnClickListener { findNavController().navigateUp() }
installerId = Preferences.getInteger(requireContext(), PREFERENCE_INSTALLER_ID)
if (AppInstaller.hasShizukuOrSui(requireContext())) {
Shizuku.addBinderReceivedListenerSticky(shizukuAliveListener)
Shizuku.addBinderDeadListener(shizukuDeadListener)
Shizuku.addRequestPermissionResultListener(shizukuResultListener)
}
// RecyclerView
binding.epoxyRecycler.withModels {
setFilterDuplicates(true)
AppInstaller.getAvailableInstallersInfo(requireContext()).forEach {
add(
InstallerViewModel_()
.id(it.id)
.installer(it)
.markChecked(installerId == it.id)
.click { _ ->
save(it.id)
requestModelBuild()
}
)
}
}
if (isMIUI && !isMiuiOptimizationDisabled) {
findNavController().navigate(R.id.deviceMiuiSheet)
}
}
override fun onDestroy() {
if (AppInstaller.hasShizukuOrSui(requireContext())) {
Shizuku.removeBinderReceivedListener(shizukuAliveListener)
Shizuku.removeBinderDeadListener(shizukuDeadListener)
Shizuku.removeRequestPermissionResultListener(shizukuResultListener)
}
super.onDestroy()
}
private fun save(installerId: Int) {
when (installerId) {
0 -> {
if (isMIUI && !isMiuiOptimizationDisabled) {
findNavController().navigate(R.id.deviceMiuiSheet)
}
this.installerId = installerId
save(PREFERENCE_INSTALLER_ID, installerId)
}
2 -> {
if (AppInstaller.hasRootAccess()) {
this.installerId = installerId
save(PREFERENCE_INSTALLER_ID, installerId)
} else {
showDialog(
R.string.action_installations,
R.string.installer_root_unavailable
)
}
}
3 -> {
if (AppInstaller.hasAuroraService(requireContext())) {
this.installerId = installerId
save(PREFERENCE_INSTALLER_ID, installerId)
} else {
showDialog(
R.string.action_installations,
R.string.installer_service_unavailable
)
}
}
4 -> {
if (AppInstaller.hasAppManager(requireContext())) {
this.installerId = installerId
save(PREFERENCE_INSTALLER_ID, installerId)
} else {
showDialog(
R.string.action_installations,
R.string.installer_am_unavailable
)
}
}
5 -> {
if (isOAndAbove && AppInstaller.hasShizukuOrSui(requireContext())) {
if (shizukuAlive && AppInstaller.hasShizukuPerm()) {
this.installerId = installerId
save(PREFERENCE_INSTALLER_ID, installerId)
} else if (shizukuAlive && !Shizuku.shouldShowRequestPermissionRationale()) {
Shizuku.requestPermission(9000)
} else {
showDialog(
R.string.action_installations,
R.string.installer_shizuku_unavailable
)
}
} else {
showDialog(
R.string.action_installations,
R.string.installer_shizuku_unavailable
)
}
}
else -> {
this.installerId = installerId
save(PREFERENCE_INSTALLER_ID, installerId)
}
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.viewmodel.preferences
import android.content.Context
import android.content.pm.PackageManager
import android.util.Log
import androidx.core.content.edit
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aurora.extensions.isMIUI
import com.aurora.extensions.isMiuiOptimizationDisabled
import com.aurora.extensions.observeAsStateFlow
import com.aurora.store.R
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.model.Installer
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.aurora.store.util.save
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import rikka.shizuku.Shizuku
import rikka.sui.Sui
import javax.inject.Inject
@HiltViewModel
class InstallerViewModel @Inject constructor(
@ApplicationContext private val context: Context
) : ViewModel(), Shizuku.OnBinderReceivedListener, Shizuku.OnBinderDeadListener,
Shizuku.OnRequestPermissionResultListener {
private val TAG = InstallerViewModel::class.java.simpleName
private val sharedPreferences = Preferences.getPrefs(context)
private var isShizukuAlive = Sui.isSui()
private val _error = MutableSharedFlow<String>()
val error = _error.asSharedFlow()
private var _installerId: Int
get() = sharedPreferences.getInt(PREFERENCE_INSTALLER_ID, 0)
set(value) = sharedPreferences.edit { putInt(PREFERENCE_INSTALLER_ID, value) }
val currentInstaller = sharedPreferences.observeAsStateFlow(
key = PREFERENCE_INSTALLER_ID,
scope = viewModelScope,
initial = AppInstaller.getCurrentInstaller(context).ordinal,
valueProvider = { _installerId }
)
init {
Shizuku.addBinderReceivedListenerSticky(this)
Shizuku.addBinderDeadListener(this)
Shizuku.addRequestPermissionResultListener(this)
}
override fun onBinderReceived() {
isShizukuAlive = true
}
override fun onBinderDead() {
isShizukuAlive = false
}
override fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
if (grantResult == PackageManager.PERMISSION_GRANTED) {
save(Installer.SHIZUKU)
} else {
viewModelScope.launch {
Log.e(TAG, "Permission denied for shizuku")
_error.emit(context.getString(R.string.permissions_denied))
}
}
}
fun save(installer: Installer) {
viewModelScope.launch {
// Error handling for different installers
when (installer) {
Installer.SESSION -> {
if (isMIUI && !isMiuiOptimizationDisabled) {
Log.e(TAG, "Trying to set session installer with MIUI optimizations")
_error.emit(context.getString(R.string.device_miui_description))
return@launch
}
}
Installer.ROOT -> {
if (!AppInstaller.hasRootAccess()) {
Log.e(TAG, "Trying to set root installer without root access")
_error.emit(context.getString(R.string.installer_root_unavailable))
return@launch
}
}
Installer.SERVICE -> {
if (!AppInstaller.hasAuroraService(context)) {
Log.e(TAG, "Trying to set service installer without companion app")
_error.emit(context.getString(R.string.installer_service_unavailable))
return@launch
}
}
Installer.SHIZUKU -> {
if (AppInstaller.hasShizukuOrSui(context) && isShizukuAlive) {
if (!AppInstaller.hasShizukuPerm()) {
Log.i(TAG, "Requesting permission for shizuku")
Shizuku.requestPermission(9000)
return@launch
}
} else {
Log.e(TAG, "Trying to set shizuku installer without appropriate setup")
_error.emit(context.getString(R.string.installer_shizuku_unavailable))
return@launch
}
}
Installer.AM -> {
if (!AppInstaller.hasAppManager(context)) {
Log.e(TAG, "Trying to set AM installer without companion app")
_error.emit(context.getString(R.string.installer_am_unavailable))
return@launch
}
}
else -> Log.i(TAG, "Trying to set ${installer.name} installer without any checks")
}
context.save(PREFERENCE_INSTALLER_ID, installer.ordinal)
}
}
}

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_arrow_back"
app:title="@string/pref_install_mode_title" />
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/epoxy_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:overScrollMode="never"
android:padding="@dimen/padding_normal"
android:scrollbars="none"
app:itemSpacing="@dimen/margin_small"
tools:listitem="@layout/view_installer" />
</LinearLayout>

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_arrow_back"
app:title="@string/pref_install_mode_title" />
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/epoxy_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:overScrollMode="never"
android:padding="@dimen/padding_normal"
android:scrollbars="none"
app:itemSpacing="@dimen/margin_small"
tools:listitem="@layout/view_installer" />
</LinearLayout>

View File

@@ -1,63 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
~
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:padding="@dimen/padding_small">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/line1"
style="@style/AuroraTextStyle.Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/radiobutton" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/line2"
style="@style/AuroraTextStyle.Line1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/line1"
android:layout_alignStart="@id/line1"
android:layout_alignEnd="@id/line1"
android:layout_marginTop="@dimen/margin_xxsmall" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/line3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/line2"
android:layout_alignStart="@id/line1"
android:layout_alignEnd="@id/line1"
android:layout_marginTop="@dimen/margin_xxsmall"
android:textSize="12sp" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/radiobutton"
android:layout_width="@dimen/icon_size_category"
android:layout_height="@dimen/icon_size_category"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:clickable="false"
android:focusable="false"
android:minWidth="0dp" />
</RelativeLayout>

View File

@@ -184,11 +184,6 @@
app:popUpTo="@id/splashFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/installerFragment"
android:name="com.aurora.store.view.ui.preferences.installation.InstallerFragment"
android:label="InstallerFragment"
tools:layout="@layout/fragment_installer" />
<dialog
android:id="@+id/appMenuSheet"
android:name="com.aurora.store.view.ui.sheets.AppMenuSheet"