Microsoft's standalone Android Emulator

Microsoft Visual Studio 2015 now has options for Android development: C++, Cordova, and C# with Xamarin. When choosing one of those Android development options, Visual Studio will also install the brand new Visual Studio Emulator for Android to use as a target for debugging your app. You can also download the emulator without needing to install Visual Studio.

Source: Introducing Visual Studio’s Emulator for Android




Visual Studio Emulator for Android:



see it in action:

Tuesday, July 28, 2015

Open more than one project in Android Studio

Open more than one project in Android Studio:
- With one project opened
- Select menu File -> Reopen Project -> select the project to open
- Select New Window to open project.




FREE eBook: AdMob No-Nonsense Guide to Growing Your Mobile App

This handbook will walk you through practical ways to increase your app’s user engagement to help you eventually transition to growth. You’ll learn how to:
  • Pick the right metric to represent user engagement
  • Look at data to audit your app and find areas to fix
  • Promote your app after you’ve reached a healthy level of user engagement
~ Download link

Sunday, July 26, 2015

FREE eBook - An Introduction to Network Programming with Java, 3rd Edition

An Introduction to Network Programming with Java, 3rd Edition

Description: Since the second edition of this text, the use of the Internet and networks generally has continued to expand at a phenomenal rate. This has led to both an increase in demand for network software and to improvements in the technology used to run such networks, with the latter naturally leading to changes in the former. During this time, the Java libraries have been updated to keep up with the new developments in network technology, so that the Java programming language continues to be one of the mainstays of network software development.

~ Link


StaggeredGridLayoutManager on RecyclerView


android.support.v7.widget.StaggeredGridLayoutManager is a LayoutManager that lays out children in a staggered grid formation. It supports horizontal & vertical layout as well as an ability to layout children in reverse.

Example to create StaggeredGridLayoutManager:
        staggeredGridLayoutManagerVertical =
                new StaggeredGridLayoutManager(
                        2, //The number of Columns in the grid
                        LinearLayoutManager.VERTICAL);
        staggeredGridLayoutManagerHorizontal =
                new StaggeredGridLayoutManager(
                        3, //The number of rows in the grid
                        LinearLayoutManager.HORIZONTAL);


Modify last example "Android example using RecyclerView with CardView":

layout/activity_main.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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <EditText
            android:id="@+id/namefield"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/addbutton"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Add"/>

        <RadioGroup
            android:id="@+id/optGroupLayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >
            <RadioButton
                android:id="@+id/optLinearLayoutVertical"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="LinearLayout Vertical"
                android:checked="true" />
            <RadioButton
                android:id="@+id/optLinearLayoutHorizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="LinearLayout Horizontal" />
            <RadioButton
                android:id="@+id/optGridLayoutVertical"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="GridLayout Vertical" />
            <RadioButton
                android:id="@+id/optGridLayoutHorizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="GridLayout Horizontal" />
            <RadioButton
                android:id="@+id/optStaggeredGridLayoutVertical"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="StaggeredGridLayout Vertical" />
            <RadioButton
                android:id="@+id/optStaggeredGridLayoutHorizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="StaggeredGridLayout Horizontal" />
        </RadioGroup>
    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/myrecyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>


com.example.androidrecyclerview.MainActivity.java
package com.example.androidrecyclerview;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements RecyclerViewAdapter.OnItemClickListener{

    private RecyclerView myRecyclerView;
    private RecyclerViewAdapter myRecyclerViewAdapter;

    EditText nameField;
    Button btnAdd;

    RadioGroup optGroupLayout;
    RadioButton optLinearLayoutHorizontal;
    RadioButton optLinearLayoutVertical;
    RadioButton optGridLayoutHorizontal;
    RadioButton optGridLayoutVertical;
    RadioButton optStaggeredGridLayoutHorizontal;
    RadioButton optStaggeredGridLayoutVertical;

    private LinearLayoutManager linearLayoutManagerVertical;
    private LinearLayoutManager linearLayoutManagerHorizontal;
    private GridLayoutManager gridLayoutManagerVertical;
    private GridLayoutManager gridLayoutManagerHorizontal;
    private StaggeredGridLayoutManager staggeredGridLayoutManagerVertical;
    private StaggeredGridLayoutManager staggeredGridLayoutManagerHorizontal;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myRecyclerView = (RecyclerView)findViewById(R.id.myrecyclerview);

        linearLayoutManagerVertical =
                new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        linearLayoutManagerHorizontal =
                new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        gridLayoutManagerVertical =
                new GridLayoutManager(this,
                        2, //The number of Columns in the grid
                        LinearLayoutManager.VERTICAL,
                        false);
        gridLayoutManagerHorizontal =
                new GridLayoutManager(this,
                        3, //The number of rows in the grid
                        LinearLayoutManager.HORIZONTAL,
                        false);

        staggeredGridLayoutManagerVertical =
                new StaggeredGridLayoutManager(
                        2, //The number of Columns in the grid
                        LinearLayoutManager.VERTICAL);
        staggeredGridLayoutManagerHorizontal =
                new StaggeredGridLayoutManager(
                        3, //The number of rows in the grid
                        LinearLayoutManager.HORIZONTAL);

        //set SpanSizeLookup()
        gridLayoutManagerVertical.setSpanSizeLookup(new MySpanSizeLookup(5, 1, 2));
        gridLayoutManagerHorizontal.setSpanSizeLookup(new MySpanSizeLookup(4, 1, 3));

        myRecyclerViewAdapter = new RecyclerViewAdapter(this);
        myRecyclerViewAdapter.setOnItemClickListener(this);
        myRecyclerView.setAdapter(myRecyclerViewAdapter);
        myRecyclerView.setLayoutManager(linearLayoutManagerVertical);

        //Add MyItemDecoration
        myRecyclerView.addItemDecoration(new MyItemDecoration(this));

        nameField = (EditText)findViewById(R.id.namefield);
        btnAdd = (Button)findViewById(R.id.addbutton);
        btnAdd.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                String newName = nameField.getText().toString();

                if(!newName.equals("")){
                    if(myRecyclerViewAdapter.getItemCount()>1){
                        myRecyclerViewAdapter.add(1, newName);
                    }else{
                        myRecyclerViewAdapter.add(0, newName);
                    }
                }
            }
        });

        optGroupLayout = (RadioGroup)findViewById(R.id.optGroupLayout);
        optLinearLayoutHorizontal = (RadioButton)findViewById(R.id.optLinearLayoutHorizontal);
        optLinearLayoutVertical = (RadioButton)findViewById(R.id.optLinearLayoutVertical);
        optGridLayoutHorizontal = (RadioButton)findViewById(R.id.optGridLayoutHorizontal);
        optGridLayoutVertical = (RadioButton)findViewById(R.id.optGridLayoutVertical);
        optStaggeredGridLayoutHorizontal = (RadioButton)findViewById(R.id.optStaggeredGridLayoutHorizontal);
        optStaggeredGridLayoutVertical = (RadioButton)findViewById(R.id.optStaggeredGridLayoutVertical);
        optGroupLayout.setOnCheckedChangeListener(optLayoutCheckedChangeListener);

        //pre-load dummy items
        myRecyclerViewAdapter.add(0, "SpanSizeLookup");
        myRecyclerViewAdapter.add(0, "ItemDecoration");
        myRecyclerViewAdapter.add(0, "GridLayoutManager");
        myRecyclerViewAdapter.add(0, "LinearLayoutManager");
        myRecyclerViewAdapter.add(0, "RecyclerViewAdapter");
        myRecyclerViewAdapter.add(0, "RecyclerView example");
        myRecyclerViewAdapter.add(0, "RecyclerView");
        myRecyclerViewAdapter.add(0, "android-er.blogspot.com");
        myRecyclerViewAdapter.add(0, "android-er");
        myRecyclerViewAdapter.add(0, "android");

    }

    private RadioGroup.OnCheckedChangeListener optLayoutCheckedChangeListener =
            new RadioGroup.OnCheckedChangeListener(){
                @Override
                public void onCheckedChanged(RadioGroup group, int checkedId) {
                    if(optLinearLayoutVertical.isChecked()){
                        myRecyclerView.setLayoutManager(linearLayoutManagerVertical);
                    }else if(optLinearLayoutHorizontal.isChecked()){
                        myRecyclerView.setLayoutManager(linearLayoutManagerHorizontal);
                    }else if(optGridLayoutHorizontal.isChecked()){
                        myRecyclerView.setLayoutManager(gridLayoutManagerHorizontal);
                    }else if(optGridLayoutVertical.isChecked()){
                        myRecyclerView.setLayoutManager(gridLayoutManagerVertical);
                    }else if(optStaggeredGridLayoutHorizontal.isChecked()){
                        myRecyclerView.setLayoutManager(staggeredGridLayoutManagerHorizontal);
                    }else if(optStaggeredGridLayoutVertical.isChecked()){
                        myRecyclerView.setLayoutManager(staggeredGridLayoutManagerVertical);
                    }
                }
            };


    @Override
    public void onItemClick(RecyclerViewAdapter.ItemHolder item, int position) {
        Toast.makeText(this,
                "Remove " + position + " : " + item.getItemName(),
                Toast.LENGTH_SHORT).show();
        myRecyclerViewAdapter.remove(position);
    }
}



download filesDownload the files (Android Studio Format).


- More step-by-step examples of RecyclerView.

Saturday, July 25, 2015

100 Questions and Answers to help you land your Dream Android Job: or to hire the right candidate!

100 Questions and Answers to help you land your Dream Android Job: or to hire the right candidate!

There is a boom in the mobile applications market. It has been projected than 2015 more than a billion (with b) smartphones will be sold, twice as many as the number of personal computers . Mobile channels are increasing their revenues figures, with percentages over 100% being the norm and not the exception. Each business needs a mobile application, and therefore the demand of engineers, UX designers and QA testers is under huge demand. There is far more demand for jobs than offer, and the situation is even more optimistic for senior developers. "100 Questions and Answers to make you get your Android Dream Job" collects 100 questions and answers, divided in three levels, that the author has been summarising during several years both as an interviewer and as a candidate. If you are looking for a position as an Android developer, no matter how experience you are, or if you are looking to hire new members for your organisation, this book will provide you all the material you need to take the right decision and hire the right candidate!

Android read file "/proc/mounts"

Android example code to read "/proc/mounts", easy to be modified to read other text files.


com.example.android_proc_mounts.MainActivity.java
package com.example.android_proc_mounts;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.TextView;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class MainActivity extends ActionBarActivity {

    TextView info;
    String targetPath = "/proc/mounts";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        info = (TextView)findViewById(R.id.info);
        info.setText(ReadFile(targetPath));
    }

    private String ReadFile(String path){
        String result = "";

        File file = new File(path);
        if(file.exists()){
            result += path + ":\n"
                    + "======\n";
            Scanner scanner = null;
            try {
                scanner = new Scanner(file);
                while (scanner.hasNext()) {
                    String line = scanner.nextLine();
                    result += line + "\n"
                            + "------\n";
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                result += e.toString();
            }

        }else{
            return (path + " NOT exists");
        }

        return result;
    }
}


layout/activity_main.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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>

</LinearLayout>


Friday, July 24, 2015

Android example using RecyclerView with CardView

The former post show "Android CardView example", It can be embedded in RecyclerView.



Modify from last exercise of RecyclerView, "onDraw() and onDrawOver() of ItemDecoration for RecyclerView".

Create layout of the CardView, layout/layout_cardview.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    card_view:cardCornerRadius="20sp"
    card_view:cardElevation="5sp">

    <TextView
        android:id="@+id/card_item_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="40dp"/>

</android.support.v7.widget.CardView>

Modify constructor of ItemHolder() in com.example.androidrecyclerview.RecyclerViewAdapter.java, ItemHolder(CardView cView, RecyclerViewAdapter parent), to include CardView. And also modify the method onCreateViewHolder(ViewGroup parent, int viewType).

com.example.androidrecyclerview.RecyclerViewAdapter.java
package com.example.androidrecyclerview;

import android.content.Context;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemHolder> {

    private List<String> itemsName;
    private OnItemClickListener onItemClickListener;
    private LayoutInflater layoutInflater;

    public RecyclerViewAdapter(Context context){
        layoutInflater = LayoutInflater.from(context);
        itemsName = new ArrayList<String>();
    }

    @Override
    public RecyclerViewAdapter.ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //View itemView = layoutInflater.inflate(R.layout.layout_item, parent, false);
        //return new ItemHolder(itemView, this);
        CardView itemCardView = (CardView)layoutInflater.inflate(R.layout.layout_cardview, parent, false);
        return new ItemHolder(itemCardView, this);
    }

    @Override
    public void onBindViewHolder(RecyclerViewAdapter.ItemHolder holder, int position) {
        holder.setItemName(itemsName.get(position));
    }

    @Override
    public int getItemCount() {
        return itemsName.size();
    }

    public void setOnItemClickListener(OnItemClickListener listener){
        onItemClickListener = listener;
    }

    public OnItemClickListener getOnItemClickListener(){
        return onItemClickListener;
    }

    public interface OnItemClickListener{
        public void onItemClick(ItemHolder item, int position);
    }

    public void add(int location, String iName){
        itemsName.add(location, iName);
        notifyItemInserted(location);
    }

    public void remove(int location){
        if(location >= itemsName.size())
            return;

        itemsName.remove(location);
        notifyItemRemoved(location);
    }

    public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

        private RecyclerViewAdapter parent;
        TextView textItemName;

        private CardView cardView;

        /*
        public ItemHolder(View itemView, RecyclerViewAdapter parent) {
            super(itemView);
            itemView.setOnClickListener(this);
            this.parent = parent;
            textItemName = (TextView) itemView.findViewById(R.id.item_name);
        }
        */

        public ItemHolder(CardView cView, RecyclerViewAdapter parent) {
            super(cView);
            cardView = cView;
            cardView.setOnClickListener(this);
            this.parent = parent;
            textItemName = (TextView) cardView.findViewById(R.id.card_item_name);
        }

        public void setItemName(CharSequence name){
            textItemName.setText(name);
        }

        public CharSequence getItemName(){
            return textItemName.getText();
        }

        @Override
        public void onClick(View v) {
            final OnItemClickListener listener = parent.getOnItemClickListener();
            if(listener != null){
                listener.onItemClick(this, getPosition());
            }
        }
    }
}


download filesDownload the files (Android Studio Format).

Next:
- StaggeredGridLayoutManager (Google+ App-like) on RecyclerView


- More step-by-step examples of RecyclerView.

Thursday, July 23, 2015

getExternalFilesDirs() and getExternalCacheDirs()

Start from Android 4.4 (API Level 19), methods getExternalFilesDirs(String type) and getExternalCacheDirs() were introduced.

File[] getExternalFilesDirs (String type) -
Returns absolute paths to application-specific directories on all external storage devices where the application can place persistent files it owns. These files are internal to the application, and not typically visible to the user as media.

File[] getExternalCacheDirs () -
Returns absolute paths to application-specific directories on all external storage devices where the application can place cache files it owns. These files are internal to the application, and not typically visible to the user as media.

Example code run on RedMi 2 (Android 4.4.4), WITH external SD Card:
both /storage/emulated/0/...(internal memory) and /storage/sdcard1/...(SD Card) are listed.


Run on Nexus 7 (Android 5.1.1) WITHOUT external SD Card:


Example code:
com.example.androidexternalstorage.MainActivity.java
package com.example.androidexternalstorage;

import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.widget.TextView;

import java.io.File;

public class MainActivity extends ActionBarActivity {

    TextView info;

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

        File extFilesDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);

        String strInfo =
            "Environment.getRootDirectory(): \n"
            + Environment.getRootDirectory() + "\n"
            + "Environment.getExternalStorageDirectory(): \n"
            + Environment.getExternalStorageDirectory() + "\n";

        strInfo +=
            "\ngetExternalFilesDir(null): \n"
            + getExternalFilesDir(null) + "\n"
            + "getExternalFilesDir(Environment.DIRECTORY_PICTURES): \n"
            + getExternalFilesDir(Environment.DIRECTORY_PICTURES) + "\n"
            + "getExternalFilesDir(Environment.DIRECTORY_MUSIC): \n"
            + getExternalFilesDir(Environment.DIRECTORY_MUSIC) + "\n";


        //API Level 19
        File[] externalFilesDirs = getExternalFilesDirs(null);
        strInfo += "\ngetExternalFilesDirs(null):\n";
        for(File f : externalFilesDirs){
            strInfo += f.getAbsolutePath() + "\n";
        }

        //API Level 19
        File[] externalCacheDirs = getExternalCacheDirs();
        strInfo += "\ngetExternalCacheDirs():\n";
        for(File f : externalCacheDirs){
            strInfo += f.getAbsolutePath() + "\n";
        }

        /*
        //API Level 21
        File[] externalMediaDirs = getExternalMediaDirs();
        strInfo += "\ngetExternalMediaDirs():\n";
        for(File f : externalMediaDirs){
            strInfo += f.getAbsolutePath() + "\n";
        }
        */

        info.setText(strInfo);
    }

}


layout/activity_main.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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>


</LinearLayout>


Android CardView example

android.support.v7.widget.CardView is a FrameLayout with a rounded corner background and shadow.

Before use CardView in your app, you have to "Add Support Libraries of RecyclerView, CardView to Android Studio Project".

Example of using CardView in layout xml.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    <android.support.v7.widget.CardView
        android:id="@+id/cardview1"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="28sp"
            android:text="CardView 1"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:id="@+id/cardview2"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="28sp"
            android:textColor="#ffa31f15"
            android:text="CardView 2"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:id="@+id/cardview3"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="20sp"
        card_view:cardBackgroundColor="#ffff0000"
        >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="35sp"
            android:textColor="#ffffffff"
            android:text="CardView 3"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:id="@+id/cardview4"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="20sp"
        card_view:cardElevation="5sp"
        card_view:cardBackgroundColor="#ff0000ff"
        >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="35sp"
            android:textColor="#ffffffff"
            android:text="CardView 4"/>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView
        android:id="@+id/cardview5"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="20sp"
        card_view:cardElevation="5sp"
        card_view:cardBackgroundColor="#ffffffff"
        >
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="35sp"
            android:textColor="#ff505050"
            android:text="CardView 5"/>
    </android.support.v7.widget.CardView>

</LinearLayout>


Tested on Nexus 7 running Android 5.1.1:


Related:
Android example using RecyclerView with CardView

Android Studio Color Chooser, when edit color in layout xml

When edit color value in layout xml using Android Studio, a color dot will appear on the left side. Click the color dot will open Color Chooser for you.






Wednesday, July 22, 2015

YouTube App now support vertical video in portrait full screen

When you view vertical video in Youtube App, just rotate your device in portrait mode and tap on the full screen icon. It can display properly.

Tuesday, July 21, 2015

onDraw() and onDrawOver() of ItemDecoration for RecyclerView


In the exercise of "Implement custom ItemDecoration for RecyclerView" we already implement our ItemDecoration to draw lines on items in RecyclerView, in onDraw() method. Further work on last exercise "set SpanSizeLookup to GridLayoutManager of RecyclerView", here we do more:
- draw Border on canvas of whole RecyclerView, in onDraw()
- draw a enlarged bitmap on low-right corner of RecyclerView, in onDrawOver()


com.example.androidrecyclerview.MyItemDecoration.java
package com.example.androidrecyclerview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class MyItemDecoration extends RecyclerView.ItemDecoration{

    private Paint paintBlue, paintRed, paintBorder;
    private int offset;

    Bitmap bitmap;
    int bitmap_w, bitmap_h;
    Rect rectSrc;

    public MyItemDecoration(Context c){
        offset = 10;
        paintBlue = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintBlue.setColor(Color.BLUE);
        paintBlue.setStyle(Paint.Style.STROKE);
        paintBlue.setStrokeWidth(3);

        paintRed = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintRed.setColor(Color.RED);
        paintRed.setStyle(Paint.Style.STROKE);
        paintRed.setStrokeWidth(1);

        paintBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintBorder.setColor(Color.GREEN);
        paintBorder.setStyle(Paint.Style.STROKE);
        paintBorder.setStrokeWidth(10);

        bitmap = BitmapFactory.decodeResource(
                c.getResources(),
                android.R.drawable.ic_menu_info_details);
        bitmap_w = bitmap.getWidth();
        bitmap_h = bitmap.getHeight();
        rectSrc = new Rect(0, 0, bitmap_w, bitmap_h);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.set(offset, offset, offset, offset);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();

        c.drawRect(
                0,
                0,
                c.getWidth(),
                c.getHeight(),
                paintBorder);

        for(int i=0; i<parent.getChildCount(); i++){
            final View child = parent.getChildAt(i);
            c.drawRect(
                    layoutManager.getDecoratedLeft(child),
                    layoutManager.getDecoratedTop(child),
                    layoutManager.getDecoratedRight(child),
                    layoutManager.getDecoratedBottom(child),
                    paintBlue);
            c.drawRect(
                    layoutManager.getDecoratedLeft(child) + offset,
                    layoutManager.getDecoratedTop(child) + offset,
                    layoutManager.getDecoratedRight(child) - offset,
                    layoutManager.getDecoratedBottom(child) - offset,
                    paintRed);

        }

    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        c.drawBitmap(bitmap,
                rectSrc,
                new Rect(
                        c.getWidth()-(2*bitmap_w),
                        c.getHeight()-(2*bitmap_h),
                        c.getWidth(),
                        c.getHeight()),
                null);
    }
}


Have to modify constructor call of MyItemDecoration in MainActivity.java, such that we can get Resources drawable in MyItemDecoration.

        myRecyclerView.addItemDecoration(new MyItemDecoration());
to:
        myRecyclerView.addItemDecoration(new MyItemDecoration(this));

download filesDownload the files (Android Studio Format).

Next:
Android example using RecyclerView with CardView


- More step-by-step examples of RecyclerView.

Monday, July 20, 2015

set SpanSizeLookup to GridLayoutManager of RecyclerView

GridLayoutManager.SpanSizeLookup help to provide the number of spans each item of RecyclerView to occupies. Call setSpanSizeLookup(GridLayoutManager.SpanSizeLookup) method of GridLayoutManager to apply it.




This example modify from last exercise of "LinearLayoutManager and GridLayoutManager for RecyclerView". Create own custom GridLayoutManager.SpanSizeLookup, com.example.androidrecyclerview.MySpanSizeLookup.java
package com.example.androidrecyclerview;

import android.support.v7.widget.GridLayoutManager;

public class MySpanSizeLookup extends GridLayoutManager.SpanSizeLookup {

    int spanPos, spanCnt1, spanCnt2;

    public MySpanSizeLookup(int spanPos, int spanCnt1, int spanCnt2) {
        super();
        this.spanPos = spanPos;
        this.spanCnt1 = spanCnt1;
        this.spanCnt2 = spanCnt2;
    }

    @Override
    public int getSpanSize(int position) {
        return (position % spanPos ==0 ? spanCnt2 : spanCnt1);
    }
}


Modify com.example.androidrecyclerview.MainActivity.java, in onCreate() to implement and set MySpanSizeLookup().
package com.example.androidrecyclerview;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements RecyclerViewAdapter.OnItemClickListener{

    private RecyclerView myRecyclerView;
    private RecyclerViewAdapter myRecyclerViewAdapter;

    EditText nameField;
    Button btnAdd;

    RadioGroup optGroupLayout;
    RadioButton optLinearLayoutHorizontal;
    RadioButton optLinearLayoutVertical;
    RadioButton optGridLayoutHorizontal;
    RadioButton optGridLayoutVertical;

    private LinearLayoutManager linearLayoutManagerVertical;
    private LinearLayoutManager linearLayoutManagerHorizontal;
    private GridLayoutManager gridLayoutManagerVertical;
    private GridLayoutManager gridLayoutManagerHorizontal;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myRecyclerView = (RecyclerView)findViewById(R.id.myrecyclerview);

        linearLayoutManagerVertical =
                new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        linearLayoutManagerHorizontal =
                new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        gridLayoutManagerVertical =
                new GridLayoutManager(this,
                        2, //The number of Columns in the grid
                        LinearLayoutManager.VERTICAL,
                        false);
        gridLayoutManagerHorizontal =
                new GridLayoutManager(this,
                        3, //The number of rows in the grid
                        LinearLayoutManager.HORIZONTAL,
                        false);

        //set SpanSizeLookup()
        gridLayoutManagerVertical.setSpanSizeLookup(new MySpanSizeLookup(5, 1, 2));
        gridLayoutManagerHorizontal.setSpanSizeLookup(new MySpanSizeLookup(4, 1, 3));

        myRecyclerViewAdapter = new RecyclerViewAdapter(this);
        myRecyclerViewAdapter.setOnItemClickListener(this);
        myRecyclerView.setAdapter(myRecyclerViewAdapter);
        myRecyclerView.setLayoutManager(linearLayoutManagerVertical);

        //Add MyItemDecoration
        myRecyclerView.addItemDecoration(new MyItemDecoration());

        nameField = (EditText)findViewById(R.id.namefield);
        btnAdd = (Button)findViewById(R.id.addbutton);
        btnAdd.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                String newName = nameField.getText().toString();

                if(!newName.equals("")){
                    if(myRecyclerViewAdapter.getItemCount()>1){
                        myRecyclerViewAdapter.add(1, newName);
                    }else{
                        myRecyclerViewAdapter.add(0, newName);
                    }
                }
            }
        });

        optGroupLayout = (RadioGroup)findViewById(R.id.optGroupLayout);
        optLinearLayoutHorizontal = (RadioButton)findViewById(R.id.optLinearLayoutHorizontal);
        optLinearLayoutVertical = (RadioButton)findViewById(R.id.optLinearLayoutVertical);
        optGridLayoutHorizontal = (RadioButton)findViewById(R.id.optGridLayoutHorizontal);
        optGridLayoutVertical = (RadioButton)findViewById(R.id.optGridLayoutVertical);
        optGroupLayout.setOnCheckedChangeListener(optLayoutCheckedChangeListener);

        //pre-load dummy items
        myRecyclerViewAdapter.add(0, "SpanSizeLookup");
        myRecyclerViewAdapter.add(0, "ItemDecoration");
        myRecyclerViewAdapter.add(0, "GridLayoutManager");
        myRecyclerViewAdapter.add(0, "LinearLayoutManager");
        myRecyclerViewAdapter.add(0, "RecyclerViewAdapter");
        myRecyclerViewAdapter.add(0, "RecyclerView example");
        myRecyclerViewAdapter.add(0, "RecyclerView");
        myRecyclerViewAdapter.add(0, "android-er.blogspot.com");
        myRecyclerViewAdapter.add(0, "android-er");
        myRecyclerViewAdapter.add(0, "android");

    }

    private RadioGroup.OnCheckedChangeListener optLayoutCheckedChangeListener =
            new RadioGroup.OnCheckedChangeListener(){
                @Override
                public void onCheckedChanged(RadioGroup group, int checkedId) {
                    if(optLinearLayoutVertical.isChecked()){
                        myRecyclerView.setLayoutManager(linearLayoutManagerVertical);
                    }else if(optLinearLayoutHorizontal.isChecked()){
                        myRecyclerView.setLayoutManager(linearLayoutManagerHorizontal);
                    }else if(optGridLayoutHorizontal.isChecked()){
                        myRecyclerView.setLayoutManager(gridLayoutManagerHorizontal);
                    }else if(optGridLayoutVertical.isChecked()){
                        myRecyclerView.setLayoutManager(gridLayoutManagerVertical);
                    }
                }
            };


    @Override
    public void onItemClick(RecyclerViewAdapter.ItemHolder item, int position) {
        Toast.makeText(this,
                "Remove " + position + " : " + item.getItemName(),
                Toast.LENGTH_SHORT).show();
        myRecyclerViewAdapter.remove(position);
    }
}



download filesDownload the files (Android Studio Format).

Next:
onDraw() and onDrawOver() of ItemDecoration for RecyclerView

- More step-by-step examples of RecyclerView.

Sunday, July 19, 2015

Android Bluetooth to control LED brightness on Arduino Due + HC-06

Previous exercise "Android example to communicate with Bluetooth device, HC-06 Bluetooth Module" show how to send string. Here we implement our command format, such that we can use SeekBar on Android to control brightness of the Arduino Due on-board LED (pin 13), via Bluetooth to HC-06 Bluetooth Module.



Android Side:

com.example.androidbtcontrol.MainActivity.java
/*
Android Example to connect to and communicate with Bluetooth
In this exercise, the target is a Arduino Due + HC-06 (Bluetooth Module)

 */
package com.example.androidbtcontrol;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;


public class MainActivity extends ActionBarActivity {

    private static final int REQUEST_ENABLE_BT = 1;

    BluetoothAdapter bluetoothAdapter;

    ArrayList<BluetoothDevice> pairedDeviceArrayList;

    TextView textInfo, textStatus;
    ListView listViewPairedDevice;
    LinearLayout inputPane;

    SeekBar barAnalogOut;

    ArrayAdapter<BluetoothDevice> pairedDeviceAdapter;
    private UUID myUUID;
    private final String UUID_STRING_WELL_KNOWN_SPP =
        "00001101-0000-1000-8000-00805F9B34FB";

    ThreadConnectBTdevice myThreadConnectBTdevice;
    ThreadConnected myThreadConnected;

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

        textInfo = (TextView)findViewById(R.id.info);
        textStatus = (TextView)findViewById(R.id.status);
        listViewPairedDevice = (ListView)findViewById(R.id.pairedlist);

        inputPane = (LinearLayout)findViewById(R.id.inputpane);

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)){
            Toast.makeText(this,
                    "FEATURE_BLUETOOTH NOT support",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        //using the well-known SPP UUID
        myUUID = UUID.fromString(UUID_STRING_WELL_KNOWN_SPP);

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Toast.makeText(this,
                    "Bluetooth is not supported on this hardware platform",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        String stInfo = bluetoothAdapter.getName() + "\n" +
                bluetoothAdapter.getAddress();
        textInfo.setText(stInfo);

        barAnalogOut = (SeekBar)findViewById(R.id.analogOut);
        barAnalogOut.setOnSeekBarChangeListener(OnAnalogOutChangeListener);

    }

    SeekBar.OnSeekBarChangeListener OnAnalogOutChangeListener =
            new SeekBar.OnSeekBarChangeListener(){
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                    //will generate too much data sent!
                    //SendAnalogOut(progress);
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {

                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    SendAnalogOut(seekBar.getProgress());
                }
            };

    private final byte SYNC_BYTE = (byte) 0xAA;
    private final byte LENGTH_ANALOG = (byte) 3;
    private final byte CMD_ANALOG = (byte) 1;

    private void SendAnalogOut(int val){
        byte[] bytesToSend = {SYNC_BYTE, LENGTH_ANALOG, CMD_ANALOG, (byte) val};
        myThreadConnected.write(bytesToSend);
    }

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

        //Turn ON BlueTooth if it is OFF
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        }

        setup();
    }

    private void setup() {
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            pairedDeviceArrayList = new ArrayList<BluetoothDevice>();

            for (BluetoothDevice device : pairedDevices) {
                pairedDeviceArrayList.add(device);
            }

            pairedDeviceAdapter = new ArrayAdapter<BluetoothDevice>(this,
                    android.R.layout.simple_list_item_1, pairedDeviceArrayList);
            listViewPairedDevice.setAdapter(pairedDeviceAdapter);

            listViewPairedDevice.setOnItemClickListener(new AdapterView.OnItemClickListener() {

                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                                        int position, long id) {
                    BluetoothDevice device =
                            (BluetoothDevice) parent.getItemAtPosition(position);
                    Toast.makeText(MainActivity.this,
                            "Name: " + device.getName() + "\n"
                                    + "Address: " + device.getAddress() + "\n"
                                    + "BondState: " + device.getBondState() + "\n"
                                    + "BluetoothClass: " + device.getBluetoothClass() + "\n"
                                    + "Class: " + device.getClass(),
                            Toast.LENGTH_LONG).show();

                    textStatus.setText("start ThreadConnectBTdevice");
                    myThreadConnectBTdevice = new ThreadConnectBTdevice(device);
                    myThreadConnectBTdevice.start();
                }
            });
        }
    }

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

        if(myThreadConnectBTdevice!=null){
            myThreadConnectBTdevice.cancel();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode==REQUEST_ENABLE_BT){
            if(resultCode == Activity.RESULT_OK){
                setup();
            }else{
                Toast.makeText(this,
                        "BlueTooth NOT enabled",
                        Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    //Called in ThreadConnectBTdevice once connect successed
    //to start ThreadConnected
    private void startThreadConnected(BluetoothSocket socket){

        myThreadConnected = new ThreadConnected(socket);
        myThreadConnected.start();
    }

    /*
    ThreadConnectBTdevice:
    Background Thread to handle BlueTooth connecting
    */
    private class ThreadConnectBTdevice extends Thread {

        private BluetoothSocket bluetoothSocket = null;
        private final BluetoothDevice bluetoothDevice;


        private ThreadConnectBTdevice(BluetoothDevice device) {
            bluetoothDevice = device;

            try {
                bluetoothSocket = device.createRfcommSocketToServiceRecord(myUUID);
                textStatus.setText("bluetoothSocket: \n" + bluetoothSocket);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            boolean success = false;
            try {
                bluetoothSocket.connect();
                success = true;
            } catch (IOException e) {
                e.printStackTrace();

                final String eMessage = e.getMessage();
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        textStatus.setText("something wrong bluetoothSocket.connect(): \n" + eMessage);
                    }
                });

                try {
                    bluetoothSocket.close();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }

            if(success){
                //connect successful
                final String msgconnected = "connect successful:\n"
                        + "BluetoothSocket: " + bluetoothSocket + "\n"
                        + "BluetoothDevice: " + bluetoothDevice;

                runOnUiThread(new Runnable(){

                    @Override
                    public void run() {
                        textStatus.setText(msgconnected);

                        listViewPairedDevice.setVisibility(View.GONE);
                        inputPane.setVisibility(View.VISIBLE);
                    }});

                startThreadConnected(bluetoothSocket);
            }else{
                //fail
            }
        }

        public void cancel() {

            Toast.makeText(getApplicationContext(),
                    "close bluetoothSocket",
                    Toast.LENGTH_LONG).show();

            try {
                bluetoothSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

    /*
    ThreadConnected:
    Background Thread to handle Bluetooth data communication
    after connected
     */
    private class ThreadConnected extends Thread {
        private final BluetoothSocket connectedBluetoothSocket;
        private final InputStream connectedInputStream;
        private final OutputStream connectedOutputStream;

        public ThreadConnected(BluetoothSocket socket) {
            connectedBluetoothSocket = socket;
            InputStream in = null;
            OutputStream out = null;

            try {
                in = socket.getInputStream();
                out = socket.getOutputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            connectedInputStream = in;
            connectedOutputStream = out;
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];
            int bytes;

            while (true) {
                try {
                    bytes = connectedInputStream.read(buffer);
                    String strReceived = new String(buffer, 0, bytes);
                    final String msgReceived = String.valueOf(bytes) +
                            " bytes received:\n"
                            + strReceived;

                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            textStatus.setText(msgReceived);
                        }});

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();

                    final String msgConnectionLost = "Connection lost:\n"
                            + e.getMessage();
                    runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            textStatus.setText(msgConnectionLost);
                        }});
                }
            }
        }

        public void write(byte[] buffer) {
            try {
                connectedOutputStream.write(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void cancel() {
            try {
                connectedBluetoothSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}


layout/activity_main.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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/info"
        android:textStyle="bold|italic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <ListView
        android:id="@+id/pairedlist"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:id="@+id/inputpane"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="gone">

        <SeekBar
            android:id="@+id/analogOut"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="99"
            android:progress="0"/>

    </LinearLayout>

</LinearLayout>


uses-permission of "android.permission.BLUETOOTH" is needed in AndroidManifest.xml.

download filesDownload the files (Android Studio Format).


Arduino side:


Connection between Arduino and HC-06, refer to "Connect Arduino Due with HC-06 (Bluetooth Module)".

DueHC06_AT.ino
/*
Arduino Due + HC-06 (Bluetooth) - 
receive command to control LED (pin 13) brightness

Serial (Tx/Rx) communicate to PC via USB
Serial3 (Tx3/Rx3) connect to HC-06
HC-06 Rx - Due Tx3
HC-06 Tx - Due Rx3
HC-06 GND - Due GND
HC-06 VCC - Due 3.3V

*/
#define HC06 Serial3

int SYNC_BYTE = 0xAA;
int CMD_ANALOG = 1;

int rxState;
int rxLength;
int rxCmd;
const int rxState_0_idle = 0;
const int rxState_1_sync = 1;  //SYNC_BYTE received
const int rxState_2_cmd = 2;   //length received, waiting CMD
const int rxState_3_data = 3;  //CMD received, receiving data

int LED = 13;

void setup()
{
  pinMode(LED, OUTPUT);
  delay(1000);
  Serial.begin(9600);
  HC06.begin(9600);
  
  Serial.write("\nTest Start\n");
  rxState = rxState_0_idle;
}

void loop()
{
  while(HC06.available())
  {
    char rxData = (int)HC06.read();

    switch(rxState){
      case rxState_0_idle:
          if(rxData == SYNC_BYTE){
            rxState = rxState_1_sync;
            Serial.println("SYNC_BYTE received");
          }
          break;
      case rxState_1_sync:
          rxLength = rxData;
          Serial.println("Length: " + String(rxLength));
          rxLength--;
          rxState = rxState_2_cmd;
          
          break;

      case rxState_2_cmd:
          rxCmd = rxData;
          Serial.println("CMD: " + String(rxCmd));
          
          rxLength--;
          rxState = rxState_3_data;
          break;
          
      case rxState_3_data:
          Serial.println("data: " + String(rxData));
          //we have one command only
          if(rxCmd==CMD_ANALOG){
            int brightness = map(rxData, 0, 99, 0, 255);
            analogWrite(LED, brightness);
            Serial.println("brightness: " + String(brightness));
            HC06.println("brightness: " + String(brightness));
          }
        
          rxLength--;
          if(rxLength==0){
            rxState = rxState_0_idle;
          }

          break;
          
      default:
          rxState = rxState_0_idle;
    };
    
  }
}


You can also monitor the state on PC using Serial Monitor.


Import Android Eclipse project to Android Studio

This video show how to import Android Eclipse project to Android Studio. Here we use the the Eclipse project of "CalendarView example" as a example.


And how it run on device:



>> Know more about Migrating to Android Studio

Saturday, July 18, 2015

Android example to communicate with Bluetooth device, HC-06 Bluetooth Module

The former exercise "Test HC-06 Bluetooth Module with Android BluetoothChat" show how to communicate between Android and HC-06 Bluetooth Module, using "Android BluetoothChat example".

And the post in my another blog "Arduino-er: Connect Arduino Due with HC-06 (Bluetooth Module)", show how to receive data from HC-06, send back to Bluetooth device and PC via USB.

Here, we are going to implement our own Android app (in Android Studio) to connect and communicate to Arduino Due + HC-06 Bluetooth Module. Actually, it is copied (with modified UUID) from my old example "Bluetooth communication between Android devices", AndroidBlueTooth part.


From the video, it can be noted that the first character always missed, or overwritten. We can verify it in Arduino IDE's Serial Monitor, the data received on Arduino Due (via Bluetooth) is correct.


[Updated@2015-10-31: Lost character Fixed, refer "Android communicate with Arduino + HC-06 Bluetooth Module, part II"]

com.example.androidbtcontrol.MainActivity.java
/*
Android Example to connect to and communicate with Bluetooth
In this exercise, the target is a Arduino Due + HC-06 (Bluetooth Module)

Ref:
- Make BlueTooth connection between Android devices
http://android-er.blogspot.com/2014/12/make-bluetooth-connection-between.html
- Bluetooth communication between Android devices
http://android-er.blogspot.com/2014/12/bluetooth-communication-between-android.html
 */
package com.example.androidbtcontrol;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;


public class MainActivity extends ActionBarActivity {

    private static final int REQUEST_ENABLE_BT = 1;

    BluetoothAdapter bluetoothAdapter;

    ArrayList<BluetoothDevice> pairedDeviceArrayList;

    TextView textInfo, textStatus;
    ListView listViewPairedDevice;
    LinearLayout inputPane;
    EditText inputField;
    Button btnSend;

    ArrayAdapter<BluetoothDevice> pairedDeviceAdapter;
    private UUID myUUID;
    private final String UUID_STRING_WELL_KNOWN_SPP =
        "00001101-0000-1000-8000-00805F9B34FB";

    ThreadConnectBTdevice myThreadConnectBTdevice;
    ThreadConnected myThreadConnected;

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

        textInfo = (TextView)findViewById(R.id.info);
        textStatus = (TextView)findViewById(R.id.status);
        listViewPairedDevice = (ListView)findViewById(R.id.pairedlist);

        inputPane = (LinearLayout)findViewById(R.id.inputpane);
        inputField = (EditText)findViewById(R.id.input);
        btnSend = (Button)findViewById(R.id.send);
        btnSend.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                if(myThreadConnected!=null){
                    byte[] bytesToSend = inputField.getText().toString().getBytes();
                    myThreadConnected.write(bytesToSend);
                }
            }});

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)){
            Toast.makeText(this,
                    "FEATURE_BLUETOOTH NOT support",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        //using the well-known SPP UUID
        myUUID = UUID.fromString(UUID_STRING_WELL_KNOWN_SPP);

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Toast.makeText(this,
                    "Bluetooth is not supported on this hardware platform",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        String stInfo = bluetoothAdapter.getName() + "\n" +
                bluetoothAdapter.getAddress();
        textInfo.setText(stInfo);
    }

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

        //Turn ON BlueTooth if it is OFF
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        }

        setup();
    }

    private void setup() {
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            pairedDeviceArrayList = new ArrayList<BluetoothDevice>();

            for (BluetoothDevice device : pairedDevices) {
                pairedDeviceArrayList.add(device);
            }

            pairedDeviceAdapter = new ArrayAdapter<BluetoothDevice>(this,
                    android.R.layout.simple_list_item_1, pairedDeviceArrayList);
            listViewPairedDevice.setAdapter(pairedDeviceAdapter);

            listViewPairedDevice.setOnItemClickListener(new AdapterView.OnItemClickListener() {

                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                                        int position, long id) {
                    BluetoothDevice device =
                            (BluetoothDevice) parent.getItemAtPosition(position);
                    Toast.makeText(MainActivity.this,
                            "Name: " + device.getName() + "\n"
                                    + "Address: " + device.getAddress() + "\n"
                                    + "BondState: " + device.getBondState() + "\n"
                                    + "BluetoothClass: " + device.getBluetoothClass() + "\n"
                                    + "Class: " + device.getClass(),
                            Toast.LENGTH_LONG).show();

                    textStatus.setText("start ThreadConnectBTdevice");
                    myThreadConnectBTdevice = new ThreadConnectBTdevice(device);
                    myThreadConnectBTdevice.start();
                }
            });
        }
    }

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

        if(myThreadConnectBTdevice!=null){
            myThreadConnectBTdevice.cancel();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode==REQUEST_ENABLE_BT){
            if(resultCode == Activity.RESULT_OK){
                setup();
            }else{
                Toast.makeText(this,
                        "BlueTooth NOT enabled",
                        Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    //Called in ThreadConnectBTdevice once connect successed
    //to start ThreadConnected
    private void startThreadConnected(BluetoothSocket socket){

        myThreadConnected = new ThreadConnected(socket);
        myThreadConnected.start();
    }

    /*
    ThreadConnectBTdevice:
    Background Thread to handle BlueTooth connecting
    */
    private class ThreadConnectBTdevice extends Thread {

        private BluetoothSocket bluetoothSocket = null;
        private final BluetoothDevice bluetoothDevice;


        private ThreadConnectBTdevice(BluetoothDevice device) {
            bluetoothDevice = device;

            try {
                bluetoothSocket = device.createRfcommSocketToServiceRecord(myUUID);
                textStatus.setText("bluetoothSocket: \n" + bluetoothSocket);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            boolean success = false;
            try {
                bluetoothSocket.connect();
                success = true;
            } catch (IOException e) {
                e.printStackTrace();

                final String eMessage = e.getMessage();
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        textStatus.setText("something wrong bluetoothSocket.connect(): \n" + eMessage);
                    }
                });

                try {
                    bluetoothSocket.close();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }

            if(success){
                //connect successful
                final String msgconnected = "connect successful:\n"
                        + "BluetoothSocket: " + bluetoothSocket + "\n"
                        + "BluetoothDevice: " + bluetoothDevice;

                runOnUiThread(new Runnable(){

                    @Override
                    public void run() {
                        textStatus.setText(msgconnected);

                        listViewPairedDevice.setVisibility(View.GONE);
                        inputPane.setVisibility(View.VISIBLE);
                    }});

                startThreadConnected(bluetoothSocket);
            }else{
                //fail
            }
        }

        public void cancel() {

            Toast.makeText(getApplicationContext(),
                    "close bluetoothSocket",
                    Toast.LENGTH_LONG).show();

            try {
                bluetoothSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

    /*
    ThreadConnected:
    Background Thread to handle Bluetooth data communication
    after connected
     */
    private class ThreadConnected extends Thread {
        private final BluetoothSocket connectedBluetoothSocket;
        private final InputStream connectedInputStream;
        private final OutputStream connectedOutputStream;

        public ThreadConnected(BluetoothSocket socket) {
            connectedBluetoothSocket = socket;
            InputStream in = null;
            OutputStream out = null;

            try {
                in = socket.getInputStream();
                out = socket.getOutputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            connectedInputStream = in;
            connectedOutputStream = out;
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];
            int bytes;

            while (true) {
                try {
                    bytes = connectedInputStream.read(buffer);
                    String strReceived = new String(buffer, 0, bytes);
                    final String msgReceived = String.valueOf(bytes) +
                            " bytes received:\n"
                            + strReceived;

                    runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            textStatus.setText(msgReceived);
                        }});

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();

                    final String msgConnectionLost = "Connection lost:\n"
                            + e.getMessage();
                    runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            textStatus.setText(msgConnectionLost);
                        }});
                }
            }
        }

        public void write(byte[] buffer) {
            try {
                connectedOutputStream.write(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void cancel() {
            try {
                connectedBluetoothSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}


layout/activity_main.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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/info"
        android:textStyle="bold|italic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/status"
        android:textSize="28sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <ListView
        android:id="@+id/pairedlist"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:id="@+id/inputpane"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="gone">

        <EditText
            android:id="@+id/input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/send"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Sent"/>

    </LinearLayout>

</LinearLayout>


Have to modify AndroidManifest.xml to uses-permission of "android.permission.BLUETOOTH".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidbtcontrol" >

    <uses-permission android:name="android.permission.BLUETOOTH"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <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>


download filesDownload the files (Android Studio Format).

Next:
Android Bluetooth to control LED brightness on Arduino Due + HC-06