jp.realglobe.sugo.actor.android.hitoe.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for jp.realglobe.sugo.actor.android.hitoe.MainActivity.java

Source

/*----------------------------------------------------------------------
 * Copyright 2017 realglobe Inc.
 *
 * 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 jp.realglobe.sugo.actor.android.hitoe;

import android.Manifest;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import jp.ne.docomo.smt.dev.hitoetransmitter.sdk.HitoeSdkAPIImpl;
import jp.realglobe.sugo.actor.Actor;
import jp.realglobe.sugo.actor.Emitter;

public class MainActivity extends AppCompatActivity {

    private static final String LOG_TAG = MainActivity.class.getName();

    private static final long LOCATION_INTERVAL = 10_000;

    // ??
    private static final String KEY_HEART_RATE = "heartRate";
    private static final String KEY_LOCATION = "location";
    private static final String KEY_DATE = "date";
    private static final String KEY_ID = "id";

    // 
    private enum State {
        MAIN, WARNING, EMERGENCY,
    }

    private final int permissionRequestCode = (int) (Integer.MAX_VALUE * Math.random());

    private Vibrator vibrator;
    private Ringtone ringtone;
    private GoogleApiClient googleApiClient;
    private static HitoeWrapper hitoe;

    private Handler handler;
    private Handler timer;
    private CountDownTimer callTimer;

    private State state = State.MAIN;

    // ??
    private volatile Location location;
    // hitoe ???????
    private boolean hitoeReady;
    // ???
    private volatile Pair<Long, Integer> heartrate;
    // ???
    private volatile TextView heartrateView;
    // ??
    private int reportId = Math.abs((int) System.nanoTime());

    // ?
    private TextView warningView;

    private Actor actor;

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

        // ?? actor ID ??
        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        final String actorSuffix = preferences.getString(getString(R.string.key_actor_suffix), null);
        if (actorSuffix == null) {
            preferences.edit().putString(getString(R.string.key_actor_suffix),
                    String.valueOf(Math.abs((new Random(System.nanoTime())).nextInt()))).apply();
        }

        this.vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
        this.ringtone = RingtoneManager.getRingtone(this,
                RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM));
        this.googleApiClient = new GoogleApiClient.Builder(this).addApi(LocationServices.API)
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(@Nullable Bundle bundle) {
                        if (ActivityCompat.checkSelfPermission(MainActivity.this,
                                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                                && ActivityCompat.checkSelfPermission(MainActivity.this,
                                        Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                            return;
                        }
                        LocationServices.FusedLocationApi.requestLocationUpdates(MainActivity.this.googleApiClient,
                                LocationRequest.create().setInterval(LOCATION_INTERVAL)
                                        .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY),
                                location -> {
                                    MainActivity.this.location = location;
                                    Log.d(LOG_TAG, "Location changed to " + location);
                                });
                        Log.d(LOG_TAG, "Location monitor started");
                    }

                    @Override
                    public void onConnectionSuspended(int i) {
                        Log.d(LOG_TAG, "Location monitor suspended");
                    }
                }).addOnConnectionFailedListener(connectionResult -> {
                    final String warning = "Location detection error: " + connectionResult;
                    MainActivity.this.warningView.post(() -> setWarning(warning));
                    Log.w(LOG_TAG, warning);
                }).build();
        hitoe = new HitoeWrapper(HitoeSdkAPIImpl.getInstance(this.getApplicationContext()));
        hitoe.setHeartrateReceiver(() -> {
            synchronized (this) {
                // ?? hitoe ???????
                if (!this.hitoeReady) {
                    this.hitoeReady = true;
                    handler.post(() -> {
                        synchronized (this) {
                            if (this.hitoeReady) {
                                this.disableHitoeSetting();
                            }
                        }
                    });
                }
            }
        }, (date, heartrate) -> {
            this.heartrate = new Pair<>(date, heartrate);
            this.heartrateView.post(() -> this.heartrateView.setText(String.format(Locale.US, "%d", heartrate)));
        });
        hitoe.setDisconnectCallback(() -> {
            synchronized (this) {
                // ?? hitoe ???????
                this.hitoeReady = false;
                handler.post(() -> {
                    synchronized (this) {
                        if (!this.hitoeReady) {
                            this.enableHitoeSetting();
                        }
                    }
                });
            }
        });
        this.handler = new Handler();
        this.timer = new Handler();
        this.heartrate = new Pair<>(0L, 0);

        // ??
        reset();

        // ???????????
        checkPermission();
    }

    private synchronized void setWarning(String warning) {
        this.warningView.setText(warning);
    }

    /**
     * hitoe ?????
     */
    private void enableHitoeSetting() {
        final Button button = (Button) findViewById(R.id.button_hitoe_setting);
        if (button == null) {
            return;
        }
        button.setEnabled(true);
        button.setVisibility(View.VISIBLE);
    }

    /**
     * hitoe ?????
     */
    private void disableHitoeSetting() {
        final Button button = (Button) findViewById(R.id.button_hitoe_setting);
        if (button == null) {
            return;
        }
        button.setEnabled(false);
        button.setVisibility(View.INVISIBLE);
    }

    /**
     * ?????????????????????
     */
    private void checkPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // ??????
            this.requestPermissions(new String[] { Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION, }, this.permissionRequestCode);
        } else {
            showPermissionStatus(true);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        if (requestCode != this.permissionRequestCode) {
            return;
        }

        final Set<String> required = new HashSet<>(Arrays.asList(Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION));
        for (int i = 0; i < permissions.length; i++) {
            if (!required.contains(permissions[i])) {
                continue;
            }
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                continue;
            }
            required.remove(permissions[i]);
        }

        showPermissionStatus(required.isEmpty());
    }

    /**
     * ????
     *
     * @param allowed ??????? true
     */
    private void showPermissionStatus(boolean allowed) {
        final String message;
        if (allowed) {
            message = "????????????";
            synchronized (this) {
                if (!this.hitoeReady) {
                    enableHitoeSetting();
                }
            }
        } else {
            message = "??????????????\n?????????";
        }
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        reset();
        hitoe.disconnect(() -> {
        });
    }

    @Override
    public void onBackPressed() {
        (new FinishDialog()).show(getFragmentManager(), "dialog");
    }

    private void superOnBackPressed() {
        super.onBackPressed();
    }

    /**
     * ?
     */
    public static class FinishDialog extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final MainActivity activity = (MainActivity) getActivity();
            return (new AlertDialog.Builder(activity)).setTitle("?????")
                    .setPositiveButton("??", (dialog, which) -> activity.superOnBackPressed())
                    .create();
        }
    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.item_settings) {
            startActivity(new Intent(this, SettingsActivity.class));
        } else if (item.getItemId() == R.id.item_allow) {
            checkPermission();
        } else if (item.getItemId() == R.id.item_hitoe_settings) {
            startActivity(new Intent(this, HitoeSettingActivity.class));
        } else if (item.getItemId() == R.id.item_reset) {
            reset();
        } else if (item.getItemId() == R.id.item_call) {
            callAfterDialog();
        } else if (item.getItemId() == R.id.item_timer_start) {
            startEventTimer();
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * ???
     */
    private synchronized void reset() {
        setContentView(R.layout.activity_main);
        this.state = State.MAIN;
        this.timer.removeCallbacksAndMessages(null);
        if (this.callTimer != null) {
            this.callTimer.cancel();
            this.callTimer = null;
        }
        this.vibrator.cancel();
        this.ringtone.stop();
        this.googleApiClient.disconnect();
        final Button hitoeSettingButton = (Button) findViewById(R.id.button_hitoe_setting);
        hitoeSettingButton.setOnClickListener(v -> startActivity(new Intent(this, HitoeSettingActivity.class)));
        if (this.hitoeReady) {
            disableHitoeSetting();
        } else {
            enableHitoeSetting();
        }
        this.heartrateView = (TextView) findViewById(R.id.text_heartrate_value);
        this.heartrateView.setText(String.format(Locale.US, "%d", heartrate.second));

        if (this.actor != null) {
            this.actor.disconnect();
            this.actor = null;
        }

        relayWarningView();

        Log.d(LOG_TAG, "Mode was reset");
    }

    private synchronized void relayWarningView() {
        final TextView old = this.warningView;
        this.warningView = (TextView) findViewById(R.id.text_warning);
        if (old != null) {
            this.warningView.setText(old.getText());
        }
    }

    /**
     * ??
     */
    private synchronized void warn() {
        if (this.state != State.MAIN) {
            // ????
            return;
        }

        final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        final long delay = Long.parseLong(
                sharedPreferences.getString(getString(R.string.key_delay), getString(R.string.default_delay)));

        setContentView(R.layout.activity_warning);
        this.state = State.WARNING;
        this.timer.removeCallbacksAndMessages(null);
        if (this.callTimer != null) {
            this.callTimer.cancel();
        }
        this.callTimer = new CountDownTimer(1_000L * delay, 100) {
            @Override
            public void onTick(long l) {
                final TextView view = (TextView) findViewById(R.id.text_counter_count);
                if (view == null) {
                    return;
                }
                view.setText(String.format(Locale.US, "%d", (int) Math.ceil(l / 1_000.0)));
            }

            @Override
            public void onFinish() {
                call();
            }
        };
        this.callTimer.start();

        this.vibrator.vibrate(new long[] { 500, 1_000 }, 0);
        this.ringtone.play();
        if (!(this.googleApiClient.isConnecting() || this.googleApiClient.isConnected())) {
            this.googleApiClient.connect();
        }

        findViewById(R.id.button_call).setOnClickListener(view -> callAfterDialog());
        findViewById(R.id.button_stop)
                .setOnClickListener(view -> (new CancelDialog()).show(getFragmentManager(), "dialog"));

        this.heartrateView = (TextView) findViewById(R.id.text_heartrate_value);
        this.heartrateView.setText(String.format(Locale.US, "%d", heartrate.second));

        startReport();

        relayWarningView();

        Log.d(LOG_TAG, "Warning mode started");
    }

    /**
     * 
     */
    public static class CancelDialog extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final MainActivity activity = (MainActivity) getActivity();
            return (new AlertDialog.Builder(activity)).setTitle("?????")
                    .setPositiveButton("?", (dialog, which) -> {
                        synchronized (activity) {
                            if (activity.state == State.WARNING) {
                                activity.reset();
                            }
                        }
                    }).create();
        }
    }

    /**
     * ??
     */
    private synchronized void call() {
        if (this.state == State.EMERGENCY) {
            return;
        }
        setContentView(R.layout.activity_emergency);
        this.state = State.EMERGENCY;
        this.timer.removeCallbacksAndMessages(null);
        if (this.callTimer != null) {
            this.callTimer.cancel();
            this.callTimer = null;
        }
        this.vibrator.cancel();
        this.ringtone.stop();
        if (!(this.googleApiClient.isConnecting() || this.googleApiClient.isConnected())) {
            this.googleApiClient.connect();
        }
        this.heartrateView = (TextView) findViewById(R.id.text_heartrate_value);
        this.heartrateView.setText(String.format(Locale.US, "%d", heartrate.second));

        startReport();

        relayWarningView();

        Log.d(LOG_TAG, "Emergency mode started");
    }

    /**
     * ???????
     */
    private void callAfterDialog() {
        (new CallDialog()).show(getFragmentManager(), "dialog");
    }

    /**
     * 
     */
    public static class CallDialog extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final MainActivity activity = (MainActivity) getActivity();
            return (new AlertDialog.Builder(activity)).setTitle("?????")
                    .setPositiveButton("??", (dialog, which) -> activity.call()).create();
        }
    }

    /**
     * ????
     */
    private synchronized void startEventTimer() {
        final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        final long delay = 1_000 * Long.parseLong(
                sharedPreferences.getString(getString(R.string.key_timer), getString(R.string.default_timer)));

        this.timer.removeCallbacksAndMessages(null);
        this.timer.postDelayed(() -> {
            warn();
            Log.d(LOG_TAG, "Dummy emergency was triggered");
        }, delay);
        Log.d(LOG_TAG, "Event timer started");
    }

    /**
     * ????
     */
    private synchronized void startReport() {
        if (this.actor != null) {
            Log.d(LOG_TAG, "Already connecting");
            return;
        }
        this.reportId++;
        final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        final String server = sharedPreferences.getString(getString(R.string.key_server),
                getString(R.string.default_server));
        final String actorKey = getString(R.string.actor_prefix) + sharedPreferences
                .getString(getString(R.string.key_actor_suffix), getString(R.string.default_actor_suffix));
        final long interval = 1_000L * Long.parseLong(sharedPreferences
                .getString(getString(R.string.key_report_interval), getString(R.string.default_report_interval)));

        this.actor = new Actor(actorKey, getString(R.string.module), null);
        final Emitter emitter;
        try {
            emitter = actor.addModule(getString(R.string.module),
                    getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName,
                    getString(R.string.description), new Object());
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(e);
        }
        actor.setOnConnect(() -> report(emitter, interval));
        actor.connect(server);
    }

    private synchronized void report(Emitter emitter, long interval) {
        if (this.actor == null) {
            // 
            return;
        }

        final Map<String, Object> data = new HashMap<>();
        final Pair<Long, Integer> heartrate = this.heartrate;
        data.put(KEY_ID, this.reportId);
        data.put(KEY_DATE, (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US))
                .format(new Date(heartrate.first)));
        data.put(KEY_HEART_RATE, heartrate.second);
        final Location curLocation = this.location;
        if (curLocation != null) {
            data.put(KEY_LOCATION, Arrays.asList(curLocation.getLatitude(), curLocation.getLongitude(),
                    curLocation.getAltitude()));
        } else {
            data.put(KEY_LOCATION, Arrays.asList(0, 0, 0));
        }
        emitter.emit(this.state.name().toLowerCase(), data);
        Log.d(LOG_TAG, "Sent report");

        this.handler.postDelayed(() -> report(emitter, interval), interval);
    }

    /**
     * hitoe ???
     *
     * @return hitoe ??
     */
    static HitoeWrapper getHitoe() {
        return hitoe;
    }

}