WhatsApp Status like View: Android

fun ImageView.loadImage(imageUrl:String) {
val req = Glide.with(this)
.load(imageUrl)
.into(this)
}
fun View.show() {
if (this.visibility != View.VISIBLE)
this.visibility = View.VISIBLE
}
fun View.gone() {
if (this.visibility != View.GONE)
this.visibility = View.GONE
}
fun Context.getScreenWidth(): Int {
val metrics = this.resources.displayMetrics
return metrics.widthPixels
}
fun Context.convertDpToPixel(dp: Float): Float {
val resources = this.resources
val metrics = resources.displayMetrics
return dp * (metrics.densityDpi / 160f)
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/ll_progress_bar"
android:layout_width="match_parent"
android:orientation="horizontal"
android:background="#4D4D4D"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/ll_status"
android:layout_below="@id/ll_progress_bar"
android:layout_width="match_parent"
android:background="#000000"
android:layout_height="match_parent"/>
</RelativeLayout>
private val imagesList = mutableListOf(  "https://www.fillmurray.com/640/360",                                                                                           "https://loremflickr.com/640/360", "https://www.placecage.com/640/360", "https://placekitten.com/640/360")
private fun setImageStatusData() {
imagesList.forEach { imageUrl->
val imageView: ImageView = ImageView(this)
imageView.layoutParams = ViewGroup.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
imageView.gone()
imageView.loadImage(imageUrl)
ll_status.addView(imageView)
}
private fun setProgressData() {
ll_progress_bar.weightSum = imagesList.size.toFloat()
imagesList.forEachIndexed { index, progressData ->
val progressBar: ProgressBar = ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal) //horizontal progress bar
val params = LinearLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT, 1.0f)
params.height = convertDpToPixel(8f).toInt()
if (index != 3) {
params.marginEnd = convertDpToPixel(10f).toInt()
}
progressBar.layoutParams = params
progressBar.max = 40 // max progress i am using is 40 for
//each progress bar you can modify it
progressBar.indeterminateDrawable.setColorFilter(Color.BLACK, PorterDuff.Mode.MULTIPLY);
progressBar.progress = 0 //initial progress
ll_progress_bar.addView(progressBar)
}
}
var mDisposable: Disposable? = null
var mCurrentProgress: Long = 0
var mCurrentIndex: Int = 0
private fun emitStatusProgress() {
mDisposable = Observable.intervalRange(mCurrentProgress, 40-mCurrentProgress, 0, 100, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.computation())
.subscribeOn(AndroidSchedulers.mainThread())
.doOnComplete {
moveToNextStatus()
}
.subscribe({
updateProgress(it)
}, {
it
.printStackTrace()
})
}
  1. start => It shows the initial value that will be emitted. We have set this to mCurrentProgress . Initially, it's 0, but we will see why I have not put here 0, why I am using mCurrentProgress .
  2. count => It shows the number of emission from this observable. I have already mentioned that I am using max progress as 40. But again I have used 40-mCurrentProgress instead of 40, will describe later.
  3. initial delay => It shows the first emission delay. Which is 0 in our case as we don't want to delay the first emission here.
  4. interval => it shows at how many intervals observable emit data. We have used 100( which is 100 milliseconds).
  5. TimeUnit => 5th parameter shows which time unit you are going to use for the interval. As we have interval 100 and time units in milliseconds, so it will emit next time after 100 milliseconds and so on.
private fun moveToNextStatus() {
if ( mCurrentIndex < imagesList.size-1) {
mCurrentProgress = 0
mDisposable?.dispose()
mDisposable = null
runOnUiThread {
ll_status[mCurrentIndex].gone()
mCurrentIndex++
ll_status[mCurrentIndex].show()
}
if (mCurrentIndex != imagesList.size-1)
emitStatusProgress()
} else {
mDisposable?.dispose()
mDisposable = null
}
}
private fun updateProgress(progress: Long) {
mCurrentProgress = progress
runOnUiThread {
(ll_progress_bar[mCurrentIndex] as? ProgressBar)?.progress = progress.toInt()
}
}
private fun startViewing() {
ll_status[0].show()
emitStatusProgress()
}
var startTime: Long = System.currentTimeMillis()
private val onTouchListener = OnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startTime = System.currentTimeMillis()
pauseStatus()
return@OnTouchListener true
}
MotionEvent.ACTION_UP -> {
if (System.currentTimeMillis() - startTime > 2000) {
resumeStatus()
} else {
onSingleTapClicked(event.x)
}
startTime = 0
return@OnTouchListener true
}
MotionEvent.ACTION_BUTTON_RELEASE -> {
resumeStatus()
return@OnTouchListener true
}
}
false
}
private fun pauseStatus() {
mDisposable?.dispose()
mDisposable = null
}
private fun resumeStatus() {
emitStatusProgress()
}
private fun onSingleTapClicked(x: Float) {
if (x < getScreenWidth()/2) {
startPreviousStatus()
} else {
startStatusNext()
}
}
private fun startPreviousStatus() {
mCurrentProgress = 0
runOnUiThread {
if (mCurrentIndex != 0) {
(ll_progress_bar[mCurrentIndex] as? ProgressBar)?.progress = 0
ll_status[mCurrentIndex].gone()
mCurrentIndex--
ll_status[mCurrentIndex].show()
if (mCurrentIndex != imagesList.size-1)
emitStatusProgress()
} else {
mCurrentIndex = 0
(ll_progress_bar[mCurrentIndex] as? ProgressBar)?.progress = 0
ll_status[mCurrentIndex].show()
emitStatusProgress()
}
}
}
private fun startStatusNext() {
mCurrentProgress = 0
runOnUiThread {
if (mCurrentIndex != imagesList.size-1) {
(ll_progress_bar[mCurrentIndex] as? ProgressBar)?.progress = 40
ll_status[mCurrentIndex].gone()
mCurrentIndex++
ll_status[mCurrentIndex].show()
(ll_progress_bar[mCurrentIndex] as? ProgressBar)?.progress = 0
emitStatusProgress()
}
}
}

Working remotely as Android Developer

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Auto Generate Kotlin Android Documentation with Dokka

Debugging live android applications

OpenGL on Android — working with textures

Kotlin coroutine dispatchers overview

Efficient Android Testing Using Raccoon And Espresso

How to add country code picker in Android studio

Why should we go with Kotlin app development?

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Gaurav Rajput

Gaurav Rajput

Working remotely as Android Developer

More from Medium

KMM — Publish iOS/ watchOS / tvOS SDK Binaries

ClusterStack 1.1.38 — UFOria

MVP module in Swift

Android Studio & HMS Core Kit Integration

Android Studio