Permalink
Browse files

Places SDK integration with Location Presenter, Model, and Activity (#8)

* Blank location files created

Activity, view, presenter and layout created for Location piece that connects to Places SDK. All files empty as starting point

* Laptop transfer

Layout put together, LocationActivity almost finished, DataManager created for subscribing to observables to drive changes

* Create CONTRIBUTING.MD

Most of content borrowed from Android-Boilerplate and Android-Guidelines by Ribot

* DataManager and Location Model class

* Update AndroidManifest.xml
  • Loading branch information...
ppeters0502 committed Aug 30, 2018
1 parent 735845c commit bb91ac6968c6614347deaad119f726dc42ab499f
View
@@ -0,0 +1,39 @@
## Contributing to the NWS Android App Project
### We're excited that you want to contribute to this project!
### Here is how you can contribute:
* Check for open issues and feature requests.
**If there is an issue that you are interested in, leave a comment, and the issue can be assigned to you!**
* Join the slack channel hosted [here](https://opensourceideas.herokuapp.com)
* Fork a copy of the repository, clone it to your local environment, and open the project in Android Studio!
* Once you finish and commit your changes to your forked repository, open a pull request,
attribute the request to the issue that is assigned to you, and submit your changes! We'll review them and accept/decline the changes!
### Application Architecture Guidelines
This project uses the standard MVP architecture (Model View Presenter) and was
originated from the android-boilerplate project created by the UK company Ribot
([Original Repository](https://github.com/ribot/android-boilerplate))
* __View (UI layer)__: this is where Activities, Fragments and other standard Android components live. It's responsible for displaying the data received from the presenters to the user. It also handles user interactions and inputs (click listeners, etc) and triggers the right action in the Presenter if needed.
* __Presenter__: presenters subscribe to RxJava Observables provided by the `DataManager`. They are in charge of handling the subscription lifecycle, analysing/modifying the data returned by the `DataManager` and calling the appropriate methods in the View in order to display the data.
* __Model (Data Layer)__: this is responsible for retrieving, saving, caching and massaging data. It can communicate with local databases and other data stores as well as with restful APIs or third party SDKs. It is divided in two parts: a group of helpers and a `DataManager`. The number of helpers vary between project and each of them has a very specific function, e.g. talking to an API or saving data in `SharedPreferences`. The `DataManager` combines and transforms the outputs from different helpers using Rx operators so it can: 1) provide meaningful data to the Presenter, 2) group actions that will always happen together. This layer also contains the actual model classes that define how the data structure is.
**Here is a diagram of how we implement the MVP architecture (courtesy of ribot)**
![](https://github.com/ribot/android-guidelines/raw/master/architecture_guidelines/architecture_diagram.png)
### How to implement a new screen following MVP
1. Create a new package under `ui` called `signin` (or whatever screen you are working on)
2. Create an new Activity called `ActivitySignIn`. You could also use a Fragment.
3. Define the view interface that your Activity is going to implement. Create a new interface called `SignInMvpView` that extends `MvpView`. Add the methods that you think will be necessary, e.g. `showSignInSuccessful()`
4. Create a `SignInPresenter` class that extends `BasePresenter<SignInMvpView>`
5. Implement the methods in `SignInPresenter` that your Activity requires to perform the necessary actions, e.g. `signIn(String email)`. Once the sign in action finishes you should call `getMvpView().showSignInSuccessful()`.
6. Create a `SignInPresenterTest`and write unit tests for `signIn(email)`. Remember to mock the `SignInMvpView` and also the `DataManager`.
7. Make your `ActivitySignIn` implement `SignInMvpView` and implement the required methods like `showSignInSuccessful()`
8. In your activity, inject a new instance of `SignInPresenter` and call `presenter.attachView(this)` from `onCreate` and `presenter.detachView()` from `onDestroy()`. Also, set up a click listener in your button that calls `presenter.signIn(email)`.
View
@@ -12,6 +12,7 @@ android {
versionCode 1_000_000
versionName "0.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
@@ -56,8 +57,16 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.5.1'
implementation 'com.android.support:multidex:1.0.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//Google Play Services
implementation "com.android.support:support-v4:27.1.1"
implementation "com.android.support:support-v13:27.1.1"
implementation "com.android.support:cardview-v7:27.1.1"
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.google.android.gms:play-services:8.4.0'
}
@@ -19,6 +19,11 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ui.location.LocationActivity"
android:theme="@style/AppTheme"/>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="INSERT-API-KEY-HERE"/>
</application>
</manifest>
@@ -0,0 +1,18 @@
package org.osii.nwsapp.data;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.osii.nwsapp.data.remote.ApiService;
public class DataManager {
private final ApiService mApiService;
@Inject
public DataManager(ApiService apiService)
{
mApiService = apiService;
}
}
@@ -0,0 +1,24 @@
package org.osii.nwsapp.data.model;
import com.google.android.gms.maps.model.LatLng;
import android.content.res.Resources;
public class Location {
public Resources PlaceResource;
public LatLng LatitudeLongitude;
public CharSequence PlaceName;
public String PlaceID;
public Location(){}
public Location(String id){PlaceID = id;}
public Location(Resources res, String id, CharSequence name, LatLng latLong)
{
this.PlaceResource = res;
this.PlaceName = name;
this.PlaceID = id;
this.LatitudeLongitude = latLong;
}
}
@@ -5,6 +5,7 @@ import dagger.Subcomponent
import org.osii.nwsapp.injection.PerActivity
import org.osii.nwsapp.ui.main.MainActivity
import org.osii.nwsapp.ui.tmp.TmpActivity
import org.osii.nwsapp.ui.location.LocationActivity
/**
* This component inject dependencies to all Activities across the application
@@ -15,5 +16,6 @@ interface ActivityComponent {
fun inject(tmpActivity: TmpActivity)
fun inject(mainActivity: MainActivity)
fun inject(locationActivity: LocationActivity)
}
@@ -0,0 +1,82 @@
package org.osii.nwsapp.ui.location;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlaceAutocompleteFragment;
import com.google.android.gms.location.places.ui.PlaceSelectionListener;
import com.google.android.gms.maps.model.LatLng;
import android.text.Spanned;
import android.content.res.Resources;
import android.text.Html;
import org.osii.nwsapp.injection.component.ActivityComponent;
import org.osii.nwsapp.ui.base.BaseActivity;
import android.text.TextUtils;
import android.widget.TextView;
import android.os.Bundle;
import android.widget.Toast;
import org.osii.nwsapp.R;
import javax.inject.Inject;
public class LocationActivity extends BaseActivity implements LocationMvpView, PlaceSelectionListener {
private TextView mPlaceDetailsText;
private TextView mPlaceAttribution;
@Inject LocationPresenter mLocationPresenter;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.getActivityComponent().inject(this);
this.setContentView(R.layout.activity_location);
//Retrieve the PlaceAutoCompleteFragment
PlaceAutocompleteFragment autocompleteFragment = (PlaceAutocompleteFragment)
getFragmentManager().findFragmentById(R.id.autocomplete_fragment);
//With autocompleteFragment set, register listener to receive callback when a place
//has been selected or an error has occurred
autocompleteFragment.setOnPlaceSelectedListener(this);
//Temporary text views that display details about the selected place.
mPlaceDetailsText = (TextView) findViewById(R.id.place_details);
mPlaceAttribution = (TextView) findViewById(R.id.place_attribution);
mLocationPresenter.attachView(this);
mLocationPresenter.setup();
}
@Override
public void onPlaceSelected(Place place){
//Eventually will want to use SQLHelper to save PlaceId and et Latitude and Longitude
//within the app. For now, just display Place details
mPlaceDetailsText.setText(formatPlaceDetails(getResources(), place.getName(), place.getId(),
place.getLatLng()));
CharSequence attributions = place.getAttributions();
if (!TextUtils.isEmpty(attributions)) {
mPlaceAttribution.setText(Html.fromHtml(attributions.toString()));
} else {
mPlaceAttribution.setText("");
}
}
@Override
public void onError(Status status){
Toast.makeText(this, "Place selection failed: " + status.getStatusMessage(),
Toast.LENGTH_SHORT).show();
}
@Override
public void showLocations(){
}
//Temporary helper for formatting information about the place to display
private static Spanned formatPlaceDetails (Resources res, CharSequence name, String id, LatLng latLong){
return Html.fromHtml(res.getString(R.string.place_details, name, id, latLong));
}
}
@@ -0,0 +1,8 @@
package org.osii.nwsapp.ui.location;
import org.osii.nwsapp.ui.base.MvpView;
public interface LocationMvpView extends MvpView {
void showLocations();
}
@@ -0,0 +1,19 @@
package org.osii.nwsapp.ui.location;
import org.osii.nwsapp.data.DataManager;
import org.osii.nwsapp.ui.base.BasePresenter;
import javax.inject.Inject;
public class LocationPresenter extends BasePresenter {
private final DataManager mDataManager;
@Inject
public LocationPresenter(DataManager dataManager) { mDataManager = dataManager; }
public void setup(){
}
}
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="Location" />
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is a Button" />
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp">
<fragment
android:id="@+id/autocomplete_fragment"
android:name="com.google.android.gms.location.places.ui.PlaceAutocompleteFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
<!-- Temporary TextViews to show last selected place -->
<TextView
android:id="@+id/place_details"
android:autoLink="all"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/place_attribution"
android:autoLink="all"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp" />
</LinearLayout>
@@ -1,4 +1,13 @@
<resources>
<string name="app_name">nwsapp</string>
<string name="city_state_holder">%s, %s</string>
<string name="place_details">
<![CDATA[
<b>%1$s</b>
<br/>
<i>Place Id: %2$s</i>
<br/>
LatLong: %3$s
]]>
</string>
</resources>
View
@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.android.tools.build:gradle:3.1.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

0 comments on commit bb91ac6

Please sign in to comment.