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

Java tutorial

Introduction

Here is the source code for jp.realglobe.sugo.actor.android.call.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.call;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
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.telephony.TelephonyManager;
import android.util.Log;
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.LocationListener;
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.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

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_DETECTOR_INTERVAL = 10_000L;

    // ?
    private static final String EVENT_EMERGENCY = "emergency";

    // ??
    private static final String KEY_LOCATION = "location";
    private static final String KEY_DATE = "date";
    private static final String KEY_ID = "id";
    private static final String KEY_PHONE_NUMBER = "phoneNumber";

    private static final int PERMISSION_REQUEST_CODE = 24876;
    private static final String[] REQUIRED_PERMISSIONS = new String[] { Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.READ_SMS,
            Manifest.permission.CALL_PHONE, };

    private GoogleApiClient googleApiClient;

    private LinkedList<String> errors;

    // ??
    private volatile Location location;
    // ?
    private volatile String phoneNumber;

    private Actor actor;
    private int reportId;

    private TextView messageView;
    private Button callButton;
    private Button talkButton;
    private Button resetButton;
    private TextView errorView;
    private Handler handler;

    /**
     * ??
     *
     * @param interval      ??????
     * @param context       
     * @param listener      ????
     * @param errorCallback ??
     * @return ??
     */
    private static GoogleApiClient setupLocationClient(long interval, Context context, LocationListener listener,
            StringCallback errorCallback) {
        final AtomicReference<GoogleApiClient> client = new AtomicReference<>();
        client.set((new GoogleApiClient.Builder(context)).addApi(LocationServices.API)
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(@Nullable Bundle bundle) {
                        if (ActivityCompat.checkSelfPermission(context,
                                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                                && ActivityCompat.checkSelfPermission(context,
                                        Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                            return;
                        }
                        LocationServices.FusedLocationApi.requestLocationUpdates(client.get(), LocationRequest
                                .create().setInterval(interval).setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY),
                                location -> {
                                    Log.d(LOG_TAG, "Location changed to " + location);
                                    if (listener != null) {
                                        listener.onLocationChanged(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 detector error: " + connectionResult;
                    Log.w(LOG_TAG, warning);
                    if (errorCallback != null) {
                        errorCallback.call(connectionResult.getErrorMessage());
                    }
                }).build());
        return client.get();
    }

    @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.currentTimeMillis())).nextInt()))).apply();
        }

        // ???
        this.googleApiClient = setupLocationClient(LOCATION_DETECTOR_INTERVAL, this,
                location -> this.location = location, this::showError);

        this.errors = new LinkedList<>();

        this.reportId = Math.abs((new Random(System.currentTimeMillis())).nextInt());

        setContentView(R.layout.activity_main);

        this.messageView = (TextView) findViewById(R.id.text_message);
        this.callButton = (Button) findViewById(R.id.button_call);
        this.talkButton = (Button) findViewById(R.id.button_talk);
        this.resetButton = (Button) findViewById(R.id.button_reset);
        this.errorView = (TextView) findViewById(R.id.text_error);
        this.handler = new Handler();

        this.callButton.setOnClickListener(view -> call());
        this.talkButton.setOnClickListener(view -> talk());
        this.resetButton.setOnClickListener(view -> reset());

        reset();

        checkPermission();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (this.actor != null) {
            this.actor.disconnect();
            this.actor = null;
        }
        this.googleApiClient.disconnect();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.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();
        }
        return true;
    }

    /**
     * ?
     *
     * @param error ?
     */
    private void showError(String error) {
        this.errors.add(error);
        this.errorView.post(() -> this.errorView.setText(error));
    }

    /**
     * ?????????????????????
     */
    private void checkPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // ??????
            this.requestPermissions(REQUIRED_PERMISSIONS, PERMISSION_REQUEST_CODE);
        } else {
            showPermissionStatus(true);
        }
    }

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

        final Set<String> required = new HashSet<>(Arrays.asList(REQUIRED_PERMISSIONS));
        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]);
        }

        if (!required.contains(Manifest.permission.READ_SMS)) {
            final TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            this.phoneNumber = manager.getLine1Number();
            Log.i(LOG_TAG, "Phone number is " + this.phoneNumber);
        }

        showPermissionStatus(required.isEmpty());
    }

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

    private synchronized void reset() {
        if (this.actor != null) {
            this.actor.disconnect();
            this.actor = null;
        }
        this.googleApiClient.disconnect();

        this.messageView.setText("");

        this.callButton.setEnabled(true);
        this.callButton.setVisibility(View.VISIBLE);

        this.talkButton.setEnabled(false);
        this.talkButton.setVisibility(View.INVISIBLE);

        this.resetButton.setEnabled(false);
        this.resetButton.setVisibility(View.INVISIBLE);
    }

    private synchronized void call() {
        this.messageView.setText(getString(R.string.message_called));

        this.callButton.setEnabled(false);
        this.callButton.setVisibility(View.VISIBLE);

        this.talkButton.setEnabled(true);
        this.talkButton.setVisibility(View.VISIBLE);

        this.resetButton.setEnabled(true);
        this.resetButton.setVisibility(View.VISIBLE);

        if (!(this.googleApiClient.isConnecting() || this.googleApiClient.isConnected())) {
            this.googleApiClient.connect();
        }
        report();
        talk();
    }

    /**
     *  hub ??
     */
    private void report() {
        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        final String server = preferences.getString(getString(R.string.key_server),
                getString(R.string.default_server));
        final String key = getString(R.string.actor_prefix) + preferences
                .getString(getString(R.string.key_actor_suffix), getString(R.string.default_actor_suffix));

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

    private synchronized int nextReportId() {
        if (this.reportId < 0) {
            this.reportId = 0;
        }
        return this.reportId++;
    }

    private void reportRoutine(Emitter emitter, int reportId) {
        if (this.actor == null) {
            // 
            return;
        }

        final Map<String, Object> data = new HashMap<>();
        data.put(KEY_ID, reportId);
        data.put(KEY_DATE, (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US)).format(new Date()));
        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, null);
        }
        data.put(KEY_PHONE_NUMBER, this.phoneNumber);
        emitter.emit(EVENT_EMERGENCY, data);
        Log.d(LOG_TAG, "Sent report");

        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        final long interval = 1_000L * Long.parseLong(preferences.getString(getString(R.string.key_report_interval),
                String.valueOf(getResources().getInteger(R.integer.default_report_interval))));
        this.handler.postDelayed(() -> reportRoutine(emitter, reportId), interval);
    }

    /**
     * ?
     */
    private void talk() {
        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        final String phoneNumber = preferences.getString(getString(R.string.key_phone_number), null);
        if (phoneNumber == null) {
            Log.e(LOG_TAG, "No phone number");
            showError("???????");
            return;
        }
        Log.d(LOG_TAG, "Call " + phoneNumber);
        if (ActivityCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
            startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber)));
            return;
        }
        startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber)));
    }

    private interface StringCallback {
        void call(String error);
    }

}