diff --git a/app/build.gradle b/app/build.gradle
index 820cb941c3d8c2a89045b58e787e0abb572d05da..90d0f8717ec184f8d7d9ff9399f43e77f51e3c23 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -153,7 +153,7 @@ dependencies {
api files('libs/splitinstall-lib.jar')
implementation 'foundation.e.lib:telemetry:0.0.11-alpha'
- implementation "foundation.e:gplayapi:3.2.10-1"
+ implementation "foundation.e:gplayapi:3.2.10-2"
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.fragment:fragment-ktx:1.5.6'
diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml
index 59db6f3315a6c9afa962f295b4d6f0dddf99aa87..19dfb1935430d6fc1e7148dbe74bd0b24c07a85d 100644
--- a/app/detekt-baseline.xml
+++ b/app/detekt-baseline.xml
@@ -30,7 +30,7 @@
InstanceOfCheckForException:GPlayHttpClient.kt$GPlayHttpClient$e is SocketTimeoutException
InvalidPackageDeclaration:Trackers.kt$package foundation.e.apps.data.exodus
LargeClass:ApplicationFragment.kt$ApplicationFragment : TimeoutFragment
- LongParameterList:ApplicationDialogFragment.kt$ApplicationDialogFragment$( drawable: Int = -1, title: String, message: String, positiveButtonText: String = "", positiveButtonAction: (() -> Unit)? = null, cancelButtonText: String = "", cancelButtonAction: (() -> Unit)? = null, cancelable: Boolean = true, onDismissListener: (() -> Unit)? = null, )
+ LongParameterList:ApplicationDialogFragment.kt$ApplicationDialogFragment$( title: String, message: String, @DrawableRes drawableResId: Int = -1, drawable: Drawable? = null, positiveButtonText: String = "", positiveButtonAction: (() -> Unit)? = null, cancelButtonText: String = "", cancelButtonAction: (() -> Unit)? = null, cancelable: Boolean = true, onDismissListener: (() -> Unit)? = null, )
LongParameterList:ApplicationListRVAdapter.kt$ApplicationListRVAdapter$( private val applicationInstaller: ApplicationInstaller, private val privacyInfoViewModel: PrivacyInfoViewModel, private val appInfoFetchViewModel: AppInfoFetchViewModel, private val mainActivityViewModel: MainActivityViewModel, private val currentDestinationId: Int, private var lifecycleOwner: LifecycleOwner?, private var paidAppHandler: ((Application) -> Unit)? = null )
LongParameterList:ApplicationViewModel.kt$ApplicationViewModel$( id: String, packageName: String, origin: Origin, isFdroidLink: Boolean, authObjectList: List<AuthObject>, retryBlock: (failedObjects: List<AuthObject>) -> Boolean, )
LongParameterList:CleanApkRetrofit.kt$CleanApkRetrofit$( @Query("keyword") keyword: String, @Query("source") source: String = APP_SOURCE_FOSS, @Query("type") type: String = APP_TYPE_ANY, @Query("nres") nres: Int = 20, @Query("page") page: Int = 1, @Query("by") by: String? = null, )
diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt
index 7b2b1217cad46ddb26e61bc8fb7a71899a33cfb2..dfc6b776b2bd9a6574db9e88f546b25d9d5bfb87 100644
--- a/app/src/main/java/foundation/e/apps/MainActivity.kt
+++ b/app/src/main/java/foundation/e/apps/MainActivity.kt
@@ -413,7 +413,7 @@ class MainActivity : AppCompatActivity() {
ApplicationDialogFragment(
title = getString(R.string.account_unavailable),
message = getString(R.string.too_many_requests_desc),
- drawable = R.drawable.ic_warning,
+ drawableResId = R.drawable.ic_warning,
positiveButtonText = getString(R.string.refresh_session),
positiveButtonAction = {
refreshSession()
diff --git a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt
index ffc19d94c345ea9feab2075a17e53e8d0de97707..dd32f1a5d7cebbef84d56f431118a9be4fefd2a4 100644
--- a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt
+++ b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt
@@ -21,6 +21,7 @@ package foundation.e.apps.data.application.data
import android.content.Context
import android.net.Uri
import com.aurora.gplayapi.Constants.Restriction
+import com.aurora.gplayapi.data.models.ContentRating
import com.google.gson.annotations.SerializedName
import foundation.e.apps.R
import foundation.e.apps.data.enums.FilterLevel
@@ -100,7 +101,8 @@ data class Application(
var filterLevel: FilterLevel = FilterLevel.UNKNOWN,
var isGplayReplaced: Boolean = false,
@SerializedName(value = "on_fdroid")
- val isFDroidApp: Boolean = false
+ val isFDroidApp: Boolean = false,
+ val contentRating: ContentRating = ContentRating()
) {
fun updateType() {
this.type = if (this.is_pwa) PWA else NATIVE
diff --git a/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt
index be26c5669b9ad838ef0353104bd290fb94fb07e2..a9ffd87cfaa624a3492ae619e20fab8d3d06c3c8 100644
--- a/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt
+++ b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt
@@ -1,6 +1,5 @@
/*
- * Copyright MURENA SAS 2023
- * Apps Quickly and easily install Android apps onto your device!
+ * Copyright (C) 2024 MURENA SAS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +13,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
+ *
*/
package foundation.e.apps.data.application.utils
@@ -58,6 +58,7 @@ fun App.toApplication(context: Context): Application {
isFree = this.isFree,
price = this.price,
restriction = this.restriction,
+ contentRating = this.contentRating
)
return app
}
diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt
index a38538b11b35c2e3f4c874f4109e2e0756ec542f..d71d6def54bc50c53f44a07174826322ea67a2cb 100644
--- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt
+++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt
@@ -42,7 +42,11 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
+import coil.ImageLoader
import coil.load
+import coil.request.ImageRequest
+import coil.request.SuccessResult
+import com.aurora.gplayapi.data.models.ContentRating
import com.google.android.material.button.MaterialButton
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textview.MaterialTextView
@@ -72,9 +76,11 @@ import foundation.e.apps.ui.application.ShareButtonVisibilityState.Visible
import foundation.e.apps.ui.application.model.ApplicationScreenshotsRVAdapter
import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment
import foundation.e.apps.ui.parentFragment.TimeoutFragment
+import foundation.e.apps.utils.isValid
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.Locale
import javax.inject.Inject
@@ -226,7 +232,12 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
observeDownloadStatus(binding.root)
stopLoadingUI()
+ collectState()
+ }
+
+ private fun collectState() {
collectShareVisibilityState()
+ collectAppContentRatingState()
}
private fun showWarningMessage(it: Application) {
@@ -265,9 +276,9 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
binding.privacyInclude.apply {
appPermissions.setOnClickListener { _ ->
ApplicationDialogFragment(
- R.drawable.ic_perm,
- getString(R.string.permissions),
- getPermissionListString()
+ drawableResId = R.drawable.ic_perm,
+ title = getString(R.string.permissions),
+ message = getPermissionListString()
).show(childFragmentManager, TAG)
}
appTrackers.setOnClickListener {
@@ -276,9 +287,9 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
buildTrackersString(fusedApp)
ApplicationDialogFragment(
- R.drawable.ic_tracker,
- getString(R.string.trackers_title),
- trackers
+ drawableResId = R.drawable.ic_tracker,
+ title = getString(R.string.trackers_title),
+ message = trackers
).show(childFragmentManager, TAG)
}
}
@@ -335,15 +346,18 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
)
appRating.setCompoundDrawablesWithIntrinsicBounds(
- null, null, getRatingDrawable(rating), null
+ ContextCompat.getDrawable(requireContext(), R.drawable.ic_star_blank),
+ null,
+ getRatingDrawable(rating),
+ null
)
appRating.compoundDrawablePadding = 15
}
appRatingLayout.setOnClickListener {
ApplicationDialogFragment(
- R.drawable.ic_star,
- getString(R.string.rating),
- getString(R.string.rating_description)
+ drawableResId = R.drawable.ic_star_blank,
+ title = getString(R.string.rating),
+ message = getString(R.string.rating_description)
).show(childFragmentManager, TAG)
}
@@ -360,15 +374,15 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
private fun showRequestExodusReportDialog() {
ApplicationDialogFragment(
- R.drawable.ic_lock,
- getString(R.string.request_exodus_report),
- getRequestExodusReportDialogDetailsText(),
- getString(R.string.ok),
- {
+ drawableResId = R.drawable.ic_lock,
+ title = getString(R.string.request_exodus_report),
+ message = getRequestExodusReportDialogDetailsText(),
+ positiveButtonText = getString(R.string.ok),
+ positiveButtonAction = {
shouldReloadPrivacyInfo = true
openRequestExodusReportUrl()
},
- getString(R.string.cancel)
+ cancelButtonText = getString(R.string.cancel)
).show(childFragmentManager, TAG)
}
@@ -387,9 +401,9 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
private fun showPrivacyScoreCalculationLoginDialog() {
ApplicationDialogFragment(
- R.drawable.ic_lock,
- getString(R.string.privacy_score),
- getString(
+ drawableResId = R.drawable.ic_lock,
+ title = getString(R.string.privacy_score),
+ message = getString(
R.string.privacy_description,
PRIVACY_SCORE_SOURCE_CODE_URL,
generateExodusUrl(),
@@ -477,6 +491,69 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
}
}
+ private fun collectAppContentRatingState() {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ applicationViewModel.appContentRating.collectLatest(::updateContentRatingUi)
+ }
+ }
+ }
+
+ private fun updateContentRatingUi(contentRating: ContentRating) {
+ fun loadContentRating(contentRating: ContentRating) {
+ lifecycleScope.launch {
+ val drawable = loadContentRatingDrawable(contentRating.artwork.url)
+ displayRating(contentRating, drawable)
+ }
+ }
+
+ fun hideContentRating() {
+ binding.ratingsInclude.appContentRatingLayout.visibility = View.GONE
+ }
+
+ if (contentRating.isValid()) {
+ loadContentRating(contentRating)
+ } else {
+ hideContentRating()
+ }
+ }
+
+ private fun displayRating(contentRating: ContentRating, drawable: Drawable?) {
+ binding.ratingsInclude.apply {
+ appContentRatingTitle.text = contentRating.title
+
+ if (drawable != null) {
+ appContentRatingProgress.visibility = View.GONE
+ appContentRatingIcon.setImageDrawable(drawable)
+ }
+
+ appContentRatingLayout.apply {
+ visibility = View.VISIBLE
+
+ setOnClickListener {
+ openContentRatingDialog(
+ contentRating.title,
+ drawable,
+ contentRating.description
+ )
+ }
+ }
+ }
+ }
+
+ private suspend fun loadContentRatingDrawable(url: String): Drawable? {
+ return withContext(Dispatchers.IO) {
+ val imageRequest = ImageRequest.Builder(requireContext()).data(url).build()
+ val result = ImageLoader.invoke(requireContext()).execute(imageRequest)
+ if (result is SuccessResult) result.drawable else null
+ }
+ }
+
+ private fun openContentRatingDialog(title: String, iconUrl: Drawable?, recommendation: String) {
+ ApplicationDialogFragment(drawable = iconUrl, title = title, message = recommendation)
+ .show(childFragmentManager, TAG)
+ }
+
override fun loadData(authObjectList: List) {
if (isDetailsLoaded) return
/* Show the loading bar. */
@@ -831,7 +908,8 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
val progressPercentage =
((progressResult.second / progressResult.first.toDouble()) * 100f).toInt()
binding.downloadInclude.appInstallPB.progress = progressPercentage
- binding.downloadInclude.percentage.text = String.format("%d%%", progressPercentage)
+ binding.downloadInclude.percentage.text =
+ String.format(Locale.getDefault(), "%d%%", progressPercentage)
binding.downloadInclude.downloadedSize.text = downloadedSize
}
@@ -890,7 +968,10 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
)
appPrivacyScore.setCompoundDrawablesRelativeWithIntrinsicBounds(
- null, null, getPrivacyDrawable(privacyScore.toString()), null
+ ContextCompat.getDrawable(requireContext(), R.drawable.ic_lock_blank),
+ null,
+ getPrivacyDrawable(privacyScore.toString()),
+ null
)
appPrivacyScore.compoundDrawablePadding = 15
}
diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt
index 2148be2f55bdbe16c7201fcf81b84bb3f2442790..8d1815588b215f8ff99a433f278ccda6b24f4890 100644
--- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt
+++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt
@@ -21,6 +21,7 @@ package foundation.e.apps.ui.application
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.data.models.ContentRating
import com.aurora.gplayapi.exceptions.ApiException
import dagger.hilt.android.lifecycle.HiltViewModel
import foundation.e.apps.R
@@ -43,6 +44,7 @@ import foundation.e.apps.ui.parentFragment.LoadingViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -62,6 +64,9 @@ class ApplicationViewModel @Inject constructor(
private val _shareButtonVisibilityState = MutableStateFlow(Hidden)
val shareButtonVisibilityState = _shareButtonVisibilityState.asStateFlow()
+ private val _appContentRating = MutableStateFlow(ContentRating())
+ val appContentRating = _appContentRating.asStateFlow()
+
fun loadData(
id: String,
packageName: String,
@@ -114,6 +119,7 @@ class ApplicationViewModel @Inject constructor(
application.postValue(appData)
updateShareVisibilityState(appData.first.shareUri.toString())
+ updateAppContentRatingState(appData.first.contentRating)
val status = appData.second
@@ -140,6 +146,10 @@ class ApplicationViewModel @Inject constructor(
}
}
+ private fun updateAppContentRatingState(value: ContentRating) {
+ _appContentRating.update { value }
+ }
+
private fun updateShareVisibilityState(shareUri: String) {
val isValidUri = shareUri.isNotBlank()
_shareButtonVisibilityState.value = if (isValidUri) Visible else Hidden
diff --git a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt
index 7237822701ab6063183e6c98f60be13cd1f26dad..597c437e5394e9234139a9fd73f6bfbe77760610 100644
--- a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt
+++ b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt
@@ -1,6 +1,5 @@
/*
- * Apps Quickly and easily install Android apps onto your device!
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021-2024 MURENA SAS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,12 +13,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
+ *
*/
package foundation.e.apps.ui.application.subFrags
import android.app.Dialog
import android.content.DialogInterface
+import android.graphics.drawable.Drawable
import android.os.Bundle
import android.text.Html
import android.text.SpannableString
@@ -27,6 +28,7 @@ import android.text.TextPaint
import android.text.method.LinkMovementMethod
import android.text.style.URLSpan
import android.widget.TextView
+import androidx.annotation.DrawableRes
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
@@ -35,9 +37,10 @@ import foundation.e.apps.R
@AndroidEntryPoint
class ApplicationDialogFragment() : DialogFragment() {
- private var drawable: Int = -1
private var title: String? = null
private var message: String? = null
+ @DrawableRes private var drawableResId: Int = -1
+ private var drawable: Drawable? = null
private var positiveButtonText: String? = null
private var positiveButtonAction: (() -> Unit)? = null
private var cancelButtonText: String? = null
@@ -46,9 +49,10 @@ class ApplicationDialogFragment() : DialogFragment() {
private var onDismissListener: (() -> Unit)? = null
constructor(
- drawable: Int = -1,
title: String,
message: String,
+ @DrawableRes drawableResId: Int = -1,
+ drawable: Drawable? = null,
positiveButtonText: String = "",
positiveButtonAction: (() -> Unit)? = null,
cancelButtonText: String = "",
@@ -56,9 +60,10 @@ class ApplicationDialogFragment() : DialogFragment() {
cancelable: Boolean = true,
onDismissListener: (() -> Unit)? = null,
) : this() {
- this.drawable = drawable
this.title = title
this.message = message
+ this.drawableResId = drawableResId
+ this.drawable = drawable
this.positiveButtonText = positiveButtonText
this.positiveButtonAction = positiveButtonAction
this.cancelButtonText = cancelButtonText
@@ -84,9 +89,14 @@ class ApplicationDialogFragment() : DialogFragment() {
this.dismiss()
}
}
- if (drawable != -1) {
+ if (drawableResId != -1) {
+ materialAlertDialogBuilder.setIcon(drawableResId)
+ }
+
+ if (drawableResId == -1 && drawable != null) {
materialAlertDialogBuilder.setIcon(drawable)
}
+
return materialAlertDialogBuilder.create()
}
diff --git a/app/src/main/java/foundation/e/apps/utils/Extensions.kt b/app/src/main/java/foundation/e/apps/utils/Extensions.kt
index aa763f6a816a0d2f6d8d73b135c7e6a1528c247d..07f652e9cac50eaa129638380a6fb6e88dc4852d 100644
--- a/app/src/main/java/foundation/e/apps/utils/Extensions.kt
+++ b/app/src/main/java/foundation/e/apps/utils/Extensions.kt
@@ -1,9 +1,28 @@
+/*
+ * Copyright (C) 2024 MURENA SAS
+ *
+ * This program 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see .
+ *
+ */
+
package foundation.e.apps.utils
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import androidx.appcompat.app.AlertDialog
+import com.aurora.gplayapi.data.models.ContentRating
import foundation.e.apps.R
import java.text.SimpleDateFormat
import java.util.Date
@@ -42,3 +61,5 @@ fun Context.isNetworkAvailable(): Boolean {
return false
}
+
+fun ContentRating.isValid() = title.isNotBlank() && artwork.url.isNotBlank()
diff --git a/app/src/main/res/drawable/ic_lock_blank.xml b/app/src/main/res/drawable/ic_lock_blank.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8d37663af8d2b228649b0e796855e51f835b83ad
--- /dev/null
+++ b/app/src/main/res/drawable/ic_lock_blank.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_rating_privacy_circle.xml b/app/src/main/res/drawable/ic_rating_privacy_circle.xml
index dd96d8abea9178fa800fab510d9f18ec5cad3007..188591202f1fb68d27a1da1d56b75d10f65c3af1 100644
--- a/app/src/main/res/drawable/ic_rating_privacy_circle.xml
+++ b/app/src/main/res/drawable/ic_rating_privacy_circle.xml
@@ -1,6 +1,5 @@
diff --git a/app/src/main/res/drawable/ic_star_blank.xml b/app/src/main/res/drawable/ic_star_blank.xml
new file mode 100644
index 0000000000000000000000000000000000000000..21444f178aa18eecc337b27489698ef627d70cb6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_star_blank.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_application_ratings.xml b/app/src/main/res/layout/fragment_application_ratings.xml
index b211c8a308e9b57a4f5735e21328247890a72cba..dd02f2ba61621d7cebe869f2c50b153525d33b3e 100644
--- a/app/src/main/res/layout/fragment_application_ratings.xml
+++ b/app/src/main/res/layout/fragment_application_ratings.xml
@@ -1,6 +1,5 @@
@@ -43,6 +44,7 @@
android:id="@+id/appRatingLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
@@ -56,23 +58,23 @@
android:layout_height="wrap_content"
android:text="@string/not_available"
android:textColor="?android:textColorPrimary"
- android:textSize="25sp" />
+ android:textSize="24sp"
+ tools:drawableStart="@drawable/ic_star_blank" />
+ android:textSize="14sp" />
+ app:layout_constraintTop_toTopOf="@+id/appPrivacyScore"
+ tools:visibility="gone" />
+ app:layout_constraintTop_toTopOf="parent"
+ tools:drawableStart="@drawable/ic_lock_blank"
+ tools:text="3/10"
+ tools:visibility="visible" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_application_title.xml b/app/src/main/res/layout/fragment_application_title.xml
index a94b2af8c038a37ec2c59c19f4bf8df06d91db66..96ec06c1f3a50de7f8ac28f892ff122d7158f22d 100644
--- a/app/src/main/res/layout/fragment_application_title.xml
+++ b/app/src/main/res/layout/fragment_application_title.xml
@@ -1,6 +1,5 @@
@@ -71,6 +71,7 @@
android:paddingEnd="2dp"
android:textColor="@color/app_info_text_color_grey"
android:textSize="16sp" />
+
@@ -87,6 +88,7 @@
android:id="@+id/sourceTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginEnd="10dp"
android:background="@drawable/bg_tag_rounded"
android:paddingStart="10dp"
android:paddingTop="4dp"
@@ -94,7 +96,6 @@
android:paddingBottom="4dp"
android:textAllCaps="false"
android:textColor="#626262"
- android:layout_marginEnd="10dp"
android:textSize="14sp"
android:visibility="gone"
tools:text="Open Source" />
@@ -103,6 +104,7 @@
android:id="@+id/categoryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginEnd="20dp"
android:background="@drawable/bg_tag_rounded"
android:paddingStart="10dp"
android:paddingTop="4dp"
@@ -110,9 +112,8 @@
android:paddingBottom="4dp"
android:textAllCaps="false"
android:textColor="#626262"
- android:textSize="14sp"
- android:layout_marginEnd="20dp"
android:textIsSelectable="false"
+ android:textSize="14sp"
tools:text="Racing" />