Android Request To Turn On GPS Pragmatically

In the previous tutorial we have seen  how to get location using FusedLocationProviderClient , for apps to request location the device needs to enable the appropriate system settings, such as GPS or Wi-Fi scanning . to turn on gps programmatically using android gms location request and setting clients by presenting the Location Settings dialog for the user to update their settings with a single tap.


1. Gradle


file : build.gradle
dependencies {

    implementation "com.google.android.gms:play-services-location:18.0.0"
}


2. XML Layout


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

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

3. Set up a location request .

val locationRequest = LocationRequest.create().apply {
            interval = 10000
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }

Create the location request and set the parameters as shown in this code sample:
  • Update interval : This method sets the rate in milliseconds at which your app prefers to receive location updates.
  • Fastest update interval : This method sets the fastest rate in milliseconds at which your app can handle location updates .
  • Priority : This method sets the priority of the request . It takes the following values 
1. PRIORITY_HIGH_ACCURACY 
2. PRIORITY_LOW_POWER 
3. PRIORITY_NO_POWER 

Prompt the user to change location settings


1.  Create a LocationSettingsRequest.Builder .

2.  Create SettingsClient instance .

3.  checkLocationSettings method of SettingsClient Checks if the relevant system settings are enabled on the device to  carry out the desired location requests.Call checkLocationSettings on settingsclient instance by passing locationSettingsRequest builder as parameter , it returns Task<LocationSettingsResponse> .
   
4.  Call addOnFailureListener and addOnFailureListener on Task .
  • Inside addOnSuccessListener All location settings are satisfied. The client can initialize locationrequests here.
  • Inside addOnFailureListener Location settings are not satisfied, but this can be fixed by showing the user a dialog. Show the dialog by calling startResolutionForResult() and check the result in onActivityResult().

3. Activity


MainActivity full code .

file : MainActivity.kt
package com.tutorialsbuzz.devicelocationsettings

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }


    override fun onResume() {
        super.onResume()
        requestDeviceLocationSettings();
    }

    // request device

    fun requestDeviceLocationSettings() {
        val locationRequest = LocationRequest.create().apply {
            interval = 10000
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }

        val builder = LocationSettingsRequest.Builder()
            .addLocationRequest(locationRequest)

        val client: SettingsClient = LocationServices.getSettingsClient(this)
        val task: Task<LocationSettingsResponse> = client.checkLocationSettings(builder.build())

        task.addOnSuccessListener { locationSettingsResponse ->
            // All location settings are satisfied. The client can initialize
            // location requests here.
            // ...
            val state = locationSettingsResponse.locationSettingsStates

           val label =
	           "GPS >> (Present: ${state.isGpsPresent}  | Usable: ${state.isGpsUsable} ) \n\n" +
	           "Network >> ( Present: ${state.isNetworkLocationPresent} | Usable: ${state.isNetworkLocationUsable} ) \n\n" +
	           "Location >> ( Present: ${state.isLocationPresent} | Usable: ${state.isLocationUsable} )"

            showToast(label)

            textView.text = label

        }

        task.addOnFailureListener { exception ->
            if (exception is ResolvableApiException) {
                // Location settings are not satisfied, but this can be fixed
                // by showing the user a dialog.
                try {
                    // Show the dialog by calling startResolutionForResult(),
                    // and check the result in onActivityResult().
                    exception.startResolutionForResult(
                        this@MainActivity,
                        100
                    )
                } catch (sendEx: IntentSender.SendIntentException) {
                    // Ignore the error.
                    textView.text = sendEx.message.toString()
                }
            }
        }

    }

    fun showToast(msg: String) {
        Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
    }
}




Read More »

Android Handle Location Permission Deny (Don't Ask Again)

From Android Marshmallow On-words Building Location Based Application , App should grant Location permission , In the previous tutorial working with FusedLocation API initially we have request for location permission which is need for fetching device location details to get user lastLocation . In this tutorial we will see the use case of how to handle location permission deny with don't ask again i.e checking/knowing the status of don't ask again action by user and handling.

When you make request for permission there will be 3 actions user's can perform .

Action 1 :  Allow 
Action 2 :  Just Deny ( deny without selecting the check-box "don't ask again")
Action 3 :  Force Deny ( deny with selecting the check-box "don't ask again")

When you deny permission by selecting the check-box "don't ask again" After that when you make subsequent request again for the same permission Nothing will happen ( i.e The system will not show "Allow Deny" popup again )  .

  UseCase  
Scenerio
1
Request Permission > Pop-up > allow > (TextView Updated With Permission Granted).

 2
Request Permission > Pop-up > deny > (TextView Updated With Permission Denied).

 3

    Request Permission > Pop-up > deny with "don't ask again" selected >
    (TextView Updated With Permission Denied and Show Button for visit settings).
 
   Now on Click of "visit settings" you will navigate to app settings 
   once you grant permission and resume back to app  inside onResume
   again we will check if permission update the UI  (i.e TextView Updated 
   with Permission  Granted and hide Button for visit settings ).


Use Case 1 ( Permission Granted )


Use Case 2 ( Permission Denied )



Use Case 3 ( Pemission Deny By Selecting Don't Ask Again )


1. AndroidManifest


file : AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tutorialsbuzz.handlelocationpermissiondeny">

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    ...........
	...........
	...........

</manifest>


2. XML Layout


file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="22sp" />

    <Button
        android:id="@+id/requestBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:text="Request Permission" />


    <Button
        android:id="@+id/visitAppSettingsBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:text="Visit App Settings And Allow Permission"
        android:visibility="gone" />

</LinearLayout>


3. Activity


MainActivity Full Code Handling Above 3 Use Cases 

file : MainActivity.kt
package com.tutorialsbuzz.handlelocationpermissiondeny

class MainActivity : AppCompatActivity() {

    val PERMISSION_ID = 42

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestBtn.setOnClickListener({
            if (!checkPermissionsGranted())
                requestPermissions();
        })

        visitAppSettingsBtn.setOnClickListener({
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
            val uri = Uri.fromParts("package", packageName, null)
            intent.data = uri
            startActivity(intent)
        })

    }

    override fun onResume() {
        super.onResume()
        if (checkPermissionsGranted()) {
            visitAppSettingsBtn.visibility = View.GONE
            requestBtn.visibility = View.GONE
            label.setText("Permission Granted")
        } else {
            label.setText("Permission Denied")
            checkUserRequestedDontAskAgain()
        }

    }

    fun requestPermissions() {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION
            ),
            PERMISSION_ID
        )
    }

    fun checkPermissionsGranted(): Boolean {
        return ActivityCompat.checkSelfPermission(
            this, Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(
                    this, Manifest.permission.ACCESS_COARSE_LOCATION
                ) == PackageManager.PERMISSION_GRANTED
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        if (requestCode == PERMISSION_ID) {
            if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                //when granted
                label.setText("Permission Granted:")
                showToast("Permission Granted:")
            } else {
                //Permission Denied
                label.setText("Permission Denied:")
                showToast("Permission Denied:")

                checkUserRequestedDontAskAgain()
            }
        }
    }

    private fun checkUserRequestedDontAskAgain() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val rationalFalgCOARSE =
                shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION)
            val rationalFalgFINE =
                shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
            if (!rationalFalgCOARSE && !rationalFalgFINE) {
                label.setText("permission_denied_forcefully")
                visitAppSettingsBtn.visibility = View.VISIBLE
                requestBtn.visibility = View.GONE
                showToast("permission_denied_forcefully")
            }
        }
    }

    fun showToast(msg: String) {
        Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
    }

}


Read More »

Android Working With FusedLocation LastLocation

Building Location Based Based Application , Getting location data for your app based on combined signals from the device sensors using a battery-efficient API , The location APIs available in Google Play services facilitate adding location awareness to your app with automated location tracking.

The fused location provider is a location API in Google Play services that intelligently combines different signals to provide the location information that your app needs. fused location provider API is Simple, battery-efficient location API for Android .


1. Gradle


Add Google Service Dependency to app level gradle .

file : build.gradle(App Level)
dependencies {

    implementation "com.google.android.gms:play-services-location:18.0.0"
}


2. AndroidManifest


Add Location Permission to Android Manifest .

file : AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tutorialsbuzz.mylocation">

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

   ..........
   ..........
   ..........

</manifest>


3. XML Layout


Create XML Layout and Add TextView and Button to it .

file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="22sp" />

    <Button
        android:id="@+id/fetchMyLocation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="Fetch My Location"
        android:textSize="20sp" />

</LinearLayout>


4. Activity


  1. Create MainActivity class which extends AppCompatActivity and inside onCreate function set the content of Activity with Above define XML Layout .
  2. On Button click call fetch Current Location Details .
  3. Before Getting Location Details using FusedLocation API , Request for App Location permission once the permission is granted update UI with Latitude and Longitude .

Get Last Known Location

  • Create an instance of the Fused Location Provider Client. 
  • Once you have created the Location Services client you can get the last known location of a user's device. When your app is connected to these you can use the fused location provider's getLastLocation() method to retrieve the device location.
mFusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->

		val location: Location? = task.result

		if (location == null) {

			// Create Location Request
			
		} else {
			// Update Ui 
            // Lat Lng ( location.latitude, location.longitude )
		   
		}		
	}

Making Location Request .

If the returned location  is null then make another location request update on locationProvider Client , requestLocationUpdates function takes locationRequest and a Callback (Receives Location) .

The following code snippet illustrates the request and callBack on Update :

// 1. Create Location Request
val locationRequest = LocationRequest.create().apply {
	interval = 10000
	fastestInterval = 5000
	priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}

// 2. Request for location update
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
mFusedLocationClient.requestLocationUpdates(
	locationRequest, mLocationCallback,
	Looper.myLooper()
)		

//3. CallBack for location update Request
mLocationCallback = object : LocationCallback() {
	override fun onLocationResult(locationResult: LocationResult) {
		// Update UI With Lat Lng 
		// locationResult.lastLocation.latitude
		//	locationResult.lastLocation.longitude
	}
}

file : MainActivity.kt
package com.tutorialsbuzz.mylocation

class MainActivity : AppCompatActivity() {

    private lateinit var mFusedLocationClient: FusedLocationProviderClient
    private lateinit var mLocationCallback: LocationCallback

    val PERMISSION_ID = 42

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        fetchMyLocation.setOnClickListener({
            getMyCurrenLocation()
        })

        //CallBack for location update Request
        mLocationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                // Update UI With Lat Lng
                updateUIWithLatLng(
                    locationResult.lastLocation.latitude,
                    locationResult.lastLocation.longitude
                )
            }
        }

    }

    fun updateUIWithLatLng(latitude: Double, longitude: Double) {
        val textLabel = " Latitude: $latitude \n Longitude: $longitude "
        textView.text = textLabel
    }

    fun requestPermissions() {
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION
            ),
            PERMISSION_ID
        )
    }

    fun getMyCurrenLocation() {

        if (ActivityCompat.checkSelfPermission(
                this, Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED &&
            ActivityCompat.checkSelfPermission(
                this, Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            //request for location permission
            requestPermissions()

        } else {

            mFusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->

                val location: Location? = task.result

                if (location == null) {

                    // Create Location Request
                    val locationRequest = LocationRequest.create().apply {
                        interval = 10000
                        fastestInterval = 5000
                        priority = LocationRequest.PRIORITY_HIGH_ACCURACY
                    }

                    // Request for location update
                    mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
                    mFusedLocationClient.requestLocationUpdates(
                        locationRequest, mLocationCallback,
                        Looper.myLooper()
                    )

                } else {
                    // Update Ui With Lat Lng
                    updateUIWithLatLng(location.latitude, location.longitude)
                }
            }
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        if (requestCode == PERMISSION_ID) {
            if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                //Permission Granted
                getMyCurrenLocation()
                showToast("Permission Granted:")
            } else {
               // Permission Denied
                showToast("Permission Denied:")
            }
        }
    }

    fun showToast(msg: String) {
        Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show()
    }
}


Read More »

Android ViewModelProvider Factory Passing Parameter to ViewModel In Kotlin

In the previous tutorial we learned about one of the Android Architecture Components, ViewModel with LiveData , we have seen example using ViewModel how objects survives configuration changes (On Screen-Rotate) there we did'nt create ViewModel on our own by default ViewModelProviders instantiate ViewModels with no arg constructor.

So if I have a ViewModel with multiple arguments, then I need to use a Factory that I can pass to ViewModelProviders to use when an instance of ViewModel is required let's see sample example for demo .

1. Gradle


Add ViewModel Architecture Component dependency to gradle .


file: build.gradle(app)
dependencies {
 
 implementation "android.arch.lifecycle:extensions:1.1.1"
 annotationProcessor "android.arch.lifecycle:compiler:1.1.1"
}


2. API Helper Class


Create a class APIHelper , this is class reference we pass as argument to ViewModel .

file : ApiHelper.kt
package com.tutorialsbuzz.viewmodelfactorysample.data

class ApiHelper {
    fun getUserInfo(): String = (0..10).random().toString() + " Hello World"
}


3. ViewModel


  • Create a MainViewModel by extending ViewModel class .
  • Constructor of MainViewModel takes APIHelper reference . 
  • Define a LiveData by wrapping to the String data variable .

file : MainViewModel.kt
package com.tutorialsbuzz.viewmodelfactorysample

class MainViewModel(private val apiHelper: ApiHelper) : ViewModel() {

    val userInfo = MutableLiveData<String>().apply { postValue("") }

    fun getUsersInfo() {
        userInfo.postValue(apiHelper.getUserInfo())
    }

}


4. ViewModelProvider Factory


  • Create ViewModelFactory by extending ViewModelProvider.Factory interface .
  • Override the create function which creates a new instance of the given class .
  • @param  modelClass a class whose instance is requested
  • @param <T> the type parameter for the ViewModel.
  • @return a newly created ViewModel

file : ViewModelFactory.kt
package com.tutorialsbuzz.viewmodelfactorysample

class ViewModelFactory(private val apiHelper: ApiHelper) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
            return MainViewModel(apiHelper) as T
        }
        throw IllegalArgumentException("Unknown class name")
    }
}


5. XML


Create XML Layout activity_main.xml , this will be set as content view for launcher Activity (MainActivity.kt) and add TextView and Button to your layout file .

file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvNumber"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:text=""
        android:textSize="30sp" />

    <Button
        android:id="@+id/update"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="30dp"
        android:gravity="center"
        android:text="update"
        android:textSize="30sp" />

</LinearLayout>



6. MainActivity


  • Create a koltin class MainActivity.kt and extend it to AppCompatActivity class .
  • Override onCreate function and set the content of this MainActivity with above defined xml layout (activity_main.xml). 

Setting Up ViewModel :

  • ViewModelProviders is a utility class that provides ViewModels for a given Activity scope .
  • ViewModelProviders.Factory interface are responsible to instantiate ViewModels.
  • ViewModelProvider.of is a static function .
 Parameter :
  1. An activity, in whose scope ViewModels should be retained
  2. Factory to instantiate new ViewModels

 Returns : 
  a ViewModelProvider instance

The get function on ViewModelProvider returns  ViewModel instance of the given Type . 
viewModel = ViewModelProviders.of(this,ViewModelFactory(ApiHelper()))
		   .get(MainViewModel::class.java)

Setting Up Observer : 

 
LiveData is defined in ViewModel , if any change change to live data can be observed .

viewModel.userInfo.observe(this, {
		tvNumber.text = it
	})
Main Activity Complete code .

file : MainActivity.kt
package com.tutorialsbuzz.viewmodelfactorysample

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setupViewModel()
        setUpObserver()

        update.setOnClickListener({
            viewModel.getUsersInfo()
        })

    }

    private fun setupViewModel() {
        viewModel = ViewModelProviders.of(
            this,
            ViewModelFactory(ApiHelper())
        ).get(MainViewModel::class.java)
    }

    private fun setUpObserver() {
        viewModel.userInfo.observe(this, {
            tvNumber.text = it
        })
    }

}





Read More »

Android FCM Topic Grouping Message In Kotlin

In this Previous tutorial we have seen example FCM Push Notification , In this tutorial we will see how to do grouping of  messages using FCM Topic Messaging . 

FCM Topic Messaging is based on the publish/subscribe model, FCM topic messaging allows you to send a message to segment or group of device that have subscribed for a particular topic , while composing push notification when you add topic message FCM handles routing and delivering the message reliably to the right devices .

For Example , Let say news app which sends news notification for 3 different languages (English , Spanish , Russian ) , users can opt for language of his interest by subscribing from the client and while composing notification when you select the particular language only these users we will get message who have subscribed for that topic .

  • One app instance can be subscribed to no more than 2000 topics.  
  • The frequency of new subscriptions is rate-limited per project. 
  • If you send too many subscription requests in a short period of time, FCM servers will respond with a 429 RESOURCE_EXHAUSTED ("quota exceeded") response. Retry with exponential backoff.

1. Storing Data In App's SharedPreference 


To Store the topic name with status using sharedPreference .

file : PreferenceManager.kt
package com.tutorialsbuzz.fcmdemo

class PreferenceManager internal constructor(context: Context) {

    private val mPrefs: SharedPreferences

    init {
        mPrefs = context.getSharedPreferences(PREFERENCES, 0)
    }

    companion object {
        private const val PREFERENCES = "settings"
        private var mPreferenceManager: PreferenceManager? = null
        fun getInstance(context: Context): PreferenceManager? {
            if (mPreferenceManager == null) {
                mPreferenceManager = PreferenceManager(context)
            }
            return mPreferenceManager
        }
    }

    fun getStoredJSONOptimization(): List<Model>? {
        val gson = Gson()
        val response = mPrefs.getString(
            Name.JSON_DATA,
            null
        )
            ?: return null
        return gson.fromJson(response, Array<Model>::class.java).toList()
    }

    fun storeJSONOptimization(value: String?) {
        mPrefs.edit().putString(
            Name.JSON_DATA,
            value
        ).apply()
    }

    object Name {
        const val JSON_DATA = "JSON_DATA"
    }

}


1. XML Layout For RecyclerView Row Item


XML Layout for RecyclerView Items

file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/subscribeLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="10dp">

    <TextView
        android:id="@+id/subscribeLabel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginStart="5dp"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:layout_weight="1"
        android:text="English"
        android:textSize="22sp" />

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_marginRight="10dp"
        android:visibility="gone" />

    <TextView
        android:id="@+id/subscribe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="20dp"
        android:text="@string/subscribe"
        android:textSize="16sp"
        android:textStyle="bold" />

</RelativeLayout>


1. Adapter


Adapter For RecyclerView For displaying list of topics user subscribed and unsubscribed and updating the status when user updates the status .

file : SubscriptionAdapter.kt
package com.tutorialsbuzz.fcmdemo

class SubscriptionAdapter(val modelList: List<Model>, val context: Context) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val itemViewHolder = (holder as ViewHolder)
        itemViewHolder.bind(modelList.get(position));
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return ViewHolder(layoutInflater.inflate(R.layout.row_item, parent, false))
    }

    override fun getItemCount(): Int {
        return modelList.size;
    }


    lateinit var mClickListener: ClickListener

    fun setOnItemClickListener(aClickListener: ClickListener) {
        mClickListener = aClickListener
    }

    interface ClickListener {
        fun onClick(pos: Int, aView: View)
    }


    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
        View.OnClickListener {

        fun bind(model: Model): Unit {
            itemView.subscribeLabel.text = "${(adapterPosition + 1)} ) ${model.name}"

            if (model.status) {
                itemView.subscribe.setText(R.string.subscribed)
                itemView.subscribe.setTextColor(ContextCompat.getColor(context, R.color.subscribed))
            } else {
                itemView.subscribe.setText(R.string.subscribe)
                itemView.subscribe.setTextColor(ContextCompat.getColor(context, R.color.subscribe))
            }

            itemView.subscribe.visibility = View.VISIBLE
            itemView.progressBar.visibility = View.GONE
            itemView.subscribe.setOnClickListener(this)
        }

        override fun onClick(view: View?) {

            if (view?.id == itemView.subscribe.id) {

                itemView.subscribe.visibility = View.GONE
                itemView.progressBar.visibility = View.VISIBLE

                mClickListener.onClick(adapterPosition, itemView)
            }

        }
    }

}



1. XML Layout for Activity


file : activity_subscribe.xml
<?xml version="1.0" encoding="utf-8"?>

<androidx.recyclerview.widget.RecyclerView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/subscriptionList"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />


1. Activity


Subscribe the client app to a topic.

// subscribe for notification topic
fun subscribeToTopics(topic: String, position: Int) {
 FirebaseMessaging.getInstance().subscribeToTopic(topic)
	.addOnCompleteListener { task -> //String msg = getString(R.string.msg_subscribed);
		if (task.isSuccessful) {
			//Topic Subscribed Succesfully Todo Task
		}
	}
}

UnSubscribe the client app to a topic.

// Unsubscribe for notification topic
fun UnSubscribeToTopics(topic: String, position: Int) {
 FirebaseMessaging.getInstance().unsubscribeFromTopic(topic)
	.addOnCompleteListener { task -> //String msg = getString(R.string.msg_subscribed);
		if (task.isSuccessful) {
			//Topic Unsubscribed Succesfully Todo Task
		}
	}
}

Complete code for Subscribe Notification Activity

file : SubscribeNotificationTopicsActivity.kt
package com.tutorialsbuzz.fcmdemo

class SubscribeNotificationTopicsActivity : AppCompatActivity() {

    private lateinit var adapter: SubscriptionAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_subscribe)

        subscriptionList.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)

        subscriptionList.addItemDecoration(SimpleDividerItemDecoration(this))

        adapter = SubscriptionAdapter(readFromAsset(), this)
        subscriptionList.adapter = adapter

        adapter.setOnItemClickListener(object : SubscriptionAdapter.ClickListener {
            override fun onClick(pos: Int, aView: View) {

                // status false user not subscribed
                if (!adapter.modelList.get(pos).status) {
                    subscribeToTopics(adapter.modelList.get(pos).name, pos)
                } else {
                    UnSubscribeToTopics(adapter.modelList.get(pos).name, pos)
                }

            }
        })
    }


    fun updateList(position: Int, status: Boolean) {

        adapter.modelList.get(position).status = status
        adapter.notifyItemChanged(position)

        val preferenceManager =
            PreferenceManager.getInstance(this@SubscribeNotificationTopicsActivity)
        preferenceManager?.storeJSONOptimization(Gson().toJson(adapter.modelList))

    }

    // subscribe for notification topic
    fun subscribeToTopics(topic: String, position: Int) {
        FirebaseMessaging.getInstance().subscribeToTopic(topic)
            .addOnCompleteListener { task -> //String msg = getString(R.string.msg_subscribed);
                if (task.isSuccessful) {
                    updateList(position, true)
                    showToast("Subscribe To " + topic)
                }
            }
    }

    // Unsubscribe for notification topic
    fun UnSubscribeToTopics(topic: String, position: Int) {
        FirebaseMessaging.getInstance().unsubscribeFromTopic(topic)
            .addOnCompleteListener { task -> //String msg = getString(R.string.msg_subscribed);
                if (task.isSuccessful) {
                    updateList(position, false)
                    showToast("UnSubscribed For " + topic)
                }
            }
    }

    fun showToast(msg: String) {
        Toast.makeText(
            this@SubscribeNotificationTopicsActivity,
            msg,
            Toast.LENGTH_SHORT
        ).show()
    }


    private fun readFromAsset(): List<Model> {
            /** Find Code for Read from Asset from 
                Below Download Code Link**/
    }

}



Composing Notification From Firebase Console 






Read More »

ANDROID PUSH NOTIFICATION USING FCM (Firebase Cloud Messaging)

Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably send messages at no cost. Using FCM, you can send notification messages to drive user re-engagement and retention .

Lets See Setting FCM Client to app .
  1. Go to the Firebase console.
  2. In the center of the project overview page, click the Android icon (plat_android) or Add app to launch the setup workflow.
  3. Enter your app's package name in the Android package name field.
  4. What's a package name, and where do you find it?
  5. Click Register app.
Application ID : com.tutorialsbuzz.fcmdemo
 Your package name is generally the applicationID in your app-level build.gradle file

Add a Firebase configuration file

1. Add the Firebase Android configuration file to your app:
  • Click Download google-services.json to obtain your Firebase Android config file (google-services.json).
  • Move your config file into the module (app-level) directory of your app.

2. To enable Firebase products in your app, add the google-services plugin to your Gradle files.
  •  In your root-level (project-level) Gradle file (build.gradle), add rules to include the Google Services Gradle plugin. Check that you have Google's Maven repository, as well.
    buildscript {
      repositories {
        // Check that you have the following line (if not, add it):
        google()  // Google's Maven repository
      }
      dependencies {
        ...
        // Add this line
        classpath 'com.google.gms:google-services:4.3.4'
      }
    }
    
    allprojects {
      ...
      repositories {
        // Check that you have the following line (if not, add it):
        google()  // Google's Maven repository
        ...
      }
    }

  • In your module (app-level) Gradle file (usually app/build.gradle), apply the Google Services Gradle plugin:
              
    apply plugin: 'com.android.application'
    // Add this line
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // Import the Firebase BoM
      implementation platform('com.google.firebase:firebase-bom:26.3.0')
    
      // Add the dependency for the Firebase SDK for Google Analytics
      // When using the BoM, don't specify versions in Firebase dependencies
      implementation 'com.google.firebase:firebase-analytics-ktx'
    
      // Add the dependencies for any other desired Firebase products
      // https://firebase.google.com/docs/android/setup#available-libraries
    }
     
  • Sync the project 

FCM Service


Create a class MyFirebaseMessagingService that extends FirebaseMessagingService , This is required if you want to do any message handling beyond receiving notifications on apps in the background .
  • Override the onNewToken : Inside this function your can print the access token , if your using custom server then you can send access token to server . 
  • Override the  onMessageReceived : Inside this function you will recieve the message token sent from fcm server Using Notification Manager we will show payload in notification template .

Compose and Retrieve Data From FCM Notification To Client App .



For NOTIFICATION Related Tutorial Please have a look : .

FCM Service Compelete code 

file : MyFirebaseMessagingService.kt
package com.tutorialsbuzz.fcmdemo


class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onNewToken(token: String) {
        super.onNewToken(token)
        Log.d("MyFirebase", "onNewToken: " + token)
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        Log.d(
            "MyFirebase",
            "onMessageReceived: FROM: " + remoteMessage.from + " \n DATA: " + remoteMessage.data
        )
        sendNotification(this, remoteMessage)
    }


    fun sendNotification(context: Context, remoteMessage: RemoteMessage) {

        val channelId = "tutorialsbuzz"

        // Create an explicit intent for an Activity in your app
        val intent = Intent(applicationContext, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val extras = Bundle()
        extras.putString(notify_title, remoteMessage.notification?.title)
        extras.putString(notify_url, remoteMessage.data.get("pageUrl"))
        intent.putExtras(extras)
        intent.action = Intent.ACTION_VIEW

        val pendingIntent: PendingIntent =
            PendingIntent.getActivity(
                applicationContext,
                NotificationID.iD,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT
            )

        val notificationBuilder =
            NotificationCompat.Builder(context.getApplicationContext(), channelId)
                .setSmallIcon(R.drawable.small_icon)
                .setAutoCancel(true)
                .setLights(Color.BLUE, 500, 500)
                .setVibrate(longArrayOf(500, 500, 500))
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setContentIntent(pendingIntent)
                .setContentTitle(remoteMessage.notification?.title)
                .setContentText(remoteMessage.notification?.body)

        val notificationManager = NotificationManagerCompat.from(context)

        // Since android Oreo notification channel is needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "news",
                NotificationManager.IMPORTANCE_HIGH
            )
            notificationManager.createNotificationChannel(channel)
        }

        GlideApp.with(applicationContext)
            .asBitmap()
            .load(remoteMessage.notification?.imageUrl)
            .into(object : CustomTarget<Bitmap>() {
                override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
                    //largeIcon
                    notificationBuilder.setLargeIcon(resource)
                    //Big Picture
                    notificationBuilder.setStyle(
                        NotificationCompat.BigPictureStyle().bigPicture(resource)
                    )
                    notificationManager.notify(NotificationID.iD, notificationBuilder.build())
                }

                override fun onLoadCleared(placeholder: Drawable?) {}
            })
    }

    companion object {
        const val notify_title: String = "notify_title"
        const val notify_url: String = "notify_url"
    }

}

internal object NotificationID {
    private val c = AtomicInteger(100)
    val iD: Int
        get() = c.incrementAndGet()
}



1. Activity


Handling Notification .
  1. The pending intent of notification target set to MainActivity , So onClick of notification MainActivity gets launch .
  2. Using intent we retrieve the data .
  3. Here in this example we get pageUrl from notification and that pageUrl to loaded into webview .

file : MainActivity.kt
package com.tutorialsbuzz.fcmdemo

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //Get Intent data which was set to notification
        val title = intent?.extras?.get(MyFirebaseMessagingService.notify_title) as String?
        val url = intent?.extras?.get(MyFirebaseMessagingService.notify_url) as String?

        url?.let { webView.loadUrl(it) };

        webView.settings.apply {
            javaScriptEnabled = true
            builtInZoomControls = true
        }

        setWebViewClient()
    }

    private fun setWebViewClient() {
        webView.webViewClient = object : WebViewClient() {
            override fun shouldOverrideUrlLoading(
                view: WebView?,
                request: WebResourceRequest?
            ): Boolean {
                return false
            }

            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                super.onPageStarted(view, url, favicon)
                progressBar.visibility = View.VISIBLE
            }

            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                progressBar.visibility = View.GONE
            }

        }
    }

    override fun onBackPressed() {
        if (webView.canGoBack()) {
            webView.goBack()
        } else {
            super.onBackPressed()
        }
    }
}




Read More »

Android Play Notification Sound From Different Resources

In the previous series of tutorial we have seen how to create notification both in kotlin and java 

In this tutorial we will see playing notification sound different resources( audio file kept different resource like from project "res/raw" and hosted in CDN ) .


Kotlin


To play notification sound we need Uri of the file hosted .


1. Default
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val r = RingtoneManager.getRingtone(context, defaultSoundUri)
r.play()

2. Resource 

Make Sure you have kept custom_sound audio inside "res/raw" folder 

val rawPathUri: Uri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.custom_sound);
val r = RingtoneManager.getRingtone(context, rawPathUri)
r.play()

3. Remote CDN
val cdnPathUri: Uri = Uri.parse("https://www.myinstants.com/media/sounds/beeper_emergency_call.mp3")
val r = RingtoneManager.getRingtone(context, cdnPathUri)
r.play()


JAVA


To play notification sound we need Uri of the file hosted .


1. Default
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone r = RingtoneManager.getRingtone(MainActivity.this, defaultSoundUri);
r.play();)

2. Resource

Make Sure you have kept custom_sound audio inside "res/raw" folder .
 
Uri rawPathUri = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.custom_sound);
Ringtone r = RingtoneManager.getRingtone(MainActivity.this, rawPathUri);
r.play();

3. Remote CDN
Uri cdnPathUri = Uri.parse("https://www.myinstants.com/media/sounds/beeper_emergency_call.mp3");
Ringtone r = RingtoneManager.getRingtone(MainActivity.this, cdnPathUri);
r.play();

Read More »