org.openbmap.activities.HostActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.openbmap.activities.HostActivity.java

Source

/*
   Radiobeacon - Openbmap wifi and cell logger
Copyright (C) 2013  wish7
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.
    
You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.openbmap.activities;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;

import org.openbmap.Preferences;
import org.openbmap.R;
import org.openbmap.RadioBeacon;
import org.openbmap.db.DataHelper;
import org.openbmap.db.models.Session;
import org.openbmap.services.ServiceManager;
import org.openbmap.services.position.GpxLoggerService;
import org.openbmap.services.position.PositioningService;
import org.openbmap.services.position.PositioningService.State;
import org.openbmap.services.wireless.WirelessLoggerService;
import org.openbmap.utils.ActivityUtils;

import java.lang.ref.WeakReference;

/**
 * HostActity for "tracking" mode. It hosts the tabs "Stats", "Wifi Overview", "Cell Overview" and "Map".
 * HostActity is also in charge of service communication. 
 * Services are automatically started onCreate() and onResume()
 * They can be manually stopped by calling INTENT_STOP_TRACKING.
 * 
 */
public class HostActivity extends ActionBarActivity {
    private static final String TAG = HostActivity.class.getSimpleName();

    /**
     * Tab pager
     */
    private CustomViewPager mPager;

    private Tab mTab;

    /**
     * Keeps the SharedPreferences.
     */
    private SharedPreferences mPrefs = null;

    /**
     * Background service collecting cell and wifi infos.
     */
    private ServiceManager mWirelessServiceManager;

    /**
     * Background gps location service.
     */
    private ServiceManager mPositionServiceManager;

    /**
     * Background gpx logger server
     */
    private ServiceManager mGpxLoggerServiceManager;

    /**
     * Database helper used for session handling here.
     */
    private DataHelper mDataHelper;

    /**
     * Selected navigation provider, default GPS
     */
    private State mSelectedProvider = State.GPS;

    private ActionBar mActionBar;

    /**
     * Receives GPS location updates 
     */
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

        /**
         * Handles start and stop service requests.
         */
        @Override
        public void onReceive(final Context context, final Intent intent) {
            Log.d(TAG, "Received intent " + intent.getAction().toString());
            /*if (RadioBeacon.INTENT_START_TRACKING.equals(intent.getAction())) {
               startServices();
            } else*/
            if (RadioBeacon.INTENT_STOP_TRACKING.equals(intent.getAction())) {
                Log.d(TAG, "INTENT_STOP_TRACKING received");
                // invalidates active track
                closeActiveSession();
                // stops background services
                stopServices();

                HostActivity.this.finish();
            }

            if (Intent.ACTION_BATTERY_LOW.equals(intent.getAction())) {
                Log.d(TAG, "ACTION_BATTERY_LOW received");
                Toast.makeText(context, getString(R.string.battery_warning), Toast.LENGTH_LONG).show();
                updateSessionStats();
                // invalidates active track
                closeActiveSession();
                // stops background services
                stopServices();

                HostActivity.this.finish();
            }
        }
    };

    /**
     * Reacts on messages from gps location service 
     */
    private static class GpsLocationHandler extends Handler {
        private final WeakReference<HostActivity> mActivity;

        GpsLocationHandler(final HostActivity activity) {
            mActivity = new WeakReference<HostActivity>(activity);
        }

        @Override
        public void handleMessage(final Message msg) {
            switch (msg.what) {
            case RadioBeacon.MSG_SERVICE_READY:
                // start tracking immediately after service is ready 
                Log.d(TAG, "Positioning Service ready. Requesting position updates");
                if (mActivity != null) {
                    final HostActivity tab = mActivity.get();
                    tab.requestPositionUpdates(State.GPS);
                }
                break;

            default:
                super.handleMessage(msg);
            }
        }
    }

    /**
     * Reacts on messages from wireless logging service 
     */
    private static class WirelessHandler extends Handler {
        private final WeakReference<HostActivity> mActivity;

        WirelessHandler(final HostActivity activity) {
            mActivity = new WeakReference<HostActivity>(activity);
        }

        @Override
        public void handleMessage(final Message msg) {
            switch (msg.what) {
            case RadioBeacon.MSG_SERVICE_READY:
                // start tracking immediately after service is ready 
                Log.d(TAG, "WirelessLogger Service ready. Requesting wireless updates");
                if (mActivity != null) {
                    final HostActivity tab = mActivity.get();
                    tab.requestWirelessUpdates();
                }
            default:
                super.handleMessage(msg);
            }
        }
    }

    /**
     * Reacts on messages from gps logging service 
     */
    private static class GpxLoggerHandler extends Handler {
        private final WeakReference<HostActivity> mActivity;

        GpxLoggerHandler(final HostActivity activity) {
            mActivity = new WeakReference<HostActivity>(activity);
        }

        @Override
        public void handleMessage(final Message msg) {
            switch (msg.what) {
            case RadioBeacon.MSG_SERVICE_READY:
                // start tracking immediately after service is ready 
                if (mActivity != null) {
                    final HostActivity tab = mActivity.get();
                    tab.requestGpxTracking();
                }
            default:
                super.handleMessage(msg);
            }
        }
    }

    /** Called when the activity is first created. */
    @Override
    public final void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d(TAG, "Creating HostActivity");

        mDataHelper = new DataHelper(this);

        // get shared preferences
        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);

        // UI related stuff
        setContentView(R.layout.host_activity);
        final boolean keepScreenOn = mPrefs.getBoolean(Preferences.KEY_KEEP_SCREEN_ON, false);
        ActivityUtils.setKeepScreenOn(this, keepScreenOn);

        initUi(savedInstanceState);

        // service related stuff
        verifyGPSProvider();

        // TODO: show warning if wifi is not enabled
        // TODO: show warning if GSM is not enabled
    }

    @Override
    protected final void onResume() {
        super.onResume();

        Log.d(TAG, "Resuming HostActivity");

        setupBroadcastReceiver();
        setupSession();

        /* TODO: sort out, what is needed here
         * // Check GPS status if (checkGPSFlag &&
         * prefs.getBoolean(OSMTracker.Preferences.KEY_GPS_CHECKSTARTUP, OSMTracker.Preferences.VAL_GPS_CHECKSTARTUP)) { checkGPSProvider(); }
         */
        // Register GPS status update for upper controls
        //((StatusBar) findViewById(R.id.gpsStatus)).requestLocationUpdates(true);

        // setup GPS and wireless logger services
        startServices();

        // explicitly request updates, automatic resume isn't working smoothly 
        requestPositionUpdates(mSelectedProvider);
        requestWirelessUpdates();
    }

    @Override
    protected final void onPause() {
        Log.d(TAG, "Pausing HostActivity");
        updateSessionStats();
        super.onPause();
    }

    @Override
    protected final void onStop() {
        Log.d(TAG, "Stopping HostActivity");
        // TODO: if receivers are unregistered on stop, there is no way to start/stop services via receivers from outside
        unregisterReceiver();
        super.onStop();
    }

    @Override
    protected final void onDestroy() {
        Log.d(TAG, "Destroying HostActivity");

        updateSessionStats();
        unregisterReceiver();

        // change from unbind to unbindAndStop caused problems, when screen was locked
        if (mPositionServiceManager != null) {
            mPositionServiceManager.unbind();
        }
        ;
        if (mWirelessServiceManager != null) {
            mWirelessServiceManager.unbind();
        }
        if (mGpxLoggerServiceManager != null) {
            mGpxLoggerServiceManager.unbind();
        }
        ;

        super.onDestroy();
    }

    /**
     * Unregisters receivers for GPS and wifi scan results.
     */
    private void unregisterReceiver() {
        try {
            Log.i(TAG, "Unregistering broadcast receivers");
            unregisterReceiver(mReceiver);
        } catch (final IllegalArgumentException e) {
            // do nothing here {@see http://stackoverflow.com/questions/2682043/how-to-check-if-receiver-is-registered-in-android}
            return;
        }
    }

    /**
     * Create context menu with start and stop buttons for "tracking" mode.
     * @param menu Menu to inflate
     * @return always true
     */
    @Override
    public final boolean onCreateOptionsMenu(final Menu menu) {
        final MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.control_menu, menu);
        return true;
    }

    /**
     * Context menu, while in "tracking" mode
     */
    @Override
    public final boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
        /*
        case R.id.menu_starttracking:
        // kill previous session
        stopServices();
            
        requestPosition(PositioningService.State.GPS);
        requestWirelessTracking();
        requestGpxTracking();
        startNotification();
        break;
        case R.id.menu_pausetracking:
        updateSessionStats();
        requestPause();
        stopNotifyBackgroundService();
        break;*/
        case R.id.menu_stoptracking:
            updateSessionStats();
            closeActiveSession();
            stopServices();

            final Intent intent = new Intent(this, StartscreenActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
            this.finish();
            break;
        default:
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Updates number of cells and wifis.
     */
    private void updateSessionStats() {
        final Session active = mDataHelper.loadActiveSession();
        if (active != null) {
            active.setNumberOfWifis(mDataHelper.countWifis(active.getId()));
            active.setNumberOfCells(mDataHelper.countCells(active.getId()));
            mDataHelper.storeSession(active, false);
        }
    }

    /**
     * Checks whether GPS is enabled.
     * If not, user is asked whether to activate GPS.
     */
    private void verifyGPSProvider() {
        final LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            // GPS isn't enabled. Offer user to go enable it
            new AlertDialog.Builder(this).setTitle(R.string.no_gps).setIcon(android.R.drawable.ic_dialog_alert)
                    .setMessage(R.string.turnOnGpsQuestion).setCancelable(true)
                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(final DialogInterface dialog, final int which) {
                            startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
                        }
                    }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(final DialogInterface dialog, final int which) {
                            dialog.cancel();
                        }
                    }).create().show();
        }
    }

    /**
     * Starts broadcasting GPS position.
     * @param provider
     * @return false on error, otherwise true
     */
    public final boolean requestPositionUpdates(final State provider) {
        // TODO check whether services have already been connected (i.e. received MSG_SERVICE_READY signal)
        Log.d(TAG, "Requesting position updates");
        try {
            if (mPositionServiceManager == null) {
                Log.w(TAG, "gpsPositionServiceManager is null. No message will be sent");
                return false;
            }

            final int session = mDataHelper.getActiveSessionId();

            if (session == RadioBeacon.SESSION_NOT_TRACKING) {
                Log.e(TAG, "Couldn't start tracking, no active session");
                return false;
            }

            final Bundle aProviderBundle = new Bundle();
            aProviderBundle.putString("provider", provider.toString());

            final Message msgGpsUp = new Message();
            msgGpsUp.what = RadioBeacon.MSG_START_TRACKING;
            msgGpsUp.setData(aProviderBundle);

            mPositionServiceManager.sendAsync(msgGpsUp);

            // update recording indicator
            //((StatusBar) findViewById(R.id.gpsStatus)).manageRecordingIndicator(true);

            updateUI();
            mSelectedProvider = provider;
            return true;
        } catch (final RemoteException e) {
            // service communication failed
            e.printStackTrace();
            return false;
        } catch (final NumberFormatException e) {
            e.printStackTrace();
            return false;
        } catch (final Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Starts wireless tracking.
     * @return false on error, otherwise true
     */
    public final boolean requestWirelessUpdates() {
        // TODO check whether services have already been connected (i.e. received MSG_SERVICE_READY signal)
        Log.d(TAG, "Requesting wireless updates");
        try {
            if (mWirelessServiceManager == null) {
                Log.w(TAG, "wirelessServiceManager is null. No message will be sent");
                return false;
            }

            final int session = mDataHelper.getActiveSessionId();

            if (session == RadioBeacon.SESSION_NOT_TRACKING) {
                Log.e(TAG, "Couldn't start tracking, no active session");
                return false;
            }

            final Bundle aSessionIdBundle = new Bundle();
            aSessionIdBundle.putInt(RadioBeacon.MSG_KEY, session);

            final Message msgWirelessUp = new Message();
            msgWirelessUp.what = RadioBeacon.MSG_START_TRACKING;
            msgWirelessUp.setData(aSessionIdBundle);

            mWirelessServiceManager.sendAsync(msgWirelessUp);

            updateUI();
            return true;
        } catch (final RemoteException e) {
            // service communication failed
            e.printStackTrace();
            return false;
        } catch (final NumberFormatException e) {
            e.printStackTrace();
            return false;
        } catch (final Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Starts GPX tracking.
     * @return false on error, otherwise true
     */
    public final boolean requestGpxTracking() {
        // TODO check whether services have already been connected (i.e. received MSG_SERVICE_READY signal)
        Log.d(TAG, "Requesting gpx tracking");
        try {
            if (mPositionServiceManager == null) {
                Log.w(TAG, "gpsPositionServiceManager is null. No message will be sent");
                return false;
            }

            final int session = mDataHelper.getActiveSessionId();

            if (session == RadioBeacon.SESSION_NOT_TRACKING) {
                Log.e(TAG, "Couldn't start tracking, no active session");
                return false;
            }

            Log.d(TAG, "Resuming session " + session);

            final Bundle aSessionIdBundle = new Bundle();
            aSessionIdBundle.putInt(RadioBeacon.MSG_KEY, session);

            final Message msgGpsUp = new Message();
            msgGpsUp.what = RadioBeacon.MSG_START_TRACKING;
            msgGpsUp.setData(aSessionIdBundle);

            mPositionServiceManager.sendAsync(msgGpsUp);

            return true;
        } catch (final RemoteException e) {
            // service communication failed
            e.printStackTrace();
            return false;
        } catch (final NumberFormatException e) {
            e.printStackTrace();
            return false;
        } catch (final Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Setups receiver for STOP_TRACKING and ACTION_BATTERY_LOW messages
     */
    private void setupBroadcastReceiver() {
        final IntentFilter filter = new IntentFilter();
        //filter.addAction(RadioBeacon.INTENT_START_TRACKING);
        filter.addAction(RadioBeacon.INTENT_STOP_TRACKING);
        filter.addAction(Intent.ACTION_BATTERY_LOW);
        registerReceiver(mReceiver, filter);
    }

    /**
     * Pauses GPS und wireless logger services.
     */
    /* doesn't work: services aren't properly restarted / finally stopped
    private void requestPause() {
       // Pause tracking
       try {
     gpsPositionServiceManager.sendAsync(Message.obtain(null, RadioBeacon.MSG_STOP_TRACKING));
     wirelessServiceManager.sendAsync(Message.obtain(null, RadioBeacon.MSG_STOP_TRACKING));
        
     Session toPause = mDataHelper.loadActiveSession();
     if (toPause != null) {
        toPause.isActive(false);
        mDataHelper.storeSession(toPause, true);
     }
        
     ((StatusBar) findViewById(R.id.gpsStatus)).manageRecordingIndicator(false); 
       } catch (RemoteException e) {
     e.printStackTrace();
       }
    }
     */

    /**
     * Opens a new session object. If there's any active session or a specific session id is provided, this session is resumed,
     * otherwise a new session created
     */
    private void setupSession() {

        final Bundle extras = getIntent().getExtras();
        if (extras != null && extras.getInt("_id") != 0) {
            // A session to resume was specified
            final int id = extras.getInt("_id");
            Log.i(TAG, "Opening specified session " + id);
            mDataHelper.invalidateActiveSessions();
            openExistingSession(id);
        } else {
            // search for any active session
            final int activeSession = mDataHelper.getActiveSessionId();
            if (activeSession != RadioBeacon.SESSION_NOT_TRACKING) {
                Log.i(TAG, "Resuming session " + activeSession);
                openExistingSession(activeSession);
            } else {
                Log.i(TAG, "Starting new session");
                openNewSession();
            }
        }
        updateSessionStats();
    }

    /**
     * Opens new session
     */
    private void openNewSession() {
        // invalidate all active session
        mDataHelper.invalidateActiveSessions();
        // Create a new session and activate it
        // Then start HostActivity. HostActivity onStart() and onResume() check active session
        // and starts services for active session
        final Session active = new Session();
        active.setCreatedAt(System.currentTimeMillis());
        active.setLastUpdated(System.currentTimeMillis());
        active.setDescription("No description yet");
        active.isActive(true);
        // id can only be set after session has been stored to database.
        final Uri result = mDataHelper.storeSession(active);
        active.setId(result);
    }

    /**
     * Resumes specific session
     * @param id
     */
    private void openExistingSession(final int id) {
        // TODO: check whether we need a INTENT_START_SERVICE here
        final Session resume = mDataHelper.loadSession(id);

        if (resume == null) {
            Log.e(TAG, "Error loading session " + id);
            return;
        }

        resume.isActive(true);
        mDataHelper.storeSession(resume, true);

    }

    /**
     * 
     */
    private void closeActiveSession() {
        mDataHelper.invalidateActiveSessions();
        // update recording indicator
        // ((StatusBar) findViewById(R.id.gpsStatus)).manageRecordingIndicator(false);
    }

    /**
     * Setups services. Any running services will be restart.
     */
    private void startServices() {
        stopServices();

        Log.d(TAG, "Starting Services");
        if (mPositionServiceManager == null) {
            mPositionServiceManager = new ServiceManager(this, PositioningService.class,
                    new GpsLocationHandler(this));
        }
        mPositionServiceManager.bindAndStart();

        if (mWirelessServiceManager == null) {
            mWirelessServiceManager = new ServiceManager(this, WirelessLoggerService.class,
                    new WirelessHandler(this));
        }
        mWirelessServiceManager.bindAndStart();

        if (mGpxLoggerServiceManager == null) {
            mGpxLoggerServiceManager = new ServiceManager(this, GpxLoggerService.class, new GpxLoggerHandler(this));
        }
        mGpxLoggerServiceManager.bindAndStart();
    }

    /**
     * Stops GPS und wireless logger services.
     */
    private void stopServices() {
        Log.d(TAG, "Stopping Services");
        // Brute force: specific ServiceManager can be null, if service hasn't been started
        // Service status is ignored, stop message is send regardless of whether started or not
        try {
            mPositionServiceManager.sendAsync(Message.obtain(null, RadioBeacon.MSG_STOP_TRACKING));
            // deactivated: let's call this from the service itself
            // positionServiceManager.unbindAndStop();
        } catch (final Exception e) {
            Log.w(TAG, "Failed to stop gpsPositionServiceManager. Is service runnign?" /*+ e.getMessage()*/);
            //e.printStackTrace();
        }

        try {
            mWirelessServiceManager.sendAsync(Message.obtain(null, RadioBeacon.MSG_STOP_TRACKING));
            // deactivated: let's call this from the service itself
            // wirelessServiceManager.unbindAndStop();
        } catch (final Exception e) {
            Log.w(TAG, "Failed to stop wirelessServiceManager. Is service running?" /*+ e.getMessage()*/);
            //e.printStackTrace();
        }

        try {
            mGpxLoggerServiceManager.sendAsync(Message.obtain(null, RadioBeacon.MSG_STOP_TRACKING));
            // deactivated: let's call this from the service itself
            // gpxLoggerServiceManager.unbindAndStop();
        } catch (final Exception e) {
            Log.w(TAG, "Failed to stop gpxLoggerServiceManager. Is service running?" /*+ e.getMessage()*/);
            //e.printStackTrace();
        }
    }

    /**
     * Configures UI elements.
     * @param savedInstanceState 
     */
    private void initUi(final Bundle savedInstanceState) {
        getSupportActionBar().setDisplayShowTitleEnabled(false);

        mActionBar = getSupportActionBar();
        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Locate ViewPager in activity_main.xml
        mPager = (CustomViewPager) findViewById(R.id.pager);
        // Activate Fragment Manager
        final FragmentManager fm = getSupportFragmentManager();

        // Capture ViewPager page swipes
        final ViewPager.SimpleOnPageChangeListener ViewPagerListener = new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(final int position) {
                super.onPageSelected(position);
                // Find the ViewPager Position
                getSupportActionBar().setSelectedNavigationItem(position);
            }
        };

        mPager.setOnPageChangeListener(ViewPagerListener);

        final CustomViewPagerAdapter viewpageradapter = new CustomViewPagerAdapter(fm);
        mPager.setAdapter(viewpageradapter);

        // Capture tab button clicks
        final ActionBar.TabListener tabListener = new ActionBar.TabListener() {
            @Override
            public void onTabSelected(final Tab tab, final FragmentTransaction ft) {
                // Pass the position on tab click to ViewPager
                mPager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(final Tab tab, final FragmentTransaction ft) {
                // TODO Auto-generated method stub
            }

            @Override
            public void onTabReselected(final Tab tab, final FragmentTransaction ft) {
                // TODO Auto-generated method stub
            }
        };

        // Create tabs
        mTab = mActionBar.newTab().setText(R.string.overview).setTabListener(tabListener);
        getSupportActionBar().addTab(mTab);

        mTab = mActionBar.newTab().setText(R.string.wifis).setTabListener(tabListener);
        getSupportActionBar().addTab(mTab);

        mTab = mActionBar.newTab().setText(R.string.cells).setTabListener(tabListener);
        getSupportActionBar().addTab(mTab);

        mTab = mActionBar.newTab().setText(R.string.map).setTabListener(tabListener);
        getSupportActionBar().addTab(mTab);
    }

    /**
     * Broadcasts requests for UI refresh on wifi and cell info.
     */
    private void updateUI() {
        final Intent intent1 = new Intent(RadioBeacon.INTENT_WIFI_UPDATE);
        sendBroadcast(intent1);
        final Intent intent2 = new Intent(RadioBeacon.INTENT_CELL_UPDATE);
        sendBroadcast(intent2);
    }

}