Clean up & Fixes
This commit is contained in:
parent
27cd0a829e
commit
e810208a14
10 changed files with 171 additions and 142 deletions
|
@ -6,7 +6,6 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewStub
|
||||
import androidx.fragment.app.Fragment
|
||||
import partisan.weforge.xyz.pulse.databinding.FragmentAboutBinding
|
||||
|
||||
|
|
|
@ -82,7 +82,12 @@ class CallRedirectionService : CallRedirectionService() {
|
|||
return
|
||||
}
|
||||
|
||||
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
|
||||
if (prefs.popupEnabled) {
|
||||
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
|
||||
} else {
|
||||
window.call(record.uri)
|
||||
cancelCall()
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.READ_CONTACTS)
|
||||
|
|
|
@ -2,8 +2,6 @@ package partisan.weforge.xyz.pulse
|
|||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import partisan.weforge.xyz.pulse.databinding.ActivityMainBinding
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
|
|
|
@ -8,8 +8,6 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.core.content.getSystemService
|
||||
import partisan.weforge.xyz.pulse.databinding.FragmentPopupSettingsBinding
|
||||
|
||||
|
@ -33,48 +31,42 @@ class PopupSettingsFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val services = listOf(
|
||||
ServiceEntry(
|
||||
"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call",
|
||||
R.string.destination_signal,
|
||||
requireContext().isServiceEnabled("vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call")
|
||||
),
|
||||
ServiceEntry(
|
||||
"vnd.android.cursor.item/vnd.org.telegram.messenger.android.call",
|
||||
R.string.destination_telegram,
|
||||
requireContext().isServiceEnabled("vnd.android.cursor.item/vnd.org.telegram.messenger.android.call")
|
||||
),
|
||||
ServiceEntry(
|
||||
"vnd.android.cursor.item/vnd.ch.threema.app.call",
|
||||
R.string.destination_threema,
|
||||
requireContext().isServiceEnabled("vnd.android.cursor.item/vnd.ch.threema.app.call")
|
||||
),
|
||||
ServiceEntry(
|
||||
"vnd.android.cursor.item/vnd.com.whatsapp.voip.call",
|
||||
R.string.destination_whatsapp,
|
||||
requireContext().isServiceEnabled("vnd.android.cursor.item/vnd.com.whatsapp.voip.call")
|
||||
),
|
||||
)
|
||||
prefs = Preferences(requireContext())
|
||||
window = PopupWindow(requireContext(), null)
|
||||
|
||||
val adapter = ServiceAdapter(
|
||||
context = requireContext(),
|
||||
services = services.toMutableList(),
|
||||
onReordered = { updatedList ->
|
||||
updatedList.forEachIndexed { index, entry ->
|
||||
requireContext().setServicePriority(entry.mimetype, index)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
binding.serviceRecycler.adapter = adapter
|
||||
binding.serviceRecycler.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
val touchHelper = ItemTouchHelper(adapter.dragHelper)
|
||||
touchHelper.attachToRecyclerView(binding.serviceRecycler)
|
||||
|
||||
adapter.setDragStartListener { viewHolder ->
|
||||
touchHelper.startDrag(viewHolder)
|
||||
binding.popupEnabledCheckbox.isChecked = prefs.popupEnabled
|
||||
binding.popupEnabledCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.popupEnabled = isChecked
|
||||
updateControls(isChecked)
|
||||
}
|
||||
|
||||
binding.popupPreview.setOnClickListener {
|
||||
window.preview()
|
||||
}
|
||||
|
||||
binding.redirectionDelay.value = (prefs.redirectionDelay / 1000).toFloat()
|
||||
binding.redirectionDelay.setLabelFormatter {
|
||||
String.format("%.1f", it)
|
||||
}
|
||||
binding.redirectionDelay.addOnChangeListener { _, value, _ ->
|
||||
prefs.redirectionDelay = (value * 1000).toLong()
|
||||
}
|
||||
|
||||
val screenHeight = getScreenHeightPx()
|
||||
binding.popupHeightSlider.valueFrom = 0f
|
||||
binding.popupHeightSlider.valueTo = screenHeight.toFloat()
|
||||
binding.popupHeightSlider.value = prefs.popupPosition.toFloat()
|
||||
binding.popupHeightSlider.addOnChangeListener { _, value, _ ->
|
||||
prefs.popupPosition = value.toInt().coerceIn(0, screenHeight)
|
||||
}
|
||||
|
||||
updateControls(prefs.popupEnabled)
|
||||
}
|
||||
|
||||
private fun updateControls(enabled: Boolean) {
|
||||
binding.redirectionDelay.isEnabled = enabled
|
||||
binding.popupHeightSlider.isEnabled = enabled
|
||||
binding.popupPreview.isEnabled = enabled
|
||||
}
|
||||
|
||||
private fun getScreenHeightPx(): Int {
|
||||
|
|
|
@ -23,7 +23,6 @@ import kotlin.concurrent.timerTask
|
|||
import android.util.Log
|
||||
import android.content.res.ColorStateList
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.google.android.material.color.utilities.MaterialDynamicColors
|
||||
import com.google.android.material.color.MaterialColors
|
||||
|
||||
class PopupWindow(
|
||||
|
@ -121,7 +120,7 @@ class PopupWindow(
|
|||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.CALL_PHONE)
|
||||
private fun call(data: Uri) {
|
||||
fun call(data: Uri) {
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
this.data = data
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
|
|
@ -11,6 +11,7 @@ class Preferences(private val context: Context) {
|
|||
private const val POPUP_POSITION = "popup_position_y"
|
||||
private const val POPUP_ENABLED = "popup_enabled"
|
||||
private const val BLACKLISTED_CONTACTS = "blacklisted_contacts"
|
||||
private val SERVICE_ORDER_KEY = "service_order"
|
||||
|
||||
private const val DEFAULT_REDIRECTION_DELAY = 2000L
|
||||
private const val DEFAULT_POPUP_POSITION = 333
|
||||
|
@ -51,7 +52,18 @@ class Preferences(private val context: Context) {
|
|||
}
|
||||
|
||||
fun getServicePriority(mimetype: String): Int {
|
||||
return prefs.getInt(makeKeyPriority(mimetype), Int.MAX_VALUE)
|
||||
val order = getServiceOrder()
|
||||
val index = order.indexOf(mimetype)
|
||||
return if (index != -1) index else Int.MAX_VALUE
|
||||
}
|
||||
|
||||
fun getServiceOrder(): List<String> {
|
||||
val stored = prefs.getString(SERVICE_ORDER_KEY, null)
|
||||
return stored?.split("|")?.filter { it.isNotBlank() } ?: emptyList()
|
||||
}
|
||||
|
||||
fun setServiceOrder(order: List<String>) {
|
||||
prefs.edit().putString(SERVICE_ORDER_KEY, order.joinToString("|")).apply()
|
||||
}
|
||||
|
||||
fun setServiceEnabled(mimetype: String, enabled: Boolean) {
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.util.AttributeSet
|
|||
import android.view.Choreographer
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import kotlin.math.*
|
||||
import kotlin.random.Random
|
||||
|
||||
|
@ -64,7 +63,7 @@ class SecretView @JvmOverloads constructor(
|
|||
private var score = 0
|
||||
|
||||
private val bullets = mutableListOf<Bullet>()
|
||||
private val mediumBulletsToFire = mutableListOf<Bullet>()
|
||||
private val enemyBullets = mutableListOf<Bullet>()
|
||||
private val enemies = mutableListOf<Enemy>()
|
||||
private val rockets = mutableListOf<Rocket>()
|
||||
private val stars = mutableListOf<Star>()
|
||||
|
@ -164,9 +163,9 @@ class SecretView @JvmOverloads constructor(
|
|||
}
|
||||
rockets.removeIf { it.x < 0 || it.x > viewWidth || it.y < 0 || it.y > viewHeight }
|
||||
|
||||
bullets.addAll(mediumBulletsToFire)
|
||||
mediumBulletsToFire.clear()
|
||||
|
||||
enemyBullets.forEach { it.y += it.dy * deltaMs / 16f }
|
||||
enemyBullets.removeIf { it.y > viewHeight }
|
||||
|
||||
checkCollisions()
|
||||
spawnEnemies(deltaMs)
|
||||
}
|
||||
|
@ -190,6 +189,14 @@ class SecretView @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
for (b in enemyBullets) {
|
||||
if (hypot(b.x - playerX, b.y - (viewHeight - 100f)) < 20f) {
|
||||
explosions.add(Explosion(playerX, viewHeight - 100f))
|
||||
gameOver = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val rocketIter = rockets.iterator()
|
||||
while (rocketIter.hasNext()) {
|
||||
val rocket = rocketIter.next()
|
||||
|
@ -258,7 +265,7 @@ class SecretView @JvmOverloads constructor(
|
|||
// The group sync isn't working as enemies gradually get out of sync, but whatever, it's fine.
|
||||
val enemy = when (currentWaveType) {
|
||||
"easy" -> EnemyEasy(x, baseY)
|
||||
"medium" -> EnemyMedium(x, baseY, sharedOffset, sharedFireTime) { mediumBulletsToFire.add(it) }
|
||||
"medium" -> EnemyMedium(x, baseY, sharedOffset, sharedFireTime) { enemyBullets.add(it) }
|
||||
"hard" -> EnemyHard(x, baseY, { rockets.add(it) }, sharedOffset, sharedFireTime)
|
||||
else -> EnemyEasy(x, baseY)
|
||||
}
|
||||
|
@ -271,6 +278,7 @@ class SecretView @JvmOverloads constructor(
|
|||
|
||||
private fun resetGame() {
|
||||
bullets.clear()
|
||||
enemyBullets.clear()
|
||||
enemies.clear()
|
||||
rockets.clear()
|
||||
explosions.clear()
|
||||
|
@ -280,6 +288,8 @@ class SecretView @JvmOverloads constructor(
|
|||
enemiesLeftInWave = 0
|
||||
gameOver = false
|
||||
playerX = viewWidth / 2f
|
||||
lastLogicTime = System.nanoTime()
|
||||
Choreographer.getInstance().postFrameCallback(this)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
|
@ -289,6 +299,8 @@ class SecretView @JvmOverloads constructor(
|
|||
canvas.drawColor(Color.parseColor("#121212"))
|
||||
stars.forEach { canvas.drawCircle(it.x, it.y, it.radius, starPaint) }
|
||||
|
||||
enemyBullets.forEach { canvas.drawLine(it.x, it.y, it.x, it.y + 20f, rocketPaint) }
|
||||
|
||||
rockets.forEach { rocket ->
|
||||
rocket.trail.forEachIndexed { index, (x, y) ->
|
||||
val alpha = ((1f - index / 20f.toFloat()) * 255).toInt()
|
||||
|
@ -328,17 +340,14 @@ class SecretView @JvmOverloads constructor(
|
|||
} else {
|
||||
canvas.drawText("Game Over", viewWidth / 2f, viewHeight / 2f - 60f, textPaint)
|
||||
canvas.drawText("Score: $score", viewWidth / 2f, viewHeight / 2f + 10f, textPaint)
|
||||
canvas.drawRoundRect(retryRect, 20f, 20f, retryPaint)
|
||||
canvas.drawText("Retry", retryRect.centerX(), retryRect.centerY() + 16f, retryTextPaint)
|
||||
canvas.drawText("Tap to restart", retryRect.centerX(), retryRect.centerY() + 16f, retryTextPaint)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (gameOver && event.action == MotionEvent.ACTION_DOWN) {
|
||||
if (retryRect.contains(event.x, event.y)) {
|
||||
resetGame()
|
||||
return true
|
||||
}
|
||||
resetGame()
|
||||
return true
|
||||
}
|
||||
|
||||
when (event.action) {
|
||||
|
@ -419,7 +428,7 @@ class SecretView @JvmOverloads constructor(
|
|||
override var y: Float,
|
||||
private val offset: Float,
|
||||
private var fireTimer: Long,
|
||||
val fireBullet: (Bullet) -> Unit
|
||||
val fireEnemyBullet: (Bullet) -> Unit
|
||||
) : Enemy {
|
||||
override fun update(deltaMs: Long) {
|
||||
y += 2.5f * deltaMs / 16f
|
||||
|
@ -427,7 +436,7 @@ class SecretView @JvmOverloads constructor(
|
|||
|
||||
fireTimer -= deltaMs
|
||||
if (fireTimer <= 0) {
|
||||
fireBullet(Bullet(x, y + 30f, dy = 10f))
|
||||
fireEnemyBullet(Bullet(x, y + 30f, dy = 10f))
|
||||
fireTimer = Random.nextLong(3000L, 6000L)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class ServiceSettingsFragment : Fragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val services = listOf(
|
||||
val available = listOf(
|
||||
ServiceEntry(
|
||||
"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call",
|
||||
R.string.destination_signal,
|
||||
|
@ -49,13 +49,24 @@ class ServiceSettingsFragment : Fragment() {
|
|||
),
|
||||
)
|
||||
|
||||
val storedOrder = Preferences(requireContext()).getServiceOrder()
|
||||
|
||||
val ordered = storedOrder.mapNotNull { mime ->
|
||||
available.find { it.mimetype == mime }
|
||||
}.toMutableList()
|
||||
|
||||
// Add any missing services that weren't stored (e.g., first run)
|
||||
val missing = available.filterNot { s -> ordered.any { it.mimetype == s.mimetype } }
|
||||
ordered += missing
|
||||
|
||||
val prefs = Preferences(requireContext())
|
||||
|
||||
val adapter = ServiceAdapter(
|
||||
context = requireContext(),
|
||||
services = services.toMutableList(),
|
||||
services = ordered,
|
||||
onReordered = { updatedList ->
|
||||
updatedList.forEachIndexed { index, entry ->
|
||||
requireContext().setServicePriority(entry.mimetype, index)
|
||||
}
|
||||
val newOrder = updatedList.map { it.mimetype }
|
||||
prefs.setServiceOrder(newOrder)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package partisan.weforge.xyz.pulse
|
||||
|
||||
import android.Manifest
|
||||
import android.app.role.RoleManager
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<ScrollView
|
||||
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"
|
||||
|
@ -8,82 +8,88 @@
|
|||
android:padding="32dp"
|
||||
tools:context=".PopupSettingsFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/popup_settings_description"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/popupEnabledCheckbox"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/popup_enabled"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/description"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/popupPreview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/test"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/popupEnabledCheckbox"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/popup_settings_description"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/redirectionDelay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stepSize="0.5"
|
||||
android:valueFrom="2"
|
||||
android:valueTo="4"
|
||||
android:layout_marginTop="8dp"
|
||||
android:contentDescription="@string/redirection_delay_description" />
|
||||
<CheckBox
|
||||
android:id="@+id/popupEnabledCheckbox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/popup_enabled"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/description"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/popupPreview"
|
||||
android:layout_marginTop="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/delayDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/redirection_delay_description"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginTop="4dp" />
|
||||
<Button
|
||||
android:id="@+id/popupPreview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/test"
|
||||
app:layout_constraintTop_toTopOf="@id/popupEnabledCheckbox"
|
||||
app:layout_constraintBottom_toBottomOf="@id/popupEnabledCheckbox"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="8dp" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/popupHeightSlider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="100"
|
||||
android:stepSize="1"
|
||||
android:layout_marginTop="16dp"
|
||||
android:contentDescription="@string/popup_position" />
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/redirectionDelay"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:stepSize="0.5"
|
||||
android:valueFrom="2"
|
||||
android:valueTo="4"
|
||||
android:contentDescription="@string/redirection_delay_description"
|
||||
app:layout_constraintTop_toBottomOf="@id/popupEnabledCheckbox"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginTop="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/heightDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/popup_position"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginTop="4dp" />
|
||||
<TextView
|
||||
android:id="@+id/delayDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/redirection_delay_description"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/redirectionDelay"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginTop="4dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/popupHeightSlider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="100"
|
||||
android:stepSize="1"
|
||||
android:contentDescription="@string/popup_position"
|
||||
app:layout_constraintTop_toBottomOf="@id/delayDescription"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginTop="16dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/serviceRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_marginTop="8dp" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<TextView
|
||||
android:id="@+id/heightDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/popup_position"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/popupHeightSlider"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginTop="4dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue