com.digi.android.wva.DashboardActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.digi.android.wva.DashboardActivity.java

Source

/* 
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
 * the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * 
 * Copyright (c) 2014 Digi International Inc., All Rights Reserved. 
 */

package com.digi.android.wva;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.app.*;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
import com.digi.android.wva.adapters.EndpointsAdapter;
import com.digi.android.wva.adapters.LogAdapter;
import com.digi.android.wva.adapters.VariableAdapter;
import com.digi.android.wva.fragments.ConnectionErrorDialog;
import com.digi.android.wva.fragments.ConnectionErrorDialog.ErrorDialogListener;
import com.digi.android.wva.fragments.EndpointsFragment;
import com.digi.android.wva.fragments.LogFragment;
import com.digi.android.wva.fragments.PreConnectionDialog;
import com.digi.android.wva.fragments.PreConnectionDialog.PreConnectionDialogListener;
import com.digi.android.wva.fragments.VariableListFragment;
import com.digi.android.wva.util.MessageCourier;
import com.digi.wva.async.WvaCallback;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

/**
 * Activity to be launched when the user selects a device to connect to.
 * 
 * <p>On tablets, the three content fragments
 * ({@link VariableListFragment}, {@link LogFragment}, {@link EndpointsFragment})
 * are displayed all at once (see layouts <b>res/layout-sw600dp/dashboard</b>
 * and <b>res/layout-sw600dp-port/dashboard</b>).</p>
 * 
 * <p>On phones (and mid-sized tablets?) the fragments will be displayed in a
 * {@link TabsAdapter} which allows the fragments to be paged through.</p>
 * 
 * @author mwadsten
 *
 */
public class DashboardActivity extends SherlockFragmentActivity
        implements ErrorDialogListener, PreConnectionDialogListener {
    public static final String INTENT_IP = "ip_address";

    private static final String TAG = "DashboardActivity";
    private ViewPager mViewPager;

    private String mActionBarTitle;
    private String mActionBarSubtitle;

    private static final int MESSAGE_LOOP_INTERVAL = 2000;

    private class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            DashboardActivity.this.processMessages();
        }

        public void sleep() {
            this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), MESSAGE_LOOP_INTERVAL);
        }
    }

    private final MessageHandler mHandler = new MessageHandler();
    private boolean isPaused = false;
    private boolean showIndeterminateProgress = true;

    protected void processMessages() {
        MessageCourier.DashboardMessage[] messages = MessageCourier.getDashboardMessages();
        for (MessageCourier.DashboardMessage message : messages) {
            if (message.isError()) {
                setIsConnecting(false);
                Log.d(TAG, "processMessages -- got error");
                showErrorDialog(message.getContents());
                return; // stop processing and handler loop
            } else if (message.isReconnecting() && !isPaused) {
                // Reconnecting to device, and we're not paused.
                setIsConnecting(true);
                Log.d(TAG, "processMessages -- got reconnecting");
                Toast.makeText(this, "Reconnecting...", Toast.LENGTH_SHORT).show();
            } else {
                Log.i(TAG, "Successfully connected to device.");
                setIsConnecting(false);
                Toast.makeText(DashboardActivity.this, getString(R.string.connected_toast_contents),
                        Toast.LENGTH_SHORT).show();
                mActionBarTitle = getString(R.string.connected_dashboard_title);
                mActionBarSubtitle = message.getContents();
                setActionBarText();
            }
        }
        if (isPaused) {
            return;
        }
        mHandler.sleep();
    }

    @Override
    protected void onPause() {
        // Ensure messages stop being processed.
        mHandler.removeMessages(0);
        isPaused = true;
        //      Log.i(TAG, "onPause");
        super.onPause();
    }

    @Override
    protected void onResume() {
        //      Log.i(TAG, "onResume");
        super.onResume();

        WvaApplication app = (WvaApplication) getApplication();
        app.dismissAlarmNotification();

        isPaused = false;
        processMessages();
    }

    /**
     * <b>finish()</b> the activity, while also sending a
     * "disconnect" intent to the {@link VehicleInfoService} and
     * dismissing any alarm notifications
     */
    @Override
    public void finish() {
        // Tell VehicleInfoService to disconnect
        startService(VehicleInfoService.buildDisconnectIntent(getApplicationContext()));
        ((WvaApplication) getApplication()).dismissAlarmNotification();
        super.finish();
    }

    @SuppressLint("CommitTransaction")
    protected void showErrorDialog(String error) {
        ConnectionErrorDialog dialog = ConnectionErrorDialog
                .newInstance(getString(R.string.dashboard_error_dialog_title), error);
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.addToBackStack(null);

        dialog.show(ft, "error_dialog");
    }

    /**
     * Implementation of the ErrorDialogListener interface defined in
     * ConnectionErrorDialog class. When the user acknowledges the
     * error message, the dialog will automatically go away, and we
     * want to finish the current dashboard activity and return to
     * the device discovery activity.
     */
    @Override
    public void onOkay() {
        navigateBackToDevices();
    }

    /**
     * Implementation of the PreConnectionDialogListener interface defined
     * in PreConnectionDialog class. When the user presses the "Okay" button
     * in that dialog, the dialog will automatically go away, and we need to
     * use the input from that dialog to configure the connection with the
     * device (username, password, whether we need to use HTTPS, etc.).
     */
    @Override
    public void onOkay(String ipAddress, String username, String password, boolean useHttps) {
        // Use VehicleInfoService to connect to the device.
        startService(VehicleInfoService.buildConnectIntent(getApplicationContext(), ipAddress, username, password,
                useHttps));
    }

    /**
     * Implementation of {@link PreConnectionDialogListener#onCancelConnection()}.
     * When the user presses the "Cancel" button in that dialog, the dialog will
     * automatically go away, and we want to respond by {@link #finish}ing the
     * DashboardActivity. (The user apparently wishes to cancel the attempted connection
     * with this device.)
     */
    @Override
    public void onCancelConnection() {
        Toast.makeText(this, "Cancelled connection to " + getConnectionIp(), Toast.LENGTH_SHORT).show();
        navigateBackToDevices();
    }

    protected void setIsConnecting(boolean is) {
        setSupportProgressBarIndeterminateVisibility(is);
        showIndeterminateProgress = is;
    }

    protected String getConnectionIp() {
        String ipAddr = getIntent().getStringExtra(INTENT_IP);

        if (ipAddr == null) {
            //          Log.e(TAG, "Got intent with null ip address!");
            ipAddr = PreferenceManager.getDefaultSharedPreferences(this).getString("pref_device_manual_ip",
                    getString(R.string.default_ip));
        }

        return ipAddr;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //      Log.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

        setContentView(R.layout.dashboard);

        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        // We want to wipe out variable and log data if this is a
        // fresh launch into this activity.
        if (savedInstanceState == null) {
            //           Log.i(TAG, "Clearing data in onCreate");
            clearData();

            mActionBarTitle = getString(R.string.pre_connected_dashboard_title);

            // Send connect command to service...
            String ipAddr = getConnectionIp();

            FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            PreConnectionDialog dlg = PreConnectionDialog.newInstance(ipAddr);
            ft.addToBackStack(null);
            dlg.show(ft, "pre_connect");
        } else { // there is a saved instance state
            mActionBarTitle = savedInstanceState.getString("title");
            if (TextUtils.isEmpty(mActionBarTitle))
                mActionBarTitle = getString(R.string.pre_connected_dashboard_title);
            mActionBarSubtitle = savedInstanceState.getString("subtitle");
            // If subtitle text is null, no subtitle will be added
            showIndeterminateProgress = savedInstanceState.getBoolean("indeterminate");
        }

        setActionBarText();

        // Set up the view pager if we are running on a device which will
        // display the view pager.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        if (mViewPager != null) { // Running on a phone.
            TabsAdapter mTabsAdapter = new TabsAdapter(getSupportFragmentManager());
            mViewPager.setAdapter(mTabsAdapter);

            if (savedInstanceState != null)
                mViewPager.setCurrentItem(savedInstanceState.getInt("page", 2));
            else
                mViewPager.setCurrentItem(2);
        }

        setIsConnecting(showIndeterminateProgress);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //       Log.i(TAG, "onSaveInstanceState");
        super.onSaveInstanceState(outState);
        // Save view pager current page.
        if (mViewPager != null)
            outState.putInt("page", mViewPager.getCurrentItem());
        outState.putString("title", mActionBarTitle);
        outState.putString("subtitle", mActionBarSubtitle);
        outState.putBoolean("indeterminate", showIndeterminateProgress);
    }

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

    protected static void clearData() {
        VariableAdapter.getInstance().clear();
        LogAdapter.getInstance().clear();
        EndpointsAdapter.getInstance().clear();
        MessageCourier.clear();
    }

    /**
     * Title and subtitle are stored in instance variables so that
     * screen rotation, etc. can end with their contents restored.
     */
    protected void setActionBarText() {
        getSupportActionBar().setTitle(mActionBarTitle);
        getSupportActionBar().setSubtitle(mActionBarSubtitle);
    }

    /**
     * Uses NavUtils task stack builder to help make it so that we can leave
     * the dashboard activity and go back to the device list.
     */
    protected void navigateBackToDevices() {
        //      Log.d(TAG, "navigateBackToDevices");

        Log.d(TAG, "Exiting dashboard, returning to device discovery.");
        ((WvaApplication) getApplication()).clearDevice();

        // developer.android.com/training/implementing-navigation/ancestral.html
        Intent upIntent = new Intent(this, DeviceListActivity.class);
        if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
            // Create new task with synthesized back stack.
            TaskStackBuilder.create(this).addNextIntent(upIntent).startActivities();
            finish();
        } else {
            // Navigate up to the parent activity (DevicesActivity)
            // -- this is exactly the support library's implementation
            // of NavUtils.navigateUpTo(this, upIntent)... finish() wasn't
            // being called for some reason (at least on 4.2.2) and this
            // works better.
            upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(upIntent);
            finish();
        }
    }

    /**
     * Make it so that when the user hits the back button, we explicitly
     * return to the device list activity.
     */
    @Override
    public void onBackPressed() {
        navigateBackToDevices();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            navigateBackToDevices();
            return true;
        case R.id.sync_time:
            Log.d(TAG, "Executing time sync.");
            WvaApplication app = (WvaApplication) getApplication();
            if (app.getDevice() != null)
                app.getDevice().setTime(DateTime.now(DateTimeZone.UTC), new WvaCallback<DateTime>() {
                    @Override
                    public void onResponse(Throwable error, DateTime response) {
                        Log.d(TAG, "Time sync error: " + error);
                        String message;
                        if (error == null)
                            message = "Time sync successful.";
                        else
                            message = "Time sync: " + error;
                        Toast.makeText(DashboardActivity.this, message, Toast.LENGTH_SHORT).show();
                    }
                });
            else
                Log.d(TAG, "Can't execute time sync: no Device in WvaApplication");
            return true;
        // startActivityForResult should make it so that backing out (i.e. finish()ing)
        // from the launched activities returns us here to DashboardActivity
        case R.id.action_settings:
            startActivityForResult(new Intent(this, SettingsActivity.class), 0);
            return true;
        case R.id.launch_chart:
            startActivityForResult(new Intent(this, ChartActivity.class), 0);
            return true;
        case R.id.fault_codes:
            startActivity(new Intent(this, FaultCodeActivity.class));
            return true;
        }
        return false;
    }

    /**
     * {@link FragmentPagerAdapter} implementation which is used when creating
     * the dashboard activity on small screens. Allows the user to swipe between
     * the variable data, log, and alarms/subscriptions fragments.
     * @author mwadsten
     *
     */
    public class TabsAdapter extends FragmentPagerAdapter {
        public TabsAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0:
                return VariableListFragment.newInstance();
            case 1:
                return LogFragment.newInstance();
            default:
                return EndpointsFragment.newInstance();
            }
        }

        /**
         * Get the title to put above a given page
         * @param pos page position
         * @return page title for the given page.
         */
        @Override
        public CharSequence getPageTitle(int pos) {
            switch (pos) {
            case 0:
                return getString(R.string.variables_header);
            case 1:
                return getString(R.string.log_header);
            default: // 2
                return getString(R.string.subscriptions_header);
            }
        }

        /**
         * Get the number of pages in the adapter.
         * @return 3 ({@link VariableListFragment}, {@link LogFragment},
         * {@link EndpointsFragment})
         */
        @Override
        public int getCount() {
            return 3;
        }

    }
}