com.example.android.wearable.mjpegviewwear.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.example.android.wearable.mjpegviewwear.MainActivity.java

Source

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.wearable.mjpegviewwear;

import android.app.Activity;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.MessageApi.SendMessageResult;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;

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 org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Collection;
import java.util.HashSet;

/**
 * Receives its own events using a listener API designed for foreground activities. Updates a data
 * item every second while it is open. Also allows user to take a photo and send that as an asset to
 * the paired wearable.
 */
public class MainActivity extends Activity implements MessageApi.MessageListener, NodeApi.NodeListener,
        ConnectionCallbacks, OnConnectionFailedListener {

    private static final boolean DEBUG = false;
    private static final String TAG = "MainActivity";

    /**
     * Request code for launching the Intent to resolve Google Play services errors.
     */
    private static final int REQUEST_RESOLVE_ERROR = 1000;

    private static final String START_ACTIVITY_PATH = "/start-activity";
    private static final String SEND_IMAGE_PATH = "/send-image";
    private static final String SEND_IMAGE_KEY = "image";
    private static final String NOTIFY_IMAGE_RECEIPT_PATH = "/notify-image-receipt";

    private GoogleApiClient mGoogleApiClient;
    private boolean mResolvingError = false;

    //for MJPEG Viewer
    private MjpegView mMv = null;
    String mURL;

    // for settings (network and resolution)
    private static final int REQUEST_SETTINGS = 0;

    private int mWidth = 640;
    private int mHeight = 480;

    private int mIp_ad1 = 192;
    private int mIp_ad2 = 168;
    private int mIp_ad3 = 2;
    private int mIp_ad4 = 1;
    private int mIp_port = 80;
    private String mIp_command = "?action=stream";

    private int mFrameSkip = 4;

    private boolean mSendableImage = true;

    final Handler mHandler = new Handler();

    private NotificationCompat.Builder mBuilder;
    int mNotificationId = 1;
    NotificationManager mNotifyMgr;

    @Override
    public void onCreate(Bundle b) {
        super.onCreate(b);

        LOGD(TAG, "onCreate");
        setContentView(R.layout.main_activity);

        mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle(getString(R.string.app_name)).setContentText(getString(R.string.running))
                .setOngoing(true);
        Intent resultIntent = new Intent(this, MainActivity.class);
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        mBuilder.setContentIntent(resultPendingIntent);
        mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        mGoogleApiClient = new GoogleApiClient.Builder(this).addApi(Wearable.API).addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this).build();

        // for MJPEG View
        SharedPreferences preferences = getSharedPreferences("SAVED_VALUES", MODE_PRIVATE);
        mWidth = preferences.getInt("width", mWidth);
        mHeight = preferences.getInt("height", mHeight);
        mIp_ad1 = preferences.getInt("ip_ad1", mIp_ad1);
        mIp_ad2 = preferences.getInt("ip_ad2", mIp_ad2);
        mIp_ad3 = preferences.getInt("ip_ad3", mIp_ad3);
        mIp_ad4 = preferences.getInt("ip_ad4", mIp_ad4);
        mIp_port = preferences.getInt("ip_port", mIp_port);
        mIp_command = preferences.getString("ip_command", mIp_command);
        mFrameSkip = preferences.getInt("frameSkip", mFrameSkip);

        StringBuilder sb = new StringBuilder();
        String s_http = "http://";
        String s_dot = ".";
        String s_colon = ":";
        String s_slash = "/";
        sb.append(s_http);
        sb.append(mIp_ad1);
        sb.append(s_dot);
        sb.append(mIp_ad2);
        sb.append(s_dot);
        sb.append(mIp_ad3);
        sb.append(s_dot);
        sb.append(mIp_ad4);
        sb.append(s_colon);
        sb.append(mIp_port);
        sb.append(s_slash);
        sb.append(mIp_command);
        mURL = new String(sb);

        mMv = (MjpegView) findViewById(R.id.mv);
        if (mMv != null) {
            mMv.setResolution(mWidth, mHeight);
            mMv.setFrameSkip(mFrameSkip);
        }

        setTitle(R.string.title_connecting);
        new DoRead().execute(mURL);
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!mResolvingError) {
            mGoogleApiClient.connect();
        }
    }

    @Override
    protected void onDestroy() {

        mNotifyMgr.cancel(mNotificationId);

        if (mMv != null) {
            if (mMv.isStreaming()) {
                mMv.stopPlayback();
            }
        }
        try {
            Thread.sleep(100);
        } catch (Exception e) {
        }
        if (!mResolvingError) {
            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
            mGoogleApiClient.disconnect();
        }
        if (mMv != null) {
            mMv.freeCameraMemory();
        }
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.option_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.settings:
            Intent settings_intent = new Intent(MainActivity.this, SettingsActivity.class);
            settings_intent.putExtra("width", mWidth);
            settings_intent.putExtra("height", mHeight);
            settings_intent.putExtra("ip_ad1", mIp_ad1);
            settings_intent.putExtra("ip_ad2", mIp_ad2);
            settings_intent.putExtra("ip_ad3", mIp_ad3);
            settings_intent.putExtra("ip_ad4", mIp_ad4);
            settings_intent.putExtra("ip_port", mIp_port);
            settings_intent.putExtra("ip_command", mIp_command);
            settings_intent.putExtra("frameSkip", mFrameSkip);
            startActivityForResult(settings_intent, REQUEST_SETTINGS);
            return true;
        case R.id.start_wearable_activity:
            new StartWearableActivityTask().execute();
            return true;
        }
        return false;
    }

    @Override //ConnectionCallbacks
    public void onConnected(Bundle connectionHint) {
        LOGD(TAG, "Google API Client was connected");
        mResolvingError = false;
        Wearable.MessageApi.addListener(mGoogleApiClient, this);
        Wearable.NodeApi.addListener(mGoogleApiClient, this);
    }

    @Override //ConnectionCallbacks
    public void onConnectionSuspended(int cause) {
        LOGD(TAG, "Connection to Google API client was suspended");
    }

    @Override //OnConnectionFailedListener
    public void onConnectionFailed(ConnectionResult result) {
        if (mResolvingError) {
            // Already attempting to resolve an error.
            return;
        } else if (result.hasResolution()) {
            try {
                mResolvingError = true;
                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
            } catch (IntentSender.SendIntentException e) {
                // There was an error with the resolution intent. Try again.
                mGoogleApiClient.connect();
            }
        } else {
            Log.e(TAG, "Connection to Google API client has failed");
            mResolvingError = false;
            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
        }
    }

    @Override //MessageListener
    public void onMessageReceived(final MessageEvent messageEvent) {
        if (messageEvent.getPath().equals(NOTIFY_IMAGE_RECEIPT_PATH)) {
            mSendableImage = true;
        }
    }

    @Override //NodeListener
    public void onPeerConnected(final Node peer) {
        LOGD(TAG, "onPeerConnected: " + peer);
    }

    @Override //NodeListener
    public void onPeerDisconnected(final Node peer) {
        LOGD(TAG, "onPeerDisconnected: " + peer);
    }

    private Collection<String> getNodes() {
        HashSet<String> results = new HashSet<String>();
        NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();

        for (Node node : nodes.getNodes()) {
            results.add(node.getId());
        }

        return results;
    }

    private void sendStartActivityMessage(String node) {
        Wearable.MessageApi.sendMessage(mGoogleApiClient, node, START_ACTIVITY_PATH, new byte[0])
                .setResultCallback(new ResultCallback<SendMessageResult>() {
                    @Override
                    public void onResult(SendMessageResult sendMessageResult) {
                        if (!sendMessageResult.getStatus().isSuccess()) {
                            Log.e(TAG, "Failed to send message with status code: "
                                    + sendMessageResult.getStatus().getStatusCode());
                        }
                    }
                });
    }

    private class StartWearableActivityTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... args) {
            mSendableImage = true;
            Collection<String> nodes = getNodes();
            for (String node : nodes) {
                sendStartActivityMessage(node);
            }
            return null;
        }
    }

    /**
     * As simple wrapper around Log.d
     */
    private static void LOGD(final String tag, String message) {
        if (Log.isLoggable(tag, Log.DEBUG)) {
            Log.d(tag, message);
        }
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case REQUEST_SETTINGS:
            if (resultCode == Activity.RESULT_OK) {
                mWidth = data.getIntExtra("width", mWidth);
                mHeight = data.getIntExtra("height", mHeight);
                mIp_ad1 = data.getIntExtra("ip_ad1", mIp_ad1);
                mIp_ad2 = data.getIntExtra("ip_ad2", mIp_ad2);
                mIp_ad3 = data.getIntExtra("ip_ad3", mIp_ad3);
                mIp_ad4 = data.getIntExtra("ip_ad4", mIp_ad4);
                mIp_port = data.getIntExtra("ip_port", mIp_port);
                mIp_command = data.getStringExtra("ip_command");
                mFrameSkip = data.getIntExtra("frameSkip", mFrameSkip);

                if (mMv != null) {
                    mMv.setResolution(mWidth, mHeight);
                    mMv.setFrameSkip(mFrameSkip);
                }
                SharedPreferences preferences = getSharedPreferences("SAVED_VALUES", MODE_PRIVATE);
                SharedPreferences.Editor editor = preferences.edit();
                editor.putInt("width", mWidth);
                editor.putInt("height", mHeight);
                editor.putInt("ip_ad1", mIp_ad1);
                editor.putInt("ip_ad2", mIp_ad2);
                editor.putInt("ip_ad3", mIp_ad3);
                editor.putInt("ip_ad4", mIp_ad4);
                editor.putInt("ip_port", mIp_port);
                editor.putString("ip_command", mIp_command);
                editor.putInt("frameSkip", mFrameSkip);

                editor.commit();

                new RestartApp().execute();
            }
            break;
        }
    }

    public void setImageError() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                setTitle(R.string.title_imageerror);
                return;
            }
        });
    }

    public class DoRead extends AsyncTask<String, Void, MjpegInputStream> {
        protected MjpegInputStream doInBackground(String... url) {
            //TODO: if camera has authentication deal with it and don't just not work
            HttpResponse res = null;
            DefaultHttpClient httpclient = new DefaultHttpClient();
            HttpParams httpParams = httpclient.getParams();
            HttpConnectionParams.setConnectionTimeout(httpParams, 5 * 1000);
            HttpConnectionParams.setSoTimeout(httpParams, 5 * 1000);
            if (DEBUG)
                Log.d(TAG, "1. Sending http request");
            try {
                res = httpclient.execute(new HttpGet(URI.create(url[0])));
                if (DEBUG)
                    Log.d(TAG, "2. Request finished, status = " + res.getStatusLine().getStatusCode());
                if (res.getStatusLine().getStatusCode() == 401) {
                    //You must turn off camera User Access Control before this will work
                    return null;
                }
                return new MjpegInputStream(res.getEntity().getContent());
            } catch (ClientProtocolException e) {
                if (DEBUG) {
                    e.printStackTrace();
                    Log.d(TAG, "Request failed-ClientProtocolException", e);
                }
                //Error connecting to camera
            } catch (IOException e) {
                if (DEBUG) {
                    e.printStackTrace();
                    Log.d(TAG, "Request failed-IOException", e);
                }
                //Error connecting to camera
            }
            return null;
        }

        protected void onPostExecute(MjpegInputStream result) {
            mMv.setSource(result);
            if (result != null) {
                result.setSkip(1);
                setTitle(R.string.app_name);
                mNotifyMgr.notify(mNotificationId, mBuilder.build());
            } else {
                setTitle(R.string.title_disconnected);
            }
            mMv.setDisplayMode(MjpegView.SIZE_BEST_FIT);
            mMv.showFps(false);
        }
    }

    public class RestartApp extends AsyncTask<Void, Void, Void> {
        protected Void doInBackground(Void... v) {
            MainActivity.this.finish();
            return null;
        }

        protected void onPostExecute(Void v) {
            startActivity((new Intent(MainActivity.this, MainActivity.class)));
        }
    }

    public void sendImage2Wear(Bitmap srcBmp) {
        if (mSendableImage) {

            Bitmap dstBmp = Bitmap.createBitmap(srcBmp, srcBmp.getWidth() / 2 - srcBmp.getHeight() / 2, 0,
                    srcBmp.getHeight(), srcBmp.getHeight());

            Bitmap dstBmp2 = Bitmap.createScaledBitmap(dstBmp, 280, 280, false);

            sendImage(toAsset(dstBmp2));
        }
    }

    /**
     * Builds an {@link com.google.android.gms.wearable.Asset} from a bitmap. The image that we get
     * back from the camera in "data" is a thumbnail size. Typically, your image should not exceed
     * 320x320 and if you want to have zoom and parallax effect in your app, limit the size of your
     * image to 640x400. Resize your image before transferring to your wearable device.
     */
    private static Asset toAsset(Bitmap bitmap) {
        ByteArrayOutputStream byteStream = null;
        try {
            byteStream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteStream);
            return Asset.createFromBytes(byteStream.toByteArray());
        } finally {
            if (null != byteStream) {
                try {
                    byteStream.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
    }

    /**
     * Sends the asset that was created form the photo we took by adding it to the Data Item store.
     */
    private void sendImage(Asset asset) {
        PutDataMapRequest dataMap = PutDataMapRequest.create(SEND_IMAGE_PATH);
        dataMap.getDataMap().putAsset(SEND_IMAGE_KEY, asset);
        PutDataRequest request = dataMap.asPutDataRequest();
        Wearable.DataApi.putDataItem(mGoogleApiClient, request);
    }
}