Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Melanie Zhao
Created July 12, 2015

PRG02: Excitement Documentation

Mobile and android smartwatch application that lets users upload a photo to twitter when excited.

Full instructions provided20
PRG02: Excitement Documentation

Code

MobileMainActivity.java

Java
package melaniezhao.excitementdocumentation;

import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;

import com.twitter.sdk.android.Twitter;
import com.twitter.sdk.android.core.TwitterAuthConfig;
import io.fabric.sdk.android.Fabric;
import com.twitter.sdk.android.core.Callback;
import com.twitter.sdk.android.core.Result;
import com.twitter.sdk.android.core.TwitterException;
import com.twitter.sdk.android.core.TwitterSession;
import com.twitter.sdk.android.core.identity.TwitterLoginButton;


public class MobileMainActivity extends Activity {

    // Note: Your consumer key and secret should be obfuscated in your source code before shipping.
    private static final String TWITTER_KEY = "nFJsTaEXJDBtt1pT2NvOMQxey";
    private static final String TWITTER_SECRET = "0XSCETadrqpK4u86Fs6Bn2lrh3gciR3sTA7toGggjdInDSnyBG";

    private TwitterLoginButton loginButton;
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        TwitterAuthConfig authConfig = new TwitterAuthConfig(TWITTER_KEY, TWITTER_SECRET);
        Fabric.with(this, new Twitter(authConfig));
        setContentView(R.layout.activity_mobile_main);

        loginButton = (TwitterLoginButton) findViewById(R.id.login_button);
        loginButton.setCallback(new Callback<TwitterSession>() {
            @Override
            public void success(Result<TwitterSession> result) {
                TextView v = (TextView) findViewById(R.id.textView2);
                v.setText(getString(R.string.done));
                loginButton.setVisibility(View.GONE);

                mHandler.postDelayed(new Runnable() {
                    public void run() {
                        Intent intent = new Intent(Intent.ACTION_MAIN);
                        intent.addCategory(Intent.CATEGORY_HOME);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        startActivity(intent);
                    }
                }, 2000);
            }

            @Override
            public void failure(TwitterException exception) {
                TextView v = (TextView) findViewById(R.id.textView2);
                v.setText(getString(R.string.try_again));
            }
        });

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // Pass the activity result to the login button.
        loginButton.onActivityResult(requestCode, resultCode, data);
    }

    @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_mobile_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_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

MobileSubmitActivity.java

Java
getOutputMediaFile and getOutputMediaFileUri functions are from Android Developers (http://developer.android.com/index.html).
package melaniezhao.excitementdocumentation;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;

import com.twitter.sdk.android.core.models.Tweet;
import com.twitter.sdk.android.tweetcomposer.TweetComposer;
import com.twitter.sdk.android.tweetui.SearchTimeline;
import com.twitter.sdk.android.tweetui.TweetTimelineListAdapter;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import io.fabric.sdk.android.Fabric;


public class MobileSubmitActivity extends Activity {
    public static final int CAMERA_REQUEST = 10;
    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;
    public static final int notificationId = 1;

    private ImageView imageView;
    private Uri fileUri;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
        startActivityForResult(cameraIntent, CAMERA_REQUEST);

        setContentView(R.layout.activity_mobile_submit);
        imageView = (ImageView) findViewById(R.id.imageContainer);
        imageView.setImageResource(R.drawable.good);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            if (requestCode == CAMERA_REQUEST) {
                Fabric.with(this, new TweetComposer());
                TweetComposer.Builder builder = new TweetComposer.Builder(this)
                        .text(getString(R.string.text_field))
                        .image(fileUri);
                builder.show();

//                final SearchTimeline searchTimeline = new SearchTimeline.Builder().query("#cs160excited").build();
//                final TweetTimelineListAdapter adapter = new TweetTimelineListAdapter(this, searchTimeline);

                NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                        .setSmallIcon(R.drawable.cat)
                        .setContentTitle("WOW")
                        .setContentText("Someone else was excited about this!");

                NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
                notificationManager.notify(notificationId, notificationBuilder.build());
            }
        }
    }


    @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_mobile_submit, 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_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }


    /** Create a file Uri for saving an image or video */
    private static Uri getOutputMediaFileUri(int type){
        return Uri.fromFile(getOutputMediaFile(type));
    }

    /** Create a File for saving an image or video */
    private static File getOutputMediaFile(int type){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "MyCameraApp");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                Log.d("MyCameraApp", "failed to create directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "IMG_"+ timeStamp + ".jpg");
        } else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "VID_"+ timeStamp + ".mp4");
        } else {
            return null;
        }

        return mediaFile;
    }

}

ReceiverService.java

Java
The function onMessageReceived is from section 05 discussion slides.
package melaniezhao.excitementdocumentation;

import android.content.Intent;
import android.util.Log;

import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;

public class ReceiverService extends WearableListenerService {

    private static final String RECEIVER_SERVICE_PATH = "/picture";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onMessageReceived(MessageEvent messageEvent) {
//        if (messageEvent.getPath().equals(RECEIVER_SERVICE_PATH)) {
            Log.i("Success", "I got a message!");
            Intent submitActivity = new Intent(this, MobileSubmitActivity.class);
            submitActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(submitActivity);
//        }
    }
}

strings.xml

XML
<resources>
    <string name="app_name">Excitement Documentation</string>
    <string name="action_settings">Settings</string>
    <string name="title_activity_mobile_submit">MobileSubmitActivity</string>

    <!-- MobileMainActivity-->
    <string name="app_desc">Welcome to Excitement Documentation!\n</string>
    <string name="excited">Let\'s get excited!</string>
    <string name="done">All Done!</string>
    <string name="try_again">Try Again!</string>
    <!-- MobileSecondActivity-->
    <string name="text_field">#cs160excited </string>
    <string name="submit">tweet</string>
    <string name="desc"></string>

</resources>

wear.xml

XML
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="android_wear_capabilities">
        <item>take_picture</item>
    </string-array>
</resources>

SenderService.java

Java
The functions here are from slide #18 of section 04 (messages and twitter) slides and pickBestNodeId is from the Android Developers (http://developer.android.com/index.html) page.
package melaniezhao.excitementdocumentation;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.wearable.CapabilityApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.Wearable;

import java.util.Set;

public class SenderService extends Service {
    private GoogleApiClient mGoogleApiClient;
    private String CAPABILITY_NAME = "take_picture";
    private String RECEIVER_SERVICE_PATH = "/picture";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        this.mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(Bundle bundle) {
                        // Do something
                    }
                    @Override
                    public void onConnectionSuspended(int i) {
                        // Do something
                    }
                })
                .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
                    @Override
                    public void onConnectionFailed(ConnectionResult connectionResult) {
                        // Do something
                    }
                })
                .addApi(Wearable.API)
                .build();
        this.mGoogleApiClient.connect();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                CapabilityApi.GetCapabilityResult capResult =
                        Wearable.CapabilityApi.getCapability(
                                mGoogleApiClient, CAPABILITY_NAME,
                                CapabilityApi.FILTER_REACHABLE)
                                .await();

                Log.i("Success", "I sent a message!");

                Wearable.MessageApi.sendMessage(
                        mGoogleApiClient, pickBestNodeId(capResult.getCapability().getNodes()),
                        RECEIVER_SERVICE_PATH, new byte[3]
                );
            }
        });

        thread.start();
        return Service.START_STICKY;
    }

    private String pickBestNodeId(Set<Node> nodes) {
        String bestNodeId = null;
        // Find a nearby node or pick one arbitrarily
        for (Node node : nodes) {
            if (node.isNearby()) {
                return node.getId();
            }
            bestNodeId = node.getId();
        }
        return bestNodeId;
    }

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

SensorActivity.java

Java
Some of the contents here are from section 03 discussion slides.
package melaniezhao.excitementdocumentation;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.app.NotificationCompat.WearableExtender;

public class SensorActivity extends Service implements SensorEventListener {

    private SensorManager mSensorManager;
    private Sensor mSensor;

    private void initSensor(){
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
    }

    private void getAccelerometer(SensorEvent event) {
        float [] values = event.values;
        float x = values[0];
        float y = values[1];
        float z = values[2];
        int notificationId = 1;

        double accelerationSquareRoot = Math.sqrt(x * x + y * y + z * z);

        if (accelerationSquareRoot >= 20) {
            PendingIntent pendingIntent = PendingIntent.getService(this, 0, new Intent(this, SenderService.class),0);
            NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setContentTitle(getString(R.string.notification_title))
                    .setContentText(getString(R.string.notification_text))
                    .addAction(R.drawable.camera, getString(R.string.camera), pendingIntent);

            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
            notificationManager.notify(notificationId, notificationBuilder.build());
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initSensor();
        return START_STICKY;
    }

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

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            getAccelerometer(event);
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }
}

WatchMainActivity.java

Java
package melaniezhao.excitementdocumentation;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.wearable.view.WatchViewStub;
import android.widget.TextView;

public class WatchMainActivity extends Activity {

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_watch_main);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);
            }
        });
        Intent sensorActivity = new Intent(this, SensorActivity.class);
        startService(sensorActivity);
    }
}

activity_watch_main.xml

XML
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.WatchViewStub
    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" android:id="@+id/watch_view_stub"
    android:layout_width="match_parent" android:layout_height="match_parent"
    app:rectLayout="@layout/rect_activity_watch_main"
    app:roundLayout="@layout/round_activity_watch_main" tools:context=".WatchMainActivity"
    tools:deviceIds="wear"></android.support.wearable.view.WatchViewStub>

strings.xml

XML
<resources>
    <string name="app_name">Excitement Documentation</string>
    <string name="hello_round">Hello Round World!</string>
    <string name="hello_square">Hello Square World!</string>
    <string name="camera">Camera</string>
    <string name="notification_title">Excitement Detected!</string>
    <string name="notification_text">You seem excited! Document it!</string>
</resources>

activity_mobile_main.xml

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=".MobileMainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical|center_horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="@string/app_desc"
            android:id="@+id/textView"
            android:gravity="center_horizontal" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="@string/excited"
            android:id="@+id/textView2"
            android:layout_gravity="center_horizontal" />

        <com.twitter.sdk.android.core.identity.TwitterLoginButton
            android:id="@+id/login_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="69dp" />
    </LinearLayout>

</RelativeLayout>

activity_mobile_submit.xml

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="melaniezhao.excitementdocumentation.MobileSubmitActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:weightSum="1">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="350dp"
            android:contentDescription="@string/desc"
            android:id="@+id/imageContainer"
            android:layout_gravity="center_horizontal" />

    </LinearLayout>
</RelativeLayout>

Credits

Melanie Zhao

Melanie Zhao

3 projects • 1 follower

Comments