Mehedi Hassan Piash | Senior Software Engineer | Android | iOS | KMP | Ktor | Jetpack Compose | React-Native.

April 01, 2021

Data save in DataStore in android

April 01, 2021 Posted by Mehedi Hassan Piash , No comments

 A new and improved data storage solution aimed at replacing SharedPreferences. Built on Kotlin coroutines and Flow

Why we should use DataStore instead of SharedPreferences

Let’s coding step by step

  1. Add gradle dependency in you build.gradle file
// data store
def dataStoreVersion = "1.0.0-alpha08"
implementation "androidx.datastore:datastore-core:$dataStoreVersion"
implementation "androidx.datastore:datastore-preferences:$dataStoreVersion"
implementation 'com.google.code.gson:gson:2.8.6'

2. Make a DataStore manager for access all necessary functionality for our use case

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.gson.Gson
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map


class DataStoreManager(var context: Context) {
// Create the dataStore and give it a name same as shared preferences
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("user_pref")
val dataStore: DataStore<Preferences> = context.dataStore

// Create some keys we will use them to store and retrieve the data
companion object {
val USER_NAME_KEY = stringPreferencesKey("USER_NAME")
val USER_AGE_KEY = intPreferencesKey("USER_AGE")
}

// Store user data
// refer to the data store and using edit


// store data as object
suspend fun <T> storeObjectData(key: Preferences.Key<String>, value: T) {
dataStore.edit { preferences ->
preferences[key] = serializeData(value)
}
}

// save primitives data
suspend fun storeStringData(key: Preferences.Key<String>, value: String) {
dataStore.edit { preferences ->
preferences[key] = value
}
}

suspend fun storeIntData(key: Preferences.Key<Int>, value: Int) {
dataStore.edit { preferences ->
preferences[key] = value
}
}

suspend fun storeDoubleData(key: Preferences.Key<Double>, value: Double) {
dataStore.edit { preferences ->
preferences[key] = value
}
}

suspend fun storeDoubleData(key: Preferences.Key<Float>, value: Float) {
dataStore.edit { preferences ->
preferences[key] = value
}
}

suspend fun storeLongData(key: Preferences.Key<Long>, value: Long) {
dataStore.edit { preferences ->
preferences[key] = value
}
}

suspend fun storeBooleanData(key: Preferences.Key<Boolean>, value: Boolean) {
dataStore.edit { preferences ->
preferences[key] = value
}
}

// Create a flow to retrieve data from the preferences
// flow comes from the kotlin coroutine

// get data as object
inline fun <reified T> getObjectData(key: Preferences.Key<String>): Flow<T> =
dataStore.data.map {
deSerializeData(it[key] ?: "")
}

// get primitives data
fun getStringData(key: Preferences.Key<String>): Flow<String> = dataStore.data.map {
it
[key] ?: ""
}

fun getIntData(key: Preferences.Key<Int>): Flow<Int> = dataStore.data.map {
it
[key] ?: 0
}

fun getDoubleData(key: Preferences.Key<Double>): Flow<Double> = dataStore.data.map {
it
[key] ?: 0.0
}

fun getFloatData(key: Preferences.Key<Float>): Flow<Float> = dataStore.data.map {
it
[key] ?: 0.0f
}

fun getLongData(key: Preferences.Key<Long>): Flow<Long> = dataStore.data.map {
it
[key] ?: 0
}

fun getBooleanData(key: Preferences.Key<Boolean>): Flow<Boolean> = dataStore.data.map {
it
[key] ?: false
}


private fun <T> serializeData(data: T): String {
return Gson().toJson(data)
}

inline fun <reified T> deSerializeData(data: String): T {
return Gson().fromJson(data, T::class.java)
}
}

3. Lets use it inside coroutine

binding.dataStore.setOnClickListener {
lifecycleScope.launch {
userDataStore.storeObjectData(DataStoreManager.USER_NAME_KEY, Geo("1.2", "1.3"))

userDataStore.getObjectData<Geo>(DataStoreManager.USER_NAME_KEY).asLiveData().observe(viewLifecycleOwner,{
Timber.e("serialize : $it")
})
}
}

Ref: https://piashcse.medium.com/data-save-in-datastore-in-android-20e0707bb938

March 23, 2021

Data communication in Activity to Activity

March 23, 2021 Posted by Mehedi Hassan Piash , No comments

 Extension function for data communication in activity to activity 

 inline fun <reified T : Activity> Context.openActivity(vararg params: Pair<String, Any>) {  
   val intent = Intent(this, T::class.java)  
   intent.putExtras(*params)  
   this.startActivity(intent)  
 }  
 fun Intent.putExtras(vararg params: Pair<String, Any>): Intent {  
   if (params.isEmpty()) return this  
   params.forEach { (key, value) ->  
     when (value) {  
       is Int -> putExtra(key, value)  
       is Byte -> putExtra(key, value)  
       is Char -> putExtra(key, value)  
       is Long -> putExtra(key, value)  
       is Float -> putExtra(key, value)  
       is Short -> putExtra(key, value)  
       is Double -> putExtra(key, value)  
       is Boolean -> putExtra(key, value)  
       is Bundle -> putExtra(key, value)  
       is String -> putExtra(key, value)  
       is IntArray -> putExtra(key, value)  
       is ByteArray -> putExtra(key, value)  
       is CharArray -> putExtra(key, value)  
       is LongArray -> putExtra(key, value)  
       is FloatArray -> putExtra(key, value)  
       is Parcelable -> putExtra(key, value)  
       is ShortArray -> putExtra(key, value)  
       is DoubleArray -> putExtra(key, value)  
       is BooleanArray -> putExtra(key, value)  
       is CharSequence -> putExtra(key, value)  
       is Array<*> -> {  
         when {  
           value.isArrayOf<String>() ->  
             putExtra(key, value as Array<String?>)  
           value.isArrayOf<Parcelable>() ->  
             putExtra(key, value as Array<Parcelable?>)  
           value.isArrayOf<CharSequence>() ->  
             putExtra(key, value as Array<CharSequence?>)  
           else -> putExtra(key, value)  
         }  
       }  
       is java.io.Serializable -> putExtra(key, value)  
     }  
   }  
   return this  
 }  

Simple using:
 startActivity<MainActivity>()  

With extra:
 startActivity<MainActivity>{  
   putExtra("param 1", "Simple")  
 }  

January 22, 2021

ViewPager2 with recyclerView adapter

January 22, 2021 Posted by Mehedi Hassan Piash No comments

It was a nightmare working with viewPager when had to handle fragmentPagerAdapter. But in terms of viewPager2 its just fun. Cause we can easily use recyclerview adapter in ViewPager2. I have just done a demo project with custom indicator for viewPager2 with recyclerView Adapter. Let’s start coding step by step.

Image for post
  1. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_Pager"
android:layout_width="match_parent"
android:layout_height="250dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.tabs.TabLayout
android:id="@+id/into_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
app:layout_constraintTop_toTopOf="@+id/view_Pager"
app:tabBackground="@drawable/pager_indicator"
android:background="@android:color/transparent"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabPaddingEnd="6dp"
app:tabPaddingStart="6dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

2. MainActivity.kt

package com.piashcse.experiment.viewpager2withrvadapter

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import com.google.android.material.tabs.TabLayoutMediator
import com.piashcse.experiment.viewpager2withrvadapter.adapter.ImageSliderAdapter
import com.piashcse.experiment.viewpager2withrvadapter.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
private lateinit var bind: ActivityMainBinding
private lateinit var imageSliderAdapter: ImageSliderAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bind = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewPager2()
}

private fun viewPager2() {
imageSliderAdapter = ImageSliderAdapter()
bind.viewPager.adapter = imageSliderAdapter
TabLayoutMediator(bind.intoTabLayout, bind.viewPager)
{ tab, position ->
}
.attach()
imageSliderAdapter.addItem(
listOf(
"https://cdn.pixabay.com/photo/2021/01/09/02/13/manhattan-5901178_960_720.jpg",
"https://cdn.pixabay.com/photo/2020/11/22/20/45/venice-5767937_960_720.jpg",
"https://cdn.pixabay.com/photo/2021/01/10/12/00/road-5904909_640.jpg"
)
)
}
}

3. ImageSliderAdapter.kt

package com.piashcse.experiment.viewpager2withrvadapter.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.piashcse.experiment.viewpager2withrvadapter.databinding.ImageSliderItemLayoutBinding

class ImageSliderAdapter: RecyclerView.Adapter<ImageSliderAdapter.ImageViewHolder>() {
private var items = ArrayList<String>()
private var onItemClick: ((String) -> Unit)? = null

fun addItem(newItems: List<String>?) {
newItems?.let {
items.addAll(newItems)
notifyDataSetChanged()
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ImageSliderItemLayoutBinding.inflate(inflater, parent, false)
return ImageViewHolder(binding)
}

override fun getItemCount() = items.size

override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
val image = items[position]
return holder.bind(image)
}

inner class ImageViewHolder(private val bind: ImageSliderItemLayoutBinding) : RecyclerView.ViewHolder(bind.root) {
fun bind(item: String) {
bind.item = item
bind.executePendingBindings()
itemView.setOnClickListener {
onItemClick?.invoke(item)
}
}
}
}

4. image_slider_item_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="java.lang.String" />
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
loadSliderImage='@{item}'/>

</androidx.appcompat.widget.LinearLayoutCompat>
</layout>

5. default_indicator_dot.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="3dp"
android:useLevel="false">
<solid android:color="@color/material_white"/>
</shape>
</item>
</layer-list>

6. selected_indicator_dot.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thickness="3dp"
android:useLevel="false">
<solid android:color="@color/material_amber900"/>
</shape>
</item>
</layer-list>

7. pager_indicator.xml

<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/selected_indicator_dot"
android:state_selected="true"/>
<item android:drawable="@drawable/default_indicator_dot"/>
</selector>

Github:https://github.com/piashcse/blog_piashcse_code/tree/master/ViewPager2WithRvAdapter
Medium:https://piashcse.medium.com/viewpager2-with-recyclerview-adapter-b048f625a208


December 14, 2020

Android color opacity Hexadecimal values

December 14, 2020 Posted by Mehedi Hassan Piash No comments

  • These two values have to add as a prefix in android color code and it will be transparent as given percentage below: 

100% — FF
99% — FC
98% — FA
97% — F7
96% — F5
95% — F2
94% — F0
93% — ED
92% — EB
91% — E8

90% — E6
89% — E3
88% — E0
87% — DE
86% — DB
85% — D9
84% — D6
83% — D4
82% — D1
81% — CF

80% — CC
79% — C9
78% — C7
77% — C4
76% — C2
75% — BF
74% — BD
73% — BA
72% — B8
71% — B5

70% — B3
69% — B0
68% — AD
67% — AB
66% — A8
65% — A6
64% — A3
63% — A1
62% — 9E
61% — 9C

60% — 99
59% — 96
58% — 94
57% — 91
56% — 8F
55% — 8C
54% — 8A
53% — 87
52% — 85
51% — 82

50% — 80
49% — 7D
48% — 7A
47% — 78
46% — 75
45% — 73
44% — 70
43% — 6E
42% — 6B
41% — 69

40% — 66
39% — 63
38% — 61
37% — 5E
36% — 5C
35% — 59
34% — 57
33% — 54
32% — 52
31% — 4F

30% — 4D
29% — 4A
28% — 47
27% — 45
26% — 42
25% — 40
24% — 3D
23% — 3B
22% — 38
21% — 36

20% — 33
19% — 30
18% — 2E
17% — 2B
16% — 29
15% — 26
14% — 24
13% — 21
12% — 1F
11% — 1C

10% — 1A
9% — 17
8% — 14
7% — 12
6% — 0F
5% — 0D
4% — 0A
3% — 08
2% — 05
1% — 03
0% — 00