compose: exodus: Add back button to request new anaylsis
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -393,7 +393,7 @@ private fun ScreenContentApp(
|
|||||||
|
|
||||||
Privacy(
|
Privacy(
|
||||||
report = exodusReport,
|
report = exodusReport,
|
||||||
onNavigateToDetailsExodus = if (!exodusReport?.trackers.isNullOrEmpty()) {
|
onNavigateToDetailsExodus = if (exodusReport?.id != -1) {
|
||||||
{ showExtraPane(ExtraScreen.Exodus) }
|
{ showExtraPane(ExtraScreen.Exodus) }
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -9,31 +9,38 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
|
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
|
||||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.dimensionResource
|
import androidx.compose.ui.res.dimensionResource
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.aurora.Constants.EXODUS_REPORT_URL
|
import com.aurora.Constants.EXODUS_REPORT_URL
|
||||||
|
import com.aurora.Constants.EXODUS_SUBMIT_PAGE
|
||||||
import com.aurora.extensions.adaptiveNavigationIcon
|
import com.aurora.extensions.adaptiveNavigationIcon
|
||||||
import com.aurora.extensions.browse
|
import com.aurora.extensions.browse
|
||||||
import com.aurora.extensions.isWindowCompact
|
import com.aurora.extensions.isWindowCompact
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.compose.composable.Error
|
||||||
import com.aurora.store.compose.composable.Header
|
import com.aurora.store.compose.composable.Header
|
||||||
import com.aurora.store.compose.composable.TopAppBar
|
import com.aurora.store.compose.composable.TopAppBar
|
||||||
import com.aurora.store.compose.composable.details.ExodusListItem
|
import com.aurora.store.compose.composable.details.ExodusListItem
|
||||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||||
import com.aurora.store.compose.preview.PreviewTemplate
|
import com.aurora.store.compose.preview.PreviewTemplate
|
||||||
import com.aurora.store.data.model.ExodusTracker
|
import com.aurora.store.data.model.ExodusTracker
|
||||||
|
import com.aurora.store.data.model.Report
|
||||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
import com.aurora.store.viewmodel.details.ExodusViewModel
|
import com.aurora.store.viewmodel.details.ExodusViewModel
|
||||||
|
|
||||||
@@ -42,39 +49,52 @@ fun ExodusScreen(
|
|||||||
packageName: String,
|
packageName: String,
|
||||||
onNavigateUp: () -> Unit,
|
onNavigateUp: () -> Unit,
|
||||||
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
|
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
|
||||||
exodusViewModel: ExodusViewModel = hiltViewModel(
|
exodusViewModel: ExodusViewModel = hiltViewModel(key = "$packageName/exodus"),
|
||||||
key = "$packageName/exodus",
|
|
||||||
creationCallback = { factory: ExodusViewModel.Factory ->
|
|
||||||
factory.create(appDetailsViewModel.exodusReport.value!!)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
val app by appDetailsViewModel.app.collectAsStateWithLifecycle()
|
val app by appDetailsViewModel.app.collectAsStateWithLifecycle()
|
||||||
val exodusReport by appDetailsViewModel.exodusReport.collectAsStateWithLifecycle()
|
val report by appDetailsViewModel.exodusReport.collectAsStateWithLifecycle()
|
||||||
val trackers by exodusViewModel.trackers.collectAsStateWithLifecycle()
|
val trackers by exodusViewModel.trackers.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = Unit) {
|
||||||
|
report?.let { exodusViewModel.getExodusTrackersFromReport(it) }
|
||||||
|
}
|
||||||
|
|
||||||
val topAppBarTitle = when {
|
val topAppBarTitle = when {
|
||||||
windowAdaptiveInfo.isWindowCompact -> app!!.displayName
|
windowAdaptiveInfo.isWindowCompact -> app!!.displayName
|
||||||
else -> stringResource(R.string.details_privacy)
|
else -> stringResource(R.string.details_privacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenContent(
|
when (report) {
|
||||||
topAppBarTitle = topAppBarTitle,
|
null -> {
|
||||||
id = exodusReport!!.id,
|
ScreenContentError(
|
||||||
version = exodusReport!!.version,
|
topAppBarTitle = topAppBarTitle,
|
||||||
trackers = trackers,
|
onNavigateUp = onNavigateUp,
|
||||||
onNavigateUp = onNavigateUp,
|
onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") }
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
ScreenContentReport(
|
||||||
|
topAppBarTitle = topAppBarTitle,
|
||||||
|
report = report,
|
||||||
|
trackers = trackers,
|
||||||
|
onNavigateUp = onNavigateUp,
|
||||||
|
onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ScreenContent(
|
private fun ScreenContentReport(
|
||||||
topAppBarTitle: String? = null,
|
topAppBarTitle: String? = null,
|
||||||
id: Int = -1,
|
report: Report? = null,
|
||||||
version: String = String(),
|
|
||||||
trackers: List<ExodusTracker> = emptyList(),
|
trackers: List<ExodusTracker> = emptyList(),
|
||||||
onNavigateUp: () -> Unit = {},
|
onNavigateUp: () -> Unit = {},
|
||||||
|
onRequestAnalysis: () -> Unit = {},
|
||||||
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -86,6 +106,14 @@ private fun ScreenContent(
|
|||||||
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||||
onNavigateUp = onNavigateUp
|
onNavigateUp = onNavigateUp
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
FloatingActionButton(onClick = onRequestAnalysis) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_scan),
|
||||||
|
contentDescription = stringResource(R.string.action_request_analysis)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -96,9 +124,17 @@ private fun ScreenContent(
|
|||||||
) {
|
) {
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
Header(
|
Header(
|
||||||
title = stringResource(R.string.exodus_report_trackers, trackers.size, version),
|
title = if (report?.trackers.isNullOrEmpty()) {
|
||||||
|
stringResource(R.string.exodus_no_tracker)
|
||||||
|
} else {
|
||||||
|
stringResource(
|
||||||
|
R.string.exodus_report_trackers,
|
||||||
|
report.trackers.size,
|
||||||
|
report.version
|
||||||
|
)
|
||||||
|
},
|
||||||
subtitle = stringResource(R.string.exodus_view_report),
|
subtitle = stringResource(R.string.exodus_view_report),
|
||||||
onClick = { context.browse(EXODUS_REPORT_URL + id) }
|
onClick = { context.browse(EXODUS_REPORT_URL + report!!.id) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,13 +145,47 @@ private fun ScreenContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
/**
|
||||||
|
* Composable to display errors related to fetching app details
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun ExodusScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
private fun ScreenContentError(
|
||||||
PreviewTemplate {
|
topAppBarTitle: String? = null,
|
||||||
ScreenContent(
|
onNavigateUp: () -> Unit = {},
|
||||||
topAppBarTitle = app.displayName,
|
onRequestAnalysis: () -> Unit = {},
|
||||||
version = app.versionName
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = topAppBarTitle,
|
||||||
|
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { paddingValues ->
|
||||||
|
Error(
|
||||||
|
modifier = Modifier.padding(paddingValues),
|
||||||
|
painter = painterResource(R.drawable.ic_disclaimer),
|
||||||
|
message = stringResource(R.string.failed_to_fetch_report),
|
||||||
|
actionMessage = stringResource(R.string.action_request_analysis),
|
||||||
|
onAction = onRequestAnalysis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ExodusScreenPreviewReport(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||||
|
PreviewTemplate {
|
||||||
|
ScreenContentReport(topAppBarTitle = app.displayName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ExodusScreenPreviewError(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||||
|
PreviewTemplate {
|
||||||
|
ScreenContentError(topAppBarTitle = app.displayName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,37 +9,25 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.store.data.model.ExodusTracker
|
import com.aurora.store.data.model.ExodusTracker
|
||||||
import com.aurora.store.data.model.Report
|
import com.aurora.store.data.model.Report
|
||||||
import dagger.assisted.Assisted
|
|
||||||
import dagger.assisted.AssistedFactory
|
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel(assistedFactory = ExodusViewModel.Factory::class)
|
@HiltViewModel
|
||||||
class ExodusViewModel @AssistedInject constructor(
|
class ExodusViewModel @Inject constructor(
|
||||||
@Assisted private val exodusReport: Report,
|
|
||||||
private val exodusTrackers: JSONObject
|
private val exodusTrackers: JSONObject
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@AssistedFactory
|
|
||||||
interface Factory {
|
|
||||||
fun create(exodusReport: Report): ExodusViewModel
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _trackers = MutableStateFlow<List<ExodusTracker>>(emptyList())
|
private val _trackers = MutableStateFlow<List<ExodusTracker>>(emptyList())
|
||||||
val trackers = _trackers.asStateFlow()
|
val trackers = _trackers.asStateFlow()
|
||||||
|
|
||||||
init {
|
fun getExodusTrackersFromReport(report: Report) {
|
||||||
getExodusTrackersFromReport()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getExodusTrackersFromReport() {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val trackerObjects = exodusReport.trackers.map {
|
val trackerObjects = report.trackers.map {
|
||||||
exodusTrackers.getJSONObject(it.toString())
|
exodusTrackers.getJSONObject(it.toString())
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
|
|||||||
14
app/src/main/res/drawable/ic_scan.xml
Normal file
14
app/src/main/res/drawable/ic_scan.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,680L240,680L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800L720,680L800,680L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM160,440L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,440L720,440L720,360L520,360L520,160L240,160Q240,160 240,160Q240,160 240,160L240,440L160,440ZM40,600L40,520L920,520L920,600L40,600ZM480,440L480,440L480,440L480,440L480,440Q480,440 480,440Q480,440 480,440ZM480,680L480,680Q480,680 480,680Q480,680 480,680L480,680Q480,680 480,680Q480,680 480,680L480,680Z"/>
|
||||||
|
</vector>
|
||||||
Reference in New Issue
Block a user