package com.sakurafish.exam.location.api; import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageManager; import android.databinding.DataBindingUtil; import android.location.Location; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Toast; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; 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.location.LocationSettingsRequest; import com.google.android.gms.location.LocationSettingsResult; import com.google.android.gms.location.LocationSettingsStatusCodes; import com.sakurafish.exam.location.api.databinding.ActivityMainBinding; import java.text.DateFormat; import java.util.Date; /** * Retrieve current location using Google Play Services Location API * Based on "https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates" */ public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { protected static final String TAG = "location-updates-sample"; /** * 10秒間隔で位置情報を更新。実際には多少頻度が多くなるかもしれない。 */ public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000; /** * 最速の更新間隔。この値より頻繁に更新されることはない。 */ public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = UPDATE_INTERVAL_IN_MILLISECONDS / 2; private final static String REQUESTING_LOCATION_UPDATES_KEY = "requesting-location-updates-key"; private final static String LOCATION_KEY = "location-key"; private final static String LAST_UPDATED_TIME_STRING_KEY = "last-updated-time-string-key"; private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1; private static final int REQUEST_CHECK_SETTINGS = 10; private ActivityMainBinding mBinding; private GoogleApiClient mGoogleApiClient; private LocationRequest mLocationRequest; private Location mCurrentLocation; private Boolean mRequestingLocationUpdates; private String mLastUpdateTime; private String mLatitudeLabel; private String mLongitudeLabel; private String mLastUpdateTimeLabel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); mLatitudeLabel = getResources().getString(R.string.latitude_label); mLongitudeLabel = getResources().getString(R.string.longitude_label); mLastUpdateTimeLabel = getResources().getString(R.string.last_update_time_label); mRequestingLocationUpdates = false; mLastUpdateTime = ""; updateValuesFromBundle(savedInstanceState); buildGoogleApiClient(); } private void updateValuesFromBundle(Bundle savedInstanceState) { Log.i(TAG, "Updating values from bundle"); if (savedInstanceState != null) { if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) { mRequestingLocationUpdates = savedInstanceState.getBoolean( REQUESTING_LOCATION_UPDATES_KEY); setButtonsEnabledState(); } if (savedInstanceState.keySet().contains(LOCATION_KEY)) { mCurrentLocation = savedInstanceState.getParcelable(LOCATION_KEY); } if (savedInstanceState.keySet().contains(LAST_UPDATED_TIME_STRING_KEY)) { mLastUpdateTime = savedInstanceState.getString(LAST_UPDATED_TIME_STRING_KEY); } updateUI(); } } protected synchronized void buildGoogleApiClient() { Log.i(TAG, "Building GoogleApiClient"); mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); createLocationRequest(); } protected void createLocationRequest() { mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } public void startUpdatesButtonHandler(View view) { clearUI(); if (!isPlayServicesAvailable(this)) return; if (!mRequestingLocationUpdates) { mRequestingLocationUpdates = true; } else { return; } if (Build.VERSION.SDK_INT < 23) { setButtonsEnabledState(); startLocationUpdates(); return; } if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { setButtonsEnabledState(); startLocationUpdates(); } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.ACCESS_FINE_LOCATION)) { showRationaleDialog(); } else { ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); } } } public void stopUpdatesButtonHandler(View view) { if (mRequestingLocationUpdates) { mRequestingLocationUpdates = false; setButtonsEnabledState(); stopLocationUpdates(); } } private void startLocationUpdates() { Log.i(TAG, "startLocationUpdates"); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(mLocationRequest); // 現在位置の取得の前に位置情報の設定が有効になっているか確認する PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build()); result.setResultCallback(new ResultCallback<LocationSettingsResult>() { @Override public void onResult(@NonNull LocationSettingsResult locationSettingsResult) { final Status status = locationSettingsResult.getStatus(); switch (status.getStatusCode()) { case LocationSettingsStatusCodes.SUCCESS: // 設定が有効になっているので現在位置を取得する if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, MainActivity.this); } break; case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: // 設定が有効になっていないのでダイアログを表示する try { status.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS); } catch (IntentSender.SendIntentException e) { // Ignore the error. } break; case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: // Location settings are not satisfied. However, we have no way // to fix the settings so we won't show the dialog. break; } } }); } private void setButtonsEnabledState() { if (mRequestingLocationUpdates) { mBinding.startUpdatesButton.setEnabled(false); mBinding.stopUpdatesButton.setEnabled(true); } else { mBinding.startUpdatesButton.setEnabled(true); mBinding.stopUpdatesButton.setEnabled(false); } } private void clearUI() { mBinding.latitudeText.setText(""); mBinding.longitudeText.setText(""); mBinding.lastUpdateTimeText.setText(""); } private void updateUI() { if (mCurrentLocation == null) return; mBinding.latitudeText.setText(String.format("%s: %f", mLatitudeLabel, mCurrentLocation.getLatitude())); mBinding.longitudeText.setText(String.format("%s: %f", mLongitudeLabel, mCurrentLocation.getLongitude())); mBinding.lastUpdateTimeText.setText(String.format("%s: %s", mLastUpdateTimeLabel, mLastUpdateTime)); } protected void stopLocationUpdates() { Log.i(TAG, "stopLocationUpdates"); // The final argument to {@code requestLocationUpdates()} is a LocationListener // (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this); } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { setButtonsEnabledState(); startLocationUpdates(); } else { if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { mRequestingLocationUpdates = false; Toast.makeText(MainActivity.this, "このアプリの機能を有効にするには端末の設定画面からアプリの位置情報パーミッションを有効にして下さい。", Toast.LENGTH_SHORT).show(); } else { showRationaleDialog(); } } break; } } } private void showRationaleDialog() { new AlertDialog.Builder(this) .setPositiveButton("許可する", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); } }) .setNegativeButton("しない", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "位置情報パーミッションが許可されませんでした。", Toast.LENGTH_SHORT).show(); mRequestingLocationUpdates = false; } }) .setCancelable(false) .setMessage("このアプリは位置情報の利用を許可する必要があります。") .show(); } public static boolean isPlayServicesAvailable(Context context) { // Google Play Service APKが有効かどうかチェックする int resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context); if (resultCode != ConnectionResult.SUCCESS) { GoogleApiAvailability.getInstance().getErrorDialog((Activity) context, resultCode, 2).show(); return false; } return true; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_CHECK_SETTINGS: switch (resultCode) { case Activity.RESULT_OK: startLocationUpdates(); break; case Activity.RESULT_CANCELED: break; } break; } } @Override protected void onStart() { super.onStart(); mGoogleApiClient.connect(); } @Override public void onResume() { super.onResume(); isPlayServicesAvailable(this); // Within {@code onPause()}, we pause location updates, but leave the // connection to GoogleApiClient intact. Here, we resume receiving // location updates if the user has requested them. if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) { startLocationUpdates(); } } @Override protected void onPause() { super.onPause(); // Stop location updates to save battery, but don't disconnect the GoogleApiClient object. if (mGoogleApiClient.isConnected()) { stopLocationUpdates(); } } @Override protected void onStop() { stopLocationUpdates(); mGoogleApiClient.disconnect(); super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); } @Override public void onConnected(@Nullable Bundle bundle) { Log.i(TAG, "onConnected"); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } if (mCurrentLocation == null) { mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); updateUI(); } if (mRequestingLocationUpdates) { startLocationUpdates(); } } @Override public void onLocationChanged(Location location) { Log.i(TAG, "onLocationChanged"); mCurrentLocation = location; mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); updateUI(); Toast.makeText(this, getResources().getString(R.string.location_updated_message), Toast.LENGTH_SHORT).show(); } @Override public void onConnectionSuspended(int i) { // The connection to Google Play services was lost for some reason. We call connect() to // attempt to re-establish the connection. Log.i(TAG, "Connection suspended"); mGoogleApiClient.connect(); } @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { // Refer to the javadoc for ConnectionResult to see what error codes might be returned in // onConnectionFailed. Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode()); } public void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, mRequestingLocationUpdates); savedInstanceState.putParcelable(LOCATION_KEY, mCurrentLocation); savedInstanceState.putString(LAST_UPDATED_TIME_STRING_KEY, mLastUpdateTime); super.onSaveInstanceState(savedInstanceState); } }