Rebuild unit converter in Jetpack Compose
This commit is contained in:
parent
89a2754eaf
commit
120bca4b39
@ -109,8 +109,6 @@ dependencies {
|
||||
|
||||
implementation(libs.bundles.materialdialogs)
|
||||
|
||||
implementation(libs.bundles.groupie)
|
||||
|
||||
implementation(libs.draglinearlayout)
|
||||
implementation(libs.viewpropertyobjectanimator)
|
||||
|
||||
|
||||
@ -100,13 +100,6 @@ val OpenSourceLicenses = arrayOf(
|
||||
copyrightNote = "Copyright (C) 2020 Wasabeef",
|
||||
url = "https://github.com/afollestad/material-dialogs"
|
||||
),
|
||||
OpenSourceLibrary(
|
||||
name = "Groupie",
|
||||
description = "Groupie is a simple, flexible library for complex RecyclerView layouts.",
|
||||
licenseName = R.string.mit_license_name,
|
||||
licenseText = R.raw.license_mit,
|
||||
url = "https://github.com/lisawray/groupie"
|
||||
),
|
||||
OpenSourceLibrary(
|
||||
name = "DragLinearLayout",
|
||||
description = "An Android LinearLayout that supports draggable and swappable child Views",
|
||||
|
||||
@ -321,21 +321,6 @@ dependencyResolutionManagement {
|
||||
)
|
||||
)
|
||||
|
||||
version("groupie", "2.8.0")
|
||||
alias("groupie.core")
|
||||
.to("com.xwray", "groupie")
|
||||
.versionRef("groupie")
|
||||
alias("groupie.ktx")
|
||||
.to("com.xwray", "groupie-kotlin-android-extensions")
|
||||
.versionRef("groupie")
|
||||
bundle(
|
||||
"groupie",
|
||||
listOf(
|
||||
"groupie.core",
|
||||
"groupie.ktx"
|
||||
)
|
||||
)
|
||||
|
||||
alias("draglinearlayout")
|
||||
.to("com.jmedeisis", "draglinearlayout")
|
||||
.version("1.1.0")
|
||||
|
||||
@ -71,7 +71,6 @@ dependencies {
|
||||
implementation(libs.glide)
|
||||
implementation(libs.draglinearlayout)
|
||||
implementation(libs.lottie.core)
|
||||
implementation(libs.bundles.groupie)
|
||||
implementation(libs.glidetransformations)
|
||||
|
||||
implementation(libs.accompanist.insets)
|
||||
|
||||
@ -1,49 +1,37 @@
|
||||
package de.mm20.launcher2.ui.legacy.component
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.xwray.groupie.ExpandableGroup
|
||||
import com.xwray.groupie.ExpandableItem
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.Section
|
||||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||
import com.xwray.groupie.kotlinandroidextensions.Item
|
||||
import de.mm20.launcher2.ktx.sp
|
||||
import de.mm20.launcher2.search.data.CurrencyUnitConverter
|
||||
import de.mm20.launcher2.search.data.UnitConverter
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.LegacyLauncherTheme
|
||||
import de.mm20.launcher2.ui.databinding.ViewUnitconverterBinding
|
||||
import de.mm20.launcher2.ui.search.UnitConverterItem
|
||||
import de.mm20.launcher2.unitconverter.UnitConverterViewModel
|
||||
import de.mm20.launcher2.unitconverter.UnitValue
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
class UnitConverterView : FrameLayout {
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(context, attrs, defStyleRes)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleRes
|
||||
)
|
||||
|
||||
private val unitConverter: LiveData<UnitConverter?>
|
||||
private val adapter: GroupAdapter<GroupieViewHolder>
|
||||
|
||||
private val binding = ViewUnitconverterBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
|
||||
@ -54,141 +42,24 @@ class UnitConverterView : FrameLayout {
|
||||
if (it == null) visibility = View.GONE
|
||||
else {
|
||||
visibility = View.VISIBLE
|
||||
bind(it)
|
||||
}
|
||||
})
|
||||
adapter = GroupAdapter()
|
||||
binding.unitConverterValues.also {
|
||||
it.adapter = adapter
|
||||
it.addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun bind(converter: UnitConverter) {
|
||||
val title = converter.inputValue.formattedValue + " " + converter.inputValue.formattedName
|
||||
binding.unitConverterInput.text = title
|
||||
|
||||
/*val sb = StringBuilder()
|
||||
for (unit in converter.values) {
|
||||
|
||||
sb.append("${unit.formatted}\n")
|
||||
}
|
||||
sb.removeSuffix("\n")
|
||||
unitConverterValues.text = sb.toString()
|
||||
unitConverterIcon.setImageResource(when (converter.dimension) {
|
||||
Dimension.LENGTH -> R.drawable.ic_unit_length
|
||||
Dimension.MASS -> R.drawable.ic_unit_mass
|
||||
Dimension.TIME -> R.drawable.ic_unit_time
|
||||
Dimension.DATA -> R.drawable.ic_unit_datasize
|
||||
Dimension.VELOCITY -> R.drawable.ic_unit_velocity
|
||||
else -> 0
|
||||
})*/
|
||||
|
||||
adapter.clear()
|
||||
|
||||
val maxValueLength = converter.values.maxByOrNull { it.formattedValue.length }?.formattedValue?.length
|
||||
?: 0
|
||||
|
||||
val section = Section().apply {
|
||||
addAll(converter.values.subList(0, min(converter.values.size, 5)).map {
|
||||
ValueItem(it, maxValueLength * 8f * sp)
|
||||
})
|
||||
}.also {
|
||||
adapter.add(it)
|
||||
}
|
||||
|
||||
if (converter.values.size > 5) {
|
||||
binding.showAllButton.visibility = View.VISIBLE
|
||||
binding.showAllButton.setOnClickListener {
|
||||
section.addAll(converter.values.subList( 5, converter.values.size).map {
|
||||
ValueItem(it, maxValueLength * 8f * sp)
|
||||
})
|
||||
binding.showAllButton.visibility = View.GONE
|
||||
binding.showAllButton.setOnClickListener(null)
|
||||
}
|
||||
} else {
|
||||
binding.showAllButton.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
if (converter is CurrencyUnitConverter) {
|
||||
val df = DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
val date = Date().apply {
|
||||
time = converter.updateTimestamp
|
||||
}
|
||||
val infoText = SpannableStringBuilder()
|
||||
.append("European Central Bank (${df.format(date)})", object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
CustomTabsIntent
|
||||
.Builder()
|
||||
.setToolbarColor(0xFF003299.toInt())
|
||||
.build()
|
||||
.launchUrl(context,
|
||||
Uri.parse("https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html"))
|
||||
binding.composeView.setContent {
|
||||
val converter by unitConverter.observeAsState()
|
||||
LegacyLauncherTheme {
|
||||
// TODO: Temporary solution until parent widget card is rewritten in Compose
|
||||
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
|
||||
Column {
|
||||
converter?.let {
|
||||
UnitConverterItem(
|
||||
unitConverter = it,
|
||||
)
|
||||
}
|
||||
}, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
.append(" • ")
|
||||
.append(context.getString(R.string.disclaimer), object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
MaterialDialog(context).show {
|
||||
title(res = R.string.disclaimer)
|
||||
message(res = R.string.disclaimer_currency_converter)
|
||||
positiveButton(res = R.string.close) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
binding.unitConverterInfo.apply {
|
||||
text = infoText
|
||||
visibility = View.VISIBLE
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
} else {
|
||||
binding.unitConverterInfo.visibility = View.GONE
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ValueItem(private val value: UnitValue, val valueWidth: Float) : Item() {
|
||||
override fun getLayout(): Int {
|
||||
return R.layout.unit_converter_row
|
||||
}
|
||||
|
||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
||||
viewHolder.itemView.findViewById<TextView>(R.id.value).let {
|
||||
it.text = value.formattedValue
|
||||
it.layoutParams = it.layoutParams.apply {
|
||||
width = valueWidth.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
viewHolder.itemView.findViewById<TextView>(R.id.name).text = value.formattedName
|
||||
viewHolder.itemView.findViewById<TextView>(R.id.symbol).text = value.symbol
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ExpandItem : Item(), ExpandableItem {
|
||||
|
||||
private lateinit var expandableGroup: ExpandableGroup
|
||||
|
||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
||||
viewHolder.itemView.visibility = if (expandableGroup.isExpanded) View.GONE else View.VISIBLE
|
||||
viewHolder.itemView.setOnClickListener {
|
||||
expandableGroup.onToggleExpanded()
|
||||
viewHolder.itemView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLayout(): Int {
|
||||
return R.layout.unit_converter_show_all
|
||||
}
|
||||
|
||||
override fun setExpandableGroup(onToggleListener: ExpandableGroup) {
|
||||
expandableGroup = onToggleListener
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
package de.mm20.launcher2.ui.search
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.search.data.UnitConverter
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.unitconverter.Dimension
|
||||
|
||||
@Composable
|
||||
fun UnitConverterItem(
|
||||
unitConverter: UnitConverter,
|
||||
) {
|
||||
var showAll by remember { mutableStateOf(false) }
|
||||
Column(
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = unitConverter.inputValue.let { "${it.formattedValue} ${it.formattedName}" },
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
softWrap = false
|
||||
)
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.padding(12.dp)
|
||||
.size(48.dp),
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = getDimensionIcon(unitConverter.dimension),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
Column {
|
||||
for ((i, unit) in unitConverter.values.withIndex()) {
|
||||
if (!showAll && i >= 5) break
|
||||
Text(
|
||||
text = unit.formattedValue,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(
|
||||
fontWeight = FontWeight.Bold
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, bottom = 12.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Column {
|
||||
for ((i, unit) in unitConverter.values.withIndex()) {
|
||||
if (!showAll && i >= 5) break
|
||||
val text = AnnotatedString.Builder().apply {
|
||||
append(" ${unit.formattedName} (${unit.symbol})")
|
||||
}.toAnnotatedString()
|
||||
Text(
|
||||
text = "${unit.formattedName} (${unit.symbol})",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp, bottom = 12.dp, start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!showAll && unitConverter.values.size > 5) {
|
||||
TextButton(
|
||||
onClick = { showAll = true },
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(horizontal = 12.dp)
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.unit_converter_show_all))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getDimensionIcon(dimension: Dimension): ImageVector {
|
||||
return when (dimension) {
|
||||
Dimension.Mass -> Icons.Rounded.FitnessCenter
|
||||
Dimension.Length -> Icons.Rounded.Straighten
|
||||
Dimension.Velocity -> Icons.Rounded.Speed
|
||||
Dimension.Volume -> TODO()
|
||||
Dimension.Area -> Icons.Rounded.SquareFoot
|
||||
Dimension.Currency -> Icons.Rounded.Toll
|
||||
Dimension.Data -> Icons.Rounded.Storage
|
||||
Dimension.Bitrate -> TODO()
|
||||
Dimension.Pressure -> TODO()
|
||||
Dimension.Energy -> Icons.Rounded.Bolt
|
||||
Dimension.Frequency -> TODO()
|
||||
Dimension.Temperature -> Icons.Rounded.Thermostat
|
||||
Dimension.Time -> Icons.Rounded.Schedule
|
||||
}
|
||||
}
|
||||
@ -10,89 +10,9 @@
|
||||
android:clipToPadding="false"
|
||||
android:elevation="@dimen/card_elevation">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/composeView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unitConverterInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="13 kilometers" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unitConverterInfo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:paddingTop="4dp"
|
||||
android:textColor="@color/text_color_secondary"
|
||||
android:textSize="10sp"
|
||||
android:textStyle="italic"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/showAllButton"
|
||||
tools:text="European Central Bank (24.04.2020)" />
|
||||
|
||||
<!--<TextView
|
||||
android:id="@+id/unitConverterValues"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/unitConverterIcon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/unitConverterInput"
|
||||
tools:text="0.013 kilometers (km)\n130 centimeters (cm)\n1300 millimeters (mm)\n 13.1234 yards (yd)"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/unitConverterIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@tools:sample/avatars"/>-->
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/unitConverterValues"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:orientation="vertical"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/unitConverterInput" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/showAllButton"
|
||||
android:visibility="gone"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/unit_converter_show_all"
|
||||
android:paddingBottom="4dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/unitConverterValues" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</de.mm20.launcher2.ui.legacy.view.LauncherCardView>
|
||||
Loading…
x
Reference in New Issue
Block a user