com.cubic9.android.droidglove.Main.java Source code

Java tutorial

Introduction

Here is the source code for com.cubic9.android.droidglove.Main.java

Source

package com.cubic9.android.droidglove;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.http.conn.util.InetAddressUtils;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.illposed.osc.OSCListener;
import com.illposed.osc.OSCMessage;
import com.illposed.osc.OSCPort;
import com.illposed.osc.OSCPortIn;
import com.illposed.osc.OSCPortOut;

/**
 * Main class of DroidGlove.
 *
 * DroidGlove is an Android app for controlling the motion of hand
 * in virtual reality world which is provided by the software such as Unity.
 * 
 * The latest version of the sources are available at following URL.
 * <https://github.com/cubic9com/DroidGlove/>
 * 
 * DroidGlove requires JavaOSC which was developed
 * by C. Ramakrishnan, Illposed Software.
 * 
 * Get JavaOSC at following URL.
 * <https://github.com/hoijui/JavaOSC/tree/e2a1667cb198675958012d91a374f9e9705195be>
 * 
 * Copyright (C) 2014, cubic9com All rights reserved.
 * This code is licensed under the BSD 3-Clause license.
 * See file LICENSE for more information.
 * 
 * @author cubic9com
 */
public class Main extends Activity implements SensorEventListener {
    /** numbers for average because of flicker */
    private static final int AVERAGE_AMOUNT = 15;
    /** IP address key for preference */
    private static final String PREF_KEY_IP = "ip";
    /** vibration duration time key for preference */
    private static final String PREF_KEY_VIBRATION_TIME = "vibration_time";
    /** default vibration duration time */
    private static final int DEFALUT_VIBRATION_TIME = 50;
    /** OSC address for sending message from phone to PC */
    private static final String OSC_ADDRESS_TO_PC = "/droidglove_to_pc";
    /** OSC address for sending message from PC to phone */
    private static final String OSC_ADDRESS_TO_PHONE = "/droidglove_to_phone";

    /** view for message to user */
    private TextView mTextViewMessage;
    /** view for IP address */
    private TextView mTextViewIp;

    /*
     * // for debug
     * private TextView textViewX; private TextView textViewY; private TextView
     * textViewZ; private TextView textViewGrip;
     */

    // sensors
    /** Sensor Manager for detect angle */
    private SensorManager mSensorManager;
    /** Wake Lock for preventing sleep */
    private WakeLock mWakeLock;
    /** Vibrator for haptics */
    private Vibrator mVibrator;

    // settings
    /** IP address of PC */
    private String ip;
    /** vibration duration time */
    private int userVibrationTime;

    // sensors are not active soon after launch
    /** flag for sensor is initialized or not  */
    private boolean mInitirizedSensor = false;
    /** counter for initializing sensor */
    private int mCountBeforeInit = 0;

    // for getting value of sensors
    /** geomagnetic value */
    private float[] geomagnetic = new float[3];
    /** gravity value */
    private float[] gravity = new float[3];

    // for detecting angles
    /** original angles of phone */
    private float[][] mOrigAngles = new float[3][AVERAGE_AMOUNT];
    /** yaw angle when user calibrate sensor */
    private float initYaw;

    // for detecting grip
    /** point when finger down */
    private float mYPointWhenDown = 0;
    /** grip amount */
    private float mYDiff = 0;

    // for OSC send/receive value
    /** OSC Sender for motion */
    private OSCPortOut mSender;
    /** OSC Receiver for vibration */
    private OSCPortIn mReceiver;

    public Main() {
        super();
    }

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

        // prevent sleep
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "DroidGlove");

        // vibrator
        mVibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);

        // sensor
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);

        // show error dialog if Wi-Fi off and Tethering off.
        WifiManager wifi = (WifiManager) getSystemService(WIFI_SERVICE);
        if ((!wifi.isWifiEnabled()) && (!isTethering(wifi))) {
            new AlertDialog.Builder(Main.this).setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(getString(R.string.dialog_error_title))
                    .setMessage(getString(R.string.dialog_error_wifi_off))
                    .setPositiveButton(R.string.dialog_error_ok, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            appEnd();
                        }
                    }).create().show();
        }

        // get IP address
        getPreferences();

        // check IP address
        if (ip.equals("")) {
            showToast(R.string.toast_error_ip_null);
            Intent intent = new Intent(this, Settings.class);
            startActivityForResult(intent, 0);
        } else if (!ip.matches("^([0-9]{1,3}\\.){3}[0-9]{1,3}$")) {
            showToast(R.string.toast_error_ip_wrong);
            Intent intent = new Intent(this, Settings.class);
            startActivityForResult(intent, 0);
        }

        // init angle
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < AVERAGE_AMOUNT; j++) {
                mOrigAngles[i][j] = 0;
            }
        }

        // show screen
        setContentView(R.layout.main);

        // find views
        mTextViewMessage = (TextView) findViewById(R.id.textViewMessage);
        mTextViewIp = (TextView) findViewById(R.id.textViewIp);
        /*
         * textViewX = (TextView) findViewById(R.id.textViewX); textViewY =
         * (TextView) findViewById(R.id.textViewY); textViewZ = (TextView)
         * findViewById(R.id.textViewZ); textViewGrip = (TextView)
         * findViewById(R.id.textViewGrip);
         */
        Button button = (Button) findViewById(R.id.buttonReset);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mInitirizedSensor = false;
                mTextViewMessage.setText(R.string.message_swipe_down);
            }
        });
    }

    @Override
    public void onResume() {
        super.onResume();

        // show IP address
        mTextViewIp.setText("IP: " + getIPAddress());

        // prevent sleep
        mWakeLock.acquire();

        // get values of sensors
        mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_GAME);
        mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                SensorManager.SENSOR_DELAY_GAME);

        // get network information
        InetAddress mRemoteIP = null;
        try {
            mRemoteIP = InetAddress.getByName(ip);
        } catch (UnknownHostException e) {
            new AlertDialog.Builder(Main.this).setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(getString(R.string.dialog_error_title))
                    .setMessage(getString(R.string.dialog_error_get_ip))
                    .setPositiveButton(R.string.dialog_error_ok, null).create().show();
            e.printStackTrace();
        }

        // Bring the IP Address and port together to form our OSC Sender
        try {
            mSender = new OSCPortOut(mRemoteIP, OSCPort.defaultSCOSCPort());
        } catch (SocketException e) {
            new AlertDialog.Builder(Main.this).setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(getString(R.string.dialog_error_title))
                    .setMessage(getString(R.string.dialog_error_sender))
                    .setPositiveButton(R.string.dialog_error_ok, null).create().show();
            e.printStackTrace();
        }

        try {
            mReceiver = new OSCPortIn(OSCPort.defaultSCOSCPort());
        } catch (SocketException e) {
            new AlertDialog.Builder(Main.this).setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(getString(R.string.dialog_error_title))
                    .setMessage(getString(R.string.dialog_error_receiver))
                    .setPositiveButton(R.string.dialog_error_ok, null).create().show();
            e.printStackTrace();
        }
        OSCListener listener = new OSCListener() {
            public void acceptMessage(java.util.Date time, OSCMessage message) {
                Object[] args = message.getArguments();
                int vibrationTime = (Integer) args[0];
                if (vibrationTime == 0) {
                    mVibrator.vibrate(userVibrationTime);
                } else {
                    mVibrator.vibrate(vibrationTime);
                }
            }
        };
        mReceiver.addListener(OSC_ADDRESS_TO_PHONE, listener);
        mReceiver.startListening();
    }

    @Override
    public void onPause() {
        super.onPause();
        mWakeLock.release();
        mReceiver.stopListening();
        mReceiver.close();
        mSensorManager.unregisterListener(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mReceiver.close();
    }

    @Override
    public boolean onCreateOptionsMenu(android.view.Menu menu) {
        super.onCreateOptionsMenu(menu);

        menu.add(0, Menu.FIRST, 0, R.string.menu_settings).setIcon(android.R.drawable.ic_menu_preferences);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case Menu.FIRST:
            Intent intent = new Intent(Main.this, Settings.class);
            startActivityForResult(intent, 0);
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        getPreferences();
        // IP??
        if (ip.equals("")) {
            showToast(R.string.toast_error_ip_null);
        } else if (!ip.matches("^([0-9]{1,3}\\.){3}[0-9]{1,3}$")) {
            showToast(R.string.toast_error_ip_wrong);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mYPointWhenDown = event.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            mYDiff = event.getY() - mYPointWhenDown;
            if (mYDiff < 0) {
                mYDiff = 0;
                mYPointWhenDown = event.getY();
            } else if (mYDiff > 100) {
                mYDiff = 100;
                mYPointWhenDown = event.getY() - 100;
            }
            break;
        case MotionEvent.ACTION_UP:
            mYDiff = 0;
            break;
        default:
            break;
        }
        return true;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        Float[] avgAngles = new Float[3];
        float[] rotationMatrix = new float[9];
        float[] attitude = new float[3];

        switch (event.sensor.getType()) {
        case Sensor.TYPE_MAGNETIC_FIELD:
            geomagnetic = event.values.clone();
            break;
        case Sensor.TYPE_ACCELEROMETER:
            gravity = event.values.clone();
            break;
        }

        if (geomagnetic != null && gravity != null) {
            SensorManager.getRotationMatrix(rotationMatrix, null, gravity, geomagnetic);
            SensorManager.getOrientation(rotationMatrix, attitude);

            if (!mInitirizedSensor) {
                if (mCountBeforeInit > 20) {
                    mInitirizedSensor = true;
                    initYaw = (float) Math.toDegrees(attitude[0]);
                } else {
                    mCountBeforeInit++;
                }
            }

            for (int i = 0; i < 3; i++) {
                for (int j = AVERAGE_AMOUNT - 1; j > 0; j--) {
                    mOrigAngles[i][j] = mOrigAngles[i][j - 1];
                }
            }

            mOrigAngles[0][0] = (float) Math.toDegrees(attitude[0]) - initYaw;
            mOrigAngles[1][0] = (float) Math.toDegrees(attitude[1]);
            mOrigAngles[2][0] = (float) Math.toDegrees(attitude[2]);

            for (int i = 0; i < 3; i++) {
                avgAngles[i] = 0f;
                for (int j = 0; j < AVERAGE_AMOUNT; j++) {
                    avgAngles[i] += mOrigAngles[i][j];
                }
                avgAngles[i] /= AVERAGE_AMOUNT;
            }

            /*
             * textViewX.setText(avgAngles[0].toString());
             * textViewY.setText(avgAngles[1].toString());
             * textViewZ.setText(avgAngles[2].toString());
             * textViewGrip.setText(Float.toString(mYDiff));
             */

            // create message for send
            List<Object> valueList = new ArrayList<Object>();
            valueList.add(avgAngles[1]);
            valueList.add(avgAngles[0]);
            valueList.add(-avgAngles[2]);
            valueList.add(Integer.valueOf((int) mYDiff));
            OSCMessage message = new OSCMessage(OSC_ADDRESS_TO_PC, valueList);

            // send
            try {
                mSender.send(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

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

    /**
     * load preferences
     */
    private void getPreferences() {
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getBaseContext());

        ip = sharedPref.getString(PREF_KEY_IP, "");

        try {
            userVibrationTime = Integer.parseInt(sharedPref.getString(PREF_KEY_VIBRATION_TIME, ""));
        } catch (NumberFormatException e) {
            userVibrationTime = DEFALUT_VIBRATION_TIME;
            SharedPreferences.Editor editor = sharedPref.edit();
            editor.putString(PREF_KEY_VIBRATION_TIME, Integer.toString(DEFALUT_VIBRATION_TIME));
            editor.commit();
        }
    }

    /**
     * get IP Address of phone
     * @return IP address String or error message
     */
    private String getIPAddress() {
        Enumeration<NetworkInterface> interfaces = null;
        try {
            interfaces = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
            new AlertDialog.Builder(Main.this).setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(getString(R.string.dialog_error_title))
                    .setMessage(getString(R.string.dialog_error_get_ip))
                    .setPositiveButton(R.string.dialog_error_ok, null).create().show();
            e.printStackTrace();
        }

        while (interfaces.hasMoreElements()) {
            NetworkInterface network = interfaces.nextElement();
            Enumeration<InetAddress> addresses = network.getInetAddresses();

            while (addresses.hasMoreElements()) {
                String address = addresses.nextElement().getHostAddress();

                // If found not 127.0.0.1 and not 0.0.0.0, return it
                if (!"127.0.0.1".equals(address) && !"0.0.0.0".equals(address)
                        && InetAddressUtils.isIPv4Address(address)) {
                    return address;
                }
            }
        }

        return "An error occured while getting IP address.";
    }

    /**
     * detect tethering function is active or not
     * @param manager WiFi manager
     * @return active or not
     */
    private boolean isTethering(final WifiManager manager) {
        Method method = null;
        try {
            method = manager.getClass().getDeclaredMethod("isWifiApEnabled");
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        method.setAccessible(true);
        try {
            return (Boolean) method.invoke(manager);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * show short message by Toast
     * @param code message code
     */
    private void showToast(int code) {
        Toast toast = Toast.makeText(Main.this, getString(code), Toast.LENGTH_SHORT);
        toast.setGravity(Gravity.CENTER, 0, 0);
        toast.show();
    }

    /**
     * end app
     */
    private void appEnd() {
        super.finish();
    }
}