Settings

Android Settings Example

During the development of mobile applications, a common requirement is of storing app related data to the phone. In Android we perform this task with the help of Preference APIs. We may also need settings that allow users to modify preferences in app. Thankfully Android provides a powerful framework to manage user preferences.

The provided mechanism allows us to show, save and manipulate user’s preferences very easily, it also has support for automatic UI creation. Yes, you read it right, by declaring the type of a user preference, a user interface for manipulating these preferences is automatically generated, without having us to write a single line of code.  All we need to do is to simply use it in our app. Doesn’t it sound cool? Lets start then.

1. Introduction

In the Preference Framework we have four components to cover, they are:

  • Preference Activity or Fragment – these host the Preference Screen, displaying your settings. We may use any one (Activity or Fragment) as per our requirement.
  • Preference XML – a xml file defining your settings items.
  • Preference Headers – these are lists of subscreens. An xml file defines the Preference Fragments used for the Headers subscreens.
  • Shared Preference Change Listener – listens for any changes in the Shared Preference values.

So, now we have a brief introduction to the Preference Framework. We would move forward by creating the project.

2. Create a New Android Application Project

We will create a simple Android Project as we are used to. There is nothing special or new for this tutorial. We have some screenshots for those who are new to android development, which will definitely help them start with.

Figure 1 : Start with specifying company domain and project name.

 

Select Minimum SDK, click next to continue.
Figure 2 : Select Minimum SDK, click next to continue.

 

Add an Activity and Continue.
Figure 3 : Add an Activity and Continue.

 

Provide activity details. Click on Finish to start coding.
Figure 4 : Provide activity details. Click on Finish to start coding.

So, now we have our project. We also have the MainActivity, which we will use to show the Preference Settings and also as the start point of PreferenceActivity.
The final structure of the created project is shown in the image below.

Created Project Structure
Figure 5 : Created Project Structure

We’ll have a look at the auto generated MainActivity class.

MainActivity.java

package com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

}

And here is the auto generated xml layout files.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:fitsSystemWindows="true"
    tools:context="com.webege.rivu.myapplication.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.webege.rivu.myapplication.MainActivity"
    tools:showIn="@layout/activity_main">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</RelativeLayout>

and here is the primary screen.

MainActivity
Figure 6 : MainActivity

3. Use MainActivity as Starting Point

There is nothing new in the codes I’ve pasted above. We will make a slight alteration to this one. As I already told I’ll use MainActivity to show settings. I will also use the FloatingActionButton to start the PreferenceActivity. So let us first have a little alteration in the layout files and in the code of MainActivity. I have simply changed the icon in FloatingActionButton to a settings icon in activity_main.xml.

In the content_main.xml, I have provided id to the TextView, so that i can use it in code for showing the preferences in text. I have also removed the Hello World! text from there. Here is the final code.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:fitsSystemWindows="true"
    tools:context="com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_menu_preferences" />

</android.support.design.widget.CoordinatorLayout>

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample.MainActivity"
    tools:showIn="@layout/activity_main">

    <TextView
        android:id="@+id/settingsContent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

In the MainActivity, I’ve used SharedPreferences to show the settings primarily. Here it is.

MainActivity.java

package com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent modifySettings=new Intent(MainActivity.this,SettingsActivity.class);
                startActivity(modifySettings);
            }
        });

        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

        StringBuilder builder = new StringBuilder();

        builder.append("\n" + "Perform Sync:\t" + sharedPrefs.getBoolean("perform_sync", false));
        builder.append("\n" + "Sync Intervals:\t" + sharedPrefs.getString("sync_interval", "-1"));
        builder.append("\n" + "Name:\t" + sharedPrefs.getString("full_name", "Not known to us"));
        builder.append("\n" + "Email Address:\t" + sharedPrefs.getString("email_address", "No EMail Address Provided"));
        builder.append("\n" + "Customized Notification Ringtone:\t" + sharedPrefs.getString("notification_ringtone", ""));
        builder.append("\n\nClick on Settings Button at bottom right corner to Modify Your Prefrences");

        TextView settingsTextView = (TextView) findViewById(R.id.settingsContent);
        settingsTextView.setText(builder.toString());

    }

    
}

So as I told, I’m using settingsTextView to show the SharedPreferences data. For simplicity I’m assuming of an application which has background sync. I’m assuming that the application would allow user to enable or disable the background sync, and also users can specify the sync interval. This application also stores user’s name and email, and also allows to modify them. It also has an option to customise push notification ringtone.

In the above code I’ve initialised SharedPreferences with PreferenceManager.getDefaultSharedPreferences(context). I have used getBoolean and getString methods to read them if they are present and assigned a default value if they are not. There is one more thing, you can easily get from the above code that I’ve already created another activity SettingsActivity, which I’ve called on the FloatingActionButton click event. That FloatingActionButton is actually a subclass of PreferenceActivity.

4. The Preference Framework – PreferenceActivity

So now we have stepped in to the core part of this tutorial. We will go through the steps of creating the SettingsActivity, but let us first look at the framework. We will look at the four most common types of Preferences present in this framework.

  • CheckBoxPreference: Provides checkbox widget functionality. Will store a Boolean value into the SharedPreferences.
  • EditTextPreference: Provides UI for string input dialog. Will store the entered string into the SharedPreferences.
  • ListPreference: Displays a list of entries as a dialog. The selected preference will store a string into the SharedPreferences.
  • RingtonePreference: Provides UI to choose a ringtone from the device. The selected ringtone’s URI will be persisted as a string into the SharedPreferences.
  • PreferenceScreen: It is the root of a Preference hierarchy. When a PreferenceActivity points to this, it is not shown but the contained preferences are shown. When it appears inside another preference hierarchy, it is shown and serves as the gateway to another screen of preferences (may be via Dialog or via Intent).
  • PreferenceCategory: It is used to group Preference objects and provide a disabled (non-selectable) title above the group..

So now lets create the preferences.xml file in the valuesxml folder, it will be used in the SettingsActivity.

preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="Sync Information"
        android:key="sync_category">

        <CheckBoxPreference
            android:key="perform_sync"
            android:summary="Enable or disable data sync"
            android:title="Enable Auto Sync"
            android:defaultValue="true"/>

        <ListPreference
            android:key="sync_interval"
            android:title="Sync interval"
            android:summary="Define how often sync will be performed"
            android:defaultValue="1000"
            android:entries="@array/updateInterval"
            android:entryValues="@array/updateIntervalValues"
            android:dependency="perform_sync"/>

    </PreferenceCategory>

    <PreferenceCategory
        android:title="Personal Informations"
        android:key="personal_category">


        <EditTextPreference
            android:key="full_name"
            android:title="Name"
            android:summary="Enter Your Complete Name"
            android:dialogTitle="Your Name"
            android:dialogMessage="Enter Your Complete Name"
            android:defaultValue=""
            android:inputType="textCapWords"/>

        <EditTextPreference
            android:key="email_address"
            android:title="Email Address"
            android:summary="Enter Your Email Address"
            android:dialogTitle="Enter Your Email Address"
            android:dialogMessage="Enter Your Email Address"
            android:defaultValue=""
            android:inputType="textEmailAddress"/>

    </PreferenceCategory>


    <PreferenceCategory
        android:title="Customisations"
        android:key="custom_category">


        <RingtonePreference
            android:key="notification_ringtone"
            android:title="Notification Ringtone"
            android:summary="Customise Notification Ringtone for you"
            android:dialogTitle="Notification Ringtone"
            android:dialogMessage="Customise Notification Ringtone for you"/>



    </PreferenceCategory>

</PreferenceScreen>

I would like to describe a bit. As I already mentioned PreferenceScreen is the root. Under that I have three PreferenceCategory groups, that are sync_category (Sync Information), personal_category (Personal Informations) and custom_category (Customisations).

The provided user settings are already discussed. So we would move forward and create the PreferenceActivity. Please follow the screenshots.

Create new Empty Activity
Figure 7 : Create new Empty Activity

 

SettingsActivity Creation
Figure 8 : Provide Activity Name

So the SettingsActivity is created. We will now add a code to this newly created Activity.

SettingsActivity.java

package com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample;

import android.preference.PreferenceActivity;
import android.os.Bundle;

public class SettingsActivity extends PreferenceActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
}


That set. We are done. We will now look at the output screenshots.

Initial Screen
Figure 9 : Initial Screen

 

PreferenceScreen
Figure 10 : PreferenceScreen

 

Auto Sync (CheckBoxPreference) disabled
Figure 11 : Auto Sync (CheckBoxPreference) disabled

 

Figure 12 : Sync Interval (ListPreference)

 

Figure 13 : Name (EditTextPreference)

 

Figure 14 : Email (EditTextPreference, android:inputType=”textEmailAddress”)

 

Figure 15 : Notification Ringtone (RingtonePreference)

 

Figure 16 : Final Screen – All Details

 

5. PreferenceFragment and Preference Headers

Till now we have dealt with the simplest example of using Preference Framework. Its time to dive deeper. Sometime you may wish to have separate subscreens for each type of preferences. You may also want a multipane layout Tablets. These requirements can be fulfilled by the use of Preference Headers and PreferenceFragment .

As I stated in the start of the tutorial that PreferenceFragment is also used to host the PreferenceScreen as the PreferenceActivity. Preference Headers sre used to organise the Preference Screens in subscreens. So we are creating another activity SettingsActivity2. We will implement preference_header and PreferenceFragment in this activity. First we need to create 4 xml files. 3 for three Preference Screens and 1 for Preference Header. So lets get a glimpse on code.

pref_headers.xml

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- These settings headers are only used on tablets. -->

    <header
        android:fragment="com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample.SettingsActivity2$GeneralPreferenceFragment"
        android:icon="@drawable/ic_info_black_24dp"
        android:title="@string/pref_header_general">
        <extra android:name="settings" android:value="prefs_general" ></extra>
    </header>

    <header
        android:fragment="com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample.SettingsActivity2$GeneralPreferenceFragment"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/pref_header_notifications" >
        <extra android:name="settings" android:value="prefs_notification" ></extra>
    </header>

    <header
        android:fragment="com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample.SettingsActivity2$GeneralPreferenceFragment"
        android:icon="@drawable/ic_sync_black_24dp"
        android:title="@string/pref_header_data_sync" >
        <extra android:name="settings" android:value="prefs_sync" ></extra>
    </header>

</preference-headers>

pref_general.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <EditTextPreference
        android:key="full_name"
        android:title="Name"
        android:summary="Enter Your Complete Name"
        android:dialogTitle="Your Name"
        android:dialogMessage="Enter Your Complete Name"
        android:defaultValue=""
        android:inputType="textCapWords"/>

    <EditTextPreference
        android:key="email_address"
        android:title="Email Address"
        android:summary="Enter Your Email Address"
        android:dialogTitle="Enter Your Email Address"
        android:dialogMessage="Enter Your Email Address"
        android:defaultValue=""
        android:inputType="textEmailAddress"/>

</PreferenceScreen>

pref_notification.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- A 'parent' preference, which enables/disables child preferences (below)
         when checked/unchecked. -->
    <SwitchPreference
        android:defaultValue="true"
        android:key="notifications_new_message"
        android:title="@string/pref_title_new_message_notifications" />
    
    <RingtonePreference
        android:key="notification_ringtone"
        android:title="Notification Ringtone"
        android:summary="Customise Notification Ringtone for you"
        android:dialogTitle="Notification Ringtone"
        android:dialogMessage="Customise Notification Ringtone for you"
        android:dependency="notifications_new_message"/>



</PreferenceScreen>

pref_data_sync.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
         dismiss it. -->
    <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
    <CheckBoxPreference
        android:key="perform_sync"
        android:summary="Enable or disable data sync"
        android:title="Enable Auto Sync"
        android:defaultValue="true"/>

    <ListPreference
        android:key="sync_interval"
        android:title="Sync interval"
        android:summary="Define how often sync will be performed"
        android:defaultValue="1000"
        android:entries="@array/updateInterval"
        android:entryValues="@array/updateIntervalValues"
        android:dependency="perform_sync"/>

</PreferenceScreen>

So before moving to newly created SettingsActivity2 let me explain the xml files first. The pref_headers.xml file contains the Headers with Titles, Icons and target fragments. When SettingsActivity2 is created the Headers will be displayed and upon click of a header item the respective PreferenceScreen will be displayed. The <extra> is used to pass some arguments to the fragment, which we can get by calling getArguments() inside the Fragment.

I’ve divided the previous preferences.xml into 3 separate files, namely pref_general.xml, pref_notification.xml and pref_data_sync.xml. Everything in those files are almost same with previous, except a SwitchPreference used in pref_notification.xml. the functionality of SwitchPreference is almost same with CheckBoxPreference except that the SwitchPreference provides a Switch UI instead of CheckBox UI. Each of the PreferenceScreens are displayed with PreferenceFragment. So here is the new activity code.

SettingsActivity2.java

package com.javacodegeeks.examples.rivu.chakraborty.androidsettingsexample;


import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.support.v7.app.ActionBar;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.RingtonePreference;
import android.text.TextUtils;
import android.view.MenuItem;
import android.widget.Toast;

import java.util.List;

public class SettingsActivity2 extends AppCompatPreferenceActivity {

    /**
     * Helper method to determine if the device has an extra-large screen. For
     * example, 10" tablets are extra-large.
     */
    private static boolean isLargeTablet(Context context) {
        return (context.getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setupActionBar();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home) {
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Set up the {@link android.app.ActionBar}, if the API is available.
     */
    private void setupActionBar() {
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            // Show the Up button in the action bar.
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean onIsMultiPane() {
        return isLargeTablet(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }

    /**
     * This method stops fragment injection in malicious applications.
     * Make sure to deny any unknown fragments here.
     */
    protected boolean isValidFragment(String fragmentName) {
        return PreferenceFragment.class.getName().equals(fragmentName)
                || GeneralPreferenceFragment.class.getName().equals(fragmentName);
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class GeneralPreferenceFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            int preferenceFile_toLoad=-1;
            String settings = getArguments().getString("settings");
            if ("prefs_general".equalsIgnoreCase(settings)) {
                // Load the preferences from an XML resource
                preferenceFile_toLoad= R.xml.pref_general;
            }else if ("prefs_notification".equalsIgnoreCase(settings)) {
                // Load the preferences from an XML resource
                preferenceFile_toLoad=R.xml.pref_notification;
            }else if ("prefs_sync".equals(settings)) {
                // Load the preferences from an XML resource
                preferenceFile_toLoad=R.xml.pref_data_sync;
            }

            addPreferencesFromResource(preferenceFile_toLoad);


        }



    }


}


The headers are loaded using loadHeadersFromResource(R.xml.pref_headers, target) inside onBuildHeaders method. And as per our previous discussions, I have loaded the PreferenceScreen for each header in GeneralPreferenceFragment. In GeneralPreferenceFragment

I’ve first determined the required PreferenceScreen using getArguments().getString("settings") (which were passed in pref_headers.xml using <extra> tag), then initialised with addPreferencesFromResource(preferenceFile_toLoad).

There is another new method in SettingsActivity2, onIsMultiPane which is called internally to determine multipane is required or not. I’ve returned true if the device is Large Tablet. So lets have a look at the final output screenshots.

Figure 17 : Main Settings Page

 

Figure 18 : General Settings

 

Figure 19 : Notification Settings

 

Figure 20 : Data Sync Settings

 

Figure 21 : Multipane Layout

 

6. Download the Source Code

This was an example on Android Settings.

Download
You can download the full source code of this example here: AndroidSettingsExample

Rivu Chakraborty

Rivu Chakraborty is a Google Certified Android Developer, Sr. Tech Member of Institute of Engineers (India), he also have certifications on Scrum. He is also an author of multiple books on Kotlin, Reactive Programming, Functional Programming and Android Development, published by renowned publication houses. Having total 5+ years of experience he is presently working as a Sr. Software Engineer (Android) at Indus Net Technologies Pvt. Ltd. Rivu Chakraborty considers himself a Kotlin and Android enthusiast and a Kotlin evangelist. He has been using Kotlin since December 2015, so he has around 2 years' experience in Kotlin. As part of his mission to expand the use and knowledge of the Kotlin Language as much as possible, he created the KotlinKolkata User Group, one of the most active Kotlin user groups throughout the world and he is a Founder Organizer of KotlinKolkata. He is also an active member of GDG Kolkata and gives talks at GDG Kolkata Meetups.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
mike lee
mike lee
5 years ago

Great work!

Eddy
Eddy
5 years ago

Thanks

Hsandid
Hsandid
4 years ago

Great work. Thanks for the tutorial

Back to top button