com.lauszus.launchpadflightcontrollerandroid.app.LaunchPadFlightControllerActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.lauszus.launchpadflightcontrollerandroid.app.LaunchPadFlightControllerActivity.java

Source

/*******************************************************************************
 * Copyright (C) 2015 Kristian Sloth Lauszus. All rights reserved.
 *
 * This software may be distributed and modified under the terms of the GNU
 * General Public License version 2 (GPL2) as published by the Free Software
 * Foundation and appearing in the file GPL2.TXT included in the packaging of
 * this file. Please note that GPL2 Section 2[b] requires that all works based
 * on this software must also be made publicly available under the terms of
 * the GPL2 ("Copyleft").
 *
 * Contact information
 * -------------------
 *
 * Kristian Sloth Lauszus
 * Web      :  http://www.lauszus.com
 * e-mail   :  lauszus@gmail.com
 ******************************************************************************/

package com.lauszus.launchpadflightcontrollerandroid.app;

import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;

import java.lang.ref.WeakReference;

public class LaunchPadFlightControllerActivity extends AppCompatActivity
        implements TabLayout.OnTabSelectedListener {
    private static final String TAG = LaunchPadFlightControllerActivity.class.getSimpleName();
    public static final boolean D = BuildConfig.DEBUG; // This is automatically set when building

    // Message types sent from the BluetoothChatService Handler
    public static final int MESSAGE_STATE_CHANGE = 1;
    public static final int MESSAGE_READ = 2;
    public static final int MESSAGE_DEVICE_NAME = 3;
    public static final int MESSAGE_DISCONNECTED = 4;
    public static final int MESSAGE_RETRY = 5;

    // Key names received from the BluetoothChatService Handler
    public static final String DEVICE_NAME = "device_name";
    public static final String TOAST = "toast";

    public static final String ANGLE_KP_VALUE = "angle_kp_value";
    public static final String HEADING_KP_VALUE = "heading_kp_value";
    public static final String ANGLE_MAX_INC_VALUE = "angle_max_inc_value";
    public static final String ANGLE_MAX_INC_SONAR_VALUE = "angle_max_inc_sonar_value";
    public static final String STICK_SCALING_ROLL_PITCH_VALUE = "stick_scaling_roll_pitch_value";
    public static final String STICK_SCALING_YAW_VALUE = "stick_scaling_yaw_value";

    public static final String KP_ROLL_PITCH_VALUE = "kp_roll_pitch_value";
    public static final String KI_ROLL_PITCH_VALUE = "ki_roll_pitch_value";
    public static final String KD_ROLL_PITCH_VALUE = "kd_roll_pitch_value";
    public static final String INT_LIMIT_ROLL_PITCH_VALUE = "int_limit_roll_pitch_value";

    public static final String KP_YAW_VALUE = "kp_yaw_value";
    public static final String KI_YAW_VALUE = "ki_yaw_value";
    public static final String KD_YAW_VALUE = "kd_yaw_value";
    public static final String INT_LIMIT_YAW_VALUE = "int_limit_yaw_value";

    public static final String KP_SONAR_ALT_HOLD_VALUE = "kp_sonar_alt_hold_value";
    public static final String KI_SONAR_ALT_HOLD_VALUE = "ki_sonar_alt_hold_value";
    public static final String KD_SONAR_ALT_HOLD_VALUE = "kd_sonar_alt_hold_value";
    public static final String INT_LIMIT_SONAR_ALT_HOLD_VALUE = "int_limit_sonar_alt_hold_value";

    public static final String KP_BARO_ALT_HOLD_VALUE = "kp_baro_alt_hold_value";
    public static final String KI_BARO_ALT_HOLD_VALUE = "ki_baro_alt_hold_value";
    public static final String KD_BARO_ALT_HOLD_VALUE = "kd_baro_alt_hold_value";
    public static final String INT_LIMIT_BARO_ALT_HOLD_VALUE = "int_limit_baro_alt_hold_value";

    public static final String ROLL_ANGLE = "roll_angle";
    public static final String PITCH_ANGLE = "pitch_angle";
    public static final String YAW_ANGLE = "yaw_angle";

    // Intent request codes
    private static final int REQUEST_CONNECT_DEVICE = 1;
    private static final int REQUEST_ENABLE_BT = 2;

    // Local Bluetooth adapter
    private BluetoothAdapter mBluetoothAdapter = null;
    private final BluetoothHandler mBluetoothHandler = new BluetoothHandler(this);
    // Member object for the chat services
    public BluetoothChatService mChatService = null;

    BluetoothDevice btDevice; // The BluetoothDevice object
    boolean btSecure; // If it's a new device we will pair with the device
    public static boolean stopRetrying;

    private Toast mToast;

    public int currentTabSelected;

    ViewPager mViewPager;
    ViewPagerAdapter mViewPagerAdapter;

    @Override
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_launch_pad_flight_controller);

        // Get local Bluetooth adapter
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
            mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
        else
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // If the adapter is null, then Bluetooth is not supported
        if (mBluetoothAdapter == null) {
            showToast("Bluetooth is not available", Toast.LENGTH_LONG);
            finish();
            return;
        }

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Create the adapter that will return a fragment for each of the primary sections of the app.
        mViewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());

        // Set up the ViewPager with the adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mViewPagerAdapter);

        TabLayout mTabLayout = (TabLayout) findViewById(R.id.tabs);
        mTabLayout.setupWithViewPager(mViewPager);
        mTabLayout.setOnTabSelectedListener(this);
        mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
        mTabLayout.setTabMode(TabLayout.MODE_FIXED);

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Keep the screen on
    }

    public void showToast(String message, int duration) {
        if (duration != Toast.LENGTH_SHORT && duration != Toast.LENGTH_LONG)
            throw new IllegalArgumentException();
        if (mToast != null)
            mToast.cancel(); // Close the toast if it's already open
        mToast = Toast.makeText(this, message, duration);
        mToast.show();
    }

    @Override
    public void onStart() {
        super.onStart();
        if (D)
            Log.d(TAG, "++ ON START ++");
        // If BT is not on, request that it be enabled.
        // setupChat() will then be called during onActivityResult
        if (!mBluetoothAdapter.isEnabled()) {
            if (D)
                Log.d(TAG, "Request enable BT");
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        } else
            setupBTService(); // Otherwise, setup the chat session
    }

    @Override
    public void onBackPressed() {
        if (mChatService != null) {
            new Handler().postDelayed(new Runnable() {
                public void run() {
                    mChatService.stop(); // Stop the Bluetooth chat services if the user exits the app
                }
            }, 1000); // Wait 1 second before closing the connection, this is needed as onPause() will send stop messages before closing
        }
        finish(); // Exits the app
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (D)
            Log.d(TAG, "- ON PAUSE -");
        if (mChatService != null) { // Send stop command and stop sending graph data command
            if (mChatService.getState() == BluetoothChatService.STATE_CONNECTED) {
                mChatService.mBluetoothProtocol.sendAngles((byte) 0); // Stop sending angles
            }
        }
    }

    private void setupBTService() {
        if (mChatService != null)
            return;

        if (D)
            Log.d(TAG, "setupBTService()");
        mChatService = new BluetoothChatService(mBluetoothHandler, mBluetoothAdapter); // Initialize the BluetoothChatService to perform Bluetooth connections
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if (D)
            Log.d(TAG, "onPrepareOptionsMenu");
        MenuItem menuItem = menu.findItem(R.id.action_connect); // Find item
        if (mChatService != null && mChatService.getState() == BluetoothChatService.STATE_CONNECTED)
            menuItem.setIcon(R.drawable.device_access_bluetooth_connected);
        else
            menuItem.setIcon(R.drawable.device_access_bluetooth);
        return true;
    }

    @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_launch_pad_flight_controller, 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.
        switch (item.getItemId()) {
        case R.id.action_connect:
            // Launch the DeviceListActivity to see devices and do scan
            Intent serverIntent = new Intent(this, DeviceListActivity.class);
            startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
            return true;
        case R.id.action_settings:
            // Open up the settings dialog
            new SettingsDialogFragment().show(getSupportFragmentManager(), null);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        if (D)
            Log.d(TAG, "onTabSelected: " + tab.getPosition());

        currentTabSelected = tab.getPosition();
        // When the given tab is selected, switch to the corresponding page in the ViewPager.
        mViewPager.setCurrentItem(currentTabSelected);

        if (mChatService != null && mChatService.getState() == BluetoothChatService.STATE_CONNECTED) {
            if (checkTab(ViewPagerAdapter.SETTINGS_FRAGMENT))
                mChatService.mBluetoothProtocol.getSettings();
            else if (checkTab(ViewPagerAdapter.PID_FRAGMENT)) {
                mChatService.mBluetoothProtocol.getPIDRollPitch();
                mChatService.mBluetoothProtocol.getPIDYaw();
                mChatService.mBluetoothProtocol.getPIDSonarAltHold();
                mChatService.mBluetoothProtocol.getPIDBaroAltHold();
            } else if (checkTab(ViewPagerAdapter.GRAPH_FRAGMENT)) {
                if (GraphFragment.mToggleButton != null) {
                    if (GraphFragment.mToggleButton.isChecked())
                        mChatService.mBluetoothProtocol.sendAngles((byte) 1); // Request data
                    else
                        mChatService.mBluetoothProtocol.sendAngles((byte) 0); // Stop sending data
                }
            }
        }

        if (!checkTab(ViewPagerAdapter.GRAPH_FRAGMENT)) { // Needed when the user rotates the screen
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // Hide the keyboard
            imm.hideSoftInputFromWindow(getWindow().getDecorView().getApplicationWindowToken(), 0);
        }
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {
        if (D)
            Log.d(TAG, "onTabUnselected: " + tab.getPosition() + " " + currentTabSelected);

        if (mChatService != null && mChatService.getState() == BluetoothChatService.STATE_CONNECTED) {
            if (checkTab(ViewPagerAdapter.GRAPH_FRAGMENT))
                mChatService.mBluetoothProtocol.sendAngles((byte) 0); // Stop sending data
        }

        if (checkTab(ViewPagerAdapter.GRAPH_FRAGMENT)) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // Hide the keyboard
            imm.hideSoftInputFromWindow(getWindow().getDecorView().getApplicationWindowToken(), 0);
        }
    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {
    }

    public boolean checkTab(int tab) {
        return (currentTabSelected == tab);
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (D)
            Log.d(TAG, "onActivityResult " + resultCode);
        switch (requestCode) {
        case REQUEST_CONNECT_DEVICE:
            // When DeviceListActivity returns with a device to connect to
            if (resultCode == Activity.RESULT_OK)
                connectDevice(data, false);
            break;
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK)
                setupBTService(); // Bluetooth is now enabled, so set up a chat session
            else {
                // User did not enable Bluetooth or an error occurred
                if (D)
                    Log.d(TAG, "BT not enabled");
                showToast(getString(R.string.bt_not_enabled_leaving), Toast.LENGTH_SHORT);
                finish();
            }
        }
    }

    private void connectDevice(Intent data, boolean retry) {
        if (retry) {
            if (btDevice != null && !stopRetrying) {
                mChatService.start(); // This will stop all the running threads
                mChatService.connect(btDevice, btSecure); // Attempt to connect to the device
            }
        } else { // It's a new connection
            stopRetrying = false;
            mChatService.newConnection = true;
            mChatService.start(); // This will stop all the running threads
            if (data.getExtras() == null)
                return;
            String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); // Get the device Bluetooth address
            btSecure = data.getExtras().getBoolean(DeviceListActivity.EXTRA_NEW_DEVICE); // If it's a new device we will pair with the device
            btDevice = mBluetoothAdapter.getRemoteDevice(address); // Get the BluetoothDevice object
            mChatService.nRetries = 0; // Reset retry counter
            mChatService.connect(btDevice, btSecure); // Attempt to connect to the device
            showToast(getString(R.string.connecting), Toast.LENGTH_SHORT);
        }
    }

    public Fragment getFragment(int item) {
        return (Fragment) mViewPagerAdapter.instantiateItem(mViewPager, item);
    }

    // The Handler class that gets information back from the BluetoothChatService
    private static class BluetoothHandler extends Handler {
        private final WeakReference<LaunchPadFlightControllerActivity> mActivity; // See: http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html
        SettingsFragment settingsFragment;
        PIDFragment pidFragment;
        GraphFragment graphFragment;
        private String mConnectedDeviceName; // Name of the connected device

        BluetoothHandler(LaunchPadFlightControllerActivity activity) {
            mActivity = new WeakReference<LaunchPadFlightControllerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            LaunchPadFlightControllerActivity mLaunchPadFlightControllerActivity = mActivity.get();
            if (mLaunchPadFlightControllerActivity == null)
                return;
            switch (msg.what) {
            case MESSAGE_STATE_CHANGE:
                mLaunchPadFlightControllerActivity.supportInvalidateOptionsMenu();
                if (D)
                    Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
                switch (msg.arg1) {
                case BluetoothChatService.STATE_CONNECTED:
                    mLaunchPadFlightControllerActivity
                            .showToast(mLaunchPadFlightControllerActivity.getString(R.string.connected_to) + " "
                                    + mConnectedDeviceName, Toast.LENGTH_SHORT);
                    if (mLaunchPadFlightControllerActivity.mChatService == null)
                        return;
                    Handler mHandler = new Handler();
                    mHandler.postDelayed(new Runnable() {
                        public void run() {
                            LaunchPadFlightControllerActivity mLaunchPadFlightControllerActivity = mActivity.get();
                            if (mLaunchPadFlightControllerActivity != null) {
                                mLaunchPadFlightControllerActivity.mChatService.mBluetoothProtocol.getSettings();
                                mLaunchPadFlightControllerActivity.mChatService.mBluetoothProtocol
                                        .getPIDRollPitch();
                                mLaunchPadFlightControllerActivity.mChatService.mBluetoothProtocol.getPIDYaw();
                                mLaunchPadFlightControllerActivity.mChatService.mBluetoothProtocol
                                        .getPIDSonarAltHold();
                                mLaunchPadFlightControllerActivity.mChatService.mBluetoothProtocol
                                        .getPIDBaroAltHold();
                            }
                        }
                    }, 1000); // Wait 1 second before sending the message

                    if (mLaunchPadFlightControllerActivity.checkTab(ViewPagerAdapter.GRAPH_FRAGMENT)) {
                        mHandler.postDelayed(new Runnable() {
                            public void run() {
                                LaunchPadFlightControllerActivity mLaunchPadFlightControllerActivity = mActivity
                                        .get();
                                if (mLaunchPadFlightControllerActivity != null) {
                                    if (GraphFragment.mToggleButton.isChecked())
                                        mLaunchPadFlightControllerActivity.mChatService.mBluetoothProtocol
                                                .sendAngles((byte) 1); // Request data
                                    else
                                        mLaunchPadFlightControllerActivity.mChatService.mBluetoothProtocol
                                                .sendAngles((byte) 0); // Stop sending data
                                }
                            }
                        }, 2000); // Wait 2 seconds before sending the message
                    }
                    break;
                case BluetoothChatService.STATE_CONNECTING:
                    break;
                }
                settingsFragment = (SettingsFragment) mLaunchPadFlightControllerActivity
                        .getFragment(ViewPagerAdapter.SETTINGS_FRAGMENT);
                if (settingsFragment != null)
                    settingsFragment.updateSendButton();
                pidFragment = (PIDFragment) mLaunchPadFlightControllerActivity
                        .getFragment(ViewPagerAdapter.PID_FRAGMENT);
                if (pidFragment != null)
                    pidFragment.updateSendButton();
                break;

            case MESSAGE_READ:
                Bundle data = msg.getData();
                if (data != null) {
                    if (data.containsKey(ANGLE_KP_VALUE) && data.containsKey(HEADING_KP_VALUE)
                            && data.containsKey(ANGLE_MAX_INC_VALUE) && data.containsKey(ANGLE_MAX_INC_SONAR_VALUE)
                            && data.containsKey(STICK_SCALING_ROLL_PITCH_VALUE)
                            && data.containsKey(STICK_SCALING_YAW_VALUE)) {
                        settingsFragment = (SettingsFragment) mLaunchPadFlightControllerActivity
                                .getFragment(ViewPagerAdapter.SETTINGS_FRAGMENT);
                        if (settingsFragment != null)
                            settingsFragment.updateSettings(data.getInt(ANGLE_KP_VALUE),
                                    data.getInt(HEADING_KP_VALUE), data.getByte(ANGLE_MAX_INC_VALUE),
                                    data.getByte(ANGLE_MAX_INC_SONAR_VALUE),
                                    data.getInt(STICK_SCALING_ROLL_PITCH_VALUE),
                                    data.getInt(STICK_SCALING_YAW_VALUE));
                    }

                    pidFragment = (PIDFragment) mLaunchPadFlightControllerActivity
                            .getFragment(ViewPagerAdapter.PID_FRAGMENT);
                    if (pidFragment != null) {
                        if (data.containsKey(KP_ROLL_PITCH_VALUE) && data.containsKey(KI_ROLL_PITCH_VALUE)
                                && data.containsKey(KD_ROLL_PITCH_VALUE)
                                && data.containsKey(INT_LIMIT_ROLL_PITCH_VALUE))
                            pidFragment.updatePIDRollPitch(data.getInt(KP_ROLL_PITCH_VALUE),
                                    data.getInt(KI_ROLL_PITCH_VALUE), data.getInt(KD_ROLL_PITCH_VALUE),
                                    data.getInt(INT_LIMIT_ROLL_PITCH_VALUE));
                        else if (data.containsKey(KP_YAW_VALUE) && data.containsKey(KI_YAW_VALUE)
                                && data.containsKey(KD_YAW_VALUE) && data.containsKey(INT_LIMIT_YAW_VALUE))
                            pidFragment.updatePIDYaw(data.getInt(KP_YAW_VALUE), data.getInt(KI_YAW_VALUE),
                                    data.getInt(KD_YAW_VALUE), data.getInt(INT_LIMIT_YAW_VALUE));
                        else if (data.containsKey(KP_SONAR_ALT_HOLD_VALUE)
                                && data.containsKey(KI_SONAR_ALT_HOLD_VALUE)
                                && data.containsKey(KD_SONAR_ALT_HOLD_VALUE)
                                && data.containsKey(INT_LIMIT_SONAR_ALT_HOLD_VALUE))
                            pidFragment.updatePIDSonarAltHold(data.getInt(KP_SONAR_ALT_HOLD_VALUE),
                                    data.getInt(KI_SONAR_ALT_HOLD_VALUE), data.getInt(KD_SONAR_ALT_HOLD_VALUE),
                                    data.getInt(INT_LIMIT_SONAR_ALT_HOLD_VALUE));
                        else if (data.containsKey(KP_BARO_ALT_HOLD_VALUE)
                                && data.containsKey(KI_BARO_ALT_HOLD_VALUE)
                                && data.containsKey(KD_BARO_ALT_HOLD_VALUE)
                                && data.containsKey(INT_LIMIT_BARO_ALT_HOLD_VALUE))
                            pidFragment.updatePIDBaroAltHold(data.getInt(KP_BARO_ALT_HOLD_VALUE),
                                    data.getInt(KI_BARO_ALT_HOLD_VALUE), data.getInt(KD_BARO_ALT_HOLD_VALUE),
                                    data.getInt(INT_LIMIT_BARO_ALT_HOLD_VALUE));
                    }

                    if (data.containsKey(ROLL_ANGLE) && data.containsKey(PITCH_ANGLE)
                            && data.containsKey(YAW_ANGLE)) {
                        graphFragment = (GraphFragment) mLaunchPadFlightControllerActivity
                                .getFragment(ViewPagerAdapter.GRAPH_FRAGMENT);
                        if (graphFragment != null)
                            graphFragment.updateAngles(data.getString(ROLL_ANGLE), data.getString(PITCH_ANGLE),
                                    data.getString(YAW_ANGLE));
                    }
                }
                break;
            case MESSAGE_DEVICE_NAME:
                if (msg.getData() != null)
                    mConnectedDeviceName = msg.getData().getString(DEVICE_NAME); // Save the connected device's name
                break;
            case MESSAGE_DISCONNECTED:
                mLaunchPadFlightControllerActivity.supportInvalidateOptionsMenu();
                settingsFragment = (SettingsFragment) mLaunchPadFlightControllerActivity
                        .getFragment(ViewPagerAdapter.SETTINGS_FRAGMENT);
                if (settingsFragment != null)
                    settingsFragment.updateSendButton();
                pidFragment = (PIDFragment) mLaunchPadFlightControllerActivity
                        .getFragment(ViewPagerAdapter.PID_FRAGMENT);
                if (pidFragment != null)
                    pidFragment.updateSendButton();
                if (msg.getData() != null)
                    mLaunchPadFlightControllerActivity.showToast(msg.getData().getString(TOAST),
                            Toast.LENGTH_SHORT);
                break;
            case MESSAGE_RETRY:
                if (D)
                    Log.d(TAG, "MESSAGE_RETRY");
                mLaunchPadFlightControllerActivity.connectDevice(null, true);
                break;
            }
        }
    }
}