Android Basic Training Course: Creating a StackView

Android Basic Training Course: Creating a StackView

    Stack is a common design that we can see at fronted-end programming including mobile app development. When using Android phone, you can realize that showing "recent apps" is a type of stack view design:
    This design is very diverse but by this post, just about "Android basic development", I would like to talk about using StackView widget. Like ListView or GridView, it's a subclass of AdapterView, so it has the same operation mechanism (adapter of a "list" of object). Now, following this tutorial step by step, you will understand the way to use it!

Preparing some drawables resource

    The fact that stack view is usually used for display "stack of images" or something that similarity. So, after start a new Android Studio project, please add some own images to drawable folder. Make sure that these images have the same size:

Put StackView to your Activity layout

    Just including a StackView in your activity layout file like this:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="info.devexchanges.stackview.MainActivity">

    <StackView
        android:id="@+id/stack_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />

</RelativeLayout>

Create layout for each StackView item

    As noted above, Stack view is usually used for display list of images, so I will make a layout which contains an ImageView to show a drawable resource and a TextView to present it's name for each StackView item like this:
item_stack.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/imageView"
        android:contentDescription="@null"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/image_1"
        android:layout_gravity="center_horizontal" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:textColor="@android:color/holo_blue_dark"
        android:textStyle="bold" />

</LinearLayout>

Create a POJO class

    Java object for each Stack view item:
StackItem.java
package info.devexchanges.stackview;

public class StackItem {

    private int drawableId;
    private String imageName;

    public StackItem(int id, String imageName) {
        this.imageName = imageName;
        this.drawableId = id;
    }

    public String getImageName() {
        return imageName;
    }

    public int getDrawableId() {
        return drawableId;
    }
}

Create a StackView adapter class

    This is the most important step of this tutorial. Like ListView, we can create a subclass of ArrayAdapter or BaseAdapter to build the adapter class which holding and displaying data. I also provide a ViewHolder to make convertView clearly:
StackAdapter.java
package info.devexchanges.stackview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class StackAdapter extends ArrayAdapter<StackItem> {

    private List<StackItem> items;
    private Context context;

    public StackAdapter(Context context, int textViewResourceId, List<StackItem> objects) {
        super(context, textViewResourceId, objects);
        this.items = objects;
        this.context = context;
    }

    @NonNull
    @SuppressLint("InflateParams")
    public View getView(int position, View convertView, @NonNull ViewGroup parent) {
        ViewHolder holder; //initialize a null ViewHolder object
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        // If holder not exist then locate all view from UI file
        if (convertView == null) {
            // inflate UI from XML file
            convertView = inflater.inflate(R.layout.item_stack, parent, false);
            // get all UI view
            holder = new ViewHolder(convertView);
            // set tag for holder
            convertView.setTag(holder);
        } else {
            // if holder created, get tag from view
            holder = (ViewHolder) convertView.getTag();
        }
        StackItem stackItem = items.get(position);

        //set stack item data to views
        holder.image.setImageResource(stackItem.getDrawableId());
        holder.imageName.setText(stackItem.getImageName());

        return convertView;
    }

    private class ViewHolder {
        private TextView imageName;
        private ImageView image;

        public ViewHolder(View view) {
            this.image = (ImageView) view.findViewById(R.id.imageView);
            this.imageName = (TextView) view.findViewById(R.id.textView);
        }
    }
}

Activity programmatically code

    There is nothing special in the main activity code. Just initializing an adapter with available data and attaching it to the StackView:
MainActivity.java
package info.devexchanges.stackview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.StackView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private final int[] IMAGE_IDs = {R.drawable.image_1, R.drawable.image_2, R.drawable.image_3,
            R.drawable.image_4, R.drawable.image_5, R.drawable.image_6};

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

        StackView stackView = (StackView) findViewById(R.id.stack_view);

        ArrayList<StackItem> stackItems = new ArrayList<>();
        for (int imageId : IMAGE_IDs) {
            String name = getResources().getResourceEntryName(imageId);
            stackItems.add(new StackItem(imageId, name));
        }

        StackAdapter stackAdapter = new StackAdapter(this, R.layout.item_stack, stackItems);
        stackView.setAdapter(stackAdapter);
    }
}

Running the application

    Our output will be like this:

The weakness of the "default" StackView

    This "official" widget of Android SDK has the biggest weakness is swipe listener is absent, detect this user action is not a easily. Selected item event still not working well, either. I recommend to use a third-party library to make this layout better in programming. These are some of them:
I also had a post about creating Swipe Card Stack, read this tutorial HERE!




Android Basic Training Course: DownloadManager Example

Android Basic Training Course: DownloadManager Example

    The DownloadManager was introduced in Android 2.3 (API level 9). It’s a System Service that optimizes the handling of long-running downloads in the background. The download manager handles HTTP connections, monitors connectivity changes, reboots, and ensures each download completes successfully It's good practice to use the download manager for most downloads, especially if the download continues between user sessions or if success is important.

    In this example, I’m going to use the built-in DownloadManager to download an image file and handle it's completion via a BroadcastReceiver.
    Define a layout file (XML) for our main activity first:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Start Download" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:contentDescription="@null"
        android:src="@mipmap/ic_launcher" />
</LinearLayout>
In activity programmatically code, access the DownloadManager, request the DOWNLOAD_SERVICE using the getSystemService():
downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE)
Create a download request through initializing a Request object with a URL string:
Request request = new Request(Uri.parse(DOWNLOAD_URL));
After that, your you have to put it into the queue by this code:
enqueue = downloadManager.enqueue(request);
Now, the DownloadManager will carry out download work!
In the main activity, I create a BroadcastReceiver to handling it's completion. Full source code for this activity:
MainActivity.java
package info.devexchanges.downloadingfile;

import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.webkit.URLUtil;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private long enqueue;
    private DownloadManager downloadManager;
    private final static String DOWNLOAD_URL = "http://i.imgur.com/wsibrEw.gif";

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

        BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
                    intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
                    Query query = new Query();
                    query.setFilterById(enqueue);
                    Cursor cursor = downloadManager.query(query);
                    if (cursor.moveToFirst()) {
                        int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
                        if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
                            ImageView view = (ImageView) findViewById(R.id.imageView);
                            String uriString = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                            view.setImageURI(Uri.parse(uriString));
                            Toast.makeText(MainActivity.this, "Download completed!", Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            }
        };

        registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

    public void onClick(View view) {
        downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        Request request = new Request(Uri.parse(DOWNLOAD_URL));

        //Get download file name
        String fileExtenstion = MimeTypeMap.getFileExtensionFromUrl(DOWNLOAD_URL);
        String name = URLUtil.guessFileName(DOWNLOAD_URL, null, fileExtenstion);

        //Save file to destination folder
        request.setDestinationInExternalPublicDir("/Downloads", name);

        enqueue = downloadManager.enqueue(request);
    }
}
Never forget to add Internet and write file permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Running application, we'll have this output:
    Click "Start Download", the downloading process will be invoked. This is it's notification:
    When the image downloaded, it will be set to the ImageView:
    If you open "Downloads" application in your device, you will see this completed work:
    Open "File Manager" application, you will see the downloaded file in "Downloads" folder:

Conclusions

    I have presented some basic features of DownloadManager in Android SDK through downloading a file from Internet and saving it to SD card. For more details, please read it's official document to find out some custom features when initiate a download (adding title, description, allow only wifi,...). Finally, you can take my project from @Github.



Update: Runtime permission from Android M

    From version 6.0, Android divide permissions into 2 categories: normal and dangerous permissions. Downloading file need WRITE_EXTERNAL_STORAGE permission so you must request it at the runtime. Please read my NEWER POST to understanding this new mechanism.

Android Basic Training Course: Video Recording

Android Basic Training Course: Video Recording

    In previous chapter, I have guided about working with Camera and Gallery in Android. There is a popular another case is recording video by device camera. So, in this example, we are going to see the use of Android Video API in a more detailed way, and develop a simple application that captures video, switches between the front and the back camera of our mobile device and saving the video file to SD card.

Request Camera and start Video Recorder Intent

    In order to use device camera and writing the video file to sd card, you must provide these permissions in AndroidManifest.xml:
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    With this declaration, your application can be installed only in devices which has a camera! Request code for starting video recorder intent:
// create new Intentwith with Standard Intent action that can be
// sent to have the camera application capture an video and return it.
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

// create a file to save the video
fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);

// set the image file name
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

// set the video image quality to high
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);

// start the Video Capture Intent
startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
    Like taking photo from camera, you must override onActivityResult() method of Activity class to get data from a sub-Activity:
    @SuppressLint("SetTextI18n")
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                noticeText.setText("Video file has been saved!");
            } else if (resultCode == RESULT_CANCELED) {
                noticeText.setText("User cancelled the video capture.");
            } else {
                noticeText.setText("Video capture failed.");
            }
        }
    }
    And providing a function which saving data (video file) to a folder in SD Card:
    @SuppressLint({"SetTextI18n", "SimpleDateFormat"})
    private File getOutputMediaFile(int type) {

        // Check that the SDCard is mounted
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "DevExVideo");

        // Create the storage directory("DevExVideo") if it does not exist
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                noticeText.setText("Failed to create directory DevExVideo");
                return null;
            }
        }

        // Create a media file name
        // For unique file name appending current timeStamp with file name
        java.util.Date date = new java.util.Date();
        timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(date.getTime());

        File mediaFile;
        if (type == MEDIA_TYPE_VIDEO) {
            // For unique video file name appending current timeStamp with file name
            mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4");

        } else return null;

        return mediaFile;
    }

Full project code

    This is our main activity, which running after application launched:
MainActivity.java
package info.devexchanges.recordingvideo;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.File;
import java.text.SimpleDateFormat;

public class MainActivity extends AppCompatActivity {

    private Uri fileUri;
    private String timeStamp;
    public static final int MEDIA_TYPE_VIDEO = 2;
    private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;

    public TextView noticeText;
    private Button buttonRecording;

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

        buttonRecording = (Button) findViewById(R.id.btn_record);
        noticeText = (TextView) findViewById(R.id.text_view);

        buttonRecording.setOnClickListener(new Button.OnClickListener() {

            @Override
            public void onClick(View arg0) {

                // create new Intentwith with Standard Intent action that can be
                // sent to have the camera application capture an video and return it.
                Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

                // create a file to save the video
                fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);

                // set the image file name
                intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

                // set the video image quality to high
                intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);

                // start the Video Capture Intent
                startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
            }
        });
    }

    /**
     * Create a file Uri for saving an image or video
     */
    private Uri getOutputMediaFileUri(int type) {
        return Uri.fromFile(getOutputMediaFile(type));
    }

    /**
     * Create a File for saving an image or video
     */
    @SuppressLint({"SetTextI18n", "SimpleDateFormat"})
    private File getOutputMediaFile(int type) {

        // Check that the SDCard is mounted
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "DevExVideo");

        // Create the storage directory("DevExVideo") if it does not exist
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                noticeText.setText("Failed to create directory DevExVideo");
                return null;
            }
        }

        // Create a media file name
        // For unique file name appending current timeStamp with file name
        java.util.Date date = new java.util.Date();
        timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(date.getTime());

        File mediaFile;
        if (type == MEDIA_TYPE_VIDEO) {
            // For unique video file name appending current timeStamp with file name
            mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4");

        } else return null;

        return mediaFile;
    }


    @SuppressLint("SetTextI18n")
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                noticeText.setText("Video file has been saved!");
            } else if (resultCode == RESULT_CANCELED) {
                noticeText.setText("User cancelled the video capture.");
            } else {
                noticeText.setText("Video capture failed.");
            }
        }
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);

        savedInstanceState.putString("text", noticeText.getText().toString());
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        if (savedInstanceState != null) {
            noticeText.setText(savedInstanceState.getString("text"));
        }
    }
}
    And it's layout:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin">

    <Button
        android:id="@+id/btn_record"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Capture Video" />

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

</LinearLayout>

Output - running application

    After launching, you will see this output:
    Click at the button, the Video view will start:
    Click the record button, you 'll start video recording process. Moreover, if your device has 2 cameras (font and back), the "switch camera button" will appear like above.
    In recording process, Pause and Stop button will be displayed:
    Click stop button to finish recording, you will be redirect back to main screen:
    Your file stored at /Pictures/DevExVideo folder:

Conclusions

    As you can see, recording video in Android is very easy. From this basic step, now you can custom it to apply for your real application. Hope this is helpful with the beginners. Finally, you can view my project on @Github.

Android Basic Training Course: Date/Time Picker

Android Basic Training Course: Date/Time Picker

     In the Google official document, the Date Picker widget was defined:
Android Date Picker allows you to select the date consisting of day, month and year in your custom user interface. For this functionality android provides DatePicker and DatePickerDialog components.
    Through this post, as a part of Android Basic Course series, we are going to demonstrate the use of Date/Time Picker through DialogFragment and DatePickerDialog.

Project description

    I am going to use DialogFragment (available from API 11) to host the Date Picker. DialogFragment manages the life cycle and allows different layout configurations for the DatePicker. To show a Date picker dialog, we need to define a fragment class (named DatePickerFragment) which extends the DialogFragment class and returns a DatePickerDialog from its onCreateDialog() method. And similarity,  TimePickerFragment will be used to choose time from DatePickerDialog.

Creating the Date/Time Picker DialogFragment

    As the description above, we must create a subclass of DialogFragment, use a Calendar instance in onCreateDialog():
DatePickerFragment.java
package info.devexchanges.datetimepicker;

import android.app.DatePickerDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.widget.DatePicker;
import android.widget.TextView;

import java.util.Calendar;

public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {

    @NonNull
    @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
        TextView tv1 = (TextView) getActivity().findViewById(R.id.textview1);
        tv1.setText("Year: " + view.getYear() + " Month: " + view.getMonth() + " Day: " + view.getDayOfMonth());
    }
}
Change Calendar properties to Calendar.HOUR_OF_DAY, Calendar.MINUTE, we'll have the time picker fragment:
TimePickerFragment.java
package info.devexchanges.datetimepicker;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.text.format.DateFormat;
import android.widget.TextView;
import android.widget.TimePicker;

import java.util.Calendar;

public class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener {

    @NonNull
    @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()));
    }

    @SuppressLint("SetTextI18n")
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user
        TextView tv1 = (TextView) getActivity().findViewById(R.id.textview1);
        if (Build.VERSION.SDK_INT >= 23)
            tv1.setText("Hour: " + view.getHour() + " Minute: " + view.getMinute());
        else
            tv1.setText("Hour: " + view.getCurrentHour() + " Minute: " + view.getCurrentMinute());

    }
}

Creating the main activity

    As you can see at the fragments code, onDateSet() called when user complete choose the date/time, the data (year/month/day) will be shown to a TextView in an activity. So, make a main activity layout simple like this:
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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

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

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:onClick="showDatePickerDialog"
        android:text="@string/pick_date" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/btn1"
        android:onClick="showTimePickerDialog"
        android:text="Pick Time" />

</RelativeLayout>
    And it's programmatically code:
MainActivity.java
package info.devexchanges.datetimepicker;

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {

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

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

    public void showTimePickerDialog(View view) {
        DialogFragment newFragment = new TimePickerFragment();
        newFragment.show(getSupportFragmentManager(), "timePicker");
    }
}
    Running application, in Lollipop devices and later, we'll have this output (in Material design style):
And in KitKat device:
After pick date/time, it will show in the TextView:

Conclusions

    Now, you've learned how to dealing with date/time input, this is not quite hard topic in Android development. More details, let go to the official guide page of Android developer to deep understanding it. Thanks for reading and please subscribe my blog to get newest tutorials!

Android Basic Training Course: Working with Camera and Image Gallery

Android Basic Training Course: Working with Camera and Image Gallery

    Sometimes, in your Android application, you’ll want the user to choose an image from the gallery or device camera. With camera, we can take a photo directly at the moment and call back data (taken photo in Bitmap format) to our application, and with Gallery apps which available in our device (Gallery, Photos, ES File Explorer, etc), we can get any image storing on device memory in Uri format to our application. Both of 2 ways use Intent and sub-Activity.

Take photo from Camera

    Firstly, you must request camera using permission with your app first in AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA"/>
    In code, creating an Intent with action String is MediaStore.ACTION_IMAGE_CAPTURE, start a sub Activity like this:
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_PICK); //CAMERA_PICK is a final int variable!
    Handling data (in Bitmap format after taking a photo by overriding onActivityResult():
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == CAMERA_PICK && resultCode == RESULT_OK) {
            Bitmap photo = (Bitmap) data.getExtras().get("data");
            //set photo bitmap to ImageView
            imageView.setImageBitmap(photo);
        } 
    }
NOTE: RESULT_OK is code when we taking a photo from camera successfully.

Get Photo from Gallery

    In order to access to device memory to get file (in this case is photos), you must request reading data permission:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Absolutely similar with using Camera, you can open a dialog to choosing a Gallery app by Intent with ACTION_GET_CONTENT:
Intent intent = new Intent();
intent.setType("image/*"); //set type for files (image type)
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_FROM_GALLERY); //PICK_FROM_GALLERY is final int variable
And get data back in onActivityResult():
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == PICK_FROM_GALLERY && resultCode == RESULT_OK) {
            Uri selectedImage = data.getData();
            imageView.setImageURI(selectedImage);
        }
    }

Sample project full code

    Now, I will provide a simple project which allows users perform these 2 actions and get data to ImageView. The main Activity layout:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="info.devexchanges.pickimages.MainActivity">

    <ImageView
        android:id="@+id/image"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:scaleType="centerCrop"
        android:layout_centerHorizontal="true" />

    <Button
        android:id="@+id/btn_camera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:drawableLeft="@android:drawable/ic_menu_camera"
        android:text="Pick from Camera" />

    <Button
        android:id="@+id/btn_gallery"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_camera"
        android:drawableLeft="@android:drawable/ic_menu_gallery"
        android:text="Pick from Gallery" />
</RelativeLayout>
    And it's programmatically code:
MainActivity.java
package info.devexchanges.pickimages;

import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private View btnCamera;
    private View btnGallery;
    private ImageView imageView;
    private static final int CAMERA_PICK = 1;
    private static final int PICK_FROM_GALLERY = 2;

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

        btnCamera = findViewById(R.id.btn_camera);
        btnGallery = findViewById(R.id.btn_gallery);
        imageView = (ImageView) findViewById(R.id.image);

        btnCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(cameraIntent, CAMERA_PICK);
            }
        });

        btnGallery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("image/*"); //set type for files (image type)
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_FROM_GALLERY);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == CAMERA_PICK && resultCode == RESULT_OK) {
            Bitmap photo = (Bitmap) data.getExtras().get("data");
            //set photo bitmap to ImageView
            imageView.setImageBitmap(photo);
        } else if (requestCode == PICK_FROM_GALLERY && resultCode == RESULT_OK) {
            Uri selectedImage = data.getData();
            imageView.setImageURI(selectedImage);
        }
    }
}
    Running application, we have this output screen:
 
    After taking a photo from Camera successful, photo will be set to ImageView:

    When clicking at "Pick From Gallery" button, the "Gallery apps chooser" will be displayed:
    And when user choose a app and a photo then, it's also set to ImageView:

Final thoughts

    If you would like Google Play prevents the app to be installed on devices those doesn’t have camera, please adding <uses-feature android:name="android.hardware.camera" /> in your AndroidManifest.xml.
    Another common problem is working with Camera and Gallery in Fragment, your Fragment sometimes stop before onActivityResult() called. This bug happen in some devices have strict memory management (e.g: Samsung Galaxy Tab 4, Samsung Galaxy S3,...), and in order to resolved it, please uncheck "Do not keep Activities" option in Developer Options entry:


Android Basic Training Course: Combining Navigation Drawer and ViewPager (with Tab) in one screen

    In Android development, Sliding menu (also known as Navigation Drawer) and tabs layout is two popular interfaces, using in many applications. We often think of combining two types of layouts within a single screen, with DrawerLayout, NavigationView, ViewPager and TabLayout, we can combine theme in one Activity with Material design style easily.
    DEMO VIDEO:

    Firstly, adding design support library dependency to use these Material design widgets:
app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "info.devexchanges.navvp"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile 'com.android.support:design:23.4.0' //design support library
    compile 'com.android.support:appcompat-v7:23.4.0' //appcompat library (default)
}

Designing Activity layout

    Our layout will be like this:
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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        android:theme="@style/ThemeOverlay.AppCompat.Dark"
        app:titleTextColor="@android:color/white" />

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <android.support.design.widget.TabLayout
                android:id="@+id/tab_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/toolbar"
                android:background="#125688"
                android:minHeight="?attr/actionBarSize"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

            <android.support.v4.view.ViewPager
                android:id="@+id/view_pager"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>


        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"
            app:menu="@menu/drawer_menu" />

    </android.support.v4.widget.DrawerLayout>
</LinearLayout>
    As you can see, we must put a ViewPager and a TabLayout in the first child of DrawerLayout, it's will make a swipeable view with tabs later, and it's child is a NavigationView, it now is the official widget to make a Navigation Drawer!

Programmatically code

    In our main Activity, locating all views, setting adapter for ViewPager (containing some Fragments), attaching TabLayout with ViewPager. It's must implement OnNavigationItemSelectedListener interface to handling NavigationView item clicked event. Soure code is simple like this:
MainActivity.java
package info.devexchanges.navvp;

import android.content.Intent;
import android.support.design.widget.NavigationView;
import android.support.design.widget.TabLayout;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{

    private ViewPager viewPager;
    private DrawerLayout drawer;
    private TabLayout tabLayout;
    private String[] pageTitle = {"Fragment 1", "Fragment 2", "Fragment 3"};

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

        viewPager = (ViewPager)findViewById(R.id.view_pager);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        drawer = (DrawerLayout) findViewById(R.id.drawerLayout);

        setSupportActionBar(toolbar);

        //create default navigation drawer toggle
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
                R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        //setting Tab layout (number of Tabs = number of ViewPager pages)
        tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        for (int i = 0; i < 3; i++) {
            tabLayout.addTab(tabLayout.newTab().setText(pageTitle[i]));
        }

        //set gravity for tab bar
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        //handling navigation view item event
        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        assert navigationView != null;
        navigationView.setNavigationItemSelectedListener(this);

        //set viewpager adapter
        ViewPagerAdapter pagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(pagerAdapter);

        //change Tab selection when swipe ViewPager
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));

        //change ViewPager page when tab selected
        tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }

    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.fr1) {
            viewPager.setCurrentItem(0);
        } else if (id == R.id.fr2) {
            viewPager.setCurrentItem(1);
        } else if (id == R.id.fr3) {
            viewPager.setCurrentItem(2);
        } else if (id == R.id.go) {
            Intent intent = new Intent(this, DesActivity.class);
            intent.putExtra("string", "Go to other Activity by NavigationView item cliked!");
            startActivity(intent);
        } else if (id == R.id.close) {
            finish();
        }

        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    @Override
    public void onBackPressed() {
        assert drawer != null;
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }
}
    Customizing ViewPager adapter based on FragmentPagerAdapter or FragmentStatePagerAdapter:
ViewPagerAdapter.java
package info.devexchanges.navvp;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class ViewPagerAdapter extends FragmentPagerAdapter {

    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        if (position ==0) {
            return new Fragment1();
        } else if (position == 1) {
            return new Fragment2();
        } else return new Fragment3();
    }

    @Override
    public int getCount() {
        return 3;
    }
}
    There are source code of 3 Fragments in this project (the ViewPager's children):
Fragment1.java
package info.devexchanges.navvp;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

public class Fragment1 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_content, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Button button = (Button) view.findViewById(R.id.button);
        button.setVisibility(View.GONE);
        TextView textView = (TextView)view.findViewById(R.id.text_view);
        textView.setText("A TextView in Fragment");
    }
}
Fragment2.java
package info.devexchanges.navvp;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class Fragment2 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_listview, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        ListView listView = (ListView) view.findViewById(R.id.list);
        String[] dummyStrings = getResources().getStringArray(R.array.my_items);

        ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, dummyStrings);
        listView.setAdapter(adapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(getActivity(), "You clicked at position: " + (position + 1), Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(getActivity(), DesActivity.class);
                intent.putExtra("string", "go to another Activity from ListView position: " + (position + 1));
                startActivity(intent);
            }
        });
    }
}
Fragment3.java
package info.devexchanges.navvp;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class Fragment3 extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_content, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), DesActivity.class);
                intent.putExtra("string", "Go to other Activity from a ViewPager Fragment");
                startActivity(intent);
            }
        });
    }
}
Important Note: In this project, I use Toolbar as Action Bar, so the project theme is a "No Action Bar" theme:
styles.xml
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>
    And the last is a destination Activity, getting here by clicking at the ListView item (in Fragment2), clicking a NavigationView item or clicking at the Button in Fragment3:
DesActivity.java
package info.devexchanges.navvp;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.widget.TextView;

public class DesActivity extends AppCompatActivity {

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

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        TextView textView = (TextView)findViewById(R.id.text_view);

        setSupportActionBar(toolbar);
        if (getIntent() != null) {
            textView.setText(getIntent().getStringExtra("string"));
        }

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }

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

    @Override
    public void onBackPressed() {
        finish();
    }
}

Running Application

Output will be like this:

When clicking at the a ListView item:

Go to destination activity from NavigationView's item clicked:

Go to destination activity from Fragment's element clicked:

Conclusions

    Through this post, I hope you can be learned about combining 2 powerful and popular widgets in developing Android application. Further, you can read this post  to understanding the solution which making tabs layout with Material design style and this post to learned about creating Navigation Drawer without NavigationView (more custom options). Thanks for reading and please subscribe my blog to get newest tutorials!