ln.paign10.arduinopixel.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for ln.paign10.arduinopixel.MainActivity.java

Source

/**
 * Description:
 *   This is the client side of a server-client system controlling 
 *   a NeoPixel LED strip. It allows for the turning on and off of the 
 *   strip and the change of its color.
 *   
 * Specifications:
 *   For the server side:
 *     A server that accepts the following HTTP requests:
 *     > GET / HTTP/1.1                  : Responds w/ "Hello from Arduino Server"
 *     > GET /strip/status/ HTTP/1.1     : Responds w/ "ON"/"OFF" for the power state of the strip
 *     > GET /strip/color/ HTTP/1.1      : Responds w/ a JSON representation of 
 *                                         the strip color -> {"r":x,"g":x,"b":x}
 *     > PUT /strip/status/on/ HTTP/1.1  : Turns the LED strip on
 *     > PUT /strip/status/off/ HTTP/1.1 : Turns the LED strip off
 *     > PUT /strip/color/ HTTP/1.1      : Changes the color of the LED strip
 *                                         The colors are provided as a JSON object: {
 *                                                                                     "r": x,
 *                                                                                     "g": x,
 *                                                                                     "b": x
 *                                                                                   }
 *   For the client side:
 *     An Android app that:
 *     > allows to configure the host's IP address and Port number
 *     > allows to turn the LED strip on and off
 *     > allows for the continuous real-time change of the strip's color
 *     > is available on Google Play, w/ the name ArduinoPixel
 * 
 * Author:
 *   Nick Lamprianidis { paign10.ln [at] gmail [dot] com }
 * 
 * License:
 *   Copyright (c) 2014 Nick Lamprianidis 
 *   This code is released under the MIT license
 *   http://www.opensource.org/licenses/mit-license.php
 *   
 * Attribution:
 *   This app makes use of the ColorPicker view from Piotr Adamus
 *   https://github.com/chiralcode/Android-Color-Picker
 */

package ln.paign10.arduinopixel;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import ln.paign10.arduinopixel.IpDialog.DialogListener;

import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DialogFragment;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.NumberPicker;
import android.widget.Switch;
import android.widget.Toast;

import com.chiralcode.colorpicker.ColorPicker;
import com.chiralcode.colorpicker.ColorPicker.ColorListener;

public class MainActivity extends Activity implements DialogListener, ColorListener {

    // HTTP request methods
    public enum Method {
        GET, PUT
    }

    // Available URIs
    private String URI_ROOT = "/";
    private String URI_STATUS = "/strip/status/";
    private String URI_STATUS_ON = "/strip/status/on/";
    private String URI_STATUS_OFF = "/strip/status/off/";
    private String URI_COLOR = "/strip/color/";

    private static SharedPreferences mPrefs;
    private DialogFragment mDialog;
    private ScheduledExecutorService executor;
    private ScheduledFuture<?> mCheckHost;
    private static boolean online = false;
    private ColorPicker mColorPicker;
    private Switch mSwitch;
    public Toast mToast;
    private static int[] mUri = { 192, 168, 1, 100, 80 };
    @SuppressWarnings("unused")
    private boolean mState = false;
    private int[] mColor = { 128, 128, 128 };

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

        mPrefs = getPreferences(MODE_PRIVATE);
        mUri[0] = mPrefs.getInt("Int4", 192);
        mUri[1] = mPrefs.getInt("Int3", 168);
        mUri[2] = mPrefs.getInt("Int2", 1);
        mUri[3] = mPrefs.getInt("Int1", 100);
        mUri[4] = mPrefs.getInt("Port", 80);

        mColorPicker = (ColorPicker) findViewById(R.id.colorPicker);

        mSwitch = (Switch) findViewById(R.id.power_switch);
        mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                updatePowerState(isChecked);
            }
        });

        mToast = Toast.makeText(MainActivity.this, "", Toast.LENGTH_SHORT);

        executor = Executors.newScheduledThreadPool(1);
    }

    @Override
    protected void onResume() {
        online = false;
        // if (isNetworkAvailable())
        new HttpTask(Method.GET, URI_STATUS).execute();

        super.onResume();
    }

    @Override
    protected void onPause() {
        if (mCheckHost != null)
            cancelExecutor();

        super.onPause();
    }

    private class HttpTask extends AsyncTask<Integer, Void, String> {
        private AndroidHttpClient mClient = AndroidHttpClient.newInstance("");
        private Method reqMethod; // HTTP request method
        private String URL;
        private String reqUri; // Requested URI

        public HttpTask(Method method, String uri) {
            reqMethod = method;
            reqUri = uri;
            URL = "http://" + mUri[0] + "." + mUri[1] + "." + mUri[2] + "." + mUri[3] + ":" + mUri[4] + reqUri;

            Log.i("request", method.toString() + ", " + URL);
        }

        @Override
        protected String doInBackground(Integer... c) {
            ResponseHandler<String> responseHandler = new BasicResponseHandler();

            try {

                if (reqMethod == Method.GET) {

                    HttpGet request = new HttpGet(URL);
                    return mClient.execute(request, responseHandler);

                } else {

                    HttpPut request = new HttpPut(URL);
                    try {
                        if (reqUri.equals(URI_COLOR))
                            // JSON data: {"r":x,"g":x,"b":x}
                            request.setEntity(
                                    new StringEntity("{\"r\":" + c[0] + ",\"g\":" + c[1] + ",\"b\":" + c[2] + "}"));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }

                    return mClient.execute(request, responseHandler);

                }

            } catch (Exception e) {
                // e.printStackTrace();
                online = false;
                if (mCheckHost == null)
                    startExecutor();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String result) {

            if (null != mClient)
                mClient.close();

            online = true;
            if (reqMethod == Method.GET)
                onGetRequestResult(reqUri, result);
        }
    }

    // Responds to the result provided, given the HTTP GET request parameters
    private void onGetRequestResult(String uri, String result) {
        if (result == null) {
            if (uri.equals(URI_ROOT)) {
                mToast.setText("Host Unreachable");
                mToast.show();
            }

            online = false;

            if (mCheckHost == null)
                startExecutor();

            return;
        }

        if (mCheckHost != null)
            cancelExecutor();

        if (uri.equals(URI_ROOT)) {
            new HttpTask(Method.GET, URI_STATUS).execute();
            mToast.setText(result);
            mToast.show();

        } else if (uri.equals(URI_STATUS)) {
            new HttpTask(Method.GET, URI_COLOR).execute();

            if (result.equals("OFF")) {
                mState = false;
                mSwitch.setChecked(false);
            } else if (result.equals("ON")) {
                mState = true;
                mSwitch.setChecked(true);
            }

        } else {
            parseJsonData(result);
        }
    }

    private void startExecutor() {
        mCheckHost = executor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                new HttpTask(Method.GET, URI_STATUS).execute();
            }
        }, 10, 10, TimeUnit.SECONDS);

        Log.i("request", "Executor started");
    }

    private void cancelExecutor() {
        mCheckHost.cancel(true);
        mCheckHost = null;

        Log.i("request", "Executor canceled");
    }

    private void parseJsonData(String result) {
        try {
            JSONObject responseObject = (JSONObject) new JSONTokener(result).nextValue();

            String[] optNames = { "r", "g", "b" };
            for (int i = 0; i < 3; ++i)
                mColor[i] = responseObject.optInt(optNames[i]);

            mColorPicker.setColor(Color.rgb(mColor[0], mColor[1], mColor[2]));
            mColorPicker.postInvalidate();

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

    }

    // Returns true if host is reachable
    public static synchronized boolean isOnline() {
        return online;
    }

    // Updates the power state of the LED strip
    private void updatePowerState(boolean state) {
        mState = state;

        if (!isNetworkAvailable()) {
            mToast.setText("Network Unavailable");
            mToast.show();
            online = false;
            if (mCheckHost == null)
                startExecutor();
            return;
        }

        if (online) {
            if (state)
                new HttpTask(Method.PUT, URI_STATUS_ON).execute();
            else
                new HttpTask(Method.PUT, URI_STATUS_OFF).execute();
        }
    }

    @Override
    // Sets the values of the NumberPickers on the Dialog
    public void setNpValues(ArrayList<NumberPicker> np) {
        np.get(0).setValue(mUri[0]);
        np.get(1).setValue(mUri[1]);
        np.get(2).setValue(mUri[2]);
        np.get(3).setValue(mUri[3]);
        np.get(4).setValue(mUri[4]);
    }

    @Override
    // Retrieves and stores the IP address and Port number from the Dialog
    // Checks for the availability of the Server
    // and retrieves the power state of the LED strip
    public void setUri(ArrayList<NumberPicker> np) {
        mUri[0] = np.get(0).getValue();
        mUri[1] = np.get(1).getValue();
        mUri[2] = np.get(2).getValue();
        mUri[3] = np.get(3).getValue();
        mUri[4] = np.get(4).getValue();

        SharedPreferences.Editor editor = mPrefs.edit();
        editor.putInt("Int4", mUri[0]);
        editor.putInt("Int3", mUri[1]);
        editor.putInt("Int2", mUri[2]);
        editor.putInt("Int1", mUri[3]);
        editor.putInt("Port", mUri[4]);
        editor.commit();

        online = false;
        if (isNetworkAvailable())
            new HttpTask(Method.GET, URI_ROOT).execute();
    }

    @Override
    // Saves the current color to be set on the LED strip
    // Makes the HTTP PUT request with the color to set on the LED Strip
    public void onColorChanged(int color) {
        mColor[0] = Color.red(color);
        mColor[1] = Color.green(color);
        mColor[2] = Color.blue(color);

        if (online)
            new HttpTask(Method.PUT, URI_COLOR).execute(mColor[0], mColor[1], mColor[2]);
    }

    // Opens the web page (codebender project) with the server code
    public void openURL() {
        String url = "https://codebender.cc/sketch:31742";
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(Uri.parse(url));
        startActivity(intent);
    }

    private boolean isNetworkAvailable() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(
                Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
        return activeNetworkInfo != null && activeNetworkInfo.isConnected();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.settings:
            mDialog = new IpDialog();
            mDialog.show(getFragmentManager(), "Settings");
            break;
        case R.id.server_code:
            openURL();
            break;
        default:
            break;
        }
        return true;
    }
}