Sunday, May 29, 2016

Android client example 2, communicate with Java Server run on Raspberry Pi


Refer to the previous Android client example to send message to Java server on Raspberry Pi. The client Android Client connect To Java Server, and send something then close socket. This example show how Android Client connect to Java Server (run on raspberry Pi) and keep connected, and send to and receive from Server.


For the Server side, it's Java Echo Server to send back the received data to the sender.

Please notice:
Suppose the Disconnect button is used to disconnect from server once clicked. In this example, it cannot actually. Because the code bufferedReader.readLine() in ClientThread.java will block the program. The Disconnect request will wait until anything received to return from bufferedReader.readLine().

Create ClientThread.java to handle network related job in background thread.
package com.blogspot.android_er.androidclient;

import android.os.Message;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class ClientThread extends Thread{

    String dstAddress;
    int dstPort;
    private boolean running;
    MainActivity.ClientHandler handler;

    Socket socket;
    PrintWriter printWriter;
    BufferedReader bufferedReader;

    public ClientThread(String addr, int port, MainActivity.ClientHandler handler) {
        super();
        dstAddress = addr;
        dstPort = port;
        this.handler = handler;
    }

    public void setRunning(boolean running){
        this.running = running;
    }

    private void sendState(String state){
        handler.sendMessage(
                Message.obtain(handler,
                        MainActivity.ClientHandler.UPDATE_STATE, state));
    }

    public void txMsg(String msgToSend){
        if(printWriter != null){
            printWriter.println(msgToSend);
        }
    }

    @Override
    public void run() {
        sendState("connecting...");

        running = true;

        try {
            socket = new Socket(dstAddress, dstPort);
            sendState("connected");

            OutputStream outputStream = socket.getOutputStream();
            printWriter = new PrintWriter(outputStream, true);

            InputStream inputStream = socket.getInputStream();
            InputStreamReader inputStreamReader =
                    new InputStreamReader(inputStream);
            bufferedReader = new BufferedReader(inputStreamReader);

            while(running){

                //bufferedReader block the code
                String line = bufferedReader.readLine();
                if(line != null){
                    handler.sendMessage(
                            Message.obtain(handler,
                                    MainActivity.ClientHandler.UPDATE_MSG, line));
                }

            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(printWriter != null){
                printWriter.close();
            }

            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        handler.sendEmptyMessage(MainActivity.ClientHandler.UPDATE_END);
    }
}


MainActivity.java
package com.blogspot.android_er.androidclient;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    EditText editTextAddress, editTextPort, editTextMsg;
    Button buttonConnect, buttonDisconnect, buttonSend;
    TextView textViewState, textViewRx;

    ClientHandler clientHandler;
    ClientThread clientThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editTextAddress = (EditText) findViewById(R.id.address);
        editTextPort = (EditText) findViewById(R.id.port);
        editTextMsg = (EditText) findViewById(R.id.msgtosend);
        buttonConnect = (Button) findViewById(R.id.connect);
        buttonDisconnect = (Button) findViewById(R.id.disconnect);
        buttonSend = (Button)findViewById(R.id.send);
        textViewState = (TextView)findViewById(R.id.state);
        textViewRx = (TextView)findViewById(R.id.received);

        buttonDisconnect.setEnabled(false);
        buttonSend.setEnabled(false);

        buttonConnect.setOnClickListener(buttonConnectOnClickListener);
        buttonDisconnect.setOnClickListener(buttonDisConnectOnClickListener);
        buttonSend.setOnClickListener(buttonSendOnClickListener);

        clientHandler = new ClientHandler(this);
    }

    View.OnClickListener buttonConnectOnClickListener =
            new View.OnClickListener() {

                @Override
                public void onClick(View arg0) {

                    clientThread = new ClientThread(
                            editTextAddress.getText().toString(),
                            Integer.parseInt(editTextPort.getText().toString()),
                            clientHandler);
                    clientThread.start();

                    buttonConnect.setEnabled(false);
                    buttonDisconnect.setEnabled(true);
                    buttonSend.setEnabled(true);
                }
            };

    View.OnClickListener buttonDisConnectOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(clientThread != null){
                clientThread.setRunning(false);
            }

        }
    };

    View.OnClickListener buttonSendOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(clientThread != null){
                String msgToSend = editTextMsg.getText().toString();
                clientThread.txMsg(msgToSend);
            }
        }
    };

    private void updateState(String state){
        textViewState.setText(state);
    }

    private void updateRxMsg(String rxmsg){
        textViewRx.append(rxmsg + "\n");
    }

    private void clientEnd(){
        clientThread = null;
        textViewState.setText("clientEnd()");
        buttonConnect.setEnabled(true);
        buttonDisconnect.setEnabled(false);
        buttonSend.setEnabled(false);

    }

    public static class ClientHandler extends Handler {
        public static final int UPDATE_STATE = 0;
        public static final int UPDATE_MSG = 1;
        public static final int UPDATE_END = 2;
        private MainActivity parent;

        public ClientHandler(MainActivity parent) {
            super();
            this.parent = parent;
        }

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what){
                case UPDATE_STATE:
                    parent.updateState((String)msg.obj);
                    break;
                case UPDATE_MSG:
                    parent.updateRxMsg((String)msg.obj);
                    break;
                case UPDATE_END:
                    parent.clientEnd();
                    break;
                default:
                    super.handleMessage(msg);
            }

        }

    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidclient.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" />
    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="192.168."
        android:hint="dstAddress" />
    <EditText
        android:id="@+id/port"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="8000"
        android:hint="dstPort" />
    <Button
        android:id="@+id/connect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Connect"/>
    <Button
        android:id="@+id/disconnect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Disconnect"/>
    <TextView
        android:id="@+id/state"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="un-initiated"
        android:textSize="20dp"/>
    <EditText
        android:id="@+id/msgtosend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="msg to send..." />
    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send"/>
    <TextView
        android:id="@+id/received"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18dp"/>
</LinearLayout>


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


download filesDownload the files .

Saturday, May 28, 2016

Android example of using Thread and Handler


This example show how to run code in background using Thread, and also show passing data from activity to thread by calling method of thread, and passing data from thread to activity with Handler.


MainActivity.java
package com.blogspot.android_er.androidthread;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    Button btnStart, btnStop, btnSend;
    EditText editTextMsgToSend;
    TextView textViewCntReceived, textViewMsgReceived;

    MyThread myThread;
    MyHandler myHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnStart = (Button)findViewById(R.id.startthread);
        btnStop = (Button)findViewById(R.id.stopthread);
        btnSend = (Button)findViewById(R.id.send);
        editTextMsgToSend = (EditText)findViewById(R.id.msgtosend);
        textViewCntReceived = (TextView)findViewById(R.id.cntreceived);
        textViewMsgReceived = (TextView)findViewById(R.id.msgreceived);

        myHandler = new MyHandler(this);

        btnStart.setOnClickListener(btnStartOnClickListener);
        btnStop.setOnClickListener(btnStopOnClickListener);
        btnSend.setOnClickListener(btnSendOnClickListener);
    }

    View.OnClickListener btnStartOnClickListener =
            new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(myThread != null){
                myThread.setRunning(false);
            }
            myThread = new MyThread(myHandler);
            myThread.start();
        }
    };

    View.OnClickListener btnStopOnClickListener =
            new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(myThread != null){
                myThread.setRunning(false);
                myThread = null;
            }
        }
    };

    View.OnClickListener btnSendOnClickListener =
            new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(myThread != null){
                myThread.msgToThread(editTextMsgToSend.getText().toString());
            }
        }
    };

    private void updateCnt(int cnt){
        textViewCntReceived.setText(String.valueOf(cnt));
    }

    private void updateMsg(String msg){
        textViewMsgReceived.setText(msg);
    }


    public static class MyHandler extends Handler{

        public static final int UPDATE_CNT = 0;
        public static final int UPDATE_MSG = 1;
        private MainActivity parent;

        public MyHandler(MainActivity parent) {
            super();
            this.parent = parent;
        }

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what){
                case UPDATE_CNT:
                    int c = (int)msg.obj;
                    parent.updateCnt(c);
                    break;
                case UPDATE_MSG:
                    String m = (String)msg.obj;
                    parent.updateMsg(m);
                    break;
                default:
                    super.handleMessage(msg);
            }

        }
    }
}


MyThread.java
package com.blogspot.android_er.androidthread;

import android.os.Looper;
import android.os.Message;

public class MyThread extends Thread{

    private int cnt;
    private boolean running;
    MainActivity.MyHandler mainHandler;

    public MyThread(MainActivity.MyHandler mainHandler) {
        super();
        this.mainHandler = mainHandler;
    }

    public void setRunning(boolean running){
        this.running = running;
    }

    public void msgToThread(String msgin){
        String msgout = new StringBuilder(msgin).reverse().toString();
        mainHandler.sendMessage(
                Message.obtain(mainHandler,
                        MainActivity.MyHandler.UPDATE_MSG, msgout));
    }

    @Override
    public void run() {
        cnt = 0;
        running = true;

        String prompt;
        if(Looper.myLooper() == Looper.getMainLooper()){
            prompt = "myThread run in UI Thread";
        }else{
            prompt = "myThread run in NOT UI Thread";
        }
        mainHandler.sendMessage(
                Message.obtain(mainHandler,
                        MainActivity.MyHandler.UPDATE_MSG, prompt));

        while (running){
            try {
                Thread.sleep(1000);
                mainHandler.sendMessage(
                        Message.obtain(mainHandler,
                                MainActivity.MyHandler.UPDATE_CNT, cnt));

                cnt++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidthread.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" />

    <Button
        android:id="@+id/startthread"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Thread"/>
    <Button
        android:id="@+id/stopthread"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop Thread"/>
    <EditText
        android:id="@+id/msgtosend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="msg to send..." />
    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send msg to thread"/>
    <TextView
        android:id="@+id/cntreceived"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textSize="26dp"/>
    <TextView
        android:id="@+id/msgreceived"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textStyle="bold"
        android:textSize="20dp"/>
</LinearLayout>



download filesDownload the files .

Related:
Android example of using Service and BroadcastReceiver
Example of IntentService and BroadcastReceiver

Android UI Design

Android UI Design
Key Features
  • Take an initial idea for an Android app and develop it into a detailed plan, supported by sketches and wireframes
  • Provide a better experience for your users by following best practices and the new material design principles
  • Work more efficiently and save time by testing your ideas at an early stage by building a prototype
Book Description
Great design is one of the key drivers in the adoption of new applications, yet unfortunately design considerations are often neglected in the face of "will it work," "can we make it quicker," or "can we get more people using it"?

This book seeks to redress this balance by showing you how to get your PM to start treating the design phase of your project seriously. This book is focused entirely on the development of UI features, and you'll be able to practically implementing the design practices that we extol throughout the book.

Starting by briefly outlining some of the factors you need to keep in mind when building a UI, you'll learn the concepts of Android User Interface from scratch. We then move on to formulate a plan on how to implement these concepts in various applications. We will deep dive into how UI features are implemented in real-world applications where UIs are complex and dynamic.

This book offers near complete coverage of UI-specific content including, views, fragments, the wireframing process, and how to add in splash screens-everything you need to make professional standard UIs for modern applications. It will then cover material design and show you how to implement Google's design aesthetic in a practical manner. Finally, it ensures the best possible user experience by analyzing the UI using various tools, and then addressing any problems they uncover.

By the end of the book, you'll be able to leverage the concepts of Android User Interface in your applications in order to attract new customers.

What you will learn
  • Develop a user interface that adheres to all the core material design principles
  • Transform your initial app idea into a concrete and detailed plan
  • Add Views, ViewGroups, layouts, and common UI components to your own Android projects
  • Use fragments and various strategies to gather user input
  • Create a new Android Studio project and develop it into a prototype
  • Identify and solve problems with your app's UI to deliver a better user experience
About the Author
Jessica Thornsby studied poetry, prose, and script writing at Bolton University before discovering the world of open source and technical writing, and has never looked back since. Today, she is a technical writer and full-time Android enthusiast residing in sunny Sheffield, England.

She enjoys writing about rooting and flashing mobile devices, Java, Eclipse, and all things Android and Google. When not wordsmithing about technology and obsessing about the latest Android developments, she keeps a blog about her local food scene, and writes about looking after exotic pets. On the rare occasions that she's dragged away from her computer, she enjoys beer gardens, curry houses, the Great British seaside, scary movies, and spending lots of time with her house rabbits and chinchillas.

Thursday, May 26, 2016

Load WebP from Internet and display in ListView


WebP is a modern image format that provides superior lossless and lossy compression for images on the web. Details refer Google Developers - WebP.

WebP is supported starting from Android 4.0+ (reference: Android Developers - Supported Media Formats). This example modify from the post "Async load image from internet to ListView" to load WebP from internet and display in ListView. Once item clicked, use "Simplest way to open browser using CustomTabsIntent.Builder".


To use CustomTabsIntent.Builder in our app, To use CustomTabsIntent.Builder, edit app/build.gradle to add dependencies of compile 'com.android.support:customtabs:23.0.0'.

The WebP images load from the page Google Developers - WebP Image Galleries.

MainActivity.java
package com.blogspot.android_er.androidimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

    final static String src[] = {
            "https://www.gstatic.com/webp/gallery3/1_webp_ll.webp",
            "https://www.gstatic.com/webp/gallery3/1_webp_a.webp",
            "https://www.gstatic.com/webp/gallery3/2_webp_ll.webp",
            "https://www.gstatic.com/webp/gallery3/2_webp_a.webp",
            "https://www.gstatic.com/webp/gallery3/3_webp_ll.webp",
            "https://www.gstatic.com/webp/gallery3/3_webp_a.webp",
            "https://www.gstatic.com/webp/gallery3/4_webp_ll.webp",
            "https://www.gstatic.com/webp/gallery3/4_webp_a.webp",
            "https://www.gstatic.com/webp/gallery3/5_webp_ll.webp",
            "https://www.gstatic.com/webp/gallery3/5_webp_a.webp" };

    ListView imageList;

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

        imageList = (ListView) findViewById(R.id.imagelist);
        ArrayList<String> srcList = new ArrayList<String>(Arrays.asList(src));
        imageList.setAdapter(new CustomListAdapter(this, srcList));

        imageList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String imageSrc = src[position];
                Toast.makeText(MainActivity.this,
                        imageSrc,
                        Toast.LENGTH_LONG).show();

                Uri imageUri = Uri.parse(imageSrc);
                new CustomTabsIntent.Builder()
                        .build()
                        .launchUrl(MainActivity.this, imageUri);
            }
        });
    }

    // ----------------------------------------------------

    public class CustomListAdapter extends BaseAdapter {
        private ArrayList<String> listData;
        private LayoutInflater layoutInflater;

        public CustomListAdapter(Context context, ArrayList<String> listData) {
            this.listData = listData;
            layoutInflater = LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
            return listData.size();
        }

        @Override
        public Object getItem(int position) {
            return listData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                convertView = layoutInflater.inflate(R.layout.row, null);
                holder = new ViewHolder();
                holder.icon = (ImageView)convertView.findViewById(R.id.icon);
                holder.text = (TextView)convertView.findViewById(R.id.text);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            holder.text.setText(
                    String.valueOf(position) + "\n" + src[position]);

            if (holder.icon != null) {
                new BitmapWorkerTask(holder.icon).execute(listData.get(position));
            }
            return convertView;
        }

        class ViewHolder {
            ImageView icon;
            TextView text;
        }
    }

    // ----------------------------------------------------
    // Load bitmap in AsyncTask
    // ref:
    // http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
        private final WeakReference<ImageView> imageViewReference;
        private String imageUrl;

        public BitmapWorkerTask(ImageView imageView) {
            // Use a WeakReference to ensure the ImageView can be garbage
            // collected
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        // Decode image in background.
        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];
            return LoadImage(imageUrl);
        }

        // Once complete, see if ImageView is still around and set bitmap.
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (imageViewReference != null && bitmap != null) {
                final ImageView imageView = imageViewReference.get();
                if (imageView != null) {
                    imageView.setImageBitmap(bitmap);
                }
            }
        }

        private Bitmap LoadImage(String URL) {
            Bitmap bitmap = null;
            InputStream in = null;
            try {
                in = OpenHttpConnection(URL);
                bitmap = BitmapFactory.decodeStream(in);
                in.close();
            } catch (IOException e1) {
            }
            return bitmap;
        }

        private InputStream OpenHttpConnection(String strURL)
                throws IOException {
            InputStream inputStream = null;
            URL url = new URL(strURL);
            URLConnection conn = url.openConnection();

            try {
                HttpURLConnection httpConn = (HttpURLConnection) conn;
                httpConn.setRequestMethod("GET");
                httpConn.connect();

                if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    inputStream = httpConn.getInputStream();
                }
            } catch (Exception ex) {
            }
            return inputStream;
        }
    }
}


layout/row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

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

</LinearLayout>

layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidimage.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" />

    <ListView
        android:id="@+id/imagelist"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>


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

download filesDownload the files .

Wednesday, May 25, 2016

Example of IntentService and BroadcastReceiver


Last post show example of using Service and BroadcastReceiver, this example show how to do the same job using IntentService and BroadcastReceiver.


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidservice.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" />

    <Button
        android:id="@+id/startservice"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Service"/>
    <EditText
        android:id="@+id/msgtosend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="msg to send..." />
    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send msg to service"/>
    <TextView
        android:id="@+id/cntreceived"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textSize="26dp"/>
    <TextView
        android:id="@+id/msgreceived"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textStyle="bold"
        android:textSize="20dp"/>

</LinearLayout>


MainActivity.java
package com.blogspot.android_er.androidservice;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    Button btnStart, btnSend;
    EditText editTextMsgToSend;
    TextView textViewCntReceived, textViewMsgReceived;

    MyMainReceiver myMainReceiver;
    Intent myIntent = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnStart = (Button)findViewById(R.id.startservice);
        btnSend = (Button)findViewById(R.id.send);
        editTextMsgToSend = (EditText)findViewById(R.id.msgtosend);
        textViewCntReceived = (TextView)findViewById(R.id.cntreceived);
        textViewMsgReceived = (TextView)findViewById(R.id.msgreceived);

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService();
            }
        });

        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String msgToService = editTextMsgToSend.getText().toString();

                Intent intent = new Intent();
                intent.setAction(MyIntentService.ACTION_MSG_TO_SERVICE);
                intent.putExtra(MyIntentService.KEY_MSG_TO_SERVICE, msgToService);
                sendBroadcast(intent);
            }
        });
    }

    private void startService(){
        myIntent = new Intent(MainActivity.this, MyIntentService.class);
        startService(myIntent);
    }

    @Override
    protected void onStart() {
        myMainReceiver = new MyMainReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(MyIntentService.ACTION_UPDATE_CNT);
        intentFilter.addAction(MyIntentService.ACTION_UPDATE_MSG);
        registerReceiver(myMainReceiver, intentFilter);
        super.onStart();
    }

    @Override
    protected void onStop() {
        unregisterReceiver(myMainReceiver);
        super.onStop();
    }

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

    private class MyMainReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(action.equals(MyIntentService.ACTION_UPDATE_CNT)){
                int int_from_service = intent.getIntExtra(MyIntentService.KEY_INT_FROM_SERVICE, 0);
                textViewCntReceived.setText(String.valueOf(int_from_service));
            }else if(action.equals(MyIntentService.ACTION_UPDATE_MSG)){
                String string_from_service = intent.getStringExtra(MyIntentService.KEY_STRING_FROM_SERVICE);
                textViewMsgReceived.setText(String.valueOf(string_from_service));
            }
        }
    }
}


MyIntentService.java
package com.blogspot.android_er.androidservice;

import android.app.IntentService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Looper;
import android.widget.Toast;

public class MyIntentService extends IntentService {

    //from MyIntentService to MainActivity
    final static String KEY_INT_FROM_SERVICE = "KEY_INT_FROM_SERVICE";
    final static String KEY_STRING_FROM_SERVICE = "KEY_STRING_FROM_SERVICE";
    final static String ACTION_UPDATE_CNT = "UPDATE_CNT";
    final static String ACTION_UPDATE_MSG = "UPDATE_MSG";

    //from MainActivity to MyIntentService
    final static String KEY_MSG_TO_SERVICE = "KEY_MSG_TO_SERVICE";
    final static String ACTION_MSG_TO_SERVICE = "MSG_TO_SERVICE";

    MyServiceReceiver myServiceReceiver;
    int cnt;

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    public void onCreate() {
        Toast.makeText(getApplicationContext(),
                "onCreate", Toast.LENGTH_LONG).show();
        myServiceReceiver = new MyServiceReceiver();
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(getApplicationContext(),
                "onStartCommand", Toast.LENGTH_LONG).show();

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_MSG_TO_SERVICE);
        registerReceiver(myServiceReceiver, intentFilter);

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Toast.makeText(getApplicationContext(),
                "onDestroy", Toast.LENGTH_LONG).show();
        unregisterReceiver(myServiceReceiver);
        super.onDestroy();
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        String prompt;
        //check if current thread is Main Thread (UI)
        if(Looper.myLooper() == Looper.getMainLooper()){
            prompt = "onHandleIntent run in UI Thread";
        }else{
            prompt = "onHandleIntent run in NOT UI Thread";
        }

        Intent iPrompt = new Intent();
        iPrompt.setAction(ACTION_UPDATE_MSG);
        iPrompt.putExtra(KEY_STRING_FROM_SERVICE, prompt);
        sendBroadcast(iPrompt);

        cnt = 10;
        while (cnt >= 0){
            try {
                Thread.sleep(1000);

                Intent i = new Intent();
                i.setAction(ACTION_UPDATE_CNT);
                i.putExtra(KEY_INT_FROM_SERVICE, cnt);
                sendBroadcast(i);

                cnt--;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public class MyServiceReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();
            if(action.equals(ACTION_MSG_TO_SERVICE)){
                String msg = intent.getStringExtra(KEY_MSG_TO_SERVICE);

                msg = new StringBuilder(msg).reverse().toString();

                //send back to MainActivity
                Intent i = new Intent();
                i.setAction(ACTION_UPDATE_MSG);
                i.putExtra(KEY_STRING_FROM_SERVICE, msg);
                sendBroadcast(i);
            }
        }
    }

}


Modify AndroidManifest.xml to add <service> of ".MyIntentService".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidservice">

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

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

</manifest>


download filesDownload the files .

Related:
Android example of using Thread and Handler

Tuesday, May 24, 2016

Android example of using Service and BroadcastReceiver


This example show how to using Service to run long-time task in background thread, and also implement BroadcastReceiver to make communication between Activity and Service.


Reference: Android Developers Guide - Services

If a component starts the service by calling startService() (which results in a call to onStartCommand()), then the service remains running until it stops itself with stopSelf() or another component stops it by calling stopService().

If a component calls bindService() to create the service (and onStartCommand() is not called), then the service runs only as long as the component is bound to it. Once the service is unbound from all clients, the system destroys it.

The Android system will force-stop a service only when memory is low and it must recover system resources for the activity that has user focus. If the service is bound to an activity that has user focus, then it's less likely to be killed, and if the service is declared to run in the foreground (discussed later), then it will almost never be killed. Otherwise, if the service was started and is long-running, then the system will lower its position in the list of background tasks over time and the service will become highly susceptible to killing—if your service is started, then you must design it to gracefully handle restarts by the system. If the system kills your service, it restarts it as soon as resources become available again (though this also depends on the value you return from onStartCommand(), as discussed later). For more information about when the system might destroy a service, see the Processes and Threading document.



layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidservice.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" />

    <Button
        android:id="@+id/startservice"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start Service"/>
    <Button
        android:id="@+id/stopservice"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Stop Service"/>
    <EditText
        android:id="@+id/msgtosend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="msg to send..." />
    <Button
        android:id="@+id/send"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send msg to service"/>
    <TextView
        android:id="@+id/cntreceived"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textSize="26dp"/>
    <TextView
        android:id="@+id/msgreceived"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textStyle="bold"
        android:textSize="20dp"/>

</LinearLayout>


MainActivity.java
package com.blogspot.android_er.androidservice;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    Button btnStart, btnStop, btnSend;
    EditText editTextMsgToSend;
    TextView textViewCntReceived, textViewMsgReceived;

    MyMainReceiver myMainReceiver;
    Intent myIntent = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnStart = (Button)findViewById(R.id.startservice);
        btnStop = (Button)findViewById(R.id.stopservice);
        btnSend = (Button)findViewById(R.id.send);
        editTextMsgToSend = (EditText)findViewById(R.id.msgtosend);
        textViewCntReceived = (TextView)findViewById(R.id.cntreceived);
        textViewMsgReceived = (TextView)findViewById(R.id.msgreceived);

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startService();
            }
        });

        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopService();
            }
        });

        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String msgToService = editTextMsgToSend.getText().toString();

                Intent intent = new Intent();
                intent.setAction(MyService.ACTION_MSG_TO_SERVICE);
                intent.putExtra(MyService.KEY_MSG_TO_SERVICE, msgToService);
                sendBroadcast(intent);
            }
        });
    }

    private void startService(){
        myIntent = new Intent(MainActivity.this, MyService.class);
        startService(myIntent);
    }

    private void stopService(){
        if(myIntent != null){
            stopService(myIntent);
        }
        myIntent = null;
    }

    @Override
    protected void onStart() {
        myMainReceiver = new MyMainReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(MyService.ACTION_UPDATE_CNT);
        intentFilter.addAction(MyService.ACTION_UPDATE_MSG);
        registerReceiver(myMainReceiver, intentFilter);
        super.onStart();
    }

    @Override
    protected void onStop() {
        unregisterReceiver(myMainReceiver);
        super.onStop();
    }

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

    private class MyMainReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(action.equals(MyService.ACTION_UPDATE_CNT)){
                int int_from_service = intent.getIntExtra(MyService.KEY_INT_FROM_SERVICE, 0);
                textViewCntReceived.setText(String.valueOf(int_from_service));
            }else if(action.equals(MyService.ACTION_UPDATE_MSG)){
                String string_from_service = intent.getStringExtra(MyService.KEY_STRING_FROM_SERVICE);
                textViewMsgReceived.setText(String.valueOf(string_from_service));
            }
        }
    }
}


MyService.java
package com.blogspot.android_er.androidservice;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.widget.Toast;

public class MyService extends Service {

    //from MyService to MainActivity
    final static String KEY_INT_FROM_SERVICE = "KEY_INT_FROM_SERVICE";
    final static String KEY_STRING_FROM_SERVICE = "KEY_STRING_FROM_SERVICE";
    final static String ACTION_UPDATE_CNT = "UPDATE_CNT";
    final static String ACTION_UPDATE_MSG = "UPDATE_MSG";

    //from MainActivity to MyService
    final static String KEY_MSG_TO_SERVICE = "KEY_MSG_TO_SERVICE";
    final static String ACTION_MSG_TO_SERVICE = "MSG_TO_SERVICE";

    MyServiceReceiver myServiceReceiver;
    MyServiceThread myServiceThread;
    int cnt;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        Toast.makeText(getApplicationContext(),
                "onCreate", Toast.LENGTH_LONG).show();
        myServiceReceiver = new MyServiceReceiver();
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(getApplicationContext(),
                "onStartCommand", Toast.LENGTH_LONG).show();

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_MSG_TO_SERVICE);
        registerReceiver(myServiceReceiver, intentFilter);

        myServiceThread = new MyServiceThread();
        myServiceThread.start();

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Toast.makeText(getApplicationContext(),
                "onDestroy", Toast.LENGTH_LONG).show();
        myServiceThread.setRunning(false);
        unregisterReceiver(myServiceReceiver);
        super.onDestroy();
    }

    public class MyServiceReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();
            if(action.equals(ACTION_MSG_TO_SERVICE)){
                String msg = intent.getStringExtra(KEY_MSG_TO_SERVICE);

                msg = new StringBuilder(msg).reverse().toString();

                //send back to MainActivity
                Intent i = new Intent();
                i.setAction(ACTION_UPDATE_MSG);
                i.putExtra(KEY_STRING_FROM_SERVICE, msg);
                sendBroadcast(i);
            }
        }
    }

    private class MyServiceThread extends Thread{

        private boolean running;

        public void setRunning(boolean running){
            this.running = running;
        }

        @Override
        public void run() {
            cnt = 0;
            running = true;
            while (running){
                try {
                    Thread.sleep(1000);

                    Intent intent = new Intent();
                    intent.setAction(ACTION_UPDATE_CNT);
                    intent.putExtra(KEY_INT_FROM_SERVICE, cnt);
                    sendBroadcast(intent);

                    cnt++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


Modify AndroidManifest.xml to add <service> of ".MyService".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidservice">

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

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

</manifest>


download filesDownload the files .

Next:
Example of IntentService and BroadcastReceiver

Related:
Android example of using Thread and Handler

Monday, May 23, 2016

Android client to send message to Java server on Raspberry Pi


It's a Java client and server exercise in my another blogspot HelloraspberryPi. The client connect to server and send something, the server simple print the received message and close.

It's Android version of the client, to connect to server and send something.


MainActivity.java
package com.blogspot.android_er.androidclient;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class MainActivity extends AppCompatActivity {

    EditText editTextAddress, editTextPort, editTextMsg;
    Button buttonConnect;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editTextAddress = (EditText) findViewById(R.id.address);
        editTextPort = (EditText) findViewById(R.id.port);
        editTextMsg = (EditText) findViewById(R.id.msgtosend);
        buttonConnect = (Button) findViewById(R.id.connect);

        buttonConnect.setOnClickListener(buttonConnectOnClickListener);
    }

    View.OnClickListener buttonConnectOnClickListener =
            new View.OnClickListener() {

                @Override
                public void onClick(View arg0) {
                    MyClientTask myClientTask = new MyClientTask(
                            editTextAddress.getText().toString(),
                            Integer.parseInt(editTextPort.getText().toString()));
                    myClientTask.execute(editTextMsg.getText().toString());
                }
            };

    public class MyClientTask extends AsyncTask<String, Void, Void> {

        String dstAddress;
        int dstPort;
        String response;

        MyClientTask(String addr, int port) {
            dstAddress = addr;
            dstPort = port;
        }

        @Override
        protected Void doInBackground(String... params) {
            try {
                Socket socket = new Socket(dstAddress, dstPort);

                OutputStream outputStream = socket.getOutputStream();
                PrintStream printStream = new PrintStream(outputStream);
                printStream.print(params[0]);

                socket.close();

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

    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidclient.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" />
    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="dstAddress" />
    <EditText
        android:id="@+id/port"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="dstPort" />
    <EditText
        android:id="@+id/msgtosend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="msg to send..." />
    <Button
        android:id="@+id/connect"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Connect and send..."/>

</LinearLayout>


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

The Java code of the server side, refer to "Java Network exercise: client and server - client send something to server".

The next example "Android client example 2, communicate with Java Server run on Raspberry Pi" show how Android Client connect to Java Server (run on raspberry Pi) and keep connected, and send to and receive from Server.

Parse blogspot JSON feed: detect item clicked to open in browser using CustomTabsIntent


Last post show a example to "Download and parse blogspot JSON feed". In this post, OnItemClickListener is added to the ListView; for user clicking to open the url in browser, using "Simplest way to open browser using CustomTabsIntent.Builder".


To use CustomTabsIntent.Builder() in your project, you have to edit appbuild.gradle to add dependencies of compile 'com.android.support:customtabs:23.0.0'.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:customtabs:23.0.0'
}


Edit MainActivity.java to add OnItemClickListener.
package com.blogspot.android_er.androidparsejson;

import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    Button btnLoadFeed;
    TextView textViewFeedUrl;
    ListView listViewFeed;

    List<FeedItem> listFeedItems;
    ListAdapter adapterFeed;

    String myFeed = "http://android-er.blogspot.com/feeds/posts/default?alt=json";
    //String myFeed = "http://arduino-er.blogspot.com/feeds/posts/default?alt=json";
    //String myFeed = "http://helloraspberrypi.blogspot.com/feeds/posts/default?alt=json";
    //String myFeed = "http://photo-er.blogspot.com/feeds/posts/default?alt=json";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnLoadFeed = (Button)findViewById(R.id.loadfeed);
        textViewFeedUrl = (TextView)findViewById(R.id.feedurl);
        listViewFeed = (ListView)findViewById(R.id.listviewfeed);

        listFeedItems = new ArrayList<>();
        adapterFeed = new ArrayAdapter<FeedItem>(
                this, android.R.layout.simple_list_item_1, listFeedItems);
        listViewFeed.setAdapter(adapterFeed);
        listViewFeed.setOnItemClickListener(listViewFeedOnItemClickListener);

        btnLoadFeed.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textViewFeedUrl.setText(myFeed);
                new JsonTask(listFeedItems, listViewFeed).execute(myFeed);
            }
        });
    }

    AdapterView.OnItemClickListener listViewFeedOnItemClickListener =
            new AdapterView.OnItemClickListener(){
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    FeedItem clickedFeedItem = (FeedItem) parent.getItemAtPosition(position);
                    String url = clickedFeedItem.getUrl();
                    Uri uri = Uri.parse(url);
                    new CustomTabsIntent.Builder()
                            .build()
                            .launchUrl(MainActivity.this, uri);
                }
            };

    /*
    JsonTask:
    AsyncTask to download and parse JSON Feed of blogspot in background
     */
    private class JsonTask extends AsyncTask<String, FeedItem, String> {

        List<FeedItem> jsonTaskList;
        ListView jsonTaskListView;

        public JsonTask(List<FeedItem> targetList, ListView targetListView) {
            super();
            jsonTaskList = targetList;
            jsonTaskListView = targetListView;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            jsonTaskList.clear();
            jsonTaskListView.invalidateViews();
        }

        @Override
        protected String doInBackground(String... params) {

            try {
                final String queryResult = sendQuery(params[0]);
                parseQueryResult(queryResult);
            } catch (IOException e) {
                e.printStackTrace();

                final String eString = e.toString();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this,
                                eString,
                                Toast.LENGTH_LONG).show();
                    }
                });

            } catch (JSONException e) {
                e.printStackTrace();

                final String eString = e.toString();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this,
                                eString,
                                Toast.LENGTH_LONG).show();
                    }
                });
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(FeedItem... values) {
            FeedItem newItem = values[0];
            jsonTaskList.add(newItem);
            jsonTaskListView.invalidateViews();
        }

        private String sendQuery(String query) throws IOException {
            String queryReturn = "";
            URL queryURL = new URL(query);

            HttpURLConnection httpURLConnection = (HttpURLConnection)queryURL.openConnection();

            if(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
                InputStreamReader inputStreamReader =
                        new InputStreamReader(httpURLConnection.getInputStream());
                BufferedReader bufferedReader = new BufferedReader(
                        inputStreamReader, 8192);
                String line = null;
                while((line = bufferedReader.readLine()) != null){
                    queryReturn += line;
                }

                bufferedReader.close();
            }


            return queryReturn;
        }

        private void parseQueryResult(String json) throws JSONException {
            JSONObject jsonObject = new JSONObject(json);
            final JSONObject jsonObject_feed = jsonObject.getJSONObject("feed");
            final JSONArray jsonArray_entry = jsonObject_feed.getJSONArray("entry");

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(jsonArray_entry == null){
                        Toast.makeText(MainActivity.this,
                                "jsonArray_entry == null",
                                Toast.LENGTH_LONG).show();
                    }else{
                        Toast.makeText(MainActivity.this,
                                String.valueOf(jsonArray_entry.length()),
                                Toast.LENGTH_LONG).show();
                        for(int i=0; i<jsonArray_entry.length(); i++){
                            try {
                                JSONObject thisEntry = (JSONObject) jsonArray_entry.get(i);
                                JSONObject thisEntryTitle = thisEntry.getJSONObject("title");
                                String thisEntryTitleString = thisEntryTitle.getString("$t");

                                JSONArray jsonArray_EntryLink = thisEntry.getJSONArray("link");

                                //search for the link element with rel="alternate"
                                //I assume it's one and only one element with rel="alternate",
                                //and its href hold the link to the page
                                for(int j=0; j<jsonArray_EntryLink.length(); j++){
                                    JSONObject thisLink = (JSONObject) jsonArray_EntryLink.get(j);
                                    try{
                                        String thisLinkRel = thisLink.getString("rel");
                                        if(thisLinkRel.equals("alternate")){
                                            try{
                                                String thisLinkHref = thisLink.getString("href");
                                                FeedItem thisElement = new FeedItem(
                                                        thisEntryTitleString.toString(),
                                                        thisLinkHref);
                                                publishProgress(thisElement);
                                                break;
                                            }catch (JSONException e){
                                                //no such mapping exists
                                            }
                                        }
                                    }catch (JSONException e){
                                        //no such mapping exists
                                    }

                                }

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }

                        }
                    }

                }
            });
        }
    }
}


Other files, FeedItem.java, activity_main.xml and AndroidManifest.xml, refer last post.

Sunday, May 22, 2016

Download and parse blogspot JSON feed

This example show how to download and parse blogspot JSON feed.


The url of my feed is http://android-er.blogspot.com/feeds/posts/default?alt=json.

Actually I don't know the format of blogspot json feed, I use Online JSON Viewer (http://jsonviewer.stack.hu/) to guess. This video show how:


In my example, I download and parse the json feed in AsyncTask, Only post title and url retrieved, and display as String in ListView.


Create a new class FeedItem.java
package com.blogspot.android_er.androidparsejson;

public class FeedItem {
    private String title;
    private String url;

    FeedItem(String title, String url){
        this.title = title;
        this.url = url;
    }

    public String getTitle(){
        return title;
    }

    public String getUrl(){
        return url;
    }

    @Override
    public String toString() {
        return title + " :\n" + url;
    }
}


MainActivity.java
package com.blogspot.android_er.androidparsejson;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    Button btnLoadFeed;
    TextView textViewFeedUrl;
    ListView listViewFeed;

    List<FeedItem> listFeedItems;
    ListAdapter adapterFeed;

    String myFeed = "http://android-er.blogspot.com/feeds/posts/default?alt=json";
    //String myFeed = "http://arduino-er.blogspot.com/feeds/posts/default?alt=json";
    //String myFeed = "http://helloraspberrypi.blogspot.com/feeds/posts/default?alt=json";
    //String myFeed = "http://photo-er.blogspot.com/feeds/posts/default?alt=json";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnLoadFeed = (Button)findViewById(R.id.loadfeed);
        textViewFeedUrl = (TextView)findViewById(R.id.feedurl);
        listViewFeed = (ListView)findViewById(R.id.listviewfeed);

        listFeedItems = new ArrayList<>();
        adapterFeed = new ArrayAdapter<FeedItem>(
                this, android.R.layout.simple_list_item_1, listFeedItems);
        listViewFeed.setAdapter(adapterFeed);

        btnLoadFeed.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textViewFeedUrl.setText(myFeed);
                new JsonTask(listFeedItems, listViewFeed).execute(myFeed);
            }
        });
    }

    /*
    JsonTask:
    AsyncTask to download and parse JSON Feed of blogspot in background
     */
    private class JsonTask extends AsyncTask<String, FeedItem, String> {

        List<FeedItem> jsonTaskList;
        ListView jsonTaskListView;

        public JsonTask(List<FeedItem> targetList, ListView targetListView) {
            super();
            jsonTaskList = targetList;
            jsonTaskListView = targetListView;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            jsonTaskList.clear();
            jsonTaskListView.invalidateViews();
        }

        @Override
        protected String doInBackground(String... params) {

            try {
                final String queryResult = sendQuery(params[0]);
                parseQueryResult(queryResult);
            } catch (IOException e) {
                e.printStackTrace();

                final String eString = e.toString();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this,
                                eString,
                                Toast.LENGTH_LONG).show();
                    }
                });

            } catch (JSONException e) {
                e.printStackTrace();

                final String eString = e.toString();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this,
                                eString,
                                Toast.LENGTH_LONG).show();
                    }
                });
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(FeedItem... values) {
            FeedItem newItem = values[0];
            jsonTaskList.add(newItem);
            jsonTaskListView.invalidateViews();
        }

        private String sendQuery(String query) throws IOException {
            String queryReturn = "";
            URL queryURL = new URL(query);

            HttpURLConnection httpURLConnection = (HttpURLConnection)queryURL.openConnection();

            if(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
                InputStreamReader inputStreamReader =
                        new InputStreamReader(httpURLConnection.getInputStream());
                BufferedReader bufferedReader = new BufferedReader(
                        inputStreamReader, 8192);
                String line = null;
                while((line = bufferedReader.readLine()) != null){
                    queryReturn += line;
                }

                bufferedReader.close();
            }


            return queryReturn;
        }

        private void parseQueryResult(String json) throws JSONException {
            JSONObject jsonObject = new JSONObject(json);
            final JSONObject jsonObject_feed = jsonObject.getJSONObject("feed");
            final JSONArray jsonArray_entry = jsonObject_feed.getJSONArray("entry");

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(jsonArray_entry == null){
                        Toast.makeText(MainActivity.this,
                                "jsonArray_entry == null",
                                Toast.LENGTH_LONG).show();
                    }else{
                        Toast.makeText(MainActivity.this,
                                String.valueOf(jsonArray_entry.length()),
                                Toast.LENGTH_LONG).show();
                        for(int i=0; i<jsonArray_entry.length(); i++){
                            try {
                                JSONObject thisEntry = (JSONObject) jsonArray_entry.get(i);
                                JSONObject thisEntryTitle = thisEntry.getJSONObject("title");
                                String thisEntryTitleString = thisEntryTitle.getString("$t");

                                JSONArray jsonArray_EntryLink = thisEntry.getJSONArray("link");

                                //search for the link element with rel="alternate"
                                //I assume it's one and only one element with rel="alternate",
                                //and its href hold the link to the page
                                for(int j=0; j<jsonArray_EntryLink.length(); j++){
                                    JSONObject thisLink = (JSONObject) jsonArray_EntryLink.get(j);
                                    try{
                                        String thisLinkRel = thisLink.getString("rel");
                                        if(thisLinkRel.equals("alternate")){
                                            try{
                                                String thisLinkHref = thisLink.getString("href");
                                                FeedItem thisElement = new FeedItem(
                                                        thisEntryTitleString.toString(),
                                                        thisLinkHref);
                                                publishProgress(thisElement);
                                                break;
                                            }catch (JSONException e){
                                                //no such mapping exists
                                            }
                                        }
                                    }catch (JSONException e){
                                        //no such mapping exists
                                    }

                                }
                                
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }

                        }
                    }

                }
            });
        }
    }
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidparsejson.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" />

    <Button
        android:id="@+id/loadfeed"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Load Feed"/>
    <TextView
        android:id="@+id/feedurl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold|italic"/>
    <ListView
        android:id="@+id/listviewfeed"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>


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


download filesDownload the files .

Next:
Parse blogspot JSON feed: detect item clicked to open in browser using CustomTabsIntent


Remark@2017-05-20:
This video show how I TRY to locate the thumbnail from the JSON feed. In order to find the url of the thumbnail of the posts, you have to search "media$thumbnail" elements in the feeds. But I haven't actually implemented the code.


Saturday, May 21, 2016

Simplest way to open browser using CustomTabsIntent.Builder


This post show the simplest way to open browser using CustomTabsIntent.Builder.


To use CustomTabsIntent.Builder, edit app/build.gradle to add dependencies of compile 'com.android.support:customtabs:23.0.0'.


MainActivity.java
package com.blogspot.android_er.androidcustomtabsintent;

import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    Button btnLaunch;
    Uri uriMyBlog = Uri.parse("http://android-er.blogspot.com");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnLaunch = (Button)findViewById(R.id.launch);
        btnLaunch.setOnClickListener(btnLaunchOnClickListener);
    }

    View.OnClickListener btnLaunchOnClickListener = new View.OnClickListener(){

        @Override
        public void onClick(View v) {
            new CustomTabsIntent.Builder()
                    .build()
                    .launchUrl(MainActivity.this, uriMyBlog);

        }
    };
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidcustomtabsintent.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" />

    <Button
        android:id="@+id/launch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Launch"/>
</LinearLayout>


Friday, May 20, 2016

SwipeRefreshLayout, work with RecyclerView


Last two post show "Simple example of using SwipeRefreshLayout" and "SwipeRefreshLayout, refresh in background thread", target to ListView.

This example show how SwipeRefreshLayout work with RecyclerView (reference: step-by-step of using RecyclerView).


To use RecyclerView in your Android Studio project, you have to Add Support Libraries of RecyclerView as dependencies.


Create layout/layout_item.xml, to define the layout of RecyclerView item.
<?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="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/item_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="28dp"/>

</LinearLayout>

Create a new class, RecyclerViewAdapter.java.
package com.blogspot.android_er.androidswiperefresh;

import android.content.Context;
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> itemsList;
    private OnItemClickListener onItemClickListener;
    private LayoutInflater layoutInflater;

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

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

    @Override
    public void onBindViewHolder(ItemHolder holder, int position) {
        holder.setItemText(itemsList.get(position));
    }

    @Override
    public int getItemCount() {
        return itemsList.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 iString){
        itemsList.add(location, iString);
        notifyItemInserted(location);
    }

    public void set(int location, String iString){
        itemsList.set(location, iString);
        notifyItemChanged(location);
    }

    public void clear(){
        itemsList.clear();
        notifyDataSetChanged();
    }

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

        private RecyclerViewAdapter parent;
        TextView textItemText;

        public ItemHolder(View itemView, RecyclerViewAdapter parent) {
            super(itemView);
            itemView.setOnClickListener(this);
            this.parent = parent;
            textItemText = (TextView) itemView.findViewById(R.id.item_text);
        }

        public void setItemText(CharSequence itemString){
            textItemText.setText(itemString);
        }

        public CharSequence getItemText(){
            return textItemText.getText();
        }

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


Modify layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context="com.blogspot.android_er.androidswiperefresh.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" />

    <Button
        android:id="@+id/clearall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Clear All"/>

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

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

    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>


MainActivity.java
package com.blogspot.android_er.androidswiperefresh;

import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.text.DateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity implements RecyclerViewAdapter.OnItemClickListener{

    SwipeRefreshLayout swipeRefreshLayout;
    Button btnClearAll;

    private RecyclerView myRecyclerView;
    private LinearLayoutManager linearLayoutManager;
    private RecyclerViewAdapter myRecyclerViewAdapter;

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

        swipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.swiperefreshlayout);
        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refresh();
            }
        });
        btnClearAll = (Button)findViewById(R.id.clearall);
        btnClearAll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //In order to prevent the racing condition of updating removed item in refreshing,
                //disable "Clear All" if refreshing
                if(!swipeRefreshLayout.isRefreshing()){
                    myRecyclerViewAdapter.clear();
                }
            }
        });

        myRecyclerView = (RecyclerView)findViewById(R.id.myrecyclerview);
        linearLayoutManager =
                new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        myRecyclerViewAdapter = new RecyclerViewAdapter(this);
        myRecyclerViewAdapter.setOnItemClickListener(this);
        myRecyclerView.setAdapter(myRecyclerViewAdapter);
        myRecyclerView.setLayoutManager(linearLayoutManager);

    }

    private void refresh(){

        final int pos = myRecyclerViewAdapter.getItemCount();
        myRecyclerViewAdapter.add(pos, "Refreshing...");
        swipeRefreshLayout.setRefreshing(true);

        //refresh long-time task in background thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //dummy delay for 2 second
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //update ui on UI thread
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        String currentDateTime =
                                DateFormat.getDateTimeInstance().format(new Date());

                        myRecyclerViewAdapter.set(pos, pos + " - " + currentDateTime);
                        swipeRefreshLayout.setRefreshing(false);
                    }
                });

            }
        }).start();
    }

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



download filesDownload the files .