diff --git a/app/build.gradle b/app/build.gradle index 1913b202..4a0fc28f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,5 +26,7 @@ dependencies { }) compile 'com.android.support:appcompat-v7:25.1.0' compile 'com.android.support:recyclerview-v7:25.0.1' + compile 'com.google.android.gms:play-services-places:9.8.0' + compile 'com.google.android.gms:play-services-location:9.8.0' testCompile 'junit:junit:4.12' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 89b2b127..44d2738d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,11 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> + + + @@ -22,4 +27,6 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/example/android/shushme/MainActivity.java b/app/src/main/java/com/example/android/shushme/MainActivity.java index 102a5ab8..0361f01e 100644 --- a/app/src/main/java/com/example/android/shushme/MainActivity.java +++ b/app/src/main/java/com/example/android/shushme/MainActivity.java @@ -16,19 +16,54 @@ * limitations under the License. */ +import android.content.ContentValues; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import android.widget.CheckBox; +import android.widget.Toast; -public class MainActivity extends AppCompatActivity { +import com.example.android.shushme.provider.PlaceContract; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesNotAvailableException; +import com.google.android.gms.common.GooglePlayServicesRepairableException; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; +import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.location.places.Place; +import com.google.android.gms.location.places.PlaceBuffer; +import com.google.android.gms.location.places.Places; +import com.google.android.gms.location.places.ui.PlacePicker; + +import java.util.ArrayList; +import java.util.List; + +public class MainActivity extends AppCompatActivity implements + ConnectionCallbacks, + OnConnectionFailedListener { // Constants public static final String TAG = MainActivity.class.getSimpleName(); + private static final int PERMISSIONS_REQUEST_FINE_LOCATION = 111; + private static final int PLACE_PICKER_REQUEST = 1; // Member variables private PlaceListAdapter mAdapter; private RecyclerView mRecyclerView; + private GoogleApiClient mClient; /** * Called when the activity is starting @@ -43,9 +78,154 @@ protected void onCreate(Bundle savedInstanceState) { // Set up the recycler view mRecyclerView = (RecyclerView) findViewById(R.id.places_list_recycler_view); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); - mAdapter = new PlaceListAdapter(this); + mAdapter = new PlaceListAdapter(this, null); mRecyclerView.setAdapter(mAdapter); + + // Build up the LocationServices API client + // Uses the addApi method to request the LocationServices API + // Also uses enableAutoManage to automatically when to connect/suspend the client + mClient = new GoogleApiClient.Builder(this) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .addApi(LocationServices.API) + .addApi(Places.GEO_DATA_API) + .enableAutoManage(this, this) + .build(); + + } + + /*** + * Called when the Google API Client is successfully connected + * + * @param connectionHint Bundle of data provided to clients by Google Play services + */ + @Override + public void onConnected(@Nullable Bundle connectionHint) { + refreshPlacesData(); + Log.i(TAG, "API Client Connection Successful!"); + } + + /*** + * Called when the Google API Client is suspended + * + * @param cause cause The reason for the disconnection. Defined by constants CAUSE_*. + */ + @Override + public void onConnectionSuspended(int cause) { + Log.i(TAG, "API Client Connection Suspended!"); + } + + /*** + * Called when the Google API Client failed to connect to Google Play Services + * + * @param result A ConnectionResult that can be used for resolving the error + */ + @Override + public void onConnectionFailed(@NonNull ConnectionResult result) { + Log.e(TAG, "API Client Connection Failed!"); + } + + public void refreshPlacesData() { + Uri uri = PlaceContract.PlaceEntry.CONTENT_URI; + Cursor data = getContentResolver().query( + uri, + null, + null, + null, + null); + + if (data == null || data.getCount() == 0) return; + List guids = new ArrayList(); + while (data.moveToNext()) { + guids.add(data.getString(data.getColumnIndex(PlaceContract.PlaceEntry.COLUMN_PLACE_ID))); + } + PendingResult placeResult = Places.GeoDataApi.getPlaceById(mClient, + guids.toArray(new String[guids.size()])); + placeResult.setResultCallback(new ResultCallback() { + @Override + public void onResult(@NonNull PlaceBuffer places) { + mAdapter.swapPlaces(places); + + } + }); + } + + /*** + * Button Click event handler to handle clicking the "Add new location" Button + * + * @param view + */ + public void onAddPlaceButtonClicked(View view) { + if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + Toast.makeText(this, getString(R.string.need_location_permission_message), Toast.LENGTH_LONG).show(); + return; + } + try { + // Start a new Activity for the Place Picker API, this will trigger {@code #onActivityResult} + // when a place is selected or with the user cancels. + PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder(); + Intent i = builder.build(this); + startActivityForResult(i, PLACE_PICKER_REQUEST); + } catch (GooglePlayServicesRepairableException e) { + Log.e(TAG, String.format("GooglePlayServices Not Available [%s]", e.getMessage())); + } catch (GooglePlayServicesNotAvailableException e) { + Log.e(TAG, String.format("GooglePlayServices Not Available [%s]", e.getMessage())); + } catch (Exception e) { + Log.e(TAG, String.format("PlacePicker Exception: %s", e.getMessage())); + } + } + + + /*** + * Called when the Place Picker Activity returns back with a selected place (or after canceling) + * + * @param requestCode The request code passed when calling startActivityForResult + * @param resultCode The result code specified by the second activity + * @param data The Intent that carries the result data. + */ + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PLACE_PICKER_REQUEST && resultCode == RESULT_OK) { + Place place = PlacePicker.getPlace(this, data); + if (place == null) { + Log.i(TAG, "No place selected"); + return; + } + + // Extract the place information from the API + String placeName = place.getName().toString(); + String placeAddress = place.getAddress().toString(); + String placeID = place.getId(); + + // Insert a new place into DB + ContentValues contentValues = new ContentValues(); + contentValues.put(PlaceContract.PlaceEntry.COLUMN_PLACE_ID, placeID); + getContentResolver().insert(PlaceContract.PlaceEntry.CONTENT_URI, contentValues); + + // Get live data information + refreshPlacesData(); + } } + @Override + public void onResume() { + super.onResume(); + + // Initialize location permissions checkbox + CheckBox locationPermissions = (CheckBox) findViewById(R.id.location_permission_checkbox); + if (ActivityCompat.checkSelfPermission(MainActivity.this, + android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + locationPermissions.setChecked(false); + } else { + locationPermissions.setChecked(true); + locationPermissions.setEnabled(false); + } + } + + public void onLocationPermissionClicked(View view) { + ActivityCompat.requestPermissions(MainActivity.this, + new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, + PERMISSIONS_REQUEST_FINE_LOCATION); + } } diff --git a/app/src/main/java/com/example/android/shushme/PlaceListAdapter.java b/app/src/main/java/com/example/android/shushme/PlaceListAdapter.java index 793ac1d5..c48916ac 100644 --- a/app/src/main/java/com/example/android/shushme/PlaceListAdapter.java +++ b/app/src/main/java/com/example/android/shushme/PlaceListAdapter.java @@ -23,17 +23,21 @@ import android.view.ViewGroup; import android.widget.TextView; +import com.google.android.gms.location.places.PlaceBuffer; + public class PlaceListAdapter extends RecyclerView.Adapter { private Context mContext; + private PlaceBuffer mPlaces; /** * Constructor using the context and the db cursor * * @param context the calling context/activity */ - public PlaceListAdapter(Context context) { + public PlaceListAdapter(Context context, PlaceBuffer places) { this.mContext = context; + this.mPlaces = places; } /** @@ -59,9 +63,19 @@ public PlaceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { */ @Override public void onBindViewHolder(PlaceViewHolder holder, int position) { - + String placeName = mPlaces.get(position).getName().toString(); + String placeAddress = mPlaces.get(position).getAddress().toString(); + holder.nameTextView.setText(placeName); + holder.addressTextView.setText(placeAddress); } + public void swapPlaces(PlaceBuffer newPlaces){ + mPlaces = newPlaces; + if (mPlaces != null) { + // Force the RecyclerView to refresh + this.notifyDataSetChanged(); + } + } /** * Returns the number of items in the cursor @@ -70,7 +84,8 @@ public void onBindViewHolder(PlaceViewHolder holder, int position) { */ @Override public int getItemCount() { - return 0; + if(mPlaces==null) return 0; + return mPlaces.getCount(); } /** diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3d6e2bf4..3471ef2d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -59,12 +59,49 @@ android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> + + + + + + + + + + + + +