Monthly Archives: June 2015

Android – IoT – Bluetooth Client

Now for the client app.

This will allow you to connect to the device running the Bluetooth Server.

I am keeping this example very very simple, since we are connecting securely you will need to connect the server and client together manually by going to the bluetooth settings in both devices and bind them manually. Once you do this once, you won’t need to do it again.

MainAcivity.xml

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;


public class MainActivity extends ActionBarActivity {
    private BluetoothAdapter mAdapter;
    private ConnectionService mConnection;

    private static final int REQUEST_ENABLE_BT = 1;
    private static final int REQUEST_CONNECT_DEVICE_SECURE = 2;

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

        mAdapter = BluetoothAdapter.getDefaultAdapter();

        if (mAdapter == null){
            Toast.makeText(this,"Bluetooth not available",Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

    }

    @Override
    protected void onStart(){
        super.onStart();
        if (!mAdapter.isEnabled()){
            Intent enablIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enablIntent,REQUEST_ENABLE_BT);
        } else {
            if (mConnection == null){
                mConnection = new ConnectionService();
            }
        }
    }

    public void onActivityResult(int requestCode, int resultCode,Intent intent){
        switch (requestCode){
            case REQUEST_ENABLE_BT:
                if (resultCode == Activity.RESULT_OK){
                    mConnection = new ConnectionService();
                } else {
                    Toast.makeText(this,"Bluetooth Not Enabled, shutting down...",Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            case REQUEST_CONNECT_DEVICE_SECURE:
                if (resultCode == Activity.RESULT_OK){
                    connectDevice(intent);
                }
        }
    }

    private void connectDevice(Intent intent){
        String address = intent.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);

        BluetoothDevice device = mAdapter.getRemoteDevice(address);
        Toast.makeText(this,"Going to address" + address,Toast.LENGTH_SHORT).show();
        mConnection.connect(device);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_connect) {
            Intent serverIntent = new Intent(MainActivity.this,DeviceListActivity.class);
            startActivityForResult(serverIntent,REQUEST_CONNECT_DEVICE_SECURE);
            return true;
        }


        return super.onOptionsItemSelected(item);
    }

    public void onClick(View view){
        String message = "camera";
        byte[] send = message.getBytes();
        mConnection.write(send);
    }

    public void onToast(View view){
        String message = "toast";
        byte[] send = message.getBytes();
        mConnection.write(send);
    }

}

Next is the DeviceListActivity.class, this finds all of the available bluetooth devices and is launched when you select Connect from your options menu button.

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import java.util.Set;

public class DeviceListActivity extends Activity {

    public static String EXTRA_DEVICE_ADDRESS = "device_address";

    private BluetoothAdapter mAdapter;

    private ArrayAdapter<String> mPairedDevicesArrayAdapter;
    private ArrayAdapter<String> mNewDevicesArrayAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.device_list);

        setResult(Activity.RESULT_CANCELED);
        //Button for doDiscovery method
        Button scanButton = (Button)findViewById(R.id.button_scan);
        scanButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                doDiscovery();
                view.setVisibility(View.GONE); //HIDE SCAN BUTTON
            }
        });

        mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this,R.layout.device_name);
        mNewDevicesArrayAdapter = new ArrayAdapter<String>(this,R.layout.device_name);

        ListView pairedListView = (ListView)findViewById(R.id.paired_devices);
        pairedListView.setAdapter(mPairedDevicesArrayAdapter);
        pairedListView.setOnItemClickListener(mDeviceClickListener);

        ListView newDeviceListView = (ListView)findViewById(R.id.new_devices);
        newDeviceListView.setAdapter(mNewDevicesArrayAdapter);
        newDeviceListView.setOnItemClickListener(mDeviceClickListener);


        // Intent Filter creation
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        this.registerReceiver(mReceiver, filter);

        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        this.registerReceiver(mReceiver, filter);

        mAdapter = BluetoothAdapter.getDefaultAdapter();

        Set<BluetoothDevice> pairedDevices = mAdapter.getBondedDevices();

        if (pairedDevices.size() > 0){
            findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
            for (BluetoothDevice device : pairedDevices) {
                mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
            }

        } else {
            String noDevices = getResources().getText(R.string.none_paired).toString();
            mPairedDevicesArrayAdapter.add(noDevices);
        }
    }

    private void doDiscovery(){
        setProgressBarIndeterminate(true);
        setTitle(R.string.scanning);

        findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);

        if (mAdapter.isDiscovering()){
            mAdapter.cancelDiscovery();
        }

        mAdapter.startDiscovery();
    }

    private AdapterView.OnItemClickListener mDeviceClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            mAdapter.cancelDiscovery();
            String info = ((TextView) view).getText().toString();
            String address = info.substring(info.length()- 17);

            Intent intent = new Intent();
            intent.putExtra(EXTRA_DEVICE_ADDRESS,address);
            setResult(Activity.RESULT_OK, intent);
            finish();
        }
    };

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)){
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device.getBondState() != BluetoothDevice.BOND_BONDED){
                    mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
                setProgressBarIndeterminateVisibility(false);
                setTitle(R.string.select_device);
                if (mNewDevicesArrayAdapter.getCount() == 0){
                    String noDevices = getResources().getText(R.string.none_found).toString();
                    mNewDevicesArrayAdapter.add(noDevices);
                }
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mAdapter != null){
            mAdapter.cancelDiscovery();
        }

        this.unregisterReceiver(mReceiver);
    }
}

The ConnectionService.class that is used to connect to the server, and is similar to the server one, but only contains code necessary for the client.


import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;

import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;

/**
 * Created by frank on 6/19/15.
 */
public class ConnectionService {
    private static final UUID MY_UUID_SECURE = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;
    private BluetoothSocket mmSocket;

    private BluetoothAdapter mAdapter;

    public ConnectionService(){
        mAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    /**
     * Stop all threads
     */
    public synchronized void stop() {

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

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

    }

    public void write (byte[] out){
        ConnectedThread r = mConnectedThread;
        r.write(out);
    }

    public void connect(BluetoothDevice device){
        mConnectThread = new ConnectThread(device);
        mConnectThread.start();
    }
    private class ConnectThread extends Thread {

        public ConnectThread(BluetoothDevice device){

            try{
                mmSocket = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
            } catch (IOException e){e.printStackTrace(); }
        }
        public void run(){
            mAdapter.cancelDiscovery();

            try{
                mmSocket.connect();
            } catch (IOException e) {
                try {
                    mmSocket.close();
                } catch (IOException e2) {
                    e2.printStackTrace();
                }
                return;
            }
            mConnectThread = null;
            connected(mmSocket);
        }

        public void cancel(){
            try{
                mmSocket.close();
            } catch (IOException e){e.printStackTrace();
            }
        }

    }

    public void connected(BluetoothSocket socket){
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
    }

    private class ConnectedThread extends Thread {
        private OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            try {
                mmOutStream = socket.getOutputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        private void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
            } catch (IOException e){ e.printStackTrace(); }
        }

        public void cancel(){
            try {
                mmSocket.close();
            } catch (IOException e){ e.printStackTrace();

            }
        }
    }
}

The buttons for your main.xml or activity_main.xml

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

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Camera" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="onToast"
        android:text="Toast" />
    </LinearLayout>

And the menu layout for the settings / options menu button.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
    <item android:id="@+id/action_connect" android:title="@string/action_settings"
        android:orderInCategory="100" app:showAsAction="never" />
</menu>

So first make sure bluetooth is on on both devices then from your devices bluetooth options sync your devices so they “know” each other. Then run the server app on the server device and the client app on the client device. Then on the client device click on your option / settings menu and your server device should show up on the list of devices, select the server and your devices will be connected. Now you will need to have the server code modified to run applications that you know what the ComponentName path’s of the apps are. These were located in the Handler section of the server code located in the previous post.

You may also want to change the button text to reflect what apps of yours you are running.

android:text="Camera"

Android – IoT – Bluetooth Server

Updated: 08/04/2015: Previous code had issues reconnecting after the client disconnected without closing and reopening the server. Code automatically restarts listener (server socket) when an error is encountered (IO Exception) which also happens when the client disconnects. This is how the code is supposed to work, however restarting everything from the ConnectionService did not work. Sending a message by use of a handler back to the MainActivity and then restarting from there fixed the issue. I also updated configuration of the handler to a newer “preferred” way which removes the chance of memory leaks using implements Handler.Callback.

In this post I will show you how to connect two devices and use one device to run apps remotely on the other device.

First the server that will listen for a connection and perform actions through intents when certain messages are received.

This uses sections from Google’s Bluetooth Chat example also, but I separated it out into a server piece and a client piece and pull out all the extra stuff Google threw in that just confused the heck out of me when I was trying to learn just the basics of what you need to connect via Bluetooth. No synchronizing of methods for use in multi-threaded apps, no Handlers returning messages to the same person who typed the message for aesthetics. So if you ever had trouble with the same thing this post will also be very helpful.

So first the Server app:

We will have three classes in my example, I include a WakeLocker class that you can omit by leaving out 2 lines of code and a permission in the AndroidMainfest.xml file which I will point out. I include this class so I can leave the device running and have it accept commands at any time without worrying about the device turning off.

The other two classes are the MainActivity which acts upon messages sent from the client and it also calls the other class ConnectionService that accepts Bluetooth connections from the client and passes the messages sent from the client to the MainActivity.

MainAcivity.xml


import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.ComponentName;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.WindowManager;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements Handler.Callback {

    private static final int REQUEST_ENABLE_BT = 1;
    public static final int MESSAGE_READ = 2;
    public static final int MESSAGE_RESET = 3;
    private final static int cameraData = 460;

    private ConnectionService mConnection = null;

    private BluetoothAdapter mBluetoothAdapter = null;
    private final Handler mHandler = new Handler(this);



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

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        WakeLocker.acquire(this);
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        if (mBluetoothAdapter == null){
            Toast.makeText(this,"Bluetooth is not available", Toast.LENGTH_SHORT).show();
            finish();
            return;
        }
    }

    @Override
    public void onStart(){
        super.onStart();
        if (!mBluetoothAdapter.isEnabled()){
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
        } else {
            if (mConnection == null)
                mConnection = new ConnectionService(mHandler);
        }
    }

    public void onActivityResult(int requestCode, int resultCode,Intent intent){
        switch (requestCode){
            case REQUEST_ENABLE_BT:
                if (resultCode == Activity.RESULT_OK){
                    mConnection = new ConnectionService(mHandler);
                } else {
                    Toast.makeText(this,"Bluetooth Not Enabled, shutting down...",Toast.LENGTH_SHORT).show();
                    finish();
                }

        }
    }

        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_READ:
                    byte[] readBuf = (byte[])msg.obj;
                    String readMessage = new String(readBuf, 0, msg.arg1);
                    if(readMessage.contains("camera")){
                        Intent i = new  Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                        startActivityForResult(i, cameraData);
                    }
                    if(readMessage.contains("toast")) {
                        MediaPlayer windchime = MediaPlayer.create(MainActivity.this, R.raw.thybidding);
                        windchime.start();
                    }
                    break;
                case MESSAGE_RESET:

                    if (mConnection != null)
                        mConnection.stop();

                    mConnection.start();

            }
            return true;
        }

    protected void onPause(){
        WakeLocker.release();
        if (mConnection != null)
            mConnection.stop();
        super.onPause();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_receive) {
            mConnection.start();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

}

Updated: I now use implements Handler.Callback so that we don’t have to subclass the Handler class which creates a “strong” link to the application which in certain instances will keep the subclassed Handler class open causing memory leaks. We still have to create a handler to pass to other classes and we have to still create a handleMessage() method however.

First we need a couple “flags” to use to tell the application what we want to do, we make them constants since they won’t change and the first one is private because we only use it in this class. The second one we will also use in our ConnectionService class so we need to make it public.

Updated: I also added a MESSAGE_RESET constant for resetting the connection upon the client disconnecting, and a cameraData constant which we use when the client tells us to open it.

    private static final int REQUEST_ENABLE_BT = 1;
    public static final int MESSAGE_READ = 2;
    public static final int MESSAGE_RESET = 3;
    private final static int cameraData = 460;

Next we create a couple variables for the objects(classes) we will be using in this Activity, we make them private since we only use them in this class and so no one can get to them and try any funny business.

Updated: Created a Handler instance here to pass to the ConnectionClass so that it can talk back to the MainActivity.

private final Handler mHandler = new Handler(this);

    private ConnectionService mConnection = null;
    private BluetoothAdapter mBluetoothAdapter = null;

Now that we have our global variables set up we move into our onCreate() method.

If you don’t want the server device to stay on the whole time the app is running you can skip the WakeLocker line.

        WakeLocker.acquire(this);

Next we try and get a connection to our Bluetooth radio in the device.

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

If the device doesn’t have Bluetooth, not that it’s not turned on, this checks to see if a Bluetooth radio actually exists in the device. If you don’t have Bluetooth no sense in wasting your time so the application exits after telling you what has happened.

        if (mBluetoothAdapter == null){
            Toast.makeText(this,"Bluetooth is not available", Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

If we do have Bluetooth on the device, we move on to the next part of the Android LifeCycle the onStart() method where we now check to see if Bluetooth is enabled.

if (!mBluetoothAdapter.isEnabled()){
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
        } else {
            if (mConnection == null)
                mConnection = new ConnectionService(mHandler);
        }

If the BluetoothAdapter is not (!) enabled we create an Intent to enable Bluetooth on the device and send it off with the startActivityForResult command which needs to know where to go the Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) and it also needs the constant “flag” we created as a global variable so that when it returns we know what activity it is coming from.

If the BluetoothAdapter IS enabled we create a new ConnectionService object and assign it to our global variable. We also pass it our message Handler so that when the client sends it’s message to the ConnectionService on this server, the ConnectionService knows where to send it (MainActivity) so the application can act on that message. Because the ConnectionService does not access to the devices resources so it can not call our other apps to run.

If we call startActivityForResult, it actually returns and runs onActivityResult() before the onResume() method so if Bluetooth was successfully started we can jump right back in to the normal process flow and start the ConnectionService within the onResume() method without adding duplicate code. If it we weren’t successful then we again exit the program.

    public void onActivityResult(int requestCode, int resultCode,Intent intent){
        switch (requestCode){
            case REQUEST_ENABLE_BT:
                if (resultCode == Activity.RESULT_OK){
                    mConnection = new ConnectionService(mHandler);
                } else {
                    Toast.makeText(this,"Bluetooth Not Enabled, shutting down...",Toast.LENGTH_SHORT).show();
                    finish();
                }

        }
    }

Next method in the Android LifeCycle is the onResume() method. This is where we finally get to tell the server to start listening for connections.

    @Override
    protected void onResume(){
        super.onResume();
        if (mConnection != null){
            mConnection.start();
        }
    }

start() is a custom method we will create in that class to kick off the listener Thread.

Now we look at all the code that get’s called when we receive a message from the client.

Updated:

        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_READ:
. . .
                    break;
                case MESSAGE_RESET:
. . .

            }
            return true;
        }

This is our Handler code, we pass our variable(pointer) to this to the ConnectionService so that it knows where to send messages it receives from the client. Handlers are a decent size subject that I will not cover here. I will say that Handlers can contain several arguments and one of them is called what. It generally holds a “flag” that tells the Handler what you want to do, in this case MESSAGE_READ.

So if we receive a message with the MESSAGE_READ flag we need to read another argument from the Handler message, msg.obj, this contains a byte array that is a string that will tell the Handler EXACTLY what we want to do.

byte[] readBuf = (byte[])msg.obj;

We need to convert the byte[] into something we can read, one thing we need to know is how long the byte[] is and that is stored in msg.arg1.

So we convert the msg.obj into a byte[], then we take the byte[] and it’s size and convert it into a String.

                    byte[] readBuf = (byte[])msg.obj;
                    String readMessage = new String(readBuf, 0, msg.arg1);

Now we have our string and we can compare it with all the different options that we handle.

if(readMessage.contains("camera")){
                        Intent i = new  Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                        startActivityForResult(i, cameraData);
                    }

So if our string we converted contains “camera” we want to create an Intent to run our camera application.

We use the onPause() LifeCycle method to stop the Wakelocker when we exit the application so the device can now turn off the screen and not run in the background.

If you down’t want to use a WakeLocker you can leave this out.

    protected void onPause(){
        WakeLocker.release();
        if (mConnection != null)
            mConnection.stop();
        super.onPause();
    }

Updated: I also make you select receive from the options menu instead of automatically listening for connections at startup.

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_receive) {
            mConnection.start();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

Now time for the ConnectionService that does all the Bluetooth magic.

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

public class ConnectionService {

    private boolean isConnected = false;

    private static final String NAME_SECURE = "BtSecure";
    private static final UUID MY_UUID_SECURE = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");

    private BluetoothServerSocket mmServerSocket = null;
    private AcceptThread mSecureAcceptThread;
    private ConnectedThread mConnectedThread;

    private final BluetoothAdapter mAdapter;
    private final Handler mHandler;

    public ConnectionService(Handler handler){
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mHandler = handler;
    }

    public synchronized void start() {

        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}

        // Start the thread to listen on a BluetoothServerSocket
        if (mSecureAcceptThread == null) {
            mSecureAcceptThread = new AcceptThread();
            mSecureAcceptThread.start();
        }
    }

    private class AcceptThread extends Thread {

        public AcceptThread() {
            try {
                mmServerSocket = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        public void run() {
            BluetoothSocket socket = null;
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (socket != null) {
                connected(socket, socket.getRemoteDevice());
            }
        }
        public void cancel(){
            try{
                mmServerSocket.close();
            } catch (IOException e) { e.printStackTrace();
            }
        }
    }

    public void connected(BluetoothSocket socket, BluetoothDevice device){
        if (mSecureAcceptThread != null){
            mSecureAcceptThread.cancel(); // Server Socket Closed
            mSecureAcceptThread = null;
        }
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
    }

    private class ConnectedThread extends Thread{
        private BluetoothSocket mmSocket;
        private InputStream mmInStream;

        public ConnectedThread(BluetoothSocket socket){
            isConnected = true;
            mmSocket = socket;
            try{
                mmInStream = socket.getInputStream();
            } catch (IOException e){ e.printStackTrace(); }
        }

        public void run(){
            byte[] buffer = new byte[1024];
            int byteCount;
            while(isConnected) {
                try {
                    byteCount = mmInStream.read(buffer);
                    mHandler.obtainMessage(MainActivity.MESSAGE_READ, byteCount, -1, buffer).sendToTarget();
                } catch (IOException e) {
                    isConnected = false;
                    Log.e("BAD","Exception Caught");
                    mHandler.obtainMessage(MainActivity.MESSAGE_RESET).sendToTarget();
                }
            }
        }
            public void cancel(){
                try{
                    mmSocket.close();
                } catch (IOException e){ e.printStackTrace();
                }
            }
    }

    /**
     * Stop all threads
     */
    public synchronized void stop() {
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }

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

Updated: I created a boolean (true/false) variable called isConnected which reflects what status the connection is in. Before we used a while(true) (infinite loop) and in theory when the client disconnected it would cause an IO Exception and everything would be reset, but the while(true) loop just kept on going and caused several Garbage Collecting (GC) messages in the log until the server was uninstalled. Now isConnected is set to false when a IO Error is found and the loop is broken. Then isConnected is reset to true once the new connection is established.

First we create a UUID for our secure connection.

private static final UUID MY_UUID_SECURE = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");

Then we get our BluetoothServerSocket and AcceptThread and BluetoothAdapter to listen for incoming connections from our client.

    private BluetoothServerSocket mmServerSocket = null;
    private AcceptThread mSecureAcceptThread;
    private final BluetoothAdapter mAdapter;

We also need a thread to use for receiving the messages which we will switch to after the client and server have connected over the AcceptThread. All of these threads are just normal threads that we extend.

    private ConnectedThread mConnectedThread;

And a handler to send the messages received from the client to the MainActivity.

    private final Handler mHandler;

Next is our constructor which we call from the MainActivity to create an instance of this class and we pass it our Handler from the MainActivity so ConnectionService knows where to send the messages received from the client. We also go ahead and get an instance of the BluetoothAdapter.

    public ConnectionService(Handler handler){
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mHandler = handler;
    }

When we call the mConnection.start(); command in the MainActivity this is what get’s run.

    public void start(){
        if (mSecureAcceptThread == null){
            mSecureAcceptThread = new AcceptThread();
            mSecureAcceptThread.start();
        }
    }

This checks to see if the thread has already been created, if not, it calls the threads constructor and then it’s run() method (.start()).

When the AcceptThread’s constructor is called, we create a socket, the mmServerSocket, by using the BluetoothAdapter and the listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE) command. Notice that it is listening. A socket is like a hole into our computer that data can flow into, it is also our MAC Address.

        public AcceptThread() {
            try {
                mmServerSocket = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

From Google’s website:

The system will assign an unused RFCOMM channel to listen on.

The system will also register a Service Discovery Protocol (SDP) record with the local SDP server containing the specified UUID, service name, and auto-assigned channel. Remote Bluetooth devices can use the same UUID to query our SDP server and discover which channel to connect to. This SDP record will be removed when this socket is closed, or if this application closes unexpectedly.

So NAME_SECURE is a way that the SDP server uses to send information back to the Bluetooth Server and MY_UUID_SECURE is a UUID that the client must have to connect to the server.

Next is the run() method which is actually called by .start();

        public void run() {
            BluetoothSocket socket = null;
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (socket != null) {
                connected(socket, socket.getRemoteDevice());
            }
        }

The important stuff is:

socket = mmServerSocket.accept();

Which listens for an incoming connection on the mmServerSocket, when one is found it creates a new socket and passes the incoming connection off to it because the mmServerSocket is only for listening not for the passing of data.

.accept() is a blocking method, meaning nothing else in the run() method will run until a connection is accepted, this is the reason why it is in a Thread, otherwise the app would “time out” and Android would shut it down.

Once a connection is accepted and the new socket is created then the connected() method is run:

connected(socket, socket.getRemoteDevice());

socket is the new socket created for data transfer and socket.getRemoteDevice() is how you get the MAC Address of the client.

cancel() is a convenience method that we use to shut down the listening socket which we do in the connected() method.

        public void cancel(){
            try{
                mmServerSocket.close();
            } catch (IOException e) { e.printStackTrace(); }
        }
    public void connected(BluetoothSocket socket, BluetoothDevice device){
        if (mSecureAcceptThread != null){
            mSecureAcceptThread.cancel();
            mSecureAcceptThread = null;
        }
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
    }

We talked about passing in the socket for data transfer and the MAC Address of the client, then if the (mSecureAcceptThread != null) it is still in use and doesn’t need to be since we have connected to the client so we use the convenience method to shut it down (mSecureAcceptThread.cancel();) and then set it to null in case we need to check it in the future.

Once that is done we create a new Thread to receive data through by calling it’s constructor and passing in the socket we created for data transfer. Then we execute the run() method using the .start() method.

private class ConnectedThread extends Thread{
        private BluetoothSocket mmSocket;
        private InputStream mmInStream;

        public ConnectedThread(BluetoothSocket socket){
            mmSocket = socket;
            try{
                mmInStream = socket.getInputStream();
            } catch (IOException e){ e.printStackTrace(); }
        }

        public void run(){
            byte[] buffer = new byte[1024];
            int byteCount;
            while(true) {
                try {
                    byteCount = mmInStream.read(buffer);
                    mHandler.obtainMessage(MainActivity.MESSAGE_READ, byteCount, -1, buffer).sendToTarget();
                } catch (IOException e) {
                    ConnectionService.this.start();
                }
            }
        }
            public void cancel(){
                try{
                    mmSocket.close();
                } catch (IOException e){ e.printStackTrace(); }
            }
    }

The important stuff:

For the constructor we connect a InputStream to the socket so we can take the data and put it wherever we need, in memory, in a file, etc….

mmInStream = socket.getInputStream();

For the run() method we create a place in memory to store a byte[] that holds 1024 bytes:

byte[] buffer = new byte[1024];

We also create a place to save the exact amount of bytes we pass in:

int byteCount;

Then:

while(true) {

This is a infinite loop it will always try to process data as long as the server is running.

We read data off the InputStream and put it in the buffer variable byte[] and save the amount of bytes read to the byteCount variable.

byteCount = mmInStream.read(buffer);

We then take the Handler and create a message that points to the MainActivity and put all the necessary data in it, and send it off.

mHandler.obtainMessage(MainActivity.MESSAGE_READ, byteCount, -1, buffer).sendToTarget();

MainActivity.MESSAGE_READ = the what argument.
buffer = msg.obj.
byteCount = msg.arg1.
-1 = msg.arg2(not used here).

The byte array is sent to the MainActivity and reconstructed there where we check it’s contents and perform the necessary actions.

To use Bluetooth though you have to set some permissions in the AndroidManifest.xml file

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

If you use the WakeLocker class you also have to use:

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

I still have to show you the WakeLocker class.


import android.content.Context;
import android.os.PowerManager;

public abstract class WakeLocker {
    private static PowerManager.WakeLock wakeLock;

    public static void acquire(Context context){
        if (wakeLock != null){
            wakeLock.release();
        }
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE,"BlueToothIoTServer");
        wakeLock.acquire();
    }

    public static void release(){
        if (wakeLock != null){
            wakeLock.release();
            wakeLock = null;
        }
    }
}

Notice this class is abstract, that means you are not going to extend it or instantiate it (call it’s constructor, create an instance), which there is no need to.

Since you are not going to instantiate it you need a way to use it’s methods, you do this by making them static, which means you access them by putting the classes name in front of the method name instead of creating a variable with a new instance of the class and use just the method names.

WakeLocker.acquire(this);
WakeLocker.release();

You pass in context into the acquire() method so you can use the MainActivity’s credentials to access System Services to get a PowerManager object:

PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);

Android API Implementation – Web Address

In this post I will show you how to implement a API into your Android application.

We will be using a Weather Underground API to download current weather conditions.

Their API looks like this:

http://api.wunderground.com/api/98a10aa58dxxxxxx/conditions/q/64145.json

To use their API you must acquire a license, which is part of the API:

98a10aa58dxxxxxx

For this application to actually work you will need to acquire a license from Weather Underground

It is free, if you only download 10 times a minute, if you make a app and have more than 10 customers downloading weather info at the same time you will then have to start paying.

http://www.wunderground.com/weather/api/d/login.html

In this app we will just download the raw weather info into a scrollview to see that the API call actually works.

First we need a TextView that scrolls. To do that we will take a LinearLayout, put a ScrollView inside it. A ScrollView is really just a wrapper, so we have to put a LinearLayout inside it, and then we can finally put our TextView that scrolls inside all of that.

We also need a button to kick off the download and we need that to exist along side the ScrollView, so we will use a android:weightSum=”100″ for the main view and break it up into two pieces a big one for the ScrollView android:layout_weight=”15″ and a little piece for the button android:layout_weight=”85″. I know, your thinking I have the numbers backwards, but nope, that’s Android that has the numbers backwards and it will take you forever to get this straight in your head, or at least it did for me. Actually while writing this post it finally hit me how to think about these numbers, think how much space you are leaving for everything else to be displayed. When creating the Button you want to leave 85 percent of the screen open for the ScrollView and when you are creating the ScrollView you only want to leave 15 percent open for the button.

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

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="15">

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

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

        </LinearLayout>
    </ScrollView>
    <Button
        android:id="@+id/getIt"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:onClick="onClick"
        android:text="Get Info"
        android:layout_weight="85">
    </Button>

    </LinearLayout>

Now the actual code:

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

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;


public class MainActivity extends ActionBarActivity {

    TextView text1;

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

        text1 = (TextView)findViewById(R.id.tv1);
    }

    public void onClick(View view){

        DownloadWeather letsGo = new DownloadWeather();
        letsGo.execute();

    }

    class DownloadWeather extends AsyncTask<Void, Void, String> {
        @Override
        protected String doInBackground(Void... params) {

            String line;
            String res=null;
            URI website;
            DefaultHttpClient client = new DefaultHttpClient();
            try {
                website = new URI("http://api.wunderground.com/api/YOUR LICENSE CODE HERE/conditions/q/64145.json");
                HttpGet get = new HttpGet();
                get.setURI(website);
                HttpResponse r = client.execute(get);
                BufferedReader in = new BufferedReader(new InputStreamReader(r.getEntity().getContent()));

                while ((line = in.readLine()) != null) {
                    res += line;

                }

                in.close();

            } catch (URISyntaxException e) {
                e.printStackTrace();
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return res;
        }
        @Override
        protected void onPostExecute(String result)
        {
            text1.setText(result);
        }
    }


}

And permissions to download from the web in the AndroidManifest.xml file:

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

Android – Interfaces

Interfaces are kind of like American football timing plays, the receiver runs blindly to a agreed upon spot on the field, and the quarterback throws the ball to the spot. If everything goes as planned the ball is delivered to the spot at the right time.

Interfaces aren’t as much timing as an agreed upon spot. You agree on a method name and one class throws the data to the other class to the same method name and it catches the data.

The class throwing the data defines the interface and method, creates an instance of the interface that points to the catching class and uses it to throw the data to the class catching the data.

The class catching the data implements the interface and the method and uses the method to catch the data.

The Application sending the data contains:

    OnSwipePerformedListener swipePerformedListener;
 
    public interface OnSwipePerformedListener {
        public void onSwipePerformed(String swipeDirection);
    }
 
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try{
            swipePerformedListener = (OnSwipePerformedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException( activity.toString()
                                         + " must implement OnSwipePerformedListener");
        }
    }

OnSwipePerformedListener is the interface defined by the trowing class that the catching class must implement in it’s MainActivity.

onAttach is part of the Android lifecycle and what the throwing class uses to get an instance or a pointer to the catching class.

swipePerformedListener.onSwipePerformed() is the pointer to the catching classes implementation of the method where the data(“right”) is sent.

Command used to send data from throwing class to the catching class.

swipePerformedListener.onSwipePerformed("right");

The catching classes implementation of the interface will look like this:

@Override
    public void onSwipePerformed(String swipeDirection)
    {
        // TODO: Implement this method
        if(swipeDirection.contains("top")){
            Toast.makeText(this, "You Swiped Up!", Toast.LENGTH_LONG).show();
 
        } else if(swipeDirection.contains("right")){
            Toast.makeText(this, "You Swiped Right!", Toast.LENGTH_LONG).show();
             
 
        } else if(swipeDirection.contains("left")){
            Toast.makeText(this, "You Swiped Left!", Toast.LENGTH_LONG).show();
             
             
        } else if(swipeDirection.contains("bottom")){
            Toast.makeText(this, "Mix Canceled", Toast.LENGTH_LONG).show();
             
        }
    }

So the throwing class would send the string “right” and the catching class would put it in the swipeDirection variable.

swipeDirection = “right”

To implement the API/interface the catching class activity would start out something like this:

public class MainActivity extends Activity implements FragmentArt.OnSwipePerformedListener
{

API’s

API’s – Application Programming Interfaces allow you to add third party functionality to your application. This allows you to add programming to your app without doing all the hard work. As the name states they are Interfaces.

The provider of the API will have a class that defines the interface(s) and method(s), when you implement their interface(s), you must also implement the interface(s) method(s). When you call their class they will receive a pointer or instance of your Activity, they will use this to point the interface to your Activity, so when you call their interface it will send the manipulated/retrieved data to the method you implemented in your Activity where you can do whatever you need to with it.

This is what their class would contain:

    OnSwipePerformedListener swipePerformedListener;
 
    public interface OnSwipePerformedListener {
        public void onSwipePerformed(String swipeDirection);
    }
 
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try{
            swipePerformedListener = (OnSwipePerformedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException( activity.toString()
                                         + " must implement OnSwipePerformedListener");
        }
    }

OnSwipePerformedListener is the interface they define that you must implement in your Activity.
onAttach is what they use to get an instance or a pointer to your application.
swipePerformedListener.onSwipePerformed() is the pointer to the method in your specific application and is what the API uses to send the data (“right”).

Command to send data from API to client:

swipePerformedListener.onSwipePerformed("right");

Your implementation of the interface will look like this:

@Override
    public void onSwipePerformed(String swipeDirection)
    {
        // TODO: Implement this method
        if(swipeDirection.contains("top")){
            Toast.makeText(this, "You Swiped Up!", Toast.LENGTH_LONG).show();
 
        } else if(swipeDirection.contains("right")){
            Toast.makeText(this, "You Swiped Right!", Toast.LENGTH_LONG).show();
             
 
        } else if(swipeDirection.contains("left")){
            Toast.makeText(this, "You Swiped Left!", Toast.LENGTH_LONG).show();
             
             
        } else if(swipeDirection.contains("bottom")){
            Toast.makeText(this, "Mix Canceled", Toast.LENGTH_LONG).show();
             
        }
    }

So the person who created the API would send you the string “right” and you would put it in the swipeDirection variable.

swipeDirection = “right”

To implement the API/interface your activity would start out something like this:

public class MainActivity extends Activity implements FragmentArt.OnSwipePerformedListener
{

API’s can also be as simple as a web address with a special address (your “license”) that returns data in a .html or .json format.

http://api.wunderground.com/api/98a10aa58dxxxxxx/conditions/q/64145.json

Android – JSON objects and arrays

*** Click on Table of Contents for a quick way to view all posts ***

JSON is very useful and awesome, mainly because my iPhone can’t tell time! Well as long as it’s turned on. I wrote an app that I will post some time that downloads weather data using weather undergrounds API, I made one for both Android and iOS, the Android one I can schedule to download every 30 minutes and save the info to a database. iOS doesn’t let you do this, the closest you can get to this type of functionality is to create a background fetch and manually activate the fetch every 30 minutes, over time it is supposed to learn when you want to download info and do it automatically. So if you want to download stuff every half hour you need to stay awake for about a week and manually download data so it learns this, of course after a week or so it will probably forget that you want to do this and stop working.

Rant done!

With JSON I can take the data that Android downloads and transfer it to iOS! PC’s and Servers are SOoooo history!

…until Apple “improves” JSON so that it doesn’t work with anything else….like Bluetooth.

But anyway on to JSON

Here is what makes it awesome, this is all you need to know:

[{:}]

…..and were done, see you for my next post…….OK, almost done.

Here is what a JSONArray of JSONObjects looks like

[{“humid”:”50″,”wind”:”5.6″,”temp”:”85″,”sky”:”Clear”},{“humid”:”60″,”wind”:”3.3″,”temp”:”87″,”sky”:”Clear”},{“humid”:”70″,”wind”:”4.4″,”temp”:”89″,”sky”:”Partly Cloudy”},{“humid”:”80″,”wind”:”0.0″,”temp”:”90″,”sky”:”Mostly Cloudy”},{“humid”:”95″,”wind”:”5.6″,”temp”:”95″,”sky”:”Overcast”}]

It’s word wrapped, so it is actually just one continuous line.

The beginning of a JSONArray starts with [ and ends with ].

The beginning of a JSONObject starts with { and ends with }.

The data is really just a key / value pair separated by : with commas separating each key value pair in the JSONObject, and commas separating each JSONObject in the JSONArray.

Let’s see if I can give you a “graphical” view

[JSONArray{JSONObject “key”:”value”,”key”:”value”,”key”:”value” endOfJSONObject},
{JSONObject “key”:”value”,”key”:”value”,”key”:”value” endOfJSONObject},
{JSONObject “key”:”value”,”key”:”value”,”key”:”value” endOfJSONObject}endOfJSONArray]

Then you can just dump this String to a text file and read in the text file somewhere else even on an IPHONE!….shhhh don’t tell them.

So let’s do that.

First the layout which will have a TextView to display the JSONArray of JSONObjects as a string and a button to start the magic.

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android: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">

    <TextView
        android:id="@+id/tv1"
        android:text="@string/hello_world" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv1"
        android:onClick="onClick"
        android:text="Full Copy" />

</RelativeLayout>

Now the MainActivity

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

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

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;


public class MainActivity extends ActionBarActivity {

    File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
    String bank;

    String[] listItems = {"85","Clear","5.6","50","87","Clear","3.3","60","89","Partly Cloudy","4.4","70","90","Mostly Cloudy","0.0","80","95","Overcast","5.6","95",};

    TextView tv1;

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

    }

    public void onClick(View view) {

        doMagic();
    }



    public void doMagic(){

        JSONArray jsonArr = new JSONArray();

        try {

            for(int i = 0;i < listItems.length; i = i+4 ){

                JSONObject jsonReturn = new JSONObject();

                jsonReturn.put("temp",listItems[i]);
                jsonReturn.put("sky",listItems[i+1]);
                jsonReturn.put("wind",listItems[i+2]);
                jsonReturn.put("humid",listItems[i+3]);

                jsonArr.put(jsonReturn);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        bank = jsonArr.toString();

        try {
            File file = new File(path,"/testlist.txt");
            FileOutputStream fos = new FileOutputStream(file); // add ,true to only append
            fos.write(bank.getBytes());
            fos.close();
        } catch (IOException e){	Toast.makeText(this,"Error" ,Toast.LENGTH_LONG).show();}

        tv1.setText(jsonArr.toString());
    }
}

Since we are writing to the sdcard:

AndroidManifest.xml

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

The important stuff is:

Create a JSONArray

JSONArray jsonArr = new JSONArray();

Create a JSONObject

JSONObject jsonReturn = new JSONObject();

Put key/value pairs in the JSONObject

jsonReturn.put("key","value");

Put the JSONObject into the JSONArray

jsonArr.put(jsonReturn);

Before you transmit it or save to file convert it to a string

bank = jsonArr.toString();

Then you can save the string to a file or transmit it

fos.write(bank.getBytes());

Now let’s extract

We will read in the file we created, save it as a string, create a JSONArray from the string, extract JSONObjects from the JSONArray and Toast them to the screen.

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

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

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;


public class MainActivity extends ActionBarActivity {

    File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

    StringBuilder test2;
    String line;
    FileInputStream fIn;
    File myFile;

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

        myFile = new File(path, "testlist.txt");

        try {
            fIn = new FileInputStream(myFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        
        BufferedReader myReader = new BufferedReader(new InputStreamReader(fIn));
        test2 = new StringBuilder();
        if (myFile != null) {
            try {
                while((line = myReader.readLine()) != null) {
                    test2.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // convert StringBuilder to String
        String test1 = test2.toString();

        final JSONArray jsonTPL;

        try{

            jsonTPL = new JSONArray(test1);

            for(int i = 0;i<jsonTPL.length();i++){
                JSONObject jObj = jsonTPL.getJSONObject(i);

                Toast.makeText(this,"Temp: " + jObj.getString("temp") + "\n"
                        + "Sky Condition: " + jObj.getString("sky") + "\n"
                        + "Wind Speed: " + jObj.getString("wind") + "\n"
                        + "Humidity: " + jObj.getString("humid"),Toast.LENGTH_LONG).show();

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

}

And don’t forget the AndroidManifest.xml file, I’m sure you won’t because Android won’t let you!

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

Run this and you should get a Toast for 5 Objects.

The important stuff:

Read in the file and save it as a StringBuilder() object.

Then covert the StringBuilder() to a String.

String test1 = test2.toString();

Tell Android you are going to make a JSONArray.

final JSONArray jsonTPL;

Create a JSONArray initializing it with the String of JSONObjects.

jsonTPL = new JSONArray(test1);

Iterate through the JSONArray as many times as there are JSONObjects.

i<jsonTPL.length()
JSONObject jObj = jsonTPL.getJSONObject(i);

And Toast out each JSONObject.

Toast.makeText(this,"Temp: " + jObj.getString("temp") + "\n"
                        + "Sky Condition: " + jObj.getString("sky") + "\n"
                        + "Wind Speed: " + jObj.getString("wind") + "\n"
                        + "Humidity: " + jObj.getString("humid"),Toast.LENGTH_LONG).show();

Android Fragments – using getFramentManager().beginTransaction()

*** The app in this post has been checked for correctness
and works as of 06/12/2015 – Lollipop

I also go over using Fragments in my mp3 player app, but I thought I would create a post just for Fragments since I finally had time to obsess over them to figure out why no one ever tells you what the difference is between:

When you go to someone’s blog for a tutorial you see them set up the layout like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:weightSum="100"
    android:orientation="vertical"
    android:focusable="true"
    android:focusableInTouchMode="true">


    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.blogfinal5b.FragmentArt"
        android:id="@+id/frag_b"
        android:layout_weight="20"/>


    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.blogfinal5b.FragmentTime"
        android:id="@+id/frag_c"
        android:layout_weight="48"/>
    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.blogfinal5b.FragmentLst"
        android:id="@+id/frag_a"
        android:layout_weight="32"/>


</LinearLayout>

But then EVERY actual implementation of Fragments used in a real app they use a layout like this:

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:focusable="true"
    android:focusableInTouchMode="true">


    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.blogfinal5b.FragmentArt"
        android:id="@+id/frag_b"/>


<FrameLayout 
    android:id="@+id/container"
    android:layout_height="match_parent"
    android:layout_width="match_parent" />

</LinearLayout>

..and with the second implementation shown above they use Google’s recommended way of using the:

getFramentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();

What gives?

Are we both using Android here? Are the tutorials in Java but Google uses Android? ..not sure, but I am here to help set things straight.

I’m about to show you something shocking!

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:weightSum="100"
    tools:context=".MainActivity" tools:ignore="MergeRootFrame">


    <FrameLayout android:id="@+id/container" android:layout_height="match_parent"
        android:layout_width="match_parent" android:layout_weight="50"
	/>

    <FrameLayout android:id="@+id/nextone" android:layout_height="match_parent"
        android:layout_width="match_parent" android:layout_weight="50"
	/>


</LinearLayout>

Wow! that was hard (not really) but try finding this anywhere else on the web!

and yes it does actually work.

So I am done with my rant, let’s actually create something to prove that I know what I am talking about.

In this app I am mainly just showing you how to use the getFramentManager().beginTransaction() with a layout that I think helps you understand how it actually works. Most of the other examples I have seen always left me guessing.

Create your base project, nothing special needs to be done differently, use a blank activity, if you are using Android Studio no need to create the project using the Activity with a Fragment template, we will just remove everything anyway.

I prefer to use the Project View (the box above your files table view) when you create your package it puts you in Android view by default, if you click on it you can change it to Project which works a little more like Eclipse did (which is what I learned to build Android apps with originally).

Expand – yourAppName\app\src\main

Then you will see your java folder, res folder, and AndroidManifest.xml file.

First you need to expand the res folder, then right click on the res folder and select new and then directory, name the directory layout-land.

The new layout-land folder should be on the same “level” as the layout folder, the drawable folder, the values folder, the menu folder….. etc.

Now right click on the layout-land folder and create a new Layout Resource File (or a file if that is the only option you have, just make sure you give it an .xml file extension when you save it) and give it the same name as the file that is in the layout folder (main.xml or activity_main.xml).

Now you can overwrite everything in the file with the following code:

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:weightSum="100"
    tools:context=".MainActivity" tools:ignore="MergeRootFrame">


    <FrameLayout android:id="@+id/container" android:layout_height="match_parent"
        android:layout_width="match_parent" android:layout_weight="50"
	/>

    <FrameLayout android:id="@+id/nextone" android:layout_height="match_parent"
        android:layout_width="match_parent" android:layout_weight="50"
	/>


</LinearLayout>

You can also overwrite all the code in the file in the layout folder, we just need to add a VERTICAL orientation line to the code in the LinearLayout section:

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:orientation="vertical"
    android:layout_width="match_parent" android:layout_height="match_parent"
	android:weightSum="100"
    tools:context=".MainActivity" tools:ignore="MergeRootFrame">


    <FrameLayout android:id="@+id/container" android:layout_height="match_parent"
        android:layout_width="match_parent" android:layout_weight="50"
	/>

    <FrameLayout android:id="@+id/nextone" android:layout_height="match_parent"
        android:layout_width="match_parent" android:layout_weight="50"
	/>


</LinearLayout>

If you have worked with fragments before using the other way or if you noticed in my example code at the very beginning, fragments used the other way require:

android:name="com.example.blogfinal5b.FragmentArt"

for each fragment, but you don’t need that when you do them this way.

Now lets update the MainActivity.java file.

MainActivity.java

import android.app.*;
import android.os.*;
import android.view.*;
import android.widget.*;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();

            getFragmentManager().beginTransaction()
                    .add(R.id.nextone, new SecondFragment())
                    .commit();
        }
    }

    public void doit(View view){
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container,new ThirdFragment());
        ft.commit();
    }

    public void seeit(View view){
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container,new FourthFragment());
        ft.commit();
    }
}

Overwrite everything in your MainActivity.java file, even the imports, but LEAVE THE PACKAGE NAME at the very top.

You will have several errors, because we still need to create the fragment files that we will use to swap out on the fly.

The main things to notice in the code above is in the onCreate() method we have

getFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();

and

 getFragmentManager().beginTransaction()
                    .add(R.id.nextone, new SecondFragment())
                    .commit();

getFragmentManager().beginTransaction() – Tells Android we are about to give it a list of Fragment type activities that need to be performed.

Our list only consists of one thing – .add(R.id.nextone, new SecondFragment(). We want Android to find the “object” in the layout file with the id of container for the first one and nextone for the second one and replace it with the PlaceholderFragment() for the first one and SecondFragment() for the second one.(which we will create next)

and finally – .commit(); This tells Android to perform everything we told it to do since the last getFragmentManager().beginTransaction() command.

Another thing you might have noticed is the:

if (savedInstanceState == null) {

command, this relates to the:

protected void onCreate(Bundle savedInstanceState) {

command, you can use this command (which I don’t cover here) to save “state” information (variable values) so if the device is rotated while using your app you can save information to be reloaded when the app is re-created (Android actually closes your app and restarts it so it can re-draw the correct activity_main.xml / main.xml file). If this is the first time your app has been run savedInstanceState will be equal to null, and the commands within the brackets will be performed. If the device is rotated, there may not be any “state” data saved, but savedInstanceState will not be equal to null so they will not be run again. If you actually close your app the onDestroy() method is run, which will cause savedInstanceState to equal null, so the next time you run the app savedInstanceState will again equal null.

    public void doit(View view){
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container,new ThirdFragment());
        ft.commit();
    }

    public void seeit(View view){
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container,new FourthFragment());
        ft.commit();
    }

doit and seeit are linked to buttons in a fragment we will define in a moment, so when they are pressed they replace the current fragment with a new one.

These commands do the same thing we did earlier, but this time I am saving the FragmentTransaction instance as a variable and breaking up the commands into separate ones instead of using a “Builder Pattern” like in the previous example.

I should also probably move the
FragmentTransaction ft = getFragmentManager().beginTransaction();
line outside of the two methods and create it only once to reduce Object Creation overhead, in such a short example a little repetition is good to help you learn.

So we get an instance of getFragmentManager().beginTransaction() and save it as a FragmentTransaction object and give it a variable name of ft.

Then we replace whatever is in the location identified by the id container (R.id.container = android:id=”@+id/container”) with ThirdFragment() (which we define next).

ft.replace(R.id.container,new ThirdFragment());

Then we tell Android to apply all the changes we stated since the last .beginTransaction() command.

ft.commit();

Now let’s see all these other Fragments I have been talking about.

PlaceHolderFragment.java – is actually the default Fragment created by Android Studio and it will display a layout with a single TextView that says Hello World. Since we created a new layout consisting of two FrameLayouts with equal weight we essentially have two square areas on our screen reserved for fragments and this one will go in the top square.

import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * A placeholder fragment containing a simple view.
 */
public class PlaceholderFragment extends Fragment {

    public PlaceholderFragment() {
    }

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

***Notice that it extends the Fragment class.

    public PlaceholderFragment() {
    }

is a Constructor, empty in this case, I also don’t cover these here, but you can have more than one of these as long as they contain different argument types within the (). Java creates this same exact line by default “behind the scenes” so not sure exactly why they do this.

View rootView = inflater.inflate(R.layout.fragment_main, container, false);

Creates the view that will go in the top square of our custom view. This is called when savedInstanceState = null, which is the first time our app is run. The view is “inflated”/created using R.layout.fragment_main as the layout (I will show you this next) and it is sent to the location defined by container which is R.id.container which was passed from the .replace() method. The false at the end of the line represents wether you want to attach the view to the FrameLayout so you can receive touch information and other things, but Fragments perform this function for you, again “behind the scenes” so you don’t need to so it is false.

return rootView;

returns the view we created so it can be displayed on the screen, we also return it because we told Android we would when we gave our method a return identifier of View.

public View onCreateView(.....

And here is the actual xml layout for R.layout.fragment_main, this file and all the fragment .xml files go in the layout folder, no need to duplicate them in the layout-land folder, the main.xml file there will still be able to find them.

fragment_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView android:text="hello_world" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

…Yawn, not much to see here

Now for the fragment for the lower square.

import android.app.Activity;
import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;


public class SecondFragment extends Fragment {

    public SecondFragment() {
    }

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

Same as before onCreateView() is like MainActivity’s onCreate() it’s called when the fragment is called upon, this time we create a R.layout.fragment_second layout view and pass it back to the ViewGroup container that was passed in (R.id.nextone = android:id=”@+id/nextone”).

The “exciting” stuff is in the layout .xml file for this one.

fragment_second.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" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/pushed"
        android:onClick="doit"
        android:text="Press Me"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/pic"
        android:onClick="seeit"
        android:text="See Pic"/>

</LinearLayout>

This layout has two buttons with android:onClick=”doit” and android:onClick=”seeit” that point to the two methods in the MainActivity file, so those methods are run when these buttons are clicked.

Now we have the basic layout that should run, but when you click the buttons there will be no fragments to replace the first ones with, so let’s create them now.

ThirdFragment.java

import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class ThirdFragment extends Fragment {

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

fragment_third.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView android:text="YOU RULE!" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

Nothing new in the third fragment, let’s do something different for the fourth fragment, but you will need a picture of some sort, I used a album cover picture.

Change the path accordingly:

pretty.setImageURI(Uri.parse(“file:/sdcard/CoverArt/a portrait of aldo nova.jpg”));

FourthFragment.java

import android.net.Uri;
import android.os.Bundle;
import android.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class FourthFragment extends Fragment {

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

        ImageView pretty = new ImageView(getActivity());
        pretty.setImageURI(Uri.parse("file:/sdcard/CoverArt/a portrait of aldo nova.jpg"));
        return pretty;
    }
}

Notice**** This is just a blank layout, no TextView or anything.

fragment_fourth.xml

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

</LinearLayout>

Now give it a try, when you click the buttons it should replace the top square with a different fragment, even though we should be able to, I don’t change the fragment with the buttons at the bottom, someone would have to pay me to add that much complexity to the app!

BackStack functionality.

There’s one more little bit of magic I will cover for this topic, or at least touch upon it at this time, adding Fragment’s to the back stack. Right now if you click on the back arrow on your device the app will instantly close, but if we add one line to the .beginTransaction() command, it will add the replaced fragment to a back stack so that when you click back it will walk back through your view hierarchy until it reaches the beginning and then your app will close.

The command looks like this:

ft.addToBackStack(null);

and it goes here:

    public void doit(View view){
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container,new ThirdFragment());
        ft.addToBackStack(null);
        ft.commit();
    }

    public void seeit(View view){
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.container,new FourthFragment());
        ft.addToBackStack(null);
        ft.commit();
    }

***Make sure you put it somewhere in between .beginTransaction(); and .commit();

Now run it again….pretty cool, makes you feel like a real developer.