Android - Launch another Application's activity and get it's result

Android - Launch another Application's activity and get it's result

    In Android development, sometimes you must run another application which installed in the device to performing a reference work. You have become accustomed to launch a system app like Camera or Contact to get it's result (photo, contact info) but in this post, I would like to present the way to invoke another "normal" app (which can be developed by another developer) by using intent-filter. Now, let's start!

Starting 2 new projects

Open Android Studio and start 2 new project:
  • The first project has an Activity named FirstActivity and it's layout file is activity_first.xml
  • The second project has an Activity named SecondsActivity and it's layout file is activity_seconds.xml
Put some XML code in 2 layouts file to build the interface:
activity_first.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:id="@+id/activity_main"
    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.firstapplication.FistActivity">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is the first Activity" />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/text"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:text="Go to another app activity" />
</RelativeLayout>
activity_seconds.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:id="@+id/activity_second"
    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.secondapplication.SecondsActivity">

    <EditText
        android:id="@+id/edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Put some texts" />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/edit_text"
        android:text="Back to the first Activity" />
</RelativeLayout>

Configurations in AndroidManifest.xml

    Of course, we'll launch SecondsActivity of application 2 from FirstActivity later. To perform this work, we need use intent-filter to define the specifies the type of Intent accepted based on the Intent's name, data, and category. Now, do this work:
  • Keep the default “generated” AndroidManifest.xml without changes in the project one.
  • Here is an important step, we need to define the Intent action name that will be used to call the activity info.devexchanges.secondsapp.SECOND_ACTIVITY in the project 2 AndroidManifest.xml. Put this code in <activity> scope of SecondsActivity:
<intent-filter>
       <action android:name="info.devexchanges.secondsapp.SECOND_ACTIVITY" />
       <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Launching SecondsActivity from FirstActivity

    With the configuration above, we now can launch SecondActivity easily from FirstActivity with this code:
         try {
                    Intent intent = new Intent("info.devexchanges.secondsapp.SECOND_ACTIVITY");
                    startActivity(intent);
                } catch (ActivityNotFoundException ex) {
                    ex.printStackTrace();
                    Log.e("Main", "Second application is not installed!");
                }
NOTE: You can put data to SecondsActivity by use putExtras() method of Intent (before call startActivity()).
    We'll have this output:

Get result from SecondActivity

    In order to get the result from SecondsActivity, we must use startActivityForResult() method instead of startActivity() in the FirstActivity:
         try {
                    Intent intent = new Intent("info.devexchanges.secondsapp.SECOND_ACTIVITY");
                    startActivityForResult(intent, REQUEST_CODE);
                } catch (ActivityNotFoundException ex) {
                    ex.printStackTrace();
                    Log.e("Main", "Second application is not installed!");
                }
Moreover, you must override onActivityResult() to get Intent data and resultCode which returned from SecondsActivity:
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                String intentData = data.getStringExtra("EditText_Value");
                textView.setText(intentData);
            } else {
                textView.setText("User press back at Second Activity");
            }
        }
    }
In SecondsActivity, before it's finish, just call setResult() to send back resultCode to the parent Activity:
SecondsActivity.java
package info.devexchanges.secondapplication;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;

public class SecondsActivity extends AppCompatActivity {

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

        View btnBack = findViewById(R.id.button);
        final EditText editText = (EditText) findViewById(R.id.edit_text);

        btnBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.putExtra("EditText_Value", editText.getText().toString().trim());
                setResult(RESULT_OK, intent);
                finish();
            }
        });
    }
}
And this is output:

Conclusions

    With defining an intent-filter option, we can launch another app activity easily and get it's result. Hope this post is helpful in developing "reference applications" in your work. Moreover, please go to the Google official document to read more about Intent + Intent Filter, one of basic concept of Android development.

Developing a "native" Barcode Scanner in Android

Developing a "native" Barcode Scanner in Android

    As you can read at my previous, I had presented a simple Barcode reader application. It's principle is user must download and install Zxing application from Google Play first, my app will call Zxing scanner screen as a sub-Activity and get the result (Barcode or QR code information) from it after scanning process finished!

    The fact that we always would like to developing an application which be able to "embedding" this scanner in it, not warning that our device has not installed Barcode Reader application. Fortunately, there is a library developed by Dushyanth Maguluru which based on ZXing and ZBar, which be able to Barcode Scanner views, help us to develop a simple application which can scan Barcode/QR code itself.

Adding Barcode reader library to Android Studio Project

    By reading it's source code on Github, we can find out that the author has combined ZXing and ZBar, each library to a dependency module in this project! In this tutorial, I will use Zxing, ZBar guide is absolutely similar!
    In order to use this library, the simplest way is adding it's dependency to your application level build.gradle:
dependencies {
    compile 'me.dm7.barcodescanner:zxing:1.9' //barcode reader dependency
    compile 'com.android.support:appcompat-v7:24.1.1'
}

Project main activity

    This main activity has 2 important works:
  • Start the scanning barcode activity as a sub-Activity by Intent.
  • Retrieve scanning results: barcode format and content by overriding onActivityResult().
    It's source code simple like this:
MainActivity.java
package info.devexchanges.barcodescanner;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private final static int REQUEST_SCANNER = 1;
    public final static String FORMAT = "format";
    public final static String CONTENT = "content";

    private TextView content;
    private TextView format;

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

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        View btnScan = findViewById(R.id.btn_scan);
        format = (TextView) findViewById(R.id.format);
        content = (TextView) findViewById(R.id.content);

        setSupportActionBar(toolbar);

        btnScan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, ScannerActivity.class);
                startActivityForResult(intent, REQUEST_SCANNER);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == Activity.RESULT_OK) {
            content.setText(data.getStringExtra(CONTENT));
            format.setText(data.getStringExtra(FORMAT));
        }
    }
}
    And it's layout:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="info.devexchanges.barcodescanner.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_gravity="top"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/btn_scan"
        android:text="Scan Barcode"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/format"
        android:gravity="center"
        android:padding="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/content"
        android:gravity="center"
        android:padding="@dimen/activity_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Scanning Barcode/QR code Activity

    As you can see at the MainActivity code above, when Button pressed, ScannerActivity will be launch as a sub-Activity.
    The Barcode scanner view is initialized by a ZXingScannerView object. In onCreate(), declaring it like this:
ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);

scannerView = new ZXingScannerView(this);
contentFrame.addView(scannerView);
    The most important work is retrieving scanning result, so your ScannerActivity must implements ResultHandler interface and override handleResult(Result result) method. Moreover, start scanning by invoke this code:
scannerView.startCamera();
    And if you want to stop this scanning, call closing camera method:
scannerView.stopCamera();
    This is full code for this activity:
ScannerActivity.java
package info.devexchanges.barcodescanner;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.ViewGroup;

import com.google.zxing.Result;

import me.dm7.barcodescanner.zxing.ZXingScannerView;

public class ScannerActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {

    private ZXingScannerView scannerView;

    @Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_scanner);
        ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);

        scannerView = new ZXingScannerView(this);
        contentFrame.addView(scannerView);
    }

    @Override
    public void onResume() {
        super.onResume();
        scannerView.setResultHandler(this);
        scannerView.startCamera();
    }

    @Override
    public void onPause() {
        super.onPause();
        scannerView.stopCamera();
    }

    @Override
    public void handleResult(Result rawResult) {
        //Call back data to main activity
        Intent intent = new Intent();
        intent.putExtra(MainActivity.FORMAT, rawResult.getBarcodeFormat().toString());
        intent.putExtra(MainActivity.CONTENT, rawResult.getText());

        setResult(Activity.RESULT_OK, intent);
        finish();
    }
}
    And it's layout:
activity_scanner.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</FrameLayout>
    Running this application, we'll have this screen:

    Clicking button, we'll start scanning:
Scanning Barcode
    After scanning, we have this result:
    An example after scanning a QR code:

Conclusions

    Now, the simple "native" scanning Barcode/QR code app has been done! In this tutorial, we've run through the process of facilitating barcode/QR code scanning within Android app using the a third-party library based on ZXing. In your own apps, you might want to carry out further processing on the retrieved scan results, such as loading URLs or looking the data up in a third party data source. Moreover, by reading this library ZBar guide, you also can find out the way to use this module to your project - absolutely analogous!

Android Basic Training Course: Use Telephony API

Android Basic Training Course: Use Telephony API

    Most Android devices are smartphones, but currently we are not yet talking about how how can develop an app uses the phone's features. In this post, we will look at ways how to send SMS messages and make a call. By this, you can learn about developing the basic features of Android phones.

Sending SMS

    In Android, there are 2 ways to send a SMS: by SmsManager and implic Intent.
    With SmsManager, the message will immediately sent and user can't customizing it any more. This process will perform with this code:
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage("[destination_phone_number]", null, "[sms_content]", null, null);
    However, through Intent, System will call the built-in SMS application in OS and through it, you can preview and edit the phone number or SMS content:
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.putExtra("address", "[destination_phone_number]");
        intent.putExtra("sms_body", "[sms_message]");
        intent.setData(Uri.parse("smsto:" + "[destination_phone_number]"));
        intent.setType("vnd.android-dir/mms-sms");
        startActivity(intent);
    Both ways nee SEND_SMS permission in AndroidManifest.xml:
<uses-permission android:name="android.permission.SEND_SMS" />

Now, I provide an example which sending SMS through Intent. Firstly, create a simple layout like this:
activity_sms.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    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=".SMSActivity">

    <EditText
        android:id="@+id/txt_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:hint="Enter Phone Number"
        android:inputType="phone"/>

    <EditText
        android:id="@+id/sms_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter SMS"/>

    <Button
        android:id="@+id/btnSendSMS"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="Send Sms" />

</LinearLayout>

    In programmatically code, we will call the default built-in SMS app to send data after click the Button:
SMSActivity.java
package info.devexchanges.telephony;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class SMSActivity extends AppCompatActivity {

    private Button btnSend;
    private EditText txtPhoneNumber;
    private EditText txtMessageContent;

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

        btnSend = (Button) findViewById(R.id.btnSendSMS);
        txtPhoneNumber = (EditText) findViewById(R.id.txt_phone);
        txtMessageContent = (EditText) findViewById(R.id.sms_content);

        btnSend.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                sendSMSMessage();
            }
        });
    }

    protected void sendSMSMessage() {
        String phoneNo = txtPhoneNumber.getText().toString().trim();
        String message = txtMessageContent.getText().toString().trim();

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.putExtra("address", phoneNo);
        intent.putExtra("sms_body", message);
        intent.setData(Uri.parse("smsto:" + phoneNo));
        intent.setType("vnd.android-dir/mms-sms");
        startActivity(intent);
    }
}
    After running this activity, we can see this result:
    Filling content on EditTexts, click "Send SMS", you will be redirected to SMS application. You can edit the message and phone number before sending it here:

Making a phone call

    Another important feature of a phone is making a call. We can call the "dial screen" to customizing the phone to call by using Intent.ACTION_DIAL or directly call by using Intent.ACTION_CALL.
    With the first case, it's seem so simple, with Intent.ACTION_DIAL, we can go to the "dial srceen" of Android OS. Suppose we have a simple layout contains only 1 Button and 1 EditText like this:
activity_call.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">

    <EditText
        android:id="@+id/txt_phone"
        android:hint="Input a phone number here"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="phone" />

    <Button
        android:id="@+id/btn_call"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Call" />

</LinearLayout>

    Layout preview:

    Put some programmatically code to handling button event (redirect user to "dial screen" with input phone number:
DialingActivity.java
package info.devexchanges.telephony;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class DialingActivity extends AppCompatActivity {

    private Button btnCall;
    private EditText txtPhone;

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

        txtPhone = (EditText) findViewById(R.id.txt_phone);
        btnCall = (Button)findViewById(R.id.btn_call);

        btnCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (txtPhone.getText().toString().trim().equals("")) {
                    Toast.makeText(DialingActivity.this, "Please input a phone number", Toast.LENGTH_SHORT).show();
                } else {
                    String uri = "tel:"+ txtPhone.getText().toString();
                    Intent dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse(uri));
                    startActivity(dialIntent);
                }
            }
        });
    }
}

    Running this activity, input a phone number to the EditText:
    Click "Call", we will see the soft phone keyboard, you still can customizing the phone number here:
     When make a call directly from programmatically code, although the Intent is sufficient for the phone call, in some cases we want to monitor the phone state. In such situations, we use PhoneStateListener and TelephonyManager classes. In this example we create our own PhoneStateListener, in order to handle some states of the phone call. More specifically, in the state CALL_STATE_IDLE, we want to return to our application’s main activity after the end of the call. For that reason we create a suitable Intent, as you can see in the code below:
CallingActivity.java
package info.devexchanges.telephony;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class CallingActivity extends AppCompatActivity {

    private Button btnCall;
    private EditText txtPhone;
    private final static String TAG = CallingActivity.class.getSimpleName();

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

        txtPhone = (EditText) findViewById(R.id.txt_phone);
        btnCall = (Button) findViewById(R.id.btn_call);

        // add PhoneStateListener for monitoring
        CustomPhoneListener phoneListener = new CustomPhoneListener();
        TelephonyManager telephonyManager =
        (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);

        // receive notifications of telephony state changes
        telephonyManager.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);


        btnCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (txtPhone.getText().toString().trim().equals("")) {
                    Toast.makeText(CallingActivity.this, "Please input a phone number", Toast.LENGTH_SHORT).show();
                } else {
                    try {
                        String uri = "tel:" + txtPhone.getText().toString();
                        Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse(uri));
                        if (ActivityCompat.checkSelfPermission(CallingActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                            return;
                        }
                        startActivity(callIntent);
                    } catch (Exception e) {
                        Toast.makeText(getApplicationContext(), "Your call has failed...", Toast.LENGTH_SHORT).show();
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    private class CustomPhoneListener extends PhoneStateListener {

        private boolean onCall = false;

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {

            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:
                    // phone ringing...
                    Log.i(TAG, "Destination phone is ringing, please wait...");
                    break;

                case TelephonyManager.CALL_STATE_OFFHOOK:
                    // one call exists that is dialing, active, or on hold
                    Log.i(TAG, "on Calling...");

                    //because user answers the incoming call
                    onCall = true;
                    break;

                case TelephonyManager.CALL_STATE_IDLE:
                    // in initialization of the class and at the end of phone call
                    // detect flag from CALL_STATE_OFFHOOK
                    if (onCall) {
                        Log.i(TAG, "Restarting app after a call...!");

                        // restart our application
                        Intent restart = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName());
                        restart.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(restart);

                        onCall = false;
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

    With this approach, we need CALL_PHONE and READ_PHONE_STATE permissions in AndroidManifest.xml:
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

Conclusions

    This post is talking about using the basic features of a phone: send text messages and make calls. You can read the next post to learn about sending an email with Intent. Further, you can learn by yourself the way to listen for incoming messages by BroadcastReceiver. Finally, click the button below to view demo project on @Github.




Android Basic Training Course: Intent and Intent Filters

Android Basic Training Course: Intent and Intent Filters

    Up to now, the focus of this post has been on activities opened directly by the user from the device’s launcher. This, of course, is the most obvious case for getting your activity up and visible to the user. In many cases it is the primary way the user will start using your application.
    However, the Android system is based upon lots of loosely-coupled components. What you might accomplish in a desktop GUI via dialog boxes, child windows, and the like are mostly supposed to be independent activities. While one activity will be “special”, in that it shows up in the launcher, the other activities all need to be reached . . . somehow.
    The “how” is via intents.

What is Intent

    The intent itself, an Intent object, is a passive data structure holding an abstract description of an operation to be performed. From official doc page, Intent is an abstract description of an operation to be performed. It can be used with startActivity() to launch an Activity, broadcastIntent() to send it to any interested BroadcastReceiver components, and startService(Intent) or bindService(Intent, ServiceConnection, int) to communicate with a background Service.

Parts of Intents

    The two importants parts of a Intent is "action" and "data". These are almost exactly analogous to HTTP verbs and URLs - the action is the verb, and the “data” is a Uri, such as content://contact/people/1 representing a contact in the contacts database in your device. Actions are constants, such as ACTION_VIEW (to bring up a viewer for the resource), ACTION_EDIT (to edit the resource), or ACTION_PICK (to choose an available item given a Uri representing a collection, such as content://contact/people).
    If you were to create an intent combining ACTION_VIEW with a content Uri of content://contact/people/1, and pass that intent to Android, system would know to find and open an activity capable of viewing that resource.
    There are other criteria you can place inside an intent (represented as an Intent object), besides the action and “data” Uri, such as:
  • A category. Your “main” activity will be in the LAUNCHER category, indicating it should show up on the launcher menu. Other activities will probably be in the DEFAULT or ALTERNATIVE categories.
  • A MIME type, indicating the type of resource you want to operate on, if you don’t know a collection Uri.
  • A component, which is to say, the class of the activity that is supposed to receive this intent. Using components this way obviates the need for the other properties of the intent. However, it does make the intent more fragile, as it assumes specific implementations.
  • “Extras”, which is a Bundle of other information you want to pass along to the receiver with the intent, that the receiver might want to take advantage of. What pieces of information a given receiver can use is up to the receiver and (hopefully) is well-documented.

Intent Routing

    Basically, there are three rules, all of which must be true for a given activity to be eligible for a given intent:
  1. The activity must support the specified action.
  2. The activity must support the stated MIME type (if supplied).
  3. The activity must support all of the categories named in the intent.
    The upshot is that you want to make your intents specific enough to find the right receiver(s), and no more specific than that. This will become clearer as we work through some examples later.

Starting an Intent

    All Android components that wish to be notified via intents must declare intent filters, so Android knows which intents should go to that component. To do this, you need to add intent-filter elements to your AndroidManifest.xml file. For example:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.devexchanges.example">

    <application>
        <activity
            android:name=".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>
    </application>
</manifest>
    Note the intent-filter element under the activity element:
  • This is the main activity for this application.
  • This is in the LAUNCHER category, meaning it gets an icon in the Android main menu.
    Because this activity is the main one for the application, Android knows this is the component it should launch when somebody chooses the application from the main menu.
    You are welcome to have more than one action or more than one category in your intent filters. That indicates that the associated component (e.g., activity) handles multiple different sorts of intents.
More than likely, you will also want to have your secondary (non-MAIN) activities specify the MIME type of data they work on. Then, if an intent is targeted for that MIME type - either directly, or indirectly by the Uri referencing something of that type - Android will know that the component handles such data. For example:
<activity android:name=".TourActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.item" />
            </intent-filter>
</activity>
    This activity will get launched by an intent requesting to view a Uri representing a vnd.android.cursor.item piece of content. That intent could come from another activity in the same application (e.g., the MAIN activity for this application) or from another activity in another Android application that happens to know a Uri that this activity handles.

Narrow Receivers

    In the examples shown previously, the intent filters were set up on activities. Sometimes, tying intents to activities is not exactly what we want:
  • Some system events might cause us to want to trigger something in a service rather than an activity.
  • Some events might need to launch different activities in different circumstances, where the criteria are not solely based on the intent itself, but some other state (e.g., if we get intent X and the database has a Y, then launch activity A; if the database does not have a Y, then launch activity B).
    For these cases, Android offers the intent receiver, defined as a class implementing the BroadcastReceiver interface. Intent receivers are disposable objects designed to receive intents - particularly broadcast intents - and take action, typically involving launching other intents to trigger logic in an activity, service, or other component.
The BroadcastReceiver interface has only one method: onReceive(). Intent receivers implement that method, where they do whatever it is they wish to do upon an incoming intent. To declare an intent receiver, add a receiver element to your AndroidManifest.xml file:     An intent receiver is only alive for as long as it takes to process onReceive() — as soon as that method returns, the receiver instance is subject to garbage collection and will not be reused. This means intent receivers are somewhat limited in what they can do, mostly to avoid anything that involves any sort of callback. For example, they cannot bind to a service, and they cannot open a dialog box.
    The exception is if the BroadcastReceiver is implemented on some longer-lived component, such as an activity or service - in that case, the intent receiver lives as long as its “host” does (e.g., until the activity is stop). However, in this case, you cannot declare the intent receiver via AndroidManifest.xml. Instead, you need to call registerReceiver() on your Activity’s onResume() callback to declare interest in an intent, then call unRegisterReceiver() from your Activity’s onPause() when you no longer need those intents.

Conclusions

    With this post, you've learned about intent and intent filter philosophy. Up to next post, I will present the way to launching activities and sub-activities by using Intent object. Hope this helpful for readers!


Android Basic Training Course: Launching Activities And sub-Activities

Android Basic Training Course: Launching Activities And sub-Activities

    In Android development, switch to other screen often is changing Activity (another case is changing  Fragment ). With Intent, you can easily to go to other Activity from current one, and they are peer-to-peer in back stack. Another problem is sub-Activity - when it stop running, it will response a result to parent-Activity. This tutorial will show you how to create a sub-Activity from a calling-Activity, and process the results produced by the sub-Activity, if you want to do so. Also, the various ways of launching a sub-Activity are covered, along with the Android Activity  history stack. A subclass of Activity is also provided that makes it trivial to launch sub-Activities and respond to results from them.

Launching another Activity

    You can start another activity within the same application by calling startActivity(), passing it an Intent that describes the activity “class name” you want to start. Of course, never forget to declaring your Activities in AndroidManifest. This process is done with this simple code:
                Intent intent = new Intent(MainActivity.this, OtherActivity.class);
                startActivity(intent);

Put data to destination Activity

    Only switch between Activities is very simple, the most important problem is sending data through them. With Intent, we can easily to put data from the source Activity to the destination one by invoke  putExtras():
                Intent intent = new Intent(MainActivity.this, OtherActivity.class);
                intent.putExtra("TEXT", "this is a String from MainActivity");
                startActivity(intent);
    As you see, the data will be pass in (key, value) type. Through this Intent official doc page, you can find out datatypes which Intent can transport.
    In the destination Activity, you can receive this data that available in Intent by calling  getIntent():
        Intent intent = this.getIntent();
        String receivedString = intent.getStringExtra("TEXT");
    Note: the key must be same with declaration in the source Activity and you must use the correct method of receiving data (in this example is  getStringExtra()).

Activities Back (history) stack

    Please note that Android maintains a history stack of all the Activities that have been spawned in an application’s Linux process. By this way, when you get to the destination Activity and press Back button, your app will redirect you back to the source Activity. If you don't like this feature, please invoke finish() after call  startActivity(intent), your source Activity will be destroyed and you can't go back after switching. It's useful when developing Login - Home screens.

Launching Sub-Activity

   Sometimes you need to run some Activities, do some operation there and return some value to parent Activity. For this situation, Android has possibility to run Activity for result - called run sub-Activity. The method to invoke this process:
startActivityForResult(intent, requestCode);
   To receiving data from the sub-Activity, your parent-Activity must overrides onActivityResult(int requestCode, int resultCode, Intent data). In the sub-Activity, before finish, sending data to the parent-Activity through call:
setResult(resultCode, intent);
finish();
    The special thing here is you can call system app Activity (Camera, Contacts,...) or an Activity  from other apps which available in your device. Now, I will provide an example to take photos from device Camera and set it to  ImageView.
    First, creating a layout file for Activity:       This screen after running:

    In programmatically code, create an instance of ACTION_IMAGE_CAPTURE Intent and invoke it when click a  Button :
        btnGetPicture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(takePicture, REQUEST_CAMERA_CODE);
            }
        });
    We will receive the  Bitmap  after take a photo with camera and set it to  ImageView :
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CAMERA_CODE && resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            Bitmap imageBitmap = (Bitmap) extras.get("data");
            imageView.setImageBitmap(imageBitmap);
        } else {
            Toast.makeText(this, "You have not take any picture", Toast.LENGTH_SHORT).show();
        }
    }
    Full code of this Activity:
package info.devexchanges.photopicker;

import android.content.Intent;
import android.graphics.Bitmap;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private Button btnGetPicture;
    private Button btnGo;
    private ImageView imageView;
    private final static int REQUEST_CAMERA_CODE = 111;

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

        imageView = (ImageView) findViewById(R.id.image);
        btnGetPicture = (Button) findViewById(R.id.btn_get);
        btnGo = (Button) findViewById(R.id.btn_go_with_data);

        btnGetPicture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(takePicture, REQUEST_CAMERA_CODE);
            }
        });

        btnGo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, OtherActivity.class);
                intent.putExtra("TEXT", "this is a String from MainActivity");
                startActivity(intent);
            }
        });
    }

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

        if (requestCode == REQUEST_CAMERA_CODE && resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            Bitmap imageBitmap = (Bitmap) extras.get("data");
            imageView.setImageBitmap(imageBitmap);
        } else {
            Toast.makeText(this, "You have not take any picture", Toast.LENGTH_SHORT).show();
        }
    }
}
    Output screen (after taking a photo):

Conclusions

    I have completed to present the way to starting Activity and sub-Activity through Intent. If you need more example about start sub-Activity on other app in your device, please see "How to use Scan Barcode" post. Moreover, you can find out yourself how to write your own sub-Activity after follows my guide above. Finally, you can get full code for this project on @Github by click button below.