Tuesday, 17 June 2014

Android pick date time from EditText OnClick event

In this tutorial I would teach you how to pick date and time in Android on single event, for this case I am using the OnClick event of an EditText. Being a mobile app consultant at androidTutons I have come across many situations where both date and time are taken as an input from user, and rarely there’s a case where only one of the value is required. So here goes the tutorial.
I am assuming who ever is reading this tutorial has a basic understanding of how to create an android project, therefore lets continue to next step by creating a layout for your activity.

Activity_main.xml: 

<RelativeLayout 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"
    tools:context=".MainActivity"
    android:background="#FFFFFF" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:text="Pick Date Time" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/textView1"
        android:layout_alignParentRight="true"
        android:ems="10"
        android:inputType="date" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:src="@drawable/truiton" />

</RelativeLayout>

Now next step would be to create a class TimePickerFragment in our MainActivity which extends DialogFragment, remember we are using DatePicker and TimePicker as dialogs. Also we are maintaining backward compatibility with previous versions of android APIs – to do this we are using android support v4 libraries.


public static class TimePickerFragment extends DialogFragment implements
            TimePickerDialog.OnTimeSetListener {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Use the current time as the default values for the picker
            final Calendar c = Calendar.getInstance();
            int hour = c.get(Calendar.HOUR_OF_DAY);
            int minute = c.get(Calendar.MINUTE);

            // Create a new instance of TimePickerDialog and return it
            return new TimePickerDialog(getActivity(), this, hour, minute,
                    DateFormat.is24HourFormat(getActivity()));
        }

        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            // Do something with the time chosen by the user
            DateEdit.setText(DateEdit.getText() + " -" + hourOfDay + ":"    + minute);
        }
    }

Next step would be to create a class to implement DatePickerDialog. Again I would like to remind that only import android.support.v4.app.DialogFragment package as we want to maintain backward compatibility.

public static class DatePickerFragment extends DialogFragment implements
            DatePickerDialog.OnDateSetListener {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Use the current date as the default date in the picker
            final Calendar c = Calendar.getInstance();
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int day = c.get(Calendar.DAY_OF_MONTH);

            // Create a new instance of DatePickerDialog and return it
            return new DatePickerDialog(getActivity(), this, year, month, day);
        }

        public void onDateSet(DatePicker view, int year, int month, int day) {
            // Do something with the date chosen by the user
            DateEdit.setText(day + "/" + (month + 1) + "/" + year);
        }
    }
 

Now lets create methods to call these classes

Method:

public void showTruitonTimePickerDialog(View v) {
        DialogFragment newFragment = new TimePickerFragment();
        newFragment.show(getSupportFragmentManager(), "timePicker");
    }

public void showTruitonDatePickerDialog(View v) {
        DialogFragment newFragment = new DatePickerFragment();
        newFragment.show(getSupportFragmentManager(), "datePicker");
    }
 

Now we have achieved the functionality to call the Android DatePicker and TimePickerDialogs in same class, we just have to write an onCreate method to call these date and time picker dialogs    at OnClick event of an EditText. 


MainActivity:

package com.truiton.datetime;

import java.util.Calendar;
import android.os.Bundle;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TimePicker;

public class MainActivity extends FragmentActivity {
    static EditText DateEdit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DateEdit = (EditText) findViewById(R.id.editText1);
        DateEdit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showTruitonTimePickerDialog(v);
                showTruitonDatePickerDialog(v);
            }
        });
    }

    public void showTruitonDatePickerDialog(View v) {
        DialogFragment newFragment = new DatePickerFragment();
        newFragment.show(getSupportFragmentManager(), "datePicker");
    }

    public static class DatePickerFragment extends DialogFragment implements
            DatePickerDialog.OnDateSetListener {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Use the current date as the default date in the picker
            final Calendar c = Calendar.getInstance();
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int day = c.get(Calendar.DAY_OF_MONTH);

            // Create a new instance of DatePickerDialog and return it
            return new DatePickerDialog(getActivity(), this, year, month, day);
        }

        public void onDateSet(DatePicker view, int year, int month, int day) {
            // Do something with the date chosen by the user
            DateEdit.setText(day + "/" + (month + 1) + "/" + year);
        }
    }

    public void showTruitonTimePickerDialog(View v) {
        DialogFragment newFragment = new TimePickerFragment();
        newFragment.show(getSupportFragmentManager(), "timePicker");
    }

    public static class TimePickerFragment extends DialogFragment implements
            TimePickerDialog.OnTimeSetListener {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Use the current time as the default values for the picker
            final Calendar c = Calendar.getInstance();
            int hour = c.get(Calendar.HOUR_OF_DAY);
            int minute = c.get(Calendar.MINUTE);

            // Create a new instance of TimePickerDialog and return it
            return new TimePickerDialog(getActivity(), this, hour, minute,
                    DateFormat.is24HourFormat(getActivity()));
        }

        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            // Do something with the time chosen by the user
            DateEdit.setText(DateEdit.getText() + " -" + hourOfDay + ":"    + minute);
        }
    }

Although the code is self explanatory, what we are doing in this last step is just calling two methods which create date and time picker dialogs at setOnClickListener of EditText. Here first DatePickerDialog is called, its value is returned to the EditText, next TimePickerDialogis called and its value is appended to the EditText value, and Voila here we have date and time input from a single click/tap. 

Android Force Close Application : Session Timeout

Recently we started development for an app at androidtutons, in which we had to maintain sessions locally, the situation was, that if we minimize the app by tapping home key or any other key, the app’s session should log out automatically after  10 minutes. Hence I had to figure out a way in Android to Force Close Application : Session Timeout. 

Now the question arises how do I close my app/session programmatically ?

The task at hand is, we do not have to force close application: session timeout immediately as user minimizes the app. Hence the first approach that came into my mind was to schedule a task to force close application:session timeout in Android. We did this by AlarmManager class of android. With the help of AlarmManager we called a BroadcastReceiver which sent the final broadcast via sendBroadcast method.

Here it goes, we’ll start by creating the layout for our main activity.

activity_main.xml:

<RelativeLayout 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"
    tools:context=".MainActivity"
    android:background="#FFFFFF" >

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="40dp"
        android:text="Session Timed Out - Please relogin"
        android:visibility="invisible" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView2"
        android:layout_alignParentTop="true"
        android:layout_marginTop="44dp"
        android:src="@drawable/truiton" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="Login" />

</RelativeLayout>

 

MainActivity.java 

package com.androidtuton.closebroadcast;

import com.truiton.closebroadcast.R;

import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button b1 = (Button) findViewById(R.id.button1);
        final Intent i = new Intent(this, SecondActivity.class);
        b1.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                startActivityForResult(i, 1);
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == 1) {
            if (resultCode == RESULT_OK) {
                int result = data.getIntExtra("result", 0);
                if (result == 1) {
                    TextView TV1 = (TextView) findViewById(R.id.textView2);
                    TV1.setVisibility(TextView.VISIBLE);
                }
            }

            if (resultCode == RESULT_CANCELED) {
                // code here for cancelled result
            }
        }
    }


Here we have simply created an activity which listens to an OnClick event of a button via OnClickListener and starts the SecondActivity. Also this MainActivity receives the result and shows the message accordingly when app is force closed/session timeout.

SecondActivity:

package com.androidtutons.closebroadcast;

import java.util.Calendar;

import com.truiton.closebroadcast.R;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;

public class SecondActivity extends Activity {
    BroadcastReceiver myReceiver;
    AlarmManager alarmManager;
    PendingIntent pendingIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_second);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("truiton.ACTION_FINISH");
        myReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                Log.v("Second Activity", "Finishing Activity");
                /**** Returning Result *****/
                Intent returnIntent = new Intent();
                returnIntent.putExtra("result", 1);
                SecondActivity.this.setResult(RESULT_OK, returnIntent);
                /*********/
                /**** Cancel Result ****/
                /*
                 * Intent returnIntent = new Intent();
                 * SecondActivity.this.setResult(RESULT_CANCELED, returnIntent);
                 */
                /*********/
                finish();
            }
        };
        registerReceiver(myReceiver, intentFilter);
    }

    @Override
    protected void onPause() {
        super.onPause();

        Intent myIntent = new Intent(getBaseContext(),
                MyScheduledReceiver.class);
        Bundle bundle = new Bundle();
        bundle.putInt("val", 8);
        myIntent.putExtras(bundle);
        pendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0,
                myIntent, 0);
        alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.add(Calendar.SECOND, 10);
        alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                pendingIntent);
        Log.v("Second Activity", "Alarm Scheduled");
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (alarmManager != null) {
            alarmManager.cancel(pendingIntent);
            Log.v("Second Activity", "Scheduled Alarm Cancelled");
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(myReceiver);
    }

}

This SecondActivity is the activity where magic happens. This activity has anonReceive method which is used to receive the broadcast which will be sent from the next class i.e MyScheduledReceiver. This helps in force close application:session timeout on Android. Please define this method in all of the activities which you want to close when session expires (Highlighted Lines) this is the method which finish() is called to end the activity. Have a look at the onPause method if this class, here we are scheduling an alarm of 10 seconds with the help of AlarmManagerIntentPendingIntent and Calendar classes.

 MyScheduledReceiver

package com.androidtutons.closebroadcast;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

public class MyScheduledReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent recievedIntent) {
        // TODO Auto-generated method stub
        Toast.makeText(context, "Session Timeout", Toast.LENGTH_LONG).show();
        Log.v("MyScheduledReceiver", "Intent Fired");

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("truiton.ACTION_FINISH");
        context.sendBroadcast(broadcastIntent);
    }

}


In Android to force close application:session timeout, MyScheduledReceiver class is used. Although code for this class is self explanatory, here we are sending a broadcast viasendBroadcast method in onReceive method of receiver. The broadcast intent fired here has an action truiton.ACTION_FINISH which is used to finish the SecondActivity.
This is BroadcastReceiver class therefore we also need to register it as a receiver in manifest

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.truiton.closebroadcast"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.truiton.closebroadcast.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.truiton.closebroadcast.SecondActivity"
            android:label="@string/app_name" >
        </activity>
        <receiver android:name="MyScheduledReceiver" />
    </application>

</manifest>

To sum up – In this Android Force Close Application-Session Timeout Tutorial we used anAlarmManager to schedule a task, which fires an intent to close (finish) all the open classes and return a result to the main calling class. Although it did not solve my purpose, which was to securely end the session after a specific time, as in this approach if user changes the time after minimizing the app, it does not work as supposed to. I posted this code to help someone who’s working in similar sort of app, as this code can be utilized in numerous ways.