Android (Home screen) widget - Part 3: Configurable widget

Android (Home screen) widget - Part 3: Configurable widget

    In Part 2, I had presented one more example of widget: Broadcast Widget (which can update it's interface when clicked). Today, I would like to talk about a widget type that can be configurable at creation, this mean when you drag it to Home screen to use, a "configuration Activity" will be launched and you will perform a setting for your widget.

   In this sample project, we will allow users to choose a link and whenever it’s clicked we open this link on the browser.

Create layout for configuration Activity

    The most important work of creating this type of widget is developing the configuration Activity. Firstly, define a simple layout includes a Spinner to allow user selected one value from a set and a one value from a Button to confirm this work:
activity_config.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">

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_go"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:text="@string/add" />
</LinearLayout>

Activity programmatically code

    Now, looking at this source code of ConfigActivity:
ConfigActivity.java
package info.devexchanges.configurablewidget;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.RemoteViews;
import android.widget.Spinner;

import java.util.ArrayList;

public class ConfigActivity extends AppCompatActivity {

    private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    private AppWidgetManager widgetManager;
    private RemoteViews remoteViews;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setResult(RESULT_CANCELED);

        setContentView(R.layout.activity_config);

        final Spinner spinner = (Spinner)findViewById(R.id.spinner);
        View btnCreate = findViewById(R.id.btn_go);

        //create data
        ArrayList<String> spnOptions = new ArrayList<>();
        spnOptions.add("Go to my site");
        spnOptions.add("Go to Google page");

        //set adapter for the spinner
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, spnOptions);
        spinner.setAdapter(adapter);

        //initializing RemoteViews and AppWidgetManager
        widgetManager = AppWidgetManager.getInstance(this);
        remoteViews = new RemoteViews(this.getPackageName(), R.layout.widget_configurable);

        // Find the widget id from the intent
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        }
        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish();
            return;
        }
        btnCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String selectedUrl;
                if (spinner.getSelectedItemPosition() == 0) {
                    // Go to my website with this selection (position = 1)
                    selectedUrl = "http://www.devexchanges.info";
                } else {
                    selectedUrl = "https://www.google.com";
                }
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(selectedUrl));
                PendingIntent pending = PendingIntent.getActivity(ConfigActivity.this, 0, intent, 0);
                remoteViews.setOnClickPendingIntent(R.id.text_view, pending);
                if (spinner.getSelectedItemPosition() == 0) {
                    remoteViews.setTextViewText(R.id.text_view, "Click to visit my site");
                } else {
                    remoteViews.setTextViewText(R.id.text_view, "Click to visit Google");
                }
                widgetManager.updateAppWidget(mAppWidgetId, remoteViews);
                Intent resultValue = new Intent();

                // Set the results as expected from a 'configure activity'.
                resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
                setResult(RESULT_OK, resultValue);
                finish();
            }
        });
    }
}
    In onCreate() method, the first thing we do is setting setResult(RESULT_CANCELED). Why? Android triggers the configuration Activity that belongs to your widget and awaits result data. If the user did not configure as we expected, let’s say she pressed back button without entering a data, we don’t need to create a widget.
    At the Spinner, we set data to make 2 options for user to choose (go to DevExchanges home page or Google), after click the Button, we update TextView content on the widget and set the Set the RemoteViews to based on appWidgetIds.

Modifying the widget xml

    The last thing we do is modify the XML of the widget. With that modification, Android OS will know this widget has a configuration Activity. So before creating the widget, it will trigger the Activity:
res\xml\configurable_widget_info.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="info.devexchanges.configurablewidget.ConfigActivity"
    android:initialLayout="@layout/widget_configurable"
    android:minHeight="40dp"
    android:minWidth="40dp"
    android:previewImage="@mipmap/ic_launcher"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000" />
    As you notice we didn’t talk about widget class yet. We do not need to add any code for widget class because all actions done by ConfigActivity. But we have to create it anyway:
ConfigurableWidget.java
package info.devexchanges.configurablewidget;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;

public class ConfigurableWidget extends AppWidgetProvider {
    
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

    }
}
    And this is the layout file for our widget:
res\layout\widget_configurable.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:padding="8dp"
        android:background="@color/colorPrimary"
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center"
        android:textColor="@android:color/white"
        android:textStyle="bold" />

</RelativeLayout>

Running the application

    Before launching the app, make sure you add ConfigurableWidget as a receiver to your AndroidManifest.xml like other previous examples:
<application
        android:allowBackup="true"
        ....>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".ConfigurableWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/configurable_widget_info" />
        </receiver>

        <activity android:name=".ConfigActivity">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>
    </application>

    After app installed, move to "WIDGETS" tab, you will see it:
    And when you drag it to Home page to use, the configuration activity will be launched:
    When click on it:

     If you select other options at the configuration activity,  the widget text will be different:

Conclusions

    I've just provided one more example about Android widget, hope you can understand the way to configure widget before using it. Up to next part, I will talk about updating widget via a Service - the most popular and important feature of this topic! Coming soon!

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.

Meaningful motion for Activities transition in Android Lollipop

    As we can see at Material Design specs:
Motion in the world of material design is used to describe spatial relationships, functionality, and intention with beauty and fluidity. Motion design can effectively guide the user’s attention in ways that both inform and delight. Use motion to smoothly transport users between navigational contexts, explain changes in the arrangement of elements on a screen, and reinforce element hierarchy.
    From API 21, Material Design has bring us the new way to switch Activity (activity transition) with animations and of course, the element located on these screen are also affected by this transition process.
    We must apply those animations carefully to avoid the app become a true Pixar animation movie. In this post, I will present some customizing of Material meaningful motion, make our application look smoothly.
    DEMO VIDEO:

Prerequisites

    In order to custom activity transition and other related animations, make sure that your min-sdk of your project is 21 or higher. I also add some necessary dependencies which use for my sample project later:
app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"

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

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:24.2.0'
    compile 'com.android.support:design:24.2.0'
    compile 'com.android.support:gridlayout-v7:24.2.0'
    compile 'com.android.support:cardview-v7:24.2.0'
}

Custom Activity Transitions in xml

    Before Android Lollipop (API Level 21) we could only customize the activity transition animation over entire activity. All views were animated together. But now we can specify how each view is animated during that transition.
    Suppose I have 3 activities: the first called MainActivity which display 3 pictures and after click at any one, app will redirect user to second activity to show selected picture descriptions (called DetailsActivity). This activity contains a FloatingActionButton, when click on it, a translucent activity (SharingActivity) appears which give some options to shared the content. It has a yellow shape which is a drawable defined as image source of a ImageView. Now, we'll custom some animations based on xml resources.
    Create a folder named transaction in res directory, all animations xml files will put here.
    We can specify custom animations for enter and exit transitions and for transitions of shared elements between activities.
  • An enter transition determines how views move into the initial scene of the started activity. 
  • An exit transition determines how views move out of the scene when starting a new activity. 
  • A shared elements transition determines how views are shared between two activities transition.
These are all xml files that defining animation for transacting from MainActivity to DetailsActivity: and showing translucent SharingActivity:
main_reenter.xml
<?xml version="1.0" encoding="utf-8"?>
<slide xmlns:android="http://schemas.android.com/apk/res/android"
    android:slideEdge="top">
    <targets>
        <target android:excludeId="@android:id/statusBarBackground" />
        <target android:excludeId="@android:id/navigationBarBackground" />
    </targets>
</slide>
main_exit.xml
<?xml version="1.0" encoding="utf-8"?>
<explode xmlns:android="http://schemas.android.com/apk/res/android">
    <targets>
        <target android:excludeId="@android:id/statusBarBackground" />
        <target android:excludeId="@android:id/navigationBarBackground" />
    </targets>
</explode>
detail_enter.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
               android:transitionOrdering="together">

    <slide
        android:slideEdge="bottom">
        <targets>
            <target android:targetId="@id/cardview"/>
        </targets>
    </slide>
    <fade>
        <targets>
            <target android:excludeId="@android:id/statusBarBackground"/>
            <target android:excludeId="@android:id/navigationBarBackground"/>
            <target android:excludeId="@id/cardview"/>
        </targets>
    </fade>

</transitionSet>
sharing_shared_element_enter.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
               android:interpolator="@android:interpolator/accelerate_decelerate">
    <changeBounds/>
    <arcMotion
        android:maximumAngle="90"
        android:minimumHorizontalAngle="90"
        android:minimumVerticalAngle="0"/>
</transitionSet>
sharing_item_chosen.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds/>
    <fade>
        <targets>
            <target android:excludeId="@id/content_root"/>
        </targets>
    </fade>
    <changeImageTransform android:startDelay="@android:integer/config_mediumAnimTime"/>
</transitionSet>
    Activity transition definitions can be declared into theme style and our res/values/styles.xml contains all this:
styles.xml
<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.Main">
        <item name="android:windowExitTransition">@transition/main_exit</item>
        <item name="android:windowReenterTransition">@transition/main_reenter</item>
    </style>

    <style name="AppTheme.Detail">
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowAllowEnterTransitionOverlap">false</item>
        <item name="android:windowEnterTransition">@transition/detail_enter</item>
    </style>

    <style name="AppTheme.Sharing">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@color/black</item>
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowSharedElementEnterTransition">@transition/sharing_shared_element_enter</item>
    </style>

    <style name="ShareItemView">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
        <item name="android:textAppearance">@style/TextAppearance.AppCompat.Medium.Inverse</item>
    </style>

</resources>
    And never forget to use the correct theme for each activity in AndroidManifest.xml:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.devexchanges.uimotion">

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

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".DetailsActivity"
            android:theme="@style/AppTheme.Detail"/>
        <activity android:name=".SharingActivity"/>
    </application>

</manifest>

Transition from Main screen to Detail screen

    When user clicks on some image item, we must start the DetailsActivity with some information that indicates we’re starting a Customized Activity Transition. In MainActivity we have:
    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.rose) {
            openDetailActivity(R.drawable.rose, "Rose", view);
        } else if (view.getId() == R.id.sunflower) {
            openDetailActivity(R.drawable.sunflower, "Sunflower", view);
        } else {
            openDetailActivity(R.drawable.tulip, "Tulip", view);
        }
    }

    private void openDetailActivity(int drawable, String title, View view) {
        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, view, getString(R.string.picture_transition_name));
        Intent intent = new Intent(this, DetailsActivity.class);
        intent.putExtra(DetailsActivity.EXTRA_DRAWABLE, drawable);
        intent.putExtra(DetailsActivity.EXTRA_TITLE, title);

        startActivity(intent, options.toBundle());
    }
    The most important method is ActivityOptions.makeSceneTransitionAnimation(). It create an object containing information about our scene transition animation.
    As you see, I pass drawable id and string title  from MainActivity to setup  CollapsingToolbarLayout and ImageView in DetailsActivity. To finish the our motion from main to details screen we just scale up the share button when the transition is ended:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_details);
        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));

        int drawable = getIntent().getExtras().getInt(EXTRA_DRAWABLE);
        CharSequence title = getIntent().getExtras().getCharSequence(EXTRA_TITLE);

        CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
        collapsingToolbarLayout.setTitle(title);

        ImageView pictureView = (ImageView) findViewById(R.id.picture);
        pictureView.setImageResource(drawable);
        pictureView.setContentDescription(title);

        btnShare = findViewById(R.id.btn_share);
        textView = (TextView) findViewById(R.id.text);

        if (drawable == R.drawable.rose) {
            textView.setText(getString(R.string.rose));
        } else if (drawable == R.drawable.tulip) {
            textView.setText(getString(R.string.tulip));
        } else textView.setText(getString(R.string.sunflower));

        if (savedInstanceState == null) {
            btnShare.setScaleX(0);
            btnShare.setScaleY(0);
            getWindow().getEnterTransition().addListener(new TransitionAdapter() {
                @Override
                public void onTransitionEnd(Transition transition) {
                    getWindow().getEnterTransition().removeListener(this);
                    btnShare.animate().scaleX(1).scaleY(1);
                }
            });
        }
    }
    But if we are scaling up the share button when the Activity is opened then we have to scale down when the activity is finished:
    @Override
    public void onBackPressed() {
        btnShare.animate().scaleX(0).scaleY(0).setListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                supportFinishAfterTransition();
            }
        });
    }
    And when running app, we have this output:

Transition from Detail screen to Sharing screen

    Launching SharingActivity after click on the FloatingActionButton:
btnShare.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(DetailsActivity.this,
                        btnShare, getString(R.string.share_transition_name));
                Intent intent = new Intent(DetailsActivity.this, SharingActivity.class);
                startActivity(intent, options.toBundle());
            }
        });
    In SharingActivity, we have to setup initial states before the animation begin:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        rootView = (ViewGroup) findViewById(R.id.content_root);
        backgroundView = (ImageView) findViewById(R.id.background);
        btnFacebook = findViewById(R.id.facebook);
        btnInstagram = findViewById(R.id.instagram);
        btnTwitter = findViewById(R.id.twitter);
        btnGoogle = findViewById(R.id.google_plus);

        if (savedInstanceState == null) {
            // Setup initial states
            backgroundView.setVisibility(View.INVISIBLE);
            btnGoogle.setAlpha(0);
            btnTwitter.setAlpha(0);
            btnFacebook.setAlpha(0);
            btnInstagram.setAlpha(0);
        }

        getWindow().getSharedElementEnterTransition().addListener(new TransitionAdapter() {
            @Override
            public void onTransitionEnd(Transition transition) {
                getWindow().getSharedElementEnterTransition().removeListener(this);
                revealTheBackground();
                showTheItems();
            }
        });

        ...
    }
    The main work in SharingActivity is handling share items (buttons) click. The pure Transition Framework was added since Android API 19. This framework animates the views at runtime by changing some of their property values over time. One of the features is the ability of running animations based on the changes between starting and ending view property values:
    @Override
    public void onClick(View view) {
        showToast(view.getId());
        // Load the transition
        Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.sharing_item_chosen);
        // Finish this Activity when the transition is ended
        transition.addListener(new TransitionAdapter() {
            @Override
            public void onTransitionEnd(Transition transition) {
                finish();
                // Override default transition to fade out
                overridePendingTransition(0, android.R.anim.fade_out);
            }
        });
        // Capture current values in the scene root and then post a request to run a transition on the next frame
        TransitionManager.beginDelayedTransition(rootView, transition);

        // 1. Item chosen
        RelativeLayout.LayoutParams layoutParams =
                new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
        view.setLayoutParams(layoutParams);

        // 2. Rest of items
        View[] itemViews = {btnFacebook, btnInstagram, btnTwitter, btnGoogle};
        for (View itemView : itemViews) {
            if (itemView != view) {
                itemView.setVisibility(View.INVISIBLE);
            }
        }

        // 3. Background
        double diagonal = Math.sqrt(rootView.getHeight() * rootView.getHeight() + rootView.getWidth() * rootView.getWidth());
        float radius = (float) (diagonal / 2f);
        int h = backgroundView.getDrawable().getIntrinsicHeight();
        float scale = radius / (h / 2f);
        Matrix matrix = new Matrix(backgroundView.getImageMatrix());
        matrix.postScale(scale, scale, backgroundView.getWidth() / 2f, backgroundView.getHeight() / 2f);
        backgroundView.setScaleType(ImageView.ScaleType.MATRIX);
        backgroundView.setImageMatrix(matrix);
    }
    Moreover, override onBackPressed() to start the hide animation of item and background:
private void hideTheBackground() {
        Animator hide = createRevealAnimator(false);
        hide.setStartDelay(defaultAnimDuration);
        hide.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                backgroundView.setVisibility(View.INVISIBLE);
                supportFinishAfterTransition();
            }
        });
        hide.start();
    }

    @Override
    public void onBackPressed() {
        hideTheItems();
        hideTheBackground();
    }

    private void hideTheItems() {
        View[] itemViews = {btnFacebook, btnInstagram, btnTwitter, btnGoogle};
        for (int i = 0; i < itemViews.length; i++) {
            View itemView = itemViews[i];
            long startDelay = (defaultAnimDuration / itemViews.length) * (itemViews.length - i);
            itemView.animate().alpha(0).setStartDelay(startDelay);
        }
    }
    And we'll have this result:

Final thoughts

    I have presented a simple project about applying motion in our application to avoid a bad User Experience. In this sample app, we saw how to build beautiful apps with meaningful and delightful motion. And below are some links where you can go deeper into Android Motion:

Android Tip: Detect user inactivity - auto calling logout after a period of time

Android Tip: Detect user inactivity - auto calling logout after a period of time

    When working with session, in some application which have high level security (like bank apps), your session will be expired after a period of time (5-15 minutes) if you inactivity. So, as a front-end developer, we should handle this problem to take the suitable interface for the user.
    In this tip, I will provide the way to call a method (logout) when user inactivity in 5 minutes.  Depending on the specific case, you can add more operations with your own requirements.

Detecting user inactivity

    When the Activity is not visible with user, onPause() and onStop() were called and when user reopen the Activity, onStart() and onResume() were invoked! We will rely on this life cycle to solve this problem. The solution is: if onPause() was called and after 5 minutes, onResume() is not being called, we will logout and redirect user to login screen.

java.util.Timer and the sample code

   Timer and TimerTask are 2 objects will be used to resolve problem. We initialize a Timer instance in onPause() and schedule a TimerTask (will be invoked after 5 minutes (300,000ms)) and in onResume(), we must cancel this Timer instance! Source code for our activity:
MainActivity.java
package vn.ecpay.autologout;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {

    private Timer timer;
    private Toolbar toolbar;

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

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

        timer = new Timer();
        Log.i("Main", "Invoking logout timer");
        LogOutTimerTask logoutTimeTask = new LogOutTimerTask();
        timer.schedule(logoutTimeTask, 300000); //auto logout in 5 minutes
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (timer != null) {
            timer.cancel();
            Log.i("Main", "cancel timer");
            timer = null;
        }
    }

    private class LogOutTimerTask extends TimerTask {

        @Override
        public void run() {
          
            //redirect user to login screen
            Intent i = new Intent(MainActivity.this, LoginActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(i);
            finish();
        }
    }
}
    And it's layout:
activity_mian.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="vn.ecpay.autologout.MainActivity">

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:gravity="center"
        android:padding="@dimen/activity_horizontal_margin"
        android:text="Hi, friend! You logged in, after 5 minutes inactivity, you will be logout!" />
</LinearLayout>
    That is the main screen, we'll reach here after login, so this is a simple example of a login activity:
LoginActivity.java
package vn.ecpay.autologout;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class LoginActivity extends AppCompatActivity {

    private TextInputLayout userName;
    private TextInputLayout password;
    private View btnLogin;

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

        userName = (TextInputLayout) findViewById(R.id.username_field);
        password = (TextInputLayout) findViewById(R.id.pass_field);
        btnLogin = findViewById(R.id.btn_login);

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (userName.getEditText().getText().toString().trim().equals("")) {
                    Toast.makeText(LoginActivity.this, "Please input your user name", Toast.LENGTH_SHORT).show();
                } else if (password.getEditText().getText().toString().trim().equals("")) {
                    Toast.makeText(LoginActivity.this, "Please input your password", Toast.LENGTH_SHORT).show();
                } else if (userName.getEditText().getText().toString().equals("devexchanges") &&
                        password.getEditText().getText().toString().equals("admin")) {
                    //Correct user name and password, go to main screen
                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                    startActivity(intent);
                    finish();
                } else {
                    Toast.makeText(LoginActivity.this, "Wrong input data", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}
    Login screen layout:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/activity_horizontal_margin">

    <android.support.design.widget.TextInputLayout
        android:id="@+id/username_field"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/pass_field">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="User name"
            android:inputType="text" />

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

    <android.support.design.widget.TextInputLayout
        android:id="@+id/pass_field"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Password"
            android:inputType="textPassword" />

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

    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Login"
        android:layout_marginTop="@dimen/activity_horizontal_margin"
        android:layout_below="@+id/pass_field" />

</RelativeLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="vn.ecpay.autologout">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" />
    </application>

</manifest>
Important Note: I used TextInputLayout in login activity - a widget from Design Support Library, so you must add it's dependency to your app/build.gradle:
compile 'com.android.support:design:23.4.0'
    Running application, we have this output:
Click login, we will go to main screen:
 And if you click home button (to go home screen - close app) or the screen light turn off, after 5 minutes, unless reactivating your app, you will be auto redirect to the login screen!

Final thoughts

    By searching on Internet, you will find out more solutions to deal with this problem. For example: override onUserInteraction() method (reset "countdown time" when this method called). Some links in Stackoverflow you can take a glance:
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!