Mehedi Hassan Piash [Sr. Software Engineer]

June 06, 2021

Shared Preference in android [kotlin]

June 06, 2021 Posted by Piash No comments

 SharedPreference in android 


 class Preference(appContext: Context) {  
   private val preferenceName = "preference"  
   private val sharedPref: SharedPreferences =  
     appContext.getSharedPreferences(preferenceName, Context.MODE_PRIVATE)  
   private val editor: SharedPreferences.Editor = sharedPref.edit()  
   fun save(KEY_NAME: String, value: Int) {  
     editor.apply {  
       putInt(KEY_NAME, value)  
       commit()  
     }  
   }  
   fun save(KEY_NAME: String, value: String) {  
     editor.apply {  
       putString(KEY_NAME, value)  
       commit()  
     }  
   }  
   fun save(KEY_NAME: String, value: Boolean) {  
     editor.apply {  
       putBoolean(KEY_NAME, value)  
       commit()  
     }  
   }  
   fun getString(KEY_NAME: String): String? {  
     return sharedPref.getString(KEY_NAME, AppConstants.Default.STRING)  
   }  
   fun getBoolean(KEY_NAME: String): Boolean? {  
     return sharedPref.getBoolean(KEY_NAME, AppConstants.Default.BOOLEAN)  
   }  
   fun getInt(KEY_NAME: String): Int? {  
     return sharedPref.getInt(KEY_NAME, AppConstants.Default.INT)  
   }  
 }  

Some fragment observers trigger after pop from back stack although data is not changed

June 06, 2021 Posted by Piash No comments

 LiveData always stores the last value and sends it to each Observer that is registered. That way all Observers have the latest state.

As you're using viewLifecycleOwner, your previous Observer has been destroyed, so registering a new Observer is absolutely the correct thing to do - you need the new Observer and its existing state to populate the new views that are created after you go back to the Fragment (since the original Views are destroyed when the Fragment is put on the back stack).

If you're attempting to use LiveData for events (i.e., values that should only be processed once), LiveData isn't the best API for that as you must create an event wrapper or something similar to ensure that it is only processed once.


After knowing what happen I decide to go with customized live data to trigger just once. ConsumableLiveData. So I will put answer here may help others.



 class ConsumableLiveData<T>(var consume: Boolean = false) : MutableLiveData<T>() {  
   private val pending = AtomicBoolean(false)  
   override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {  
     super.observe(  
       owner,  
       Observer<T> {  
         if (consume) {  
           if (pending.compareAndSet(true, false)) observer.onChanged(it)  
         } else {  
           observer.onChanged(it)  
         }  
       }  
     )  
   }  
   override fun setValue(value: T) {  
     pending.set(true)  
     super.setValue(value)  
   }  
 }  

And for usage just put as bellow. It will trigger just once after any update value. This will great to handle navigation or listen to click or any interaction from user. Because just trigger once!


 //In viewModel  
 val goToCreditCardLiveData = ConsumableLiveData<Boolean>(true)  

And in fragment:

 viewModel.goToCreditCardLiveData.observe(viewLifecycleOwner) {  
     findNavController().navigate(...)  
   }  

How to create and configure Android Build Variants

June 06, 2021 Posted by Piash , No comments

An Android APK file is the product of weekly sprints. We may need to create different versions of an APK file based on an application type (free or paid application), the server environment type (QA, PROD), etc. 

1. Create an Android Project with default setting provided by the Android Studio IDE. Let the project be called ‘Build Variants Demo’.

2. Open module-level build.gradle file, and add productFlavors {} block. Your build.gradle will display, as depicted below:

apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.cuelogic.buildvariantsdemo"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
signingConfigs {
release {
storeFile file("my-release-key.jks")
storePassword "password"
keyAlias "my-alias"
keyPassword "password"
}
}
productFlavors {
qa {
applicationId "com.cuelogic.buildvariantsdemo.qa"
versionCode 1
versionName "1.0"
}
prod {
applicationId "com.cuelogic.buildvariantsdemo.prod"
versionCode 1
versionName "1.0"
signingConfig signingConfigs.release
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
}

Here, the productFlavours block contains qa & prod flavors with some specific settings like applicationId, versionCode and versionName.

When you sync the project, Gradle automatically creates build variants based on your build types and product flavours, and names them according to <product-flavor><Build-Type>. The display will show as follows:


You can select any option from the above list and run selected build variant type.

3. Now let’s start creating the source set for build variants. By default, Android Studio creates the main/ source set and directories which will be shared between all your build variants. However, we can create new source sets for build variants. Here, Gradle expects us to maintain the source set files and directories in a certain way, like the Java class files that are specific to qa or prod build type to be in the src/qa/java/ and src/prod/java/ directory respectively. Please check the screenshots.

Note: Depending upon your build variant selection, respective classes will be excluded from the build package. For example, you will view BaseRequest.java under prod highlighted with a green icon while BaseRequest.java under qa is depicted inactive with a red icon.










4. As per your requirement, you can add assets, java files and resources in respective product flavour folders. Here I have created a BaseRequest.java file which will hold build specific URL, like the QA URL & PRODUCTION URL:

In src/qa/java/com.cuelogic.buildvariantsdemo folder

public class BaseRequest {
public static final String BASE_URL = "https://cuelogic.com/qa";
}

In src/prod/java/com.cuelogic.buildvariantsdemo folder

public class BaseRequest {
public static final String BASE_URL = "https://cuelogic.com/production";
}

We are done! You may now try with different build configurations such as free and paid application, application white labeling, enterprise and market place app variants, etc.

Credit: https://www.cuelogic.com/blog/how-to-create-and-configure-android-build-variants