mirror of
https://github.com/whyorean/AuroraStore.git
synced 2026-06-13 02:11:07 -04:00
AppDetailsFragment: Implement compatibility section using plexus
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -32,6 +32,9 @@ object Constants {
|
||||
const val EXODUS_REPORT_URL = "https://reports.exodus-privacy.eu.org/reports/"
|
||||
const val EXODUS_SEARCH_URL = "https://reports.exodus-privacy.eu.org/api/search/"
|
||||
|
||||
const val PLEXUS_API_URL = "https://plexus.techlore.tech/api/v1/apps"
|
||||
const val PLEXUS_SEARCH_URL = "https://plexus.techlore.tech/?q="
|
||||
|
||||
const val SHARE_URL = "https://play.google.com/store/apps/details?id="
|
||||
|
||||
const val UPDATE_URL_STABLE = "https://gitlab.com/AuroraOSS/AuroraStore/raw/master/updates.json"
|
||||
|
||||
42
app/src/main/java/com/aurora/store/data/model/Plexus.kt
Normal file
42
app/src/main/java/com/aurora/store/data/model/Plexus.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.aurora.store.data.model
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.aurora.store.R
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class PlexusReport(
|
||||
@SerializedName("data")
|
||||
val report: Data?
|
||||
)
|
||||
|
||||
data class Data(
|
||||
val name: String,
|
||||
val scores: Scores,
|
||||
@SerializedName("updated_at")
|
||||
val updatedAt: String
|
||||
)
|
||||
|
||||
data class Scores(
|
||||
@SerializedName("micro_g")
|
||||
val microG: Rating,
|
||||
@SerializedName("native")
|
||||
val aosp: Rating
|
||||
)
|
||||
|
||||
data class Rating(
|
||||
val denominator: Float,
|
||||
val numerator: Float,
|
||||
val rating_type: String,
|
||||
val total_count: Long
|
||||
) {
|
||||
private val fraction get() = numerator / denominator
|
||||
|
||||
@get:StringRes
|
||||
val status: Int
|
||||
get() = when {
|
||||
fraction == 0F -> R.string.details_compatibility_status_unknown
|
||||
fraction >= 0.90 -> R.string.details_compatibility_status_compatible
|
||||
fraction >= 0.50 -> R.string.details_compatibility_status_limited
|
||||
else -> R.string.details_compatibility_status_unsupported
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,7 @@ object OkHttpClientModule {
|
||||
.add("auroraoss.com", "sha256/mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=") // GTS Root R4
|
||||
.add("*.exodus-privacy.eu.org", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=") // ISRG Root X1
|
||||
.add("gitlab.com", "sha256/x4QzPSC810K5/cMjb05Qm4k3Bw5zBn4lTdO/nEW/Td4=") // USERTrust RSA Certification Authority
|
||||
.add("plexus.techlore.tech", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=") // ISRG Root X1
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ object PackageUtil {
|
||||
|
||||
private const val TAG = "PackageUtil"
|
||||
|
||||
private const val PACKAGE_NAME_MICRO_G = "com.google.android.gms"
|
||||
const val PACKAGE_NAME_GMS = "com.google.android.gms"
|
||||
private const val VERSION_CODE_MICRO_G = 240913402
|
||||
private const val VERSION_CODE_MICRO_G_HUAWEI = 240913007
|
||||
|
||||
@@ -66,15 +66,15 @@ object PackageUtil {
|
||||
}
|
||||
|
||||
fun hasSupportedMicroG(context: Context): Boolean {
|
||||
val isMicroG = CertUtil.isMicroGGMS(context, PACKAGE_NAME_MICRO_G)
|
||||
val isMicroG = CertUtil.isMicroGGMS(context, PACKAGE_NAME_GMS)
|
||||
|
||||
// Do not proceed if MicroG variant is not installed
|
||||
if (!isMicroG) return false
|
||||
|
||||
return if (isHuawei) {
|
||||
isInstalled(context, PACKAGE_NAME_MICRO_G, VERSION_CODE_MICRO_G_HUAWEI)
|
||||
isInstalled(context, PACKAGE_NAME_GMS, VERSION_CODE_MICRO_G_HUAWEI)
|
||||
} else {
|
||||
isInstalled(context, PACKAGE_NAME_MICRO_G, VERSION_CODE_MICRO_G)
|
||||
isInstalled(context, PACKAGE_NAME_GMS, VERSION_CODE_MICRO_G)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ package com.aurora.store.view.custom.layouts
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.ViewDevInfoBinding
|
||||
@@ -87,4 +89,12 @@ class DevInfoLayout : RelativeLayout {
|
||||
binding.txtSubtitle.isVisible = text != null
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun setTitleColor(@ColorRes color: Int) {
|
||||
binding.txtTitle.setTextColor(ContextCompat.getColor(context, color))
|
||||
}
|
||||
|
||||
fun setSubtitleColor(@ColorRes color: Int) {
|
||||
binding.txtSubtitle.setTextColor(ContextCompat.getColor(context, color))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +205,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
updateToolbar(app)
|
||||
updateAppHeader(app) // Re-inflate the app details, as web data may vary.
|
||||
updateExtraDetails(app)
|
||||
updateCompatibilityInfo()
|
||||
|
||||
if (app.versionCode == 0) {
|
||||
warnAppUnavailable(app)
|
||||
@@ -331,6 +332,27 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
// Plexus Report
|
||||
viewModel.plexusReport.onEach { plexusReport ->
|
||||
if (plexusReport?.report?.scores == null) {
|
||||
binding.layoutDetailsCompatibility.txtStatusMicroG.subTitle =
|
||||
getString(R.string.details_compatibility_status_unknown)
|
||||
|
||||
binding.layoutDetailsCompatibility.txtStatusAosp.subTitle =
|
||||
getString(R.string.details_compatibility_status_unknown)
|
||||
} else {
|
||||
binding.layoutDetailsCompatibility.headerCompatibility.addClickListener {
|
||||
requireContext().browse("${Constants.PLEXUS_SEARCH_URL}${app.packageName}")
|
||||
}
|
||||
|
||||
binding.layoutDetailsCompatibility.txtStatusMicroG.subTitle =
|
||||
getString(plexusReport.report.scores.microG.status)
|
||||
|
||||
binding.layoutDetailsCompatibility.txtStatusAosp.subTitle =
|
||||
getString(plexusReport.report.scores.microG.status)
|
||||
}
|
||||
}.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
|
||||
// Beta program
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.testingProgramStatus.collect {
|
||||
@@ -1013,6 +1035,26 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCompatibilityInfo() {
|
||||
if (app.dependencies.dependentPackages.contains(PackageUtil.PACKAGE_NAME_GMS)) {
|
||||
viewModel.fetchPlexusReport(app.packageName)
|
||||
|
||||
binding.layoutDetailsCompatibility.txtGmsDependency.apply {
|
||||
title = getString(R.string.details_compatibility_gms_required_title)
|
||||
subTitle = getString(R.string.details_compatibility_gms_required_subtitle)
|
||||
setTitleColor(R.color.colorRed)
|
||||
}
|
||||
|
||||
binding.layoutDetailsCompatibility.compatibilityStatusLayout.isVisible = true
|
||||
} else {
|
||||
binding.layoutDetailsCompatibility.txtGmsDependency.apply {
|
||||
title = getString(R.string.details_compatibility_gms_not_required_title)
|
||||
subTitle = getString(R.string.details_compatibility_gms_not_required_subtitle)
|
||||
setTitleColor(R.color.colorGreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun warnAppUnavailable(app: App) {
|
||||
AuroraApp.events.send(InstallerEvent.Failed(app.packageName).apply {
|
||||
error = getString(R.string.status_unavailable)
|
||||
|
||||
@@ -13,16 +13,16 @@ import com.aurora.gplayapi.helpers.AppDetailsHelper
|
||||
import com.aurora.gplayapi.helpers.ReviewsHelper
|
||||
import com.aurora.gplayapi.helpers.web.WebDataSafetyHelper
|
||||
import com.aurora.gplayapi.network.IHttpClient
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.BuildConfig
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.helper.DownloadHelper
|
||||
import com.aurora.store.data.model.ExodusReport
|
||||
import com.aurora.store.data.model.PlexusReport
|
||||
import com.aurora.store.data.model.Report
|
||||
import com.aurora.store.data.room.favourite.Favourite
|
||||
import com.aurora.store.data.room.favourite.FavouriteDao
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.Gson
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -35,7 +35,6 @@ import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.JSONObject
|
||||
import java.lang.reflect.Modifier
|
||||
import javax.inject.Inject
|
||||
import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport
|
||||
|
||||
@@ -47,7 +46,8 @@ class AppDetailsViewModel @Inject constructor(
|
||||
private val webDataSafetyHelper: WebDataSafetyHelper,
|
||||
private val downloadHelper: DownloadHelper,
|
||||
private val favouriteDao: FavouriteDao,
|
||||
private val httpClient: IHttpClient
|
||||
private val httpClient: IHttpClient,
|
||||
private val gson: Gson
|
||||
) : ViewModel() {
|
||||
|
||||
private val TAG = AppDetailsViewModel::class.java.simpleName
|
||||
@@ -72,6 +72,10 @@ class AppDetailsViewModel @Inject constructor(
|
||||
private val _exodusReport = MutableSharedFlow<Report?>()
|
||||
val exodusReport = _exodusReport.asSharedFlow()
|
||||
|
||||
private val plexusReportStash = mutableMapOf<String, PlexusReport?>()
|
||||
private val _plexusReport = MutableSharedFlow<PlexusReport?>()
|
||||
val plexusReport = _plexusReport.asSharedFlow()
|
||||
|
||||
private val testProgramStatusStash = mutableMapOf<String, TestingProgramStatus?>()
|
||||
private val _testingProgramStatus = MutableSharedFlow<TestingProgramStatus?>()
|
||||
val testingProgramStatus = _testingProgramStatus.asSharedFlow()
|
||||
@@ -147,6 +151,24 @@ class AppDetailsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchPlexusReport(packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val plexusReport = plexusReportStash.getOrPut(packageName) {
|
||||
val url = "${Constants.PLEXUS_API_URL}/${packageName}/?scores=true"
|
||||
val playResponse = httpClient.get(url, emptyMap())
|
||||
gson.fromJson(String(playResponse.responseBytes), PlexusReport::class.java)
|
||||
}
|
||||
|
||||
_plexusReport.emit(plexusReport)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch compatibility report", exception)
|
||||
plexusReportStash[packageName] = null
|
||||
_plexusReport.emit(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchTestingProgramStatus(packageName: String, subscribe: Boolean) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
@@ -258,10 +280,6 @@ class AppDetailsViewModel @Inject constructor(
|
||||
|
||||
private fun parseExodusResponse(response: String, packageName: String): List<Report> {
|
||||
try {
|
||||
val gson = GsonBuilder()
|
||||
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
|
||||
.create()
|
||||
|
||||
val jsonObject = JSONObject(response)
|
||||
val exodusObject = jsonObject.getJSONObject(packageName)
|
||||
val exodusReport: ExodusReport = gson.fromJson(
|
||||
|
||||
11
app/src/main/res/drawable/ic_android.xml
Normal file
11
app/src/main/res/drawable/ic_android.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M40,720Q49,613 105.5,523Q162,433 256,380L182,252Q176,243 179,233Q182,223 192,218Q200,213 210,216Q220,219 226,228L300,356Q386,320 480,320Q574,320 660,356L734,228Q740,219 750,216Q760,213 768,218Q778,223 781,233Q784,243 778,252L704,380Q798,433 854.5,523Q911,613 920,720L40,720ZM280,610Q301,610 315.5,595.5Q330,581 330,560Q330,539 315.5,524.5Q301,510 280,510Q259,510 244.5,524.5Q230,539 230,560Q230,581 244.5,595.5Q259,610 280,610ZM680,610Q701,610 715.5,595.5Q730,581 730,560Q730,539 715.5,524.5Q701,510 680,510Q659,510 644.5,524.5Q630,539 630,560Q630,581 644.5,595.5Q659,610 680,610Z"/>
|
||||
</vector>
|
||||
@@ -103,6 +103,10 @@
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/view_app" />
|
||||
|
||||
<include
|
||||
android:id="@+id/layout_details_compatibility"
|
||||
layout="@layout/layout_details_compatibility" />
|
||||
|
||||
<include
|
||||
android:id="@+id/layout_details_permissions"
|
||||
layout="@layout/layout_details_permissions" />
|
||||
|
||||
51
app/src/main/res/layout/layout_details_compatibility.xml
Normal file
51
app/src/main/res/layout/layout_details_compatibility.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:divider="@drawable/divider"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/padding_small"
|
||||
android:paddingEnd="@dimen/padding_small"
|
||||
android:showDividers="middle">
|
||||
|
||||
<com.aurora.store.view.custom.layouts.ActionHeaderLayout
|
||||
android:id="@+id/header_compatibility"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:headerSubtitle="@string/plexus_powered"
|
||||
app:headerTitle="@string/details_compatibility_title" />
|
||||
|
||||
<com.aurora.store.view.custom.layouts.DevInfoLayout
|
||||
android:id="@+id/txt_gms_dependency"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:imgIcon="@drawable/ic_menu_about"
|
||||
tools:txtSubtitle="@string/details_compatibility_gms_required_subtitle"
|
||||
tools:txtTitle="@string/details_compatibility_gms_required_title" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/compatibility_status_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<com.aurora.store.view.custom.layouts.DevInfoLayout
|
||||
android:id="@+id/txt_status_aosp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:imgIcon="@drawable/ic_android"
|
||||
app:txtSubtitle="@string/plexus_progress"
|
||||
app:txtTitle="@string/details_compatibility_no_gms" />
|
||||
|
||||
<com.aurora.store.view.custom.layouts.DevInfoLayout
|
||||
android:id="@+id/txt_status_microG"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:imgIcon="@drawable/ic_android"
|
||||
app:txtSubtitle="@string/plexus_progress"
|
||||
app:txtTitle="@string/details_compatibility_microg" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -474,4 +474,17 @@
|
||||
<string name="action_unarchive">Unarchive</string>
|
||||
<string name="unable_to_open">Unable to open app</string>
|
||||
<string name="version_update" translatable="false"><xliff:g id="installed_version_name">%1$s</xliff:g> (<xliff:g id="installed_version_code">%2$d</xliff:g>) ➔ <xliff:g id="update_version_name">%3$s</xliff:g> (<xliff:g id="update_version_code">%4$d</xliff:g>)</string>
|
||||
<string name="plexus_powered">Powered by Plexus</string>
|
||||
<string name="plexus_progress">Checking compatibility…</string>
|
||||
<string name="details_compatibility_title">Compatibility</string>
|
||||
<string name="details_compatibility_gms_required_title">Requires Google Play Services</string>
|
||||
<string name="details_compatibility_gms_required_subtitle">You may need to install Google’s proprietary library or a FOSS reimplementation such as microG.</string>
|
||||
<string name="details_compatibility_gms_not_required_title">Works without Google Play Services</string>
|
||||
<string name="details_compatibility_gms_not_required_subtitle">This app works without Google’s proprietary library. However, it may still require other third-party libraries to work properly.</string>
|
||||
<string name="details_compatibility_microg">With microG Project</string>
|
||||
<string name="details_compatibility_no_gms">Without Google Play Services</string>
|
||||
<string name="details_compatibility_status_compatible">Compatible: Works without any issues</string>
|
||||
<string name="details_compatibility_status_limited">Limited: Works with limited features</string>
|
||||
<string name="details_compatibility_status_unsupported">Unsupported: Not functional</string>
|
||||
<string name="details_compatibility_status_unknown">Unknown: Not checked yet</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user