mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-09-26 19:29:04 +00:00
Attempt at a loader game.
This commit is contained in:
parent
c6caa59a90
commit
cc247ce634
6 changed files with 302 additions and 71 deletions
|
@ -32,7 +32,6 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentContainerView
|
import androidx.fragment.app.FragmentContainerView
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.whenStateAtLeast
|
|
||||||
import androidx.lifecycle.withStateAtLeast
|
import androidx.lifecycle.withStateAtLeast
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import com.futo.platformplayer.BuildConfig
|
import com.futo.platformplayer.BuildConfig
|
||||||
|
@ -114,7 +113,6 @@ import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import java.util.Queue
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
|
@ -610,6 +608,8 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
}, UIDialogs.ActionStyle.PRIMARY)
|
}, UIDialogs.ActionStyle.PRIMARY)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//startActivity(Intent(this, TestActivity::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -2,12 +2,24 @@ package com.futo.platformplayer.activities
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
|
import com.futo.platformplayer.views.TargetTapLoaderView
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class TestActivity : AppCompatActivity() {
|
class TestActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_test);
|
setContentView(R.layout.activity_test);
|
||||||
|
|
||||||
|
val view = findViewById<TargetTapLoaderView>(R.id.test_view)
|
||||||
|
view.startLoader(10000)
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
delay(5000)
|
||||||
|
view.startLoader()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -0,0 +1,273 @@
|
||||||
|
package com.futo.platformplayer.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.*
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
|
import androidx.core.graphics.toColorInt
|
||||||
|
import kotlin.math.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class TargetTapLoaderView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null
|
||||||
|
) : View(context, attrs) {
|
||||||
|
|
||||||
|
private var expectedDurationMs: Long? = null
|
||||||
|
private var startTime: Long = 0L
|
||||||
|
private var loaderFinished = false
|
||||||
|
private var forceIndeterminate = false
|
||||||
|
|
||||||
|
private val isIndeterminate: Boolean
|
||||||
|
get() = forceIndeterminate || expectedDurationMs == null || expectedDurationMs == 0L
|
||||||
|
|
||||||
|
private val targets = mutableListOf<Target>()
|
||||||
|
private val particles = mutableListOf<Particle>()
|
||||||
|
private var score = 0
|
||||||
|
|
||||||
|
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 48f
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
setShadowLayer(4f, 0f, 0f, Color.BLACK)
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
}
|
||||||
|
private val progressBarPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = "#2D63ED".toColorInt()
|
||||||
|
}
|
||||||
|
private val spinnerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = "#2D63ED".toColorInt()
|
||||||
|
strokeWidth = 12f
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeCap = Paint.Cap.ROUND
|
||||||
|
}
|
||||||
|
private val outerRingPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.WHITE
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
private val middleRingPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.RED
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
private val centerDotPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.YELLOW
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
private val shadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = Color.argb(50, 0, 0, 0)
|
||||||
|
}
|
||||||
|
private val backgroundPaint = Paint()
|
||||||
|
|
||||||
|
private var spinnerAngle = 0f
|
||||||
|
|
||||||
|
private val frameRunnable = object : Runnable {
|
||||||
|
override fun run() {
|
||||||
|
invalidate()
|
||||||
|
if (!loaderFinished) postDelayed(this, 16L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
setOnTouchListener { _, event ->
|
||||||
|
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||||
|
handleTap(event.x, event.y)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startLoader(durationMs: Long? = null) {
|
||||||
|
expectedDurationMs = durationMs?.takeIf { it > 0L }
|
||||||
|
forceIndeterminate = expectedDurationMs == null
|
||||||
|
loaderFinished = false
|
||||||
|
startTime = System.currentTimeMillis()
|
||||||
|
score = 0
|
||||||
|
targets.clear()
|
||||||
|
particles.clear()
|
||||||
|
removeCallbacks(frameRunnable)
|
||||||
|
post(frameRunnable)
|
||||||
|
post { spawnTarget() }
|
||||||
|
|
||||||
|
if (!isIndeterminate) {
|
||||||
|
postDelayed({
|
||||||
|
if (!loaderFinished) {
|
||||||
|
forceIndeterminate = true
|
||||||
|
startTime = System.currentTimeMillis()
|
||||||
|
spawnTarget()
|
||||||
|
}
|
||||||
|
}, expectedDurationMs!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finishLoader() {
|
||||||
|
loaderFinished = true
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopAndResetLoader() {
|
||||||
|
loaderFinished = true
|
||||||
|
targets.clear()
|
||||||
|
particles.clear()
|
||||||
|
removeCallbacks(frameRunnable)
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleTap(x: Float, y: Float) {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val hitIndex = targets.indexOfFirst { t -> !t.hit && hypot(x - t.x, y - t.y) <= t.radius }
|
||||||
|
if (hitIndex >= 0) {
|
||||||
|
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
|
||||||
|
val target = targets[hitIndex]
|
||||||
|
target.hit = true
|
||||||
|
target.hitTime = now
|
||||||
|
score += if (!isIndeterminate) 10 else 5
|
||||||
|
spawnParticles(target.x, target.y, target.radius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun spawnTarget() {
|
||||||
|
if (loaderFinished) return
|
||||||
|
val radius = Random.nextInt(40, 80).toFloat()
|
||||||
|
val x = Random.nextFloat() * (width - 2 * radius) + radius
|
||||||
|
val y = Random.nextFloat() * (height - 2 * radius - 60f) + radius
|
||||||
|
targets.add(Target(x, y, radius, System.currentTimeMillis()))
|
||||||
|
|
||||||
|
val delay = if (isIndeterminate) 1400L else 700L
|
||||||
|
postDelayed({ spawnTarget() }, delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun spawnParticles(cx: Float, cy: Float, radius: Float) {
|
||||||
|
repeat(12) {
|
||||||
|
val angle = Random.nextFloat() * 2f * PI.toFloat()
|
||||||
|
val speed = Random.nextFloat() * 5f + 2f
|
||||||
|
val vx = cos(angle) * speed
|
||||||
|
val vy = sin(angle) * speed
|
||||||
|
particles.add(Particle(cx, cy, vx, vy, 255, System.currentTimeMillis()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
drawBackground(canvas)
|
||||||
|
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
drawTargets(canvas, now)
|
||||||
|
drawParticles(canvas, now)
|
||||||
|
|
||||||
|
if (!loaderFinished) {
|
||||||
|
if (isIndeterminate) drawIndeterminateSpinner(canvas)
|
||||||
|
else drawDeterministicProgressBar(canvas, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawText("Score: $score", width / 2f, height - 80f, textPaint)
|
||||||
|
|
||||||
|
if (loaderFinished) {
|
||||||
|
canvas.drawText("Loading Complete!", width / 2f, height / 2f, textPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawBackground(canvas: Canvas) {
|
||||||
|
val gradient = LinearGradient(
|
||||||
|
0f, 0f, 0f, height.toFloat(),
|
||||||
|
Color.rgb(20, 20, 40), Color.BLACK,
|
||||||
|
Shader.TileMode.CLAMP
|
||||||
|
)
|
||||||
|
backgroundPaint.shader = gradient
|
||||||
|
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), backgroundPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawTargets(canvas: Canvas, now: Long) {
|
||||||
|
val expireMs = if (isIndeterminate) 2500L else 1500L
|
||||||
|
targets.removeAll { it.hit && now - it.hitTime > 300L }
|
||||||
|
targets.removeAll { !it.hit && now - it.spawnTime > expireMs }
|
||||||
|
|
||||||
|
for (t in targets) {
|
||||||
|
val scale = if (t.hit) 1f - ((now - t.hitTime) / 300f).coerceIn(0f, 1f) else 1f
|
||||||
|
val alpha = if (t.hit) ((1f - scale) * 255).toInt().coerceAtMost(255) else 255
|
||||||
|
val safeRadius = (t.radius * scale).coerceAtLeast(1f)
|
||||||
|
val glowPaint = Paint().apply {
|
||||||
|
shader = RadialGradient(t.x, t.y, safeRadius, Color.YELLOW, Color.TRANSPARENT, Shader.TileMode.CLAMP)
|
||||||
|
}
|
||||||
|
canvas.drawCircle(t.x, t.y, safeRadius * 1.2f, glowPaint)
|
||||||
|
canvas.drawCircle(t.x + 4f, t.y + 4f, safeRadius, shadowPaint)
|
||||||
|
outerRingPaint.alpha = alpha
|
||||||
|
middleRingPaint.alpha = alpha
|
||||||
|
centerDotPaint.alpha = alpha
|
||||||
|
canvas.drawCircle(t.x, t.y, safeRadius, outerRingPaint)
|
||||||
|
canvas.drawCircle(t.x, t.y, safeRadius * 0.66f, middleRingPaint)
|
||||||
|
canvas.drawCircle(t.x, t.y, safeRadius * 0.33f, centerDotPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawParticles(canvas: Canvas, now: Long) {
|
||||||
|
val lifespan = 400L
|
||||||
|
val iterator = particles.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
val p = iterator.next()
|
||||||
|
val age = now - p.startTime
|
||||||
|
if (age > lifespan) {
|
||||||
|
iterator.remove()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val alpha = ((1f - (age / lifespan.toFloat())) * 255).toInt()
|
||||||
|
val paint = Paint().apply {
|
||||||
|
color = Color.YELLOW
|
||||||
|
this.alpha = alpha
|
||||||
|
}
|
||||||
|
p.x += p.vx
|
||||||
|
p.y += p.vy
|
||||||
|
canvas.drawCircle(p.x, p.y, 6f, paint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawDeterministicProgressBar(canvas: Canvas, now: Long) {
|
||||||
|
val duration = expectedDurationMs ?: return
|
||||||
|
val rawProgress = ((now - startTime).toFloat() / duration).coerceIn(0f, 1f)
|
||||||
|
val easedProgress = AccelerateDecelerateInterpolator().getInterpolation(rawProgress)
|
||||||
|
|
||||||
|
val barHeight = 20f
|
||||||
|
val barRadius = 10f
|
||||||
|
val barWidth = width * easedProgress
|
||||||
|
|
||||||
|
val rect = RectF(0f, height - barHeight, barWidth, height.toFloat())
|
||||||
|
canvas.drawRoundRect(rect, barRadius, barRadius, progressBarPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawIndeterminateSpinner(canvas: Canvas) {
|
||||||
|
spinnerAngle = (spinnerAngle + 6f) % 360f
|
||||||
|
val cx = width / 2f
|
||||||
|
val cy = height / 2f
|
||||||
|
val radius = min(width, height) / 6f
|
||||||
|
val sweepAngle = 270f
|
||||||
|
|
||||||
|
val glowPaint = Paint(spinnerPaint).apply {
|
||||||
|
maskFilter = BlurMaskFilter(15f, BlurMaskFilter.Blur.SOLID)
|
||||||
|
}
|
||||||
|
|
||||||
|
val shader = SweepGradient(cx, cy, intArrayOf(Color.TRANSPARENT, Color.WHITE, Color.TRANSPARENT), floatArrayOf(0f, 0.5f, 1f))
|
||||||
|
spinnerPaint.shader = shader
|
||||||
|
|
||||||
|
canvas.drawArc(cx - radius, cy - radius, cx + radius, cy + radius, spinnerAngle, sweepAngle, false, glowPaint)
|
||||||
|
canvas.drawArc(cx - radius, cy - radius, cx + radius, cy + radius, spinnerAngle, sweepAngle, false, spinnerPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class Target(
|
||||||
|
val x: Float,
|
||||||
|
val y: Float,
|
||||||
|
val radius: Float,
|
||||||
|
val spawnTime: Long,
|
||||||
|
var hit: Boolean = false,
|
||||||
|
var hitTime: Long = 0L
|
||||||
|
)
|
||||||
|
|
||||||
|
private data class Particle(
|
||||||
|
var x: Float,
|
||||||
|
var y: Float,
|
||||||
|
val vx: Float,
|
||||||
|
val vy: Float,
|
||||||
|
var alpha: Int,
|
||||||
|
val startTime: Long
|
||||||
|
)
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ import com.futo.platformplayer.formatDuration
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
|
import com.futo.platformplayer.views.TargetTapLoaderView
|
||||||
import com.futo.platformplayer.views.behavior.GestureControlView
|
import com.futo.platformplayer.views.behavior.GestureControlView
|
||||||
import com.futo.platformplayer.views.others.ProgressBar
|
import com.futo.platformplayer.views.others.ProgressBar
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -154,10 +155,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
|
|
||||||
val onChapterClicked = Event1<IChapter>();
|
val onChapterClicked = Event1<IChapter>();
|
||||||
|
|
||||||
private val loaderOverlay: FrameLayout
|
private val _loaderGame: TargetTapLoaderView
|
||||||
private val loaderIndeterminate: android.widget.ProgressBar
|
|
||||||
private val loaderDeterminate: android.widget.ProgressBar
|
|
||||||
private var determinateAnimator: ValueAnimator? = null
|
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
constructor(context: Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) {
|
constructor(context: Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) {
|
||||||
|
@ -199,13 +197,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
_control_duration_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_duration);
|
_control_duration_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_duration);
|
||||||
_control_pause_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_pause);
|
_control_pause_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_pause);
|
||||||
|
|
||||||
loaderOverlay = findViewById(R.id.loader_overlay)
|
_loaderGame = findViewById(R.id.loader_overlay)
|
||||||
loaderIndeterminate = findViewById(R.id.loader_indeterminate)
|
_loaderGame.visibility = View.GONE
|
||||||
loaderDeterminate = findViewById(R.id.loader_determinate)
|
|
||||||
|
|
||||||
loaderOverlay.visibility = View.GONE
|
|
||||||
loaderIndeterminate.visibility = View.GONE
|
|
||||||
loaderDeterminate.visibility = View.GONE
|
|
||||||
|
|
||||||
_control_chapter.setOnClickListener {
|
_control_chapter.setOnClickListener {
|
||||||
_currentChapter?.let {
|
_currentChapter?.let {
|
||||||
|
@ -884,37 +877,17 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setLoading(isLoading: Boolean) {
|
override fun setLoading(isLoading: Boolean) {
|
||||||
determinateAnimator?.cancel()
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
loaderOverlay.visibility = View.VISIBLE
|
_loaderGame.visibility = View.VISIBLE
|
||||||
loaderIndeterminate.visibility = View.VISIBLE
|
_loaderGame.startLoader()
|
||||||
loaderDeterminate.visibility = View.GONE
|
|
||||||
} else {
|
} else {
|
||||||
loaderOverlay.visibility = View.GONE
|
_loaderGame.visibility = View.GONE
|
||||||
loaderIndeterminate.visibility = View.GONE
|
_loaderGame.stopAndResetLoader()
|
||||||
loaderDeterminate.visibility = View.GONE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setLoading(expectedDurationMs: Int) {
|
override fun setLoading(expectedDurationMs: Int) {
|
||||||
determinateAnimator?.cancel()
|
_loaderGame.visibility = View.VISIBLE
|
||||||
|
_loaderGame.startLoader(expectedDurationMs.toLong())
|
||||||
loaderOverlay.visibility = View.VISIBLE
|
|
||||||
loaderIndeterminate.visibility = View.GONE
|
|
||||||
loaderDeterminate.visibility = View.VISIBLE
|
|
||||||
loaderDeterminate.max = expectedDurationMs
|
|
||||||
loaderDeterminate.progress = 0
|
|
||||||
|
|
||||||
determinateAnimator = ValueAnimator.ofInt(0, expectedDurationMs).apply {
|
|
||||||
duration = expectedDurationMs.toLong()
|
|
||||||
addUpdateListener { anim ->
|
|
||||||
loaderDeterminate.progress = anim.animatedValue as Int
|
|
||||||
if(loaderDeterminate.progress > loaderDeterminate.max - 10) {
|
|
||||||
setLoading(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
start()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,9 +5,8 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:background="@color/black">
|
android:background="@color/black">
|
||||||
|
|
||||||
<com.futo.platformplayer.views.others.CircularProgressBar
|
<com.futo.platformplayer.views.TargetTapLoaderView
|
||||||
|
android:id="@+id/test_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp"
|
android:layout_height="240dp" />
|
||||||
app:progress="0%"
|
|
||||||
app:strokeWidth="20dp" />
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
|
@ -65,36 +65,10 @@
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<com.futo.platformplayer.views.TargetTapLoaderView
|
||||||
android:id="@+id/loader_overlay"
|
android:id="@+id/loader_overlay"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:background="@color/black"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/loader_indeterminate"
|
|
||||||
style="?android:attr/progressBarStyleLarge"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/loader_determinate"
|
|
||||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
|
||||||
android:progressDrawable="@drawable/progress_bar"
|
|
||||||
android:layout_width="200dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:indeterminate="false"
|
|
||||||
android:max="100"
|
|
||||||
android:progress="0"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Add table
Add a link
Reference in a new issue