From c2058f5e3dbacd3e94f59ce78efeed392b6a2007 Mon Sep 17 00:00:00 2001 From: Cameron Mace Date: Wed, 29 Nov 2017 19:41:00 -0500 Subject: [PATCH] Introduce more android arch component --- app/build.gradle | 2 + gradle/dependencies.gradle | 6 +- plugin-places/build.gradle | 2 + .../1.json | 39 ++++ .../2.json | 39 ++++ plugin-places/src/main/AndroidManifest.xml | 7 +- .../places/autocomplete/DataRepository.java | 49 +++++ .../autocomplete/OnCardItemClickListener.java | 12 -- .../autocomplete/PlaceAutocomplete.java | 16 +- .../PlaceCompleteCardActivity.java | 169 ----------------- .../PlaceCompleteFullActivity.java | 86 --------- .../places/autocomplete/PlaceConstants.java | 28 +-- .../autocomplete/RecentSearchAsyncTask.java | 71 ------- .../autocomplete/RecentSearchesDao.java | 26 --- .../autocomplete/SearchHistoryDatabase.java | 9 - .../data/SearchHistoryDatabase.java | 89 +++++++++ .../converter/CarmenFeatureConverter.java | 19 ++ .../data/dao/SearchHistoryDao.java | 34 ++++ .../entity/SearchHistoryEntity.java} | 19 +- .../autocomplete/model/SearchHistory.java | 9 + .../ui/PlaceAutocompleteActivity.java | 175 ++++++++++++++++++ .../autocomplete/{ => ui}/ResultCardView.java | 2 +- .../autocomplete/ui/ResultClickCallback.java | 7 + .../autocomplete/{ => ui}/ResultView.java | 4 +- .../{ => ui}/SearchResultAdapter.java | 21 ++- .../autocomplete/{ => ui}/SearchView.java | 28 +-- .../autocomplete/{ => utils}/Utils.java | 6 +- .../viewmodel/PlaceAutocompleteViewModel.java | 117 ++++++++++++ .../res/layout/activity_complete_card.xml | 12 +- .../res/layout/activity_complete_full.xml | 58 ++++-- .../main/res/layout/item_search_result.xml | 4 +- 31 files changed, 711 insertions(+), 454 deletions(-) create mode 100644 plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/1.json create mode 100644 plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/2.json create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/DataRepository.java delete mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/OnCardItemClickListener.java delete mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteCardActivity.java delete mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteFullActivity.java delete mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchAsyncTask.java delete mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchesDao.java delete mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchHistoryDatabase.java create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/SearchHistoryDatabase.java create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/converter/CarmenFeatureConverter.java create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/dao/SearchHistoryDao.java rename plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/{RecentSearch.java => data/entity/SearchHistoryEntity.java} (52%) create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/model/SearchHistory.java create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/PlaceAutocompleteActivity.java rename plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/{ => ui}/ResultCardView.java (93%) create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultClickCallback.java rename plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/{ => ui}/ResultView.java (93%) rename plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/{ => ui}/SearchResultAdapter.java (79%) rename plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/{ => ui}/SearchView.java (83%) rename plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/{ => utils}/Utils.java (87%) create mode 100644 plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/viewmodel/PlaceAutocompleteViewModel.java diff --git a/app/build.gradle b/app/build.gradle index b098b26e9..1211138a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,6 +33,8 @@ dependencies { implementation dependenciesList.mapboxMapSdk implementation dependenciesList.mapboxServices + implementation dependenciesList.lifecycleExtensions + // Support libraries implementation dependenciesList.supportAnnotation implementation dependenciesList.supportAppcompatV7 diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index a81172e72..ddc54b6f7 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -29,8 +29,9 @@ ext { commonsIO : '2.5', robolectric : '3.4.2', lifecycleCompiler : '1.0.0', - lifecycleRuntime : '1.0.3', + lifecycleExtensions: '1.0.0', room : '1.0.0', + androidArchCore : '1.0.0', okhttp : '3.9.0' ] @@ -71,7 +72,7 @@ ext { supportConstraintLayout: "com.android.support.constraint:constraint-layout:${version.constraintLayout}", // architecture - lifecycleExtensions : "android.arch.lifecycle:extensions:${version.lifecycleRuntime}", + lifecycleExtensions : "android.arch.lifecycle:extensions:${version.lifecycleExtensions}", lifecycleCompiler : "android.arch.lifecycle:compiler:${version.lifecycleCompiler}", roomRuntime : "android.arch.persistence.room:runtime:${version.room}", roomCompiler : "android.arch.persistence.room:compiler:${version.room}", @@ -95,6 +96,7 @@ ext { hamcrest : "org.hamcrest:hamcrest-junit:${version.hamcrest}", commonsIO : "commons-io:commons-io:${version.commonsIO}", robolectric : "org.robolectric:robolectric:${version.robolectric}", + androidArchCore : "android.arch.core:core-testing:${version.androidArchCore}", // okhttp okhttp : "com.squareup.okhttp3:okhttp:${version.okhttp}", diff --git a/plugin-places/build.gradle b/plugin-places/build.gradle index c7237bf66..66845dc8c 100644 --- a/plugin-places/build.gradle +++ b/plugin-places/build.gradle @@ -40,6 +40,7 @@ dependencies { implementation dependenciesList.supportRecyclerView implementation dependenciesList.supportV4 + implementation dependenciesList.lifecycleExtensions implementation dependenciesList.roomRuntime annotationProcessor dependenciesList.roomCompiler @@ -48,6 +49,7 @@ dependencies { // Unit testing testImplementation dependenciesList.junit testImplementation dependenciesList.mockito + testImplementation dependenciesList.androidArchCore javadocDeps dependenciesList.mapboxMapSdk } \ No newline at end of file diff --git a/plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/1.json b/plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/1.json new file mode 100644 index 000000000..7339fcd5c --- /dev/null +++ b/plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/1.json @@ -0,0 +1,39 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "6785f3a8f4d6195ea76771bea75e1fd5", + "entities": [ + { + "tableName": "searchhistory", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`placeId` TEXT NOT NULL, `carmen_feature` TEXT, PRIMARY KEY(`placeId`))", + "fields": [ + { + "fieldPath": "placeId", + "columnName": "placeId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "carmenFeature", + "columnName": "carmen_feature", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "placeId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6785f3a8f4d6195ea76771bea75e1fd5\")" + ] + } +} \ No newline at end of file diff --git a/plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/2.json b/plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/2.json new file mode 100644 index 000000000..995743fee --- /dev/null +++ b/plugin-places/schemas/com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase/2.json @@ -0,0 +1,39 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "6785f3a8f4d6195ea76771bea75e1fd5", + "entities": [ + { + "tableName": "searchhistory", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`placeId` TEXT NOT NULL, `carmen_feature` TEXT, PRIMARY KEY(`placeId`))", + "fields": [ + { + "fieldPath": "placeId", + "columnName": "placeId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "carmenFeature", + "columnName": "carmen_feature", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "placeId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6785f3a8f4d6195ea76771bea75e1fd5\")" + ] + } +} \ No newline at end of file diff --git a/plugin-places/src/main/AndroidManifest.xml b/plugin-places/src/main/AndroidManifest.xml index e6a4a9999..3b5fffaf2 100644 --- a/plugin-places/src/main/AndroidManifest.xml +++ b/plugin-places/src/main/AndroidManifest.xml @@ -4,15 +4,10 @@ - \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/DataRepository.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/DataRepository.java new file mode 100644 index 000000000..8f9ed4cc4 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/DataRepository.java @@ -0,0 +1,49 @@ +package com.mapbox.plugins.places.autocomplete; + +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.MediatorLiveData; +import android.arch.lifecycle.Observer; +import android.support.annotation.Nullable; + +import com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase; +import com.mapbox.plugins.places.autocomplete.data.entity.SearchHistoryEntity; + +import java.util.List; + +public class DataRepository { + + private static DataRepository instance; + + private final SearchHistoryDatabase database; + private MediatorLiveData> observableSearchHistory; + + private DataRepository(final SearchHistoryDatabase database) { + this.database = database; + observableSearchHistory = new MediatorLiveData<>(); + + observableSearchHistory.addSource(database.searchHistoryDao().getAll(), + new Observer>() { + @Override + public void onChanged(@Nullable List searchHistoryEntities) { + if (database.getDatabaseCreated().getValue() != null) { + observableSearchHistory.postValue(searchHistoryEntities); + } + } + }); + } + + public static DataRepository getInstance(final SearchHistoryDatabase database) { + if (instance == null) { + instance = new DataRepository(database); + } + return instance; + } + + public LiveData> getSearchHistory() { + return observableSearchHistory; + } + + public void addSearchHistoryEntity(SearchHistoryEntity searchHistory) { + SearchHistoryDatabase.insertData(database, searchHistory); + } +} diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/OnCardItemClickListener.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/OnCardItemClickListener.java deleted file mode 100644 index 3c827598e..000000000 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/OnCardItemClickListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mapbox.plugins.places.autocomplete; - -import com.mapbox.geocoding.v5.models.CarmenFeature; - -/** - * Used internally to detect when a user has selected an item from one of the results list. - * - * @since 0.1.0 - */ -interface OnCardItemClickListener { - void onItemClick(CarmenFeature carmenFeature); -} \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceAutocomplete.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceAutocomplete.java index 1112a9e0f..fcf81d0fe 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceAutocomplete.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceAutocomplete.java @@ -12,6 +12,8 @@ import com.mapbox.geocoding.v5.GeocodingCriteria.GeocodingTypeCriteria; import com.mapbox.geocoding.v5.models.CarmenFeature; import com.mapbox.geojson.Point; +import com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase; +import com.mapbox.plugins.places.autocomplete.ui.PlaceAutocompleteActivity; import java.util.ArrayList; import java.util.Arrays; @@ -69,9 +71,9 @@ public static CarmenFeature getPlace(Intent data) { * @since 0.1.0 */ public static void clearRecentHistory(Context context) { - SearchHistoryDatabase database = Room.databaseBuilder(context, - SearchHistoryDatabase.class, PlaceConstants.SEARCH_HISTORY_DATABASE_NAME).build(); - new RecentSearchAsyncTask(database).execute(); +// SearchHistoryDatabase database = Room.databaseBuilder(context, +// SearchHistoryDatabase.class, PlaceConstants.SEARCH_HISTORY_DATABASE_NAME).build(); +// new RecentSearchAsyncTask(database).execute(); } /** @@ -268,12 +270,8 @@ public IntentBuilder backgroundColor(@ColorInt int backgroundColor) { */ public Intent build(Activity activity) { intent.putStringArrayListExtra(PlaceConstants.COUNTRIES, countries); - - if (mode == MODE_FULLSCREEN) { - intent.setClass(activity, PlaceCompleteFullActivity.class); - } else { - intent.setClass(activity, PlaceCompleteCardActivity.class); - } + intent.putExtra(PlaceConstants.MODE, mode); + intent.setClass(activity, PlaceAutocompleteActivity.class); return intent; } } diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteCardActivity.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteCardActivity.java deleted file mode 100644 index a8e142921..000000000 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteCardActivity.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.mapbox.plugins.places.autocomplete; - -import android.arch.persistence.room.Room; -import android.content.Intent; -import android.graphics.Color; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.ViewTreeObserver; -import android.widget.ScrollView; - -import com.mapbox.geocoding.v5.MapboxGeocoding; -import com.mapbox.geocoding.v5.models.CarmenFeature; -import com.mapbox.geocoding.v5.models.GeocodingResponse; -import com.mapbox.places.R; -import com.mapbox.plugins.places.common.KeyboardUtils; - -import java.util.ArrayList; -import java.util.List; - -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; - - -public class PlaceCompleteCardActivity extends AppCompatActivity implements - SearchView.QueryListener, Callback, SearchView.BackButtonListener, - ViewTreeObserver.OnScrollChangedListener, OnCardItemClickListener { - - private MapboxGeocoding.Builder geocoderBuilder; - private ResultView searchResultView; - private ResultView starredView; - private ResultView recentSearchResults; - private ScrollView resultScrollView; - private SearchView searchView; - private SearchHistoryDatabase database; - private View rootView; - private View dropShadow; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_complete_card); - bindViews(); - bindClickListeners(); - - Intent intent = getIntent(); - - // Theme settings - rootView.setBackgroundColor(intent.getIntExtra(PlaceConstants.BACKGROUND, Color.TRANSPARENT)); - - fillFavoritePlacesList(intent); - geocoderBuilder = Utils.initiateSearchQuery(intent); - - // Get and populate the recent history list - database = Room.databaseBuilder(getApplicationContext(), - SearchHistoryDatabase.class, PlaceConstants.SEARCH_HISTORY_DATABASE_NAME).build(); - new RecentSearchAsyncTask(database, recentSearchResults).execute(); - - resultScrollView.getViewTreeObserver().addOnScrollChangedListener(this); - } - - private void fillFavoritePlacesList(@NonNull Intent intent) { - List serialized = intent.getStringArrayListExtra(PlaceConstants.INJECTED_PLACES); - if (serialized == null || serialized.isEmpty()) { - return; - } - List starredFeatures = new ArrayList<>(); - for (String serializedCarmenFeature : serialized) { - starredFeatures.add(CarmenFeature.fromJson(serializedCarmenFeature)); - } - starredView.getResultsList().addAll(starredFeatures); - } - - private void bindClickListeners() { - recentSearchResults.setOnItemClickListener(this); - searchResultView.setOnItemClickListener(this); - starredView.setOnItemClickListener(this); - searchView.setBackButtonListener(this); - searchView.setQueryListener(this); - } - - private void bindViews() { - rootView = findViewById(R.id.root_layout); - searchResultView = findViewById(R.id.searchResultView); - resultScrollView = findViewById(R.id.scroll_view_results); - recentSearchResults = findViewById(R.id.recentSearchResults); - starredView = findViewById(R.id.starredView); - searchView = findViewById(R.id.searchView); - dropShadow = findViewById(R.id.scroll_drop_shadow); - } - - @Override - public void onScrollChanged() { - if (resultScrollView != null) { - if (resultScrollView.getScrollY() != 0) { - KeyboardUtils.hideKeyboard(resultScrollView); - } - dropShadow.setVisibility( - resultScrollView.canScrollVertically(-1) ? VISIBLE : INVISIBLE - ); - } - } - - @Override - public void onQueryChange(CharSequence charSequence) { - String query = charSequence.toString(); - if (query.isEmpty()) { - searchResultView.getResultsList().clear(); - searchResultView.setVisibility(searchResultView.getResultsList().isEmpty() ? View.GONE : VISIBLE); - searchResultView.notifyDataSetChanged(); - return; - } - geocoderBuilder.query(query) - .build().enqueueCall(this); - } - - @Override - public void onResponse(Call call, Response response) { - if (response.isSuccessful()) { - searchResultView.getResultsList().clear(); - searchResultView.getResultsList().addAll(response.body().features()); - searchResultView.setVisibility(searchResultView.getResultsList().isEmpty() ? View.GONE : VISIBLE); - searchResultView.notifyDataSetChanged(); - } - } - - @Override - public void onFailure(Call call, Throwable throwable) { - - } - - @Override - public void onItemClick(CarmenFeature carmenFeature) { - String json = carmenFeature.toJson(); - if (!carmenFeature.properties().has(PlaceConstants.SAVED_PLACE)) { - RecentSearch recentSearch = new RecentSearch(carmenFeature.id(), json); - new RecentSearchAsyncTask(database, recentSearch).execute(); - } - - Intent intent = new Intent(); - intent.putExtra(PlaceConstants.RETURNING_CARMEN_FEATURE, json); - setResult(AppCompatActivity.RESULT_OK, intent); - finish(); - } - - @Override - protected void onDestroy() { - if (searchView != null) { - searchView.removeBackButtonListener(); - searchView.removeQueryListener(); - } - if (resultScrollView != null) { - resultScrollView.getViewTreeObserver().removeOnScrollChangedListener(this); - } - super.onDestroy(); - } - - @Override - public void onBackButtonPress() { - setResult(AppCompatActivity.RESULT_CANCELED); - finish(); - } -} \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteFullActivity.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteFullActivity.java deleted file mode 100644 index f96b49b46..000000000 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceCompleteFullActivity.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.mapbox.plugins.places.autocomplete; - -import android.content.Intent; -import android.graphics.Color; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.view.View; - -import com.mapbox.geocoding.v5.MapboxGeocoding; -import com.mapbox.geocoding.v5.models.GeocodingResponse; -import com.mapbox.places.R; - -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -public class PlaceCompleteFullActivity extends AppCompatActivity implements - SearchView.QueryListener, Callback, SearchView.BackButtonListener { - - private MapboxGeocoding.Builder geocoderBuilder; - private ResultView searchResultView; - private View rootView; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_complete_full); - bindViews(); - - Intent intent = getIntent(); - - rootView.setBackgroundColor(intent.getIntExtra(PlaceConstants.BACKGROUND, Color.TRANSPARENT)); - - geocoderBuilder = Utils.initiateSearchQuery(intent); - searchResultView = findViewById(R.id.searchResultView); - - SearchView searchView = findViewById(R.id.searchView); - searchView.setBackButtonListener(this); - searchView.setQueryListener(this); - } - - @Override - public void onQueryChange(CharSequence charSequence) { - String query = charSequence.toString(); - if (query.isEmpty()) { - searchResultView.getResultsList().clear(); - searchResultView.notifyDataSetChanged(); - return; - } - geocoderBuilder.query(query) - .build().enqueueCall(this); - } - - private void bindViews() { - rootView = findViewById(R.id.root_layout); - searchResultView = findViewById(R.id.searchResultView); - } - - @Override - public void onResponse(Call call, Response response) { - if (response.isSuccessful()) { - searchResultView.getResultsList().clear(); - searchResultView.getResultsList().addAll(response.body().features()); - searchResultView.notifyDataSetChanged(); - } - } - - @Override - public void onFailure(Call call, Throwable t) { - - } - - - @Override - protected void onDestroy() { -// searchBar.removeBackButtonListener(); -// searchBar.removeQueryListener(); - super.onDestroy(); - } - - @Override - public void onBackButtonPress() { - finish(); - } -} diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceConstants.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceConstants.java index 066964aa5..3ec847fb6 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceConstants.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/PlaceConstants.java @@ -1,23 +1,23 @@ package com.mapbox.plugins.places.autocomplete; -final class PlaceConstants { +public final class PlaceConstants { private PlaceConstants() { } - static final String RETURNING_CARMEN_FEATURE = "com.mapbox.mapboxsdk.plugins.places.carmenfeat"; - static final String SEARCH_HISTORY_DATABASE_NAME = "com.mapbox.mapboxsdk.plugins.places.database"; - static final String ACCESS_TOKEN = "com.mapbox.mapboxsdk.plugins.places.accessToken"; - static final String BBOX_SOUTHWEST_POINT + public static final String RETURNING_CARMEN_FEATURE = "com.mapbox.mapboxsdk.plugins.places.carmenfeat"; + public static final String ACCESS_TOKEN = "com.mapbox.mapboxsdk.plugins.places.accessToken"; + public static final String BBOX_SOUTHWEST_POINT = "com.mapbox.mapboxsdk.plugins.places.bbox.southwestPoint"; - static final String BBOX_NORTHEAST_POINT + public static final String BBOX_NORTHEAST_POINT = "com.mapbox.mapboxsdk.plugins.places.bbox.northeastPoint"; - static final String COUNTRIES = "com.mapbox.mapboxsdk.plugins.places.countries"; - static final String PROXIMITY = "com.mapbox.mapboxsdk.plugins.places.proximity"; - static final String TYPE = "com.mapbox.mapboxsdk.plugins.places.types"; - static final String LANGUAGE = "com.mapbox.mapboxsdk.plugins.places.language"; - static final String LIMIT = "com.mapbox.mapboxsdk.plugins.places.limit"; - static final String INJECTED_PLACES = "com.mapbox.mapboxsdk.plugins.places.injectPlaces"; - static final String BACKGROUND = "com.mapbox.mapboxsdk.plugins.places.backgroundColor"; - static final String SAVED_PLACE = "com.mapbox.mapboxsdk.plugins.places.savedcarmenfeat"; + public static final String COUNTRIES = "com.mapbox.mapboxsdk.plugins.places.countries"; + public static final String PROXIMITY = "com.mapbox.mapboxsdk.plugins.places.proximity"; + public static final String TYPE = "com.mapbox.mapboxsdk.plugins.places.types"; + public static final String LANGUAGE = "com.mapbox.mapboxsdk.plugins.places.language"; + public static final String LIMIT = "com.mapbox.mapboxsdk.plugins.places.limit"; + public static final String INJECTED_PLACES = "com.mapbox.mapboxsdk.plugins.places.injectPlaces"; + public static final String BACKGROUND = "com.mapbox.mapboxsdk.plugins.places.backgroundColor"; + public static final String SAVED_PLACE = "com.mapbox.mapboxsdk.plugins.places.savedcarmenfeat"; + public static final String MODE = "com.mapbox.mapboxsdk.plugins.places.mode"; } diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchAsyncTask.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchAsyncTask.java deleted file mode 100644 index 38a3c03c8..000000000 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchAsyncTask.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.mapbox.plugins.places.autocomplete; - -import android.os.AsyncTask; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.View; - -import com.mapbox.geocoding.v5.models.CarmenFeature; - -import java.util.ArrayList; -import java.util.List; - -class RecentSearchAsyncTask extends AsyncTask { - - private final SearchHistoryDatabase database; - private List carmenFeatures = new ArrayList<>(); - private ResultView recentSearchResults; - private RecentSearch recentSearch; - - RecentSearchAsyncTask(@NonNull SearchHistoryDatabase database) { - this.database = database; - } - - RecentSearchAsyncTask(@NonNull SearchHistoryDatabase database, - @Nullable ResultView recentSearchResults) { - this.database = database; - this.recentSearchResults = recentSearchResults; - } - - RecentSearchAsyncTask(@NonNull SearchHistoryDatabase database, - @Nullable RecentSearch recentSearch) { - this.database = database; - this.recentSearch = recentSearch; - } - - @Override - protected Void doInBackground(Void... voids) { - - if (recentSearchResults != null) { - for (RecentSearch recentSearch : database.recentSearchesDao().getAll()) { - CarmenFeature carmenFeature = CarmenFeature.fromJson(recentSearch.getCarmenFeature()); - carmenFeatures.add(carmenFeature); - } - } else if (recentSearch != null) { - RecentSearch sameRecentSearch = database.recentSearchesDao() - .findByCarmenFeature(recentSearch.getPlaceId()); - if (sameRecentSearch != null) { - return null; - } - - database.recentSearchesDao().insertAll(recentSearch); - } else { - for (RecentSearch recentSearch : database.recentSearchesDao().getAll()) { - database.recentSearchesDao().delete(recentSearch); - } - } - - return null; - } - - @Override - protected void onPostExecute(Void aVoid) { - super.onPostExecute(aVoid); - if (recentSearchResults != null) { - recentSearchResults.getResultsList().addAll(carmenFeatures); - if (!carmenFeatures.isEmpty()) { - recentSearchResults.setVisibility(View.VISIBLE); - } - } - } -} \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchesDao.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchesDao.java deleted file mode 100644 index fa3875d69..000000000 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearchesDao.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.mapbox.plugins.places.autocomplete; - -import android.arch.persistence.room.Dao; -import android.arch.persistence.room.Delete; -import android.arch.persistence.room.Insert; -import android.arch.persistence.room.Query; - -import java.util.List; - -@Dao -public interface RecentSearchesDao { - @Query("SELECT * FROM recentsearch") - List getAll(); - - @Query("SELECT * FROM recentsearch WHERE placeId IN (:placeIds)") - List loadAllByIds(String[] placeIds); - - @Query("SELECT * FROM recentsearch WHERE placeId IN (:placeId)") - RecentSearch findByCarmenFeature(String placeId); - - @Insert - void insertAll(RecentSearch... recentSearch); - - @Delete - void delete(RecentSearch recentSearch); -} \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchHistoryDatabase.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchHistoryDatabase.java deleted file mode 100644 index 76d0dfcd5..000000000 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchHistoryDatabase.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.mapbox.plugins.places.autocomplete; - -import android.arch.persistence.room.Database; -import android.arch.persistence.room.RoomDatabase; - -@Database(entities = {RecentSearch.class}, version = 1) -public abstract class SearchHistoryDatabase extends RoomDatabase { - public abstract RecentSearchesDao recentSearchesDao(); -} diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/SearchHistoryDatabase.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/SearchHistoryDatabase.java new file mode 100644 index 000000000..97b3b3515 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/SearchHistoryDatabase.java @@ -0,0 +1,89 @@ +package com.mapbox.plugins.places.autocomplete.data; + +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.MutableLiveData; +import android.arch.persistence.db.SupportSQLiteDatabase; +import android.arch.persistence.room.Database; +import android.arch.persistence.room.Room; +import android.arch.persistence.room.RoomDatabase; +import android.arch.persistence.room.TypeConverters; +import android.content.Context; +import android.os.AsyncTask; +import android.support.annotation.NonNull; + +import com.mapbox.plugins.places.autocomplete.data.converter.CarmenFeatureConverter; +import com.mapbox.plugins.places.autocomplete.data.dao.SearchHistoryDao; +import com.mapbox.plugins.places.autocomplete.data.entity.SearchHistoryEntity; + +@Database(entities = {SearchHistoryEntity.class}, version = 2) +@TypeConverters(CarmenFeatureConverter.class) +public abstract class SearchHistoryDatabase extends RoomDatabase { + + private static final String DATABASE_NAME = "com.mapbox.mapboxsdk.plugins.places.database"; + private static SearchHistoryDatabase instance; + + public abstract SearchHistoryDao searchHistoryDao(); + + private final MutableLiveData isDatabaseCreated = new MutableLiveData<>(); + + public static SearchHistoryDatabase getInstance(final Context context) { + if (instance == null) { + instance = buildDatabase(context.getApplicationContext()); + instance.updateDatabaseCreated(context.getApplicationContext()); + } + return instance; + } + + private static SearchHistoryDatabase buildDatabase(final Context appContext) { + return Room.databaseBuilder(appContext, + SearchHistoryDatabase.class, DATABASE_NAME).addCallback(new Callback() { + @Override + public void onCreate(@NonNull SupportSQLiteDatabase db) { + super.onCreate(db); + SearchHistoryDatabase database = SearchHistoryDatabase.getInstance(appContext); + database.setDatabaseCreated(); + } + }).fallbackToDestructiveMigration().build(); + // TODO remove fallbackToDestructiveMigration + } + + /** + * Check whether the database already exists and expose it via {@link #getDatabaseCreated()} + */ + private void updateDatabaseCreated(final Context context) { + if (context.getDatabasePath(DATABASE_NAME).exists()) { + setDatabaseCreated(); + } + } + + private void setDatabaseCreated() { + isDatabaseCreated.postValue(true); + } + + public static void insertData(final SearchHistoryDatabase database, + final SearchHistoryEntity searchHistory) { + new InsertSearchEntity(database, searchHistory).execute(); + } + + public LiveData getDatabaseCreated() { + return isDatabaseCreated; + } + + + private static class InsertSearchEntity extends AsyncTask { + + private final SearchHistoryDatabase database; + private final SearchHistoryEntity searchHistory; + + InsertSearchEntity(SearchHistoryDatabase database, SearchHistoryEntity searchHistory) { + this.searchHistory = searchHistory; + this.database = database; + } + + @Override + protected Void doInBackground(Void... voids) { + database.searchHistoryDao().insert(searchHistory); + return null; + } + } +} diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/converter/CarmenFeatureConverter.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/converter/CarmenFeatureConverter.java new file mode 100644 index 000000000..d83b25e60 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/converter/CarmenFeatureConverter.java @@ -0,0 +1,19 @@ +package com.mapbox.plugins.places.autocomplete.data.converter; + +import android.arch.persistence.room.TypeConverter; +import android.support.annotation.NonNull; + +import com.mapbox.geocoding.v5.models.CarmenFeature; + +public class CarmenFeatureConverter { + + @TypeConverter + public static CarmenFeature toCarmenFeature(String serializedCarmenFeature) { + return serializedCarmenFeature == null ? null : CarmenFeature.fromJson(serializedCarmenFeature); + } + + @TypeConverter + public static String fromCarmenFeature(@NonNull CarmenFeature carmenFeature) { + return carmenFeature.toJson(); + } +} diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/dao/SearchHistoryDao.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/dao/SearchHistoryDao.java new file mode 100644 index 000000000..8934fe0b3 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/dao/SearchHistoryDao.java @@ -0,0 +1,34 @@ +package com.mapbox.plugins.places.autocomplete.data.dao; + +import android.arch.lifecycle.LiveData; +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Delete; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.OnConflictStrategy; +import android.arch.persistence.room.Query; + +import com.mapbox.plugins.places.autocomplete.data.entity.SearchHistoryEntity; + +import java.util.List; + +@Dao +public interface SearchHistoryDao { + + @Query("SELECT * FROM searchhistory") + LiveData> getAll(); + + @Query("SELECT * FROM searchhistory WHERE placeId IN (:placeIds)") + LiveData> loadAllByIds(String[] placeIds); + + @Query("SELECT * FROM searchhistory WHERE placeId IN (:placeId)") + LiveData findByCarmenFeature(String placeId); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insertAll(List searchHistory); + + @Insert(onConflict = OnConflictStrategy.REPLACE) + void insert(SearchHistoryEntity searchHistory); + + @Delete + void delete(SearchHistoryEntity searchHistory); +} \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearch.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/entity/SearchHistoryEntity.java similarity index 52% rename from plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearch.java rename to plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/entity/SearchHistoryEntity.java index fb89ac835..4d34b5e74 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/RecentSearch.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/data/entity/SearchHistoryEntity.java @@ -1,14 +1,17 @@ -package com.mapbox.plugins.places.autocomplete; +package com.mapbox.plugins.places.autocomplete.data.entity; import android.arch.persistence.room.ColumnInfo; import android.arch.persistence.room.Entity; import android.arch.persistence.room.PrimaryKey; import android.support.annotation.NonNull; -@Entity -public class RecentSearch { +import com.mapbox.geocoding.v5.models.CarmenFeature; +import com.mapbox.plugins.places.autocomplete.model.SearchHistory; - public RecentSearch(@NonNull String placeId, String carmenFeature) { +@Entity(tableName = "searchhistory") +public class SearchHistoryEntity implements SearchHistory { + + public SearchHistoryEntity(@NonNull String placeId, CarmenFeature carmenFeature) { this.placeId = placeId; this.carmenFeature = carmenFeature; } @@ -18,8 +21,9 @@ public RecentSearch(@NonNull String placeId, String carmenFeature) { private String placeId; @ColumnInfo(name = "carmen_feature") - private String carmenFeature; + private CarmenFeature carmenFeature; + @Override @NonNull public String getPlaceId() { return placeId; @@ -29,11 +33,12 @@ public void setPlaceId(@NonNull String placeId) { this.placeId = placeId; } - public String getCarmenFeature() { + @Override + public CarmenFeature getCarmenFeature() { return carmenFeature; } - public void setCarmenFeature(String carmenFeature) { + public void setCarmenFeature(CarmenFeature carmenFeature) { this.carmenFeature = carmenFeature; } } diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/model/SearchHistory.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/model/SearchHistory.java new file mode 100644 index 000000000..02a73d3b6 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/model/SearchHistory.java @@ -0,0 +1,9 @@ +package com.mapbox.plugins.places.autocomplete.model; + +import com.mapbox.geocoding.v5.models.CarmenFeature; + +public interface SearchHistory { + String getPlaceId(); + + CarmenFeature getCarmenFeature(); +} diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/PlaceAutocompleteActivity.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/PlaceAutocompleteActivity.java new file mode 100644 index 000000000..c9c7323c4 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/PlaceAutocompleteActivity.java @@ -0,0 +1,175 @@ +package com.mapbox.plugins.places.autocomplete.ui; + +import android.arch.lifecycle.Observer; +import android.arch.lifecycle.ViewModelProviders; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.ScrollView; + +import com.mapbox.geocoding.v5.models.CarmenFeature; +import com.mapbox.geocoding.v5.models.GeocodingResponse; +import com.mapbox.places.R; +import com.mapbox.plugins.places.autocomplete.DataRepository; +import com.mapbox.plugins.places.autocomplete.PlaceConstants; +import com.mapbox.plugins.places.autocomplete.data.entity.SearchHistoryEntity; +import com.mapbox.plugins.places.autocomplete.viewmodel.PlaceAutocompleteViewModel; +import com.mapbox.plugins.places.common.KeyboardUtils; + +import java.util.List; + +import static android.view.View.GONE; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + +public class PlaceAutocompleteActivity extends AppCompatActivity implements ResultClickCallback, + SearchView.QueryListener, SearchView.BackButtonListener, + ViewTreeObserver.OnScrollChangedListener { + + private PlaceAutocompleteViewModel viewModel; + private ResultView searchHistoryView; + private ResultView searchResultView; + private ScrollView resultScrollView; + private ResultView starredView; + private SearchView searchView; + private View dropShadowView; + private View rootView; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + setActivityContentView(intent); + bindViews(); + bindClickListeners(); + + // View model + PlaceAutocompleteViewModel.Factory factory = new PlaceAutocompleteViewModel.Factory( + getApplication(), intent); + viewModel = ViewModelProviders.of(this, factory).get(PlaceAutocompleteViewModel.class); + viewModel.buildGeocodingRequest(); + + // Theme settings + rootView.setBackgroundColor(intent.getIntExtra(PlaceConstants.BACKGROUND, Color.TRANSPARENT)); + resultScrollView.getViewTreeObserver().addOnScrollChangedListener(this); + + updateFavoritePlacesView(); + subscribe(); + } + + @Override + public void onScrollChanged() { + if (resultScrollView != null) { + if (resultScrollView.getScrollY() != 0) { + KeyboardUtils.hideKeyboard(resultScrollView); + } + dropShadowView.setVisibility( + resultScrollView.canScrollVertically(-1) ? VISIBLE : INVISIBLE + ); + } + } + + @Override + public void onQueryChange(CharSequence charSequence) { + viewModel.onQueryChange(charSequence); + if (charSequence.length() <= 0) { + searchResultView.getResultsList().clear(); + searchResultView.setVisibility(searchResultView.getResultsList().isEmpty() ? GONE : VISIBLE); + searchResultView.notifyDataSetChanged(); + } + } + + @Override + public void onClick(CarmenFeature carmenFeature) { + Intent intent = viewModel.onItemClicked(carmenFeature); + setResult(AppCompatActivity.RESULT_OK, intent); + finish(); + } + + @Override + protected void onDestroy() { + if (resultScrollView != null) { + resultScrollView.getViewTreeObserver().removeOnScrollChangedListener(this); + } + super.onDestroy(); + } + + @Override + public void onBackButtonPress() { + setResult(AppCompatActivity.RESULT_CANCELED); + finish(); + } + + private void setActivityContentView(Intent intent) { + if (intent.getIntExtra(PlaceConstants.MODE, 1) == 2) { + setContentView(R.layout.activity_complete_card); + } else { + setContentView(R.layout.activity_complete_full); + } + } + + private void bindClickListeners() { + searchHistoryView.setOnItemClickListener(this); + searchResultView.setOnItemClickListener(this); + starredView.setOnItemClickListener(this); + searchView.setBackButtonListener(this); + searchView.setQueryListener(this); + } + + private void bindViews() { + searchHistoryView = findViewById(R.id.searchHistoryResultsView); + resultScrollView = findViewById(R.id.scroll_view_results); + searchResultView = findViewById(R.id.searchResultView); + dropShadowView = findViewById(R.id.scroll_drop_shadow); + starredView = findViewById(R.id.favoriteResultView); + searchView = findViewById(R.id.searchView); + rootView = findViewById(R.id.root_layout); + } + + private void updateSearchHistoryView(@Nullable List searchHistoryEntities) { + searchHistoryView.getResultsList().clear(); + if (searchHistoryEntities != null) { + for (SearchHistoryEntity entity : searchHistoryEntities) { + searchHistoryView.getResultsList().add(entity.getCarmenFeature()); + } + } + searchHistoryView.notifyDataSetChanged(); + searchHistoryView.setVisibility( + searchHistoryView.getResultsList().isEmpty() ? GONE : VISIBLE + ); + } + + private void updateSearchResultView(@Nullable GeocodingResponse response) { + searchResultView.getResultsList().clear(); + searchResultView.getResultsList().addAll(response.features()); + searchResultView.setVisibility(searchResultView.getResultsList().isEmpty() ? GONE : VISIBLE); + searchResultView.notifyDataSetChanged(); + } + + private void updateFavoritePlacesView() { + List favoriteFeatures = viewModel.getFavoritePlaces(); + starredView.getResultsList().addAll(favoriteFeatures); + } + + private void subscribe() { + viewModel.geocodingLiveData.observe(this, new Observer() { + @Override + public void onChanged(@Nullable GeocodingResponse geocodingResponse) { + updateSearchResultView(geocodingResponse); + } + }); + + // Subscribe to the search history database + DataRepository.getInstance(viewModel.getDatabase()).getSearchHistory().observe(this, + new Observer>() { + @Override + public void onChanged(@Nullable List searchHistoryEntities) { + updateSearchHistoryView(searchHistoryEntities); + } + }); + } +} \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ResultCardView.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultCardView.java similarity index 93% rename from plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ResultCardView.java rename to plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultCardView.java index 2d8f8f434..923636cb5 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ResultCardView.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultCardView.java @@ -1,4 +1,4 @@ -package com.mapbox.plugins.places.autocomplete; +package com.mapbox.plugins.places.autocomplete.ui; import android.content.Context; import android.support.annotation.NonNull; diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultClickCallback.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultClickCallback.java new file mode 100644 index 000000000..9c0758855 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultClickCallback.java @@ -0,0 +1,7 @@ +package com.mapbox.plugins.places.autocomplete.ui; + +import com.mapbox.geocoding.v5.models.CarmenFeature; + +interface ResultClickCallback { + void onClick(CarmenFeature carmenFeature); +} \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ResultView.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultView.java similarity index 93% rename from plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ResultView.java rename to plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultView.java index 1502177b3..2e91e45e1 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ResultView.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/ResultView.java @@ -1,4 +1,4 @@ -package com.mapbox.plugins.places.autocomplete; +package com.mapbox.plugins.places.autocomplete.ui; import android.content.Context; import android.support.annotation.NonNull; @@ -56,7 +56,7 @@ void inflateView(Context context) { inflate(context, R.layout.view_results, this); } - public void setOnItemClickListener(OnCardItemClickListener onItemClickListener) { + public void setOnItemClickListener(ResultClickCallback onItemClickListener) { if (adapter != null) { adapter.setOnItemClickListener(onItemClickListener); } diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchResultAdapter.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/SearchResultAdapter.java similarity index 79% rename from plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchResultAdapter.java rename to plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/SearchResultAdapter.java index 37745a1df..f4185de4d 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchResultAdapter.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/SearchResultAdapter.java @@ -1,6 +1,7 @@ -package com.mapbox.plugins.places.autocomplete; +package com.mapbox.plugins.places.autocomplete.ui; import android.content.Context; +import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -10,6 +11,7 @@ import com.mapbox.geocoding.v5.models.CarmenFeature; import com.mapbox.places.R; +import com.mapbox.plugins.places.autocomplete.PlaceConstants; import java.util.List; @@ -17,7 +19,10 @@ public class SearchResultAdapter extends RecyclerView.Adapter results; private final Context context; @@ -33,14 +38,14 @@ public SearchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new SearchViewHolder(view); } - public void setOnItemClickListener(OnCardItemClickListener onItemClickListener) { - this.onItemClickListener = onItemClickListener; + public void setOnItemClickListener(ResultClickCallback resultClickCallback) { + this.resultClickCallback = resultClickCallback; } @Override public void onBindViewHolder(SearchViewHolder holder, int position) { - if (onItemClickListener != null) { - holder.bind(results.get(position), onItemClickListener); + if (resultClickCallback != null) { + holder.bind(results.get(position), resultClickCallback); } if (results.get(position).properties().has(PlaceConstants.SAVED_PLACE)) { @@ -77,11 +82,11 @@ static class SearchViewHolder extends RecyclerView.ViewHolder { addressView = itemView.findViewById(R.id.tv_address); } - public void bind(final CarmenFeature carmenFeature, final OnCardItemClickListener listener) { + public void bind(final CarmenFeature carmenFeature, final ResultClickCallback listener) { itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - listener.onItemClick(carmenFeature); + listener.onClick(carmenFeature); } }); } diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchView.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/SearchView.java similarity index 83% rename from plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchView.java rename to plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/SearchView.java index b77306186..ca902bfbe 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/SearchView.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/ui/SearchView.java @@ -1,5 +1,9 @@ -package com.mapbox.plugins.places.autocomplete; +package com.mapbox.plugins.places.autocomplete.ui; +import android.arch.lifecycle.Lifecycle; +import android.arch.lifecycle.LifecycleObserver; +import android.arch.lifecycle.LifecycleOwner; +import android.arch.lifecycle.OnLifecycleEvent; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -13,7 +17,8 @@ import com.mapbox.places.R; -public class SearchView extends LinearLayout implements ImageButton.OnClickListener, TextWatcher { +public class SearchView extends LinearLayout implements ImageButton.OnClickListener, TextWatcher, + LifecycleObserver { @Nullable private BackButtonListener backButtonListener; @@ -45,6 +50,7 @@ private void initialize() { backButton.setOnClickListener(this); clearButton.setOnClickListener(this); searchEditText.addTextChangedListener(this); + ((LifecycleOwner) getContext()).getLifecycle().addObserver(this); } @Override @@ -58,6 +64,12 @@ public void onClick(View view) { } } + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + public void onDestroy() { + backButtonListener = null; + queryListener = null; + } + @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { if (queryListener != null) { @@ -80,23 +92,15 @@ public void setBackButtonListener(@Nullable BackButtonListener backButtonListene this.backButtonListener = backButtonListener; } - public void removeBackButtonListener() { - backButtonListener = null; - } - public void setQueryListener(@Nullable QueryListener queryListener) { this.queryListener = queryListener; } - public void removeQueryListener() { - queryListener = null; - } - - public interface QueryListener { + interface QueryListener { void onQueryChange(CharSequence charSequence); } - public interface BackButtonListener { + interface BackButtonListener { void onBackButtonPress(); } } \ No newline at end of file diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/Utils.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/utils/Utils.java similarity index 87% rename from plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/Utils.java rename to plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/utils/Utils.java index 9f7801b79..60dffb0a7 100644 --- a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/Utils.java +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/utils/Utils.java @@ -1,16 +1,18 @@ -package com.mapbox.plugins.places.autocomplete; +package com.mapbox.plugins.places.autocomplete.utils; import android.content.Intent; import com.mapbox.geocoding.v5.MapboxGeocoding; import com.mapbox.geojson.Point; +import com.mapbox.plugins.places.autocomplete.PlaceConstants; public class Utils { private Utils() { } - static MapboxGeocoding.Builder initiateSearchQuery(Intent intent) { + // TODO move to viewmodel and delete class + public static MapboxGeocoding.Builder initiateSearchQuery(Intent intent) { MapboxGeocoding.Builder geocoderBuilder = MapboxGeocoding.builder().autocomplete(true); geocoderBuilder.accessToken(intent.getStringExtra(PlaceConstants.ACCESS_TOKEN)); geocoderBuilder.limit(intent.getIntExtra(PlaceConstants.LIMIT, 5)); diff --git a/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/viewmodel/PlaceAutocompleteViewModel.java b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/viewmodel/PlaceAutocompleteViewModel.java new file mode 100644 index 000000000..40d9b3761 --- /dev/null +++ b/plugin-places/src/main/java/com/mapbox/plugins/places/autocomplete/viewmodel/PlaceAutocompleteViewModel.java @@ -0,0 +1,117 @@ +package com.mapbox.plugins.places.autocomplete.viewmodel; + +import android.app.Application; +import android.arch.lifecycle.AndroidViewModel; +import android.arch.lifecycle.MutableLiveData; +import android.arch.lifecycle.ViewModel; +import android.arch.lifecycle.ViewModelProvider; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.mapbox.geocoding.v5.MapboxGeocoding; +import com.mapbox.geocoding.v5.models.CarmenFeature; +import com.mapbox.geocoding.v5.models.GeocodingResponse; +import com.mapbox.plugins.places.autocomplete.DataRepository; +import com.mapbox.plugins.places.autocomplete.PlaceConstants; +import com.mapbox.plugins.places.autocomplete.data.SearchHistoryDatabase; +import com.mapbox.plugins.places.autocomplete.data.entity.SearchHistoryEntity; +import com.mapbox.plugins.places.autocomplete.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class PlaceAutocompleteViewModel extends AndroidViewModel + implements Callback { + + public final MutableLiveData geocodingLiveData = new MutableLiveData<>(); + private MapboxGeocoding.Builder geocoderBuilder; + private final Intent intent; + + PlaceAutocompleteViewModel(@NonNull Application application, @NonNull Intent intent) { + super(application); + this.intent = intent; + } + + public void buildGeocodingRequest() { + geocoderBuilder = Utils.initiateSearchQuery(intent); + } + + public void onQueryChange(CharSequence sequence) { + String query = sequence.toString(); + if (query.isEmpty()) { + return; + } + geocoderBuilder.query(query).build().enqueueCall(this); + } + + public Intent onItemClicked(CarmenFeature carmenFeature) { + saveCarmenFeatureToDatabase(carmenFeature); + + String json = carmenFeature.toJson(); + Intent returningIntent = new Intent(); + returningIntent.putExtra(PlaceConstants.RETURNING_CARMEN_FEATURE, json); + return returningIntent; + } + + public List getFavoritePlaces() { + List serialized = intent.getStringArrayListExtra(PlaceConstants.INJECTED_PLACES); + List favoriteFeatures = new ArrayList<>(); + if (serialized == null || serialized.isEmpty()) { + return favoriteFeatures; + } + for (String serializedCarmenFeature : serialized) { + favoriteFeatures.add(CarmenFeature.fromJson(serializedCarmenFeature)); + } + return favoriteFeatures; + } + + @Override + public void onResponse(@NonNull Call call, + @NonNull Response response) { + if (response.isSuccessful()) { + geocodingLiveData.setValue(response.body()); + } + } + + @Override + public void onFailure(@NonNull Call call, + @NonNull Throwable throwable) { + + } + + public SearchHistoryDatabase getDatabase() { + return SearchHistoryDatabase.getInstance(this.getApplication().getApplicationContext()); + } + + private void saveCarmenFeatureToDatabase(CarmenFeature carmenFeature) { + // Check that the carmenFeature hasn't already been added + if (carmenFeature.properties().has(PlaceConstants.SAVED_PLACE)) { + return; + } + SearchHistoryEntity searchHistory = new SearchHistoryEntity(carmenFeature.id(), carmenFeature); + DataRepository.getInstance(getDatabase()).addSearchHistoryEntity(searchHistory); + } + + public static class Factory extends ViewModelProvider.NewInstanceFactory { + + private final Application application; + private final Intent intent; + + public Factory(@NonNull Application application, @NonNull Intent intent) { + this.application = application; + this.intent = intent; + } + + @Override + @NonNull + public T create(@Nullable Class modelClass) { + //noinspection unchecked + return (T) new PlaceAutocompleteViewModel(application, intent); + } + } +} diff --git a/plugin-places/src/main/res/layout/activity_complete_card.xml b/plugin-places/src/main/res/layout/activity_complete_card.xml index f82f6c6f2..be7a2fb77 100644 --- a/plugin-places/src/main/res/layout/activity_complete_card.xml +++ b/plugin-places/src/main/res/layout/activity_complete_card.xml @@ -19,7 +19,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - - - - diff --git a/plugin-places/src/main/res/layout/activity_complete_full.xml b/plugin-places/src/main/res/layout/activity_complete_full.xml index 4a74042ed..b7351e48f 100644 --- a/plugin-places/src/main/res/layout/activity_complete_full.xml +++ b/plugin-places/src/main/res/layout/activity_complete_full.xml @@ -8,25 +8,21 @@ android:layout_height="match_parent"> - + app:layout_constraintTop_toBottomOf="@+id/toolbar"> - + + + app:layout_constraintTop_toTopOf="@+id/scroll_drop_shadow"> + + + + + + + + + + + \ No newline at end of file diff --git a/plugin-places/src/main/res/layout/item_search_result.xml b/plugin-places/src/main/res/layout/item_search_result.xml index f01c0d62a..d01e1e0f8 100644 --- a/plugin-places/src/main/res/layout/item_search_result.xml +++ b/plugin-places/src/main/res/layout/item_search_result.xml @@ -5,7 +5,9 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="60dp" - android:background="@color/cardview_light_background"> + android:background="@color/cardview_light_background" + android:clickable="true" + android:focusable="true">