org.ambientdynamix.core.HomeActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.ambientdynamix.core.HomeActivity.java

Source

/*
 * Copyright (C) The Ambient Dynamix Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ambientdynamix.core;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.ActivityRecognition;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.mixpanel.android.mpmetrics.MixpanelAPI;

import org.ambientdynamix.data.DynamixPreferences;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import org.lucasr.twowayview.TwoWayView;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import eu.smartsantander.androidExperimentation.ActivityRecognitionService;
import eu.smartsantander.androidExperimentation.fragment.SensorMeasurement;
import eu.smartsantander.androidExperimentation.fragment.SensorMeasurementAdapter;
import eu.smartsantander.androidExperimentation.operations.AsyncReportOnServerTask;
import eu.smartsantander.androidExperimentation.operations.AsyncStatusRefreshTask;
import eu.smartsantander.androidExperimentation.service.RegistrationIntentService;
import eu.smartsantander.androidExperimentation.util.Constants;

/**
 * Home user interface, which shows the current authorized Dynamix applications along with their status. This UI also
 * provides a toggle button for activating/deactivating the Dynamix Framework.
 *
 * @author Darren Carlson
 */
public class HomeActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener, LocationListener {
    // Private data
    private final static String TAG = "HomeActivity";
    private static final int ENABLE_ID = Menu.FIRST + 1;
    private static final int DELETE_ID = Menu.FIRST + 2;
    private static final int ACTIVITY_EDIT = 1;
    private static HomeActivity activity;
    private static boolean experimentationStatus = true;
    private static boolean registered = false;
    public static Location location = null;
    public final Handler uiHandler = new Handler();
    private boolean startedGcm = false;

    //Organicity
    public TextView phoneIdTv;
    public TextView expDescriptionTv;
    private MixpanelAPI mMixpanel;

    private Button pendingSendButton;
    public MapFragment mMap;

    private GoogleApiClient mGoogleApiClient;

    public ArrayList<SensorMeasurement> sensorMeasurements;
    public SensorMeasurementAdapter sensorMeasurementAdapter;
    Set<LatLng> points;

    final LocationRequest mLocationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY).setInterval(120 * 1000) // 30 seconds, in milliseconds
            .setFastestInterval(30 * 1000); // 10 seconds, in milliseconds
    private PolylineOptions line = null;
    private PendingIntent activityRecognitionPendingIntent;

    // Refreshes the UI
    public static void refreshData() {
        if (activity != null) {
            activity.uiHandler.post(new Runnable() {
                @Override
                public void run() {
                    activity.refresh();
                }
            });
        }
    }

    /**
     * Static method that allows callers to change the enabled state of the Dynamix Enable/Disable button. Note that
     * this method is only cosmetic, meaning that it does not actually call methods on the DynamixService.
     *
     * @param active
     */
    public static void setActiveState(boolean active) {
        if (activity != null) {
            // if (activity.togglebutton != null)
            //     activity.togglebutton.setChecked(active);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.v(TAG, "Activity State: onCreate()");
        super.onCreate(savedInstanceState);
        // Set our static reference
        activity = this;
        setContentView(R.layout.home_tab);

        updateCheck();

        points = new HashSet<>();
        mMixpanel = MixpanelAPI.getInstance(this, Constants.MIXPANEL_TOKEN);

        mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map_main));

        // Check if we were successful in obtaining the map.
        if (mMap == null) {
            // check if google play service in the device is not available or out-dated.
            GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
            // nothing anymore, cuz android will take care of the rest (to remind user to update google play service).
        }

        // Construct the data source
        sensorMeasurements = new ArrayList<>();
        // Create the adapter to convert the array to views
        sensorMeasurementAdapter = new SensorMeasurementAdapter(this, sensorMeasurements);
        // Attach the adapter to a ListView
        final TwoWayView listView = (TwoWayView) findViewById(R.id.lvItems);
        listView.setOrientation(TwoWayView.Orientation.VERTICAL);
        listView.setPadding(0, 0, 0, 0);
        listView.setItemMargin(0);
        listView.setAdapter(sensorMeasurementAdapter);

        //Disable for now
        final Intent activityRecognitionIntent = new Intent(this, ActivityRecognitionService.class);
        activityRecognitionPendingIntent = PendingIntent.getService(getApplicationContext(), 0,
                activityRecognitionIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        mGoogleApiClient = new GoogleApiClient.Builder(this).addApi(ActivityRecognition.API)
                .addConnectionCallbacks(this).addOnConnectionFailedListener(this).addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();

        pendingSendButton = (Button) findViewById(R.id.send_pending_now);
        pendingSendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AsyncReportOnServerTask().execute();
                try {
                    final JSONObject props = new JSONObject();
                    props.put("count", DynamixService.getDataStorageSize());
                    //mMixpanel.track("send-stored-readings", props);
                } catch (JSONException ignore) {
                }

            }
        });

        // Setup an state refresh timer, which periodically updates application
        // state in the appList
        final Timer refresher = new Timer(true);
        final TimerTask t = new TimerTask() {
            @Override
            public void run() {

                refreshData();
            }
        };
        refresher.scheduleAtFixedRate(t, 0, 5000);

        phoneIdTv = (TextView) this.findViewById(R.id.deviceId_label);

        expDescriptionTv = (TextView) this.findViewById(R.id.experiment_description);

        if (mMap.getMap() != null) {
            mMap.getMap().setMyLocationEnabled(true);
            mMap.getMap().getUiSettings().setAllGesturesEnabled(false);
            mMap.getMap().getUiSettings().setMyLocationButtonEnabled(false);
        }
    }

    private void updateCheck() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
                    int verCode = pInfo.versionCode;
                    DefaultHttpClient client = new DefaultHttpClient();
                    HttpGet httpGet = new HttpGet("http://repo.smartphone-experimentation.eu/app/");
                    try {
                        HttpResponse execute = client.execute(httpGet);
                        InputStream content = execute.getEntity().getContent();

                        BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
                        StringBuilder builder = new StringBuilder();
                        String s = "";
                        while ((s = buffer.readLine()) != null) {
                            builder.append(s);
                        }

                        try {
                            if (verCode != Integer.parseInt(builder.toString())) {
                                {
                                    final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity);
                                    alertBuilder.setTitle("Update Available");
                                    alertBuilder.setMessage(
                                            "There is a newer version available for Organicity Experimentation. "
                                                    + "Please click 'Update' to download and install the latest version.");
                                    alertBuilder.setPositiveButton("Update", new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int id) {
                                            Intent browserIntent = new Intent(Intent.ACTION_DEFAULT, Uri.parse(
                                                    "http://repo.smartphone-experimentation.eu/app/dynamix-framework-debug.apk"));
                                            startActivity(browserIntent);
                                        }
                                    });
                                    alertBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog, int id) {
                                            // User cancelled the dialog
                                        }
                                    });
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            alertBuilder.show();
                                        }
                                    });
                                }
                            }
                        } catch (Exception ignore) {
                        }
                    } catch (Exception ignore) {
                    }
                } catch (PackageManager.NameNotFoundException e) {
                }
            }
        }).start();
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.setHeaderTitle(R.string.app_list_contextmenu_title);
        menu.add(0, ENABLE_ID, 0, R.string.app_contextmenu_block);
        menu.add(0, DELETE_ID, 0, R.string.app_contextmenu_remove);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (resultCode == Activity.RESULT_OK) {
            final Bundle extras = intent.getExtras();
            switch (requestCode) {
            case ACTIVITY_EDIT:
                // Access the serialized app coming in from the Intent's Bundle
                // extra
                final DynamixApplication app = (DynamixApplication) extras.getSerializable("app");
                // Update the DynamixService with the updated application
                DynamixService.updateApplication(app);
                refresh();
                break;
            }
        }
    }

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

    private void refresh() {
        if (DynamixService.isFrameworkInitialized()) {
            // Setup toggle button with proper state
            boolean dynamixEnabled = DynamixPreferences.isDynamixEnabled(this);
            // If Dynamix is enabled, but the DynamixService is not running, then call startFramework
            if (dynamixEnabled && !DynamixService.isFrameworkStarted()) {
                DynamixService.startFramework();
            }

            final Long storageSize = DynamixService.getDataStorageSize();
            if (storageSize > 0) {
                pendingSendButton.setVisibility(View.VISIBLE);

                pendingSendButton
                        .setText(String.format(getString(R.string.pending_messages_template), storageSize));
            } else {
                pendingSendButton.setVisibility(View.GONE);
            }

            if (experimentationStatus && !registered) {

                mMixpanel.identify(String.valueOf(DynamixService.getPhoneProfiler().getPhoneId()));

                if (!mGoogleApiClient.isConnected()) {
                    //TODO : use this in a blocking connect mode - needed to do the below stuff
                    mGoogleApiClient.connect();
                } else {
                    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest,
                            this);

                    // Getting Current Location
                    final Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
                    if (location != null) {
                        registered = true;
                        HomeActivity.location = location;
                        updateMapLocation(location);
                        //Load organicity points
                        //new AsyncGetOrganicityMarkersTask().execute(this);
                    }
                }
            } else if (!experimentationStatus && registered) {
                LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
                registered = false;
            }

            if (!startedGcm && dynamixEnabled) {
                connect2Gcm();
            }
        } else {

        }
        final AsyncStatusRefreshTask task = new AsyncStatusRefreshTask(this);
        task.execute();
    }

    private void connect2Gcm() {
        if (DynamixService.isFrameworkInitialized() && DynamixService.isFrameworkStarted()
                && DynamixService.getPhoneProfiler().getPhoneId() != -1 && DynamixService.getExperiment() != null) {

            if (checkPlayServices()) {
                startedGcm = true;
                final Intent intent = new Intent(this, RegistrationIntentService.class);
                startService(intent);
            } else {
                Log.w(TAG, "PlayServices not available!");
            }
        }
    }

    private boolean checkPlayServices() {
        GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
        int resultCode = apiAvailability.isGooglePlayServicesAvailable(this);
        return resultCode == ConnectionResult.SUCCESS;
    }

    @Override
    public void onConnected(Bundle bundle) {
        //Disable for now
        ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, 10000,
                activityRecognitionPendingIntent);
    }

    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
    }

    @Override
    public void onLocationChanged(Location location) {
        HomeActivity.location = location;
    }

    private void updateMapLocation(final Location location) {
        updateMapLocation(location.getLatitude(), location.getLongitude());
    }

    public void updateMapLocation(final String latitude, final String longitude) {
        updateMapLocation(Double.parseDouble(latitude), Double.parseDouble(longitude));
    }

    private void updateMapLocation(final double latitude, final double longitude) {
        // Creating a LatLng object for the current location
        final LatLng latLng = new LatLng(latitude, longitude);
        try {
            // Showing the current location in Google Map
            mMap.getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14));
            if (points.size() > 30) {
                points.clear();
                mMap.getMap().clear();
            }
            if (!points.contains(latLng)) {
                points.add(latLng);
                mMap.getMap().addMarker(new MarkerOptions().position(latLng));
            }
        } catch (NullPointerException ignore) {
        }
    }

    public static void changeStatus(boolean status) {
        experimentationStatus = status;
    }

    public void logEvent(String message) {
        mMixpanel.track(message);
    }
}