Add app details stream (Related, Similar, More by dev)
This commit is contained in:
@@ -172,7 +172,7 @@ dependencies {
|
|||||||
implementation "com.github.topjohnwu.libsu:core:${versions.libsu}"
|
implementation "com.github.topjohnwu.libsu:core:${versions.libsu}"
|
||||||
|
|
||||||
//Love <3
|
//Love <3
|
||||||
api("com.gitlab.AuroraOSS:gplayapi:9d327499ae")
|
api("com.gitlab.AuroraOSS:gplayapi:101c69ecde")
|
||||||
}
|
}
|
||||||
|
|
||||||
Properties props = new Properties()
|
Properties props = new Properties()
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.controller
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
|
import com.aurora.store.view.epoxy.groups.CarouselModelGroup
|
||||||
|
import com.aurora.store.view.epoxy.groups.CarouselShimmerGroup
|
||||||
|
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
|
||||||
|
|
||||||
|
open class DetailsCarouselController(private val callbacks: Callbacks) :
|
||||||
|
GenericCarouselController(callbacks) {
|
||||||
|
|
||||||
|
override fun buildModels(streamBundle: StreamBundle?) {
|
||||||
|
setFilterDuplicates(true)
|
||||||
|
if (streamBundle == null) {
|
||||||
|
for (i in 1..2) {
|
||||||
|
add(
|
||||||
|
CarouselShimmerGroup()
|
||||||
|
.id(i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (streamBundle.streamClusters.isNotEmpty()) {
|
||||||
|
if (streamBundle.streamClusters.size == 1) {
|
||||||
|
streamBundle
|
||||||
|
.streamClusters
|
||||||
|
.values
|
||||||
|
.filter { applyFilter(it) }
|
||||||
|
.forEach { streamCluster ->
|
||||||
|
streamCluster.clusterAppList.forEach {
|
||||||
|
add(
|
||||||
|
AppListViewModel_()
|
||||||
|
.id(it.id)
|
||||||
|
.app(it)
|
||||||
|
.click { _ -> callbacks.onAppClick(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
streamBundle
|
||||||
|
.streamClusters
|
||||||
|
.values
|
||||||
|
.filter { applyFilter(it) }
|
||||||
|
.forEach { streamCluster ->
|
||||||
|
add(CarouselModelGroup(streamCluster, callbacks))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -324,6 +324,8 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
|
|
||||||
inflateBetaSubscription(B.layoutDetailsBeta, app)
|
inflateBetaSubscription(B.layoutDetailsBeta, app)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inflateAppStream(B.epoxyRecyclerStream, app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,12 +29,14 @@ import android.widget.RelativeLayout
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.*
|
||||||
import com.aurora.gplayapi.data.models.Review
|
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
||||||
import com.aurora.gplayapi.helpers.ReviewsHelper
|
import com.aurora.gplayapi.helpers.ReviewsHelper
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.ViewState
|
||||||
import com.aurora.store.data.model.ExodusReport
|
import com.aurora.store.data.model.ExodusReport
|
||||||
import com.aurora.store.data.model.Report
|
import com.aurora.store.data.model.Report
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
@@ -47,11 +49,14 @@ import com.aurora.store.util.extensions.load
|
|||||||
import com.aurora.store.util.extensions.show
|
import com.aurora.store.util.extensions.show
|
||||||
import com.aurora.store.util.extensions.toast
|
import com.aurora.store.util.extensions.toast
|
||||||
import com.aurora.store.view.custom.RatingView
|
import com.aurora.store.view.custom.RatingView
|
||||||
|
import com.aurora.store.view.epoxy.controller.DetailsCarouselController
|
||||||
|
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||||
import com.aurora.store.view.epoxy.views.*
|
import com.aurora.store.view.epoxy.views.*
|
||||||
import com.aurora.store.view.epoxy.views.details.ReviewViewModel_
|
import com.aurora.store.view.epoxy.views.details.ReviewViewModel_
|
||||||
import com.aurora.store.view.epoxy.views.details.ScreenshotView
|
import com.aurora.store.view.epoxy.views.details.ScreenshotView
|
||||||
import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_
|
import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_
|
||||||
import com.aurora.store.view.ui.commons.BaseActivity
|
import com.aurora.store.view.ui.commons.BaseActivity
|
||||||
|
import com.aurora.store.viewmodel.details.DetailsClusterViewModel
|
||||||
import nl.komponents.kovenant.task
|
import nl.komponents.kovenant.task
|
||||||
import nl.komponents.kovenant.ui.failUi
|
import nl.komponents.kovenant.ui.failUi
|
||||||
import nl.komponents.kovenant.ui.successUi
|
import nl.komponents.kovenant.ui.successUi
|
||||||
@@ -69,7 +74,8 @@ abstract class BaseDetailsActivity : BaseActivity() {
|
|||||||
B.txtInstalls.text = CommonUtil.addDiPrefix(app.installs)
|
B.txtInstalls.text = CommonUtil.addDiPrefix(app.installs)
|
||||||
B.txtSize.text = CommonUtil.addSiPrefix(app.size)
|
B.txtSize.text = CommonUtil.addSiPrefix(app.size)
|
||||||
B.txtRating.text = app.labeledRating
|
B.txtRating.text = app.labeledRating
|
||||||
B.txtSdk.text = String.format("Target SDK %s", "${app.targetSdk}")
|
B.txtSdk.text = ("Target SDK ${app.targetSdk}")
|
||||||
|
B.txtUpdated.text = app.updatedOn
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
B.txtDescription.justificationMode = Layout.JUSTIFICATION_MODE_INTER_WORD
|
B.txtDescription.justificationMode = Layout.JUSTIFICATION_MODE_INTER_WORD
|
||||||
@@ -280,6 +286,57 @@ abstract class BaseDetailsActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun inflateAppStream(epoxyRecyclerView: EpoxyRecyclerView, app: App) {
|
||||||
|
app.detailsStreamUrl?.let {
|
||||||
|
val VM = ViewModelProvider(this).get(DetailsClusterViewModel::class.java)
|
||||||
|
|
||||||
|
val carouselController =
|
||||||
|
DetailsCarouselController(object : GenericCarouselController.Callbacks {
|
||||||
|
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||||
|
if (streamCluster.clusterBrowseUrl.isNotEmpty())
|
||||||
|
openStreamBrowseActivity(streamCluster.clusterBrowseUrl)
|
||||||
|
else
|
||||||
|
toast("Browse page unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||||
|
VM.observeCluster(streamCluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAppClick(app: App) {
|
||||||
|
openDetailsActivity(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAppLongClick(app: App) {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
VM.liveData.observe(this, {
|
||||||
|
when (it) {
|
||||||
|
is ViewState.Empty -> {
|
||||||
|
}
|
||||||
|
is ViewState.Loading -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
is ViewState.Error -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
is ViewState.Status -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
is ViewState.Success<*> -> {
|
||||||
|
carouselController.setData(it.data as StreamBundle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
epoxyRecyclerView.setController(carouselController)
|
||||||
|
|
||||||
|
VM.getStreamBundle(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateBetaActions(B: LayoutDetailsBetaBinding, isSubscribed: Boolean) {
|
private fun updateBetaActions(B: LayoutDetailsBetaBinding, isSubscribed: Boolean) {
|
||||||
if (isSubscribed) {
|
if (isSubscribed) {
|
||||||
B.btnBetaAction.text = getString(R.string.action_leave)
|
B.btnBetaAction.text = getString(R.string.action_leave)
|
||||||
@@ -305,7 +362,6 @@ abstract class BaseDetailsActivity : BaseActivity() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun openScreenshotActivity(app: App, position: Int) {
|
private fun openScreenshotActivity(app: App, position: Int) {
|
||||||
val intent = Intent(
|
val intent = Intent(
|
||||||
this,
|
this,
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.viewmodel.details
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.aurora.gplayapi.data.models.AuthData
|
||||||
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
|
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
||||||
|
import com.aurora.gplayapi.helpers.StreamHelper
|
||||||
|
import com.aurora.store.data.RequestState
|
||||||
|
import com.aurora.store.data.ViewState
|
||||||
|
import com.aurora.store.data.network.HttpClient
|
||||||
|
import com.aurora.store.data.providers.AuthProvider
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import com.aurora.store.viewmodel.BaseAndroidViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.supervisorScope
|
||||||
|
|
||||||
|
class DetailsClusterViewModel(application: Application) : BaseAndroidViewModel(application) {
|
||||||
|
|
||||||
|
private var authData: AuthData = AuthProvider.with(application).getAuthData()
|
||||||
|
private var appDetailsHelper = AppDetailsHelper(authData).using(HttpClient.getPreferredClient())
|
||||||
|
private var streamHelper = StreamHelper(authData)
|
||||||
|
|
||||||
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
|
var streamBundle: StreamBundle = StreamBundle()
|
||||||
|
|
||||||
|
lateinit var type: StreamHelper.Type
|
||||||
|
lateinit var category: StreamHelper.Category
|
||||||
|
|
||||||
|
override fun observe() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getStreamBundle(
|
||||||
|
streamUrl: String
|
||||||
|
) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
supervisorScope {
|
||||||
|
try {
|
||||||
|
streamBundle = appDetailsHelper.getDetailsStream(streamUrl)
|
||||||
|
liveData.postValue(ViewState.Success(streamBundle))
|
||||||
|
requestState = RequestState.Complete
|
||||||
|
} catch (e: Exception) {
|
||||||
|
requestState = RequestState.Pending
|
||||||
|
liveData.postValue(ViewState.Error(e.message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observeCluster(streamCluster: StreamCluster) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
supervisorScope {
|
||||||
|
try {
|
||||||
|
if (streamCluster.hasNext()) {
|
||||||
|
val newCluster =
|
||||||
|
streamHelper.getNextStreamCluster(streamCluster.clusterNextPageUrl)
|
||||||
|
updateCluster(newCluster)
|
||||||
|
liveData.postValue(ViewState.Success(streamBundle))
|
||||||
|
} else {
|
||||||
|
Log.i("End of cluster")
|
||||||
|
streamCluster.clusterNextPageUrl = String()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
liveData.postValue(ViewState.Error(e.message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCluster(newCluster: StreamCluster) {
|
||||||
|
streamBundle.streamClusters[newCluster.id]?.apply {
|
||||||
|
clusterAppList.addAll(newCluster.clusterAppList)
|
||||||
|
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,6 @@
|
|||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<solid android:color="?colorAccent" />
|
<solid android:color="?colorAccent" />
|
||||||
<size
|
<size
|
||||||
android:width="1dp"
|
android:width="1.25dp"
|
||||||
android:height="12dp" />
|
android:height="12dp" />
|
||||||
</shape>
|
</shape>
|
||||||
@@ -18,6 +18,8 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/container"
|
android:id="@+id/container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -88,6 +90,18 @@
|
|||||||
android:id="@+id/layout_details_beta"
|
android:id="@+id/layout_details_beta"
|
||||||
layout="@layout/layout_details_beta" />
|
layout="@layout/layout_details_beta" />
|
||||||
|
|
||||||
|
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
|
android:id="@+id/epoxy_recycler_stream"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="@dimen/padding_small"
|
||||||
|
android:paddingEnd="@dimen/padding_small"
|
||||||
|
app:itemSpacing="@dimen/margin_small"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/view_app" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/layout_details_dev"
|
android:id="@+id/layout_details_dev"
|
||||||
layout="@layout/layout_details_dev" />
|
layout="@layout/layout_details_dev" />
|
||||||
|
|||||||
@@ -33,70 +33,84 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.flexbox.FlexboxLayout
|
<HorizontalScrollView
|
||||||
android:id="@+id/layout_extras"
|
android:id="@+id/layout_extras"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="@dimen/padding_small"
|
android:padding="@dimen/padding_small"
|
||||||
app:alignContent="space_between"
|
android:scrollbars="none">
|
||||||
app:alignItems="center"
|
|
||||||
app:dividerDrawableHorizontal="@drawable/divider"
|
|
||||||
app:dividerDrawableVertical="@drawable/divider_line"
|
|
||||||
app:flexDirection="row"
|
|
||||||
app:flexWrap="wrap"
|
|
||||||
app:showDividerHorizontal="middle"
|
|
||||||
app:showDividerVertical="middle">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<LinearLayout
|
||||||
android:id="@+id/txt_rating"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
android:divider="@drawable/divider_line"
|
||||||
android:layout_marginEnd="@dimen/margin_small"
|
android:orientation="horizontal"
|
||||||
android:drawablePadding="@dimen/padding_small"
|
android:showDividers="middle">
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
|
||||||
app:drawableStartCompat="@drawable/ic_star"
|
|
||||||
app:drawableTint="?colorAccent"
|
|
||||||
tools:text="3.5" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/txt_installs"
|
android:id="@+id/txt_rating"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
android:layout_marginEnd="@dimen/margin_small"
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
android:drawablePadding="@dimen/padding_small"
|
android:drawablePadding="@dimen/padding_small"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||||
app:drawableStartCompat="@drawable/ic_download"
|
app:drawableStartCompat="@drawable/ic_star"
|
||||||
app:drawableTint="?colorAccent"
|
app:drawableTint="?colorAccent"
|
||||||
tools:text="2500" />
|
tools:text="3.5" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/txt_size"
|
android:id="@+id/txt_installs"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
android:layout_marginEnd="@dimen/margin_small"
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
android:drawablePadding="@dimen/padding_small"
|
android:drawablePadding="@dimen/padding_small"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||||
app:drawableTint="?colorAccent"
|
app:drawableStartCompat="@drawable/ic_download"
|
||||||
tools:text="25 MB" />
|
app:drawableTint="?colorAccent"
|
||||||
|
tools:text="2500" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:id="@+id/txt_sdk"
|
android:id="@+id/txt_size"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/margin_small"
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
android:layout_marginEnd="@dimen/margin_small"
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
android:drawablePadding="@dimen/padding_small"
|
android:drawablePadding="@dimen/padding_small"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||||
app:drawableTint="?colorAccent"
|
app:drawableTint="?colorAccent"
|
||||||
tools:text="Android 7+" />
|
tools:text="25 MB" />
|
||||||
</com.google.android.flexbox.FlexboxLayout>
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/txt_sdk"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
|
android:drawablePadding="@dimen/padding_small"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||||
|
app:drawableTint="?colorAccent"
|
||||||
|
tools:text="Android 7+" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/txt_updated"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_small"
|
||||||
|
android:layout_marginEnd="@dimen/margin_small"
|
||||||
|
android:drawablePadding="@dimen/padding_small"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||||
|
app:drawableTint="?colorAccent"
|
||||||
|
tools:text="Jan 21 2020" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_description"
|
android:id="@+id/layout_description"
|
||||||
|
|||||||
Reference in New Issue
Block a user