diff --git a/.github/issue_label_bot.yaml b/.github/issue_label_bot.yaml new file mode 100644 index 000000000..a72fe81f8 --- /dev/null +++ b/.github/issue_label_bot.yaml @@ -0,0 +1,4 @@ +label-alias: + bug: 'bug' + feature_request: 'feature' + question: 'question' diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 81936495f..448e54bfd 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,5 +1,5 @@ -name-template: v$NEXT_PATCH_VERSION 🌈 -tag-template: v$NEXT_PATCH_VERSION +name-template: Release v$NEXT_MINOR_VERSION 🌈 +tag-template: v$NEXT_MINOR_VERSION categories: - title: 🚀 Features and Enhancements label: feature diff --git a/.travis.yml b/.travis.yml index fa8464126..3b5c69163 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: android +dist: trusty sudo: false jdk: - oraclejdk8 diff --git a/README.md b/README.md index 6f7c6ede8..3d7c6dc68 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Codacy Grade](https://img.shields.io/codacy/grade/d6ae120356c94c0d94d6449ec540f520.svg)](https://www.codacy.com/app/mb/open-event-organizer-android) [![Codecov Coverage](https://img.shields.io/codecov/c/github/fossasia/open-event-organizer-android/development.svg)](https://codecov.io/gh/fossasia/open-event-organizer-android) [![Appetize Preview](https://img.shields.io/badge/Preview-appetize.io-673AB7.svg)](https://appetize.io/app/w8v8z7pc9aewargb2uuyf108f0) -[![Gitter](https://img.shields.io/badge/chat-on%20gitter-ff006f.svg)](https://gitter.im/fossasia/open-event-orga-app) +[![Gitter](https://img.shields.io/badge/chat-on%20gitter-ff006f.svg)](https://gitter.im/fossasia/open-event-organizer-android) [![Twitter Follow](https://img.shields.io/twitter/follow/eventyay.svg?style=social&label=Follow&maxAge=2592000?style=flat-square)](https://twitter.com/eventyay) Event management app for organizers using Open Event Platform @@ -25,7 +25,7 @@ Currently, the application is released in alpha phase and available here: [Google Play](https://play.google.com/store/apps/details?id=com.eventyay.organizer) and [F-Droid](https://f-droid.org/en/packages/com.eventyay.organizer/). Get it on Google Play -Get it on F-Droid +Get it on F-Droid ## Roadmap @@ -44,19 +44,19 @@ Our chat channel is on gitter here: https://gitter.im/fossasia/open-event-orga-a - - - + + + - - + + - - - + + +
diff --git a/app/build.gradle b/app/build.gradle index f389bf1b4..7998a779c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,6 +8,7 @@ apply plugin: "com.github.b3er.local.properties" def app_name = System.getenv('app_name') ?: "Eventyay Organizer" def GOOGLE_PLACES_API_KEY = System.getenv('GOOGLE_PLACES_API_KEY') ?: "YOUR_API_KEY" +def MAPBOX_ACCESS_TOKEN = System.getenv('MAPBOX_ACCESS_TOKEN') ?: "YOUR_ACCESS_TOKEN" def LOCAL_KEY_PRESENT = project.hasProperty('SIGNING_KEY_FILE') && rootProject.file(SIGNING_KEY_FILE).exists() @@ -27,13 +28,14 @@ android { applicationId "com.eventyay.organizer" minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk - versionCode 14 - versionName "1.4.0" + versionCode 15 + versionName "1.5.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true manifestPlaceholders = [ appName: app_name, - GOOGLE_PLACES_API_KEY: GOOGLE_PLACES_API_KEY + GOOGLE_PLACES_API_KEY: GOOGLE_PLACES_API_KEY, + MAPBOX_ACCESS_TOKEN: MAPBOX_ACCESS_TOKEN ] } @@ -63,6 +65,7 @@ android { buildConfigField "String", "DEFAULT_BASE_URL", '"https://api.eventyay.com/v1/"' resValue "string", "default_base_url", '"https://api.eventyay.com/v1/"' + resValue "string", "FRONTEND_HOST", "eventyay.com" if (LOCAL_KEY_PRESENT || TRAVIS_BUILD) signingConfig signingConfigs.release @@ -71,6 +74,7 @@ android { testCoverageEnabled = true buildConfigField "String", "DEFAULT_BASE_URL", '"https://open-event-api-dev.herokuapp.com/v1/"' resValue "string", "default_base_url", '"https://open-event-api-dev.herokuapp.com/v1/"' + resValue "string", "FRONTEND_HOST", "open-event-fe.netlify.com" } } @@ -144,7 +148,7 @@ dependencies { implementation "androidx.appcompat:appcompat:1.1.0-rc01" implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.recyclerview:recyclerview:1.1.0-beta01" - implementation "com.google.android.material:material:1.1.0-alpha07" + implementation "com.google.android.material:material:1.1.0-alpha08" implementation "androidx.browser:browser:1.0.0" implementation "androidx.palette:palette:1.0.0" implementation "com.takisoft.fix:preference-v7:${versions.support_lib}.0" @@ -190,7 +194,7 @@ dependencies { implementation "com.squareup.retrofit2:retrofit:${versions.retrofit}" implementation "com.squareup.retrofit2:converter-jackson:${versions.retrofit}" implementation "com.squareup.retrofit2:adapter-rxjava2:${versions.retrofit}" - implementation 'com.squareup.okhttp3:logging-interceptor:4.0.0' + implementation 'com.squareup.okhttp3:logging-interceptor:4.0.1' implementation 'com.github.jasminb:jsonapi-converter:0.9' // RxJava @@ -255,6 +259,9 @@ dependencies { // QR Code Scanner fdroidImplementation 'com.github.blikoon:QRCodeScanner:0.1.2' + // Mapbox + fdroidImplementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-places-v8:0.9.0' + //Testing androidTestImplementation('androidx.test.espresso:espresso-core:3.2.0', { exclude group: 'com.android.support', module: 'support-annotations' @@ -265,7 +272,7 @@ dependencies { exclude group: 'com.android.support', module: 'support-core-utils' }) testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-inline:2.28.2' + testImplementation 'org.mockito:mockito-inline:3.0.0' testImplementation 'org.json:json:20180130' testImplementation "org.robolectric:robolectric:${versions.roboelectric}" testImplementation "org.robolectric:shadows-multidex:${versions.roboelectric}" diff --git a/app/src/fdroid/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java b/app/src/fdroid/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java index c578b25dd..9213598b1 100644 --- a/app/src/fdroid/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java +++ b/app/src/fdroid/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java @@ -1,24 +1,30 @@ package com.eventyay.organizer.core.event.create; -import androidx.lifecycle.ViewModelProvider; -import androidx.lifecycle.ViewModelProviders; -import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import androidx.databinding.DataBindingUtil; +import android.graphics.Color; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; +import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.ViewModelProviders; + import com.eventyay.organizer.R; import com.eventyay.organizer.common.mvp.view.BaseBottomSheetFragment; import com.eventyay.organizer.data.event.Event; import com.eventyay.organizer.databinding.EventDetailsStepOneBinding; +import com.eventyay.organizer.ui.ViewUtils; +import com.mapbox.api.geocoding.v5.models.CarmenFeature; +import com.mapbox.mapboxsdk.plugins.places.autocomplete.model.PlaceOptions; +import com.mapbox.mapboxsdk.plugins.places.autocomplete.ui.PlaceAutocompleteFragment; +import com.mapbox.mapboxsdk.plugins.places.autocomplete.ui.PlaceSelectionListener; import java.util.Arrays; import java.util.List; @@ -27,8 +33,6 @@ import timber.log.Timber; -import static android.app.Activity.RESULT_OK; - public class EventDetailsStepOne extends BaseBottomSheetFragment implements EventDetailsStepOneView { @Inject @@ -36,8 +40,6 @@ public class EventDetailsStepOne extends BaseBottomSheetFragment implements Even private CreateEventViewModel createEventViewModel; private EventDetailsStepOneBinding binding; - private static final int PLACE_PICKER_REQUEST = 1; - private final LocationPicker locationPicker = new LocationPicker(); public static EventDetailsStepOne newInstance() { return new EventDetailsStepOne(); @@ -58,7 +60,7 @@ public void onStart() { int timezoneIndex = createEventViewModel.setTimeZoneList(getTimeZoneList()); setupSpinner(); setDefaultTimeZone(timezoneIndex); - setupPlacePicker(); + setupPlacesAutocomplete(); } private void setupSpinner() { @@ -82,50 +84,52 @@ public void onNothingSelected(AdapterView parent) { }); } - private void setupPlacePicker() { - //check if there's a google places API key + private void setupPlacesAutocomplete() { + + ApplicationInfo applicationInfo = null; try { - ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = ai.metaData; - String placesApiKey = bundle.getString("com.google.android.geo.API_KEY"); - if ("YOUR_API_KEY".equals(placesApiKey)) { - Timber.d("Add Google Places API key in AndroidManifest.xml file to use Place Picker."); - binding.buttonPlacePicker.setVisibility(View.GONE); - showLocationLayouts(); - } + applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { - Timber.e(e, "Package name not found"); + Timber.e(e); } + Bundle bundle = applicationInfo.metaData; - binding.buttonPlacePicker.setOnClickListener(view -> { - boolean success = locationPicker.launchPicker(getActivity()); - if (locationPicker.shouldShowLocationLayout() || !success) - showLocationLayouts(); - }); - } + String mapboxAccessToken = bundle.getString(getString(R.string.mapbox_access_token)); - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == PLACE_PICKER_REQUEST && resultCode == RESULT_OK) { - //once place is picked from map, make location fields visible for confirmation by user - showLocationLayouts(); - //set event attributes - Location location = locationPicker.getPlace(getActivity(), data); - Event event = binding.getEvent(); - event.latitude = location.getLatitude(); - event.longitude = location.getLongitude(); - - //auto-complete location fields for confirmation by user - binding.locationName.setText(location.getAddress()); - binding.searchableLocationName.setText( - createEventViewModel.getSearchableLocationName(location.getAddress().toString())); - } - } + binding.selectLocationButton.setOnClickListener(view -> { - private void showLocationLayouts() { - binding.layoutSearchableLocation.setVisibility(View.VISIBLE); - binding.layoutLocationName.setVisibility(View.VISIBLE); + if (mapboxAccessToken.equals("YOUR_ACCESS_TOKEN")) { + ViewUtils.showSnackbar(binding.getRoot(), R.string.access_token_required); + return; + } + + PlaceAutocompleteFragment autocompleteFragment = PlaceAutocompleteFragment.newInstance( + mapboxAccessToken, PlaceOptions.builder().backgroundColor(Color.WHITE).build()); + + getFragmentManager().beginTransaction() + .replace(R.id.fragment, autocompleteFragment) + .addToBackStack(null) + .commit(); + + autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() { + @Override + public void onPlaceSelected(CarmenFeature carmenFeature) { + Event event = binding.getEvent(); + event.setLatitude(carmenFeature.center().latitude()); + event.setLongitude(carmenFeature.center().longitude()); + event.setLocationName(carmenFeature.placeName()); + event.setSearchableLocationName(carmenFeature.text()); + binding.layoutLocationName.setVisibility(View.VISIBLE); + binding.locationName.setText(event.getLocationName()); + getFragmentManager().popBackStack(); + } + + @Override + public void onCancel() { + getFragmentManager().popBackStack(); + } + }); + }); } @Override diff --git a/app/src/fdroid/java/com/eventyay/organizer/core/event/create/LocationPicker.java b/app/src/fdroid/java/com/eventyay/organizer/core/event/create/LocationPicker.java deleted file mode 100644 index 632e33a21..000000000 --- a/app/src/fdroid/java/com/eventyay/organizer/core/event/create/LocationPicker.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.eventyay.organizer.core.event.create; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Intent; - -public class LocationPicker { - - private final double DEMO_VALUE = 1; - - public boolean launchPicker(Activity activity) { - //do nothing - return false; - } - - @SuppressLint("RestrictedApi") - public Location getPlace(Activity activity, Intent data) { - return new Location(DEMO_VALUE, DEMO_VALUE, null); - } - - public boolean shouldShowLocationLayout() { - return true; - } -} diff --git a/app/src/fdroid/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java b/app/src/fdroid/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java index 51554420b..9d202db2f 100644 --- a/app/src/fdroid/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java +++ b/app/src/fdroid/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java @@ -43,6 +43,10 @@ import com.eventyay.organizer.ui.editor.RichEditorActivity; import com.eventyay.organizer.utils.Utils; import com.eventyay.organizer.utils.ValidateUtils; +import com.mapbox.api.geocoding.v5.models.CarmenFeature; +import com.mapbox.mapboxsdk.plugins.places.autocomplete.model.PlaceOptions; +import com.mapbox.mapboxsdk.plugins.places.autocomplete.ui.PlaceAutocompleteFragment; +import com.mapbox.mapboxsdk.plugins.places.autocomplete.ui.PlaceSelectionListener; import java.io.FileNotFoundException; import java.io.InputStream; @@ -70,11 +74,9 @@ public class UpdateEventFragment extends BaseFragment implements CreateEventView private ArrayAdapter timezoneAdapter; private long eventId = -1; private int countryIndex = -1; - private final LocationPicker locationPicker = new LocationPicker(); - private static final int PLACE_PICKER_REQUEST = 1; - private static final int RICH_TEXT_REQUEST = 2; - private static final int IMAGE_CHOOSER_REQUEST_CODE = 3; + private static final int RICH_TEXT_REQUEST = 1; + private static final int IMAGE_CHOOSER_REQUEST_CODE = 2; private CreateEventViewModel createEventViewModel; public static UpdateEventFragment newInstance() { @@ -135,7 +137,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, attachCountryList(createEventViewModel.getCountryList()); attachCurrencyCodesList(createEventViewModel.getCurrencyCodesList()); - setupPlacePicker(); + setupPlacesAutocomplete(); return binding.getRoot(); } @@ -301,48 +303,58 @@ public List getTimeZoneList() { return Arrays.asList(getResources().getStringArray(R.array.timezones)); } - private void setupPlacePicker() { - //check if there's an google places API key + private void setupPlacesAutocomplete() { + + ApplicationInfo applicationInfo = null; try { - ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = ai.metaData; - String placesApiKey = bundle.getString("com.google.android.geo.API_KEY"); - if ("YOUR_API_KEY".equals(placesApiKey)) { - Timber.d("Add Google Places API key in AndroidManifest.xml file to use Place Picker."); - binding.form.buttonPlacePicker.setVisibility(View.GONE); - showLocationLayouts(); - } + applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { - Timber.e(e, "Package name not found"); + Timber.e(e); } + Bundle bundle = applicationInfo.metaData; + + String mapboxAccessToken = bundle.getString(getString(R.string.mapbox_access_token)); + + binding.form.selectLocationButton.setOnClickListener(view -> { + + if (mapboxAccessToken.equals("YOUR_ACCESS_TOKEN")) { + ViewUtils.showSnackbar(binding.getRoot(), R.string.access_token_required); + return; + } + + PlaceAutocompleteFragment autocompleteFragment = PlaceAutocompleteFragment.newInstance( + mapboxAccessToken, PlaceOptions.builder().backgroundColor(Color.WHITE).build()); + + getFragmentManager().beginTransaction() + .replace(R.id.fragment, autocompleteFragment) + .addToBackStack(null) + .commit(); + + autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() { + @Override + public void onPlaceSelected(CarmenFeature carmenFeature) { + Event event = binding.getEvent(); + event.setLatitude(carmenFeature.center().latitude()); + event.setLongitude(carmenFeature.center().longitude()); + event.setLocationName(carmenFeature.placeName()); + event.setSearchableLocationName(carmenFeature.text()); + binding.form.layoutLocationName.setVisibility(View.VISIBLE); + binding.form.locationName.setText(event.getLocationName()); + getFragmentManager().popBackStack(); + } - binding.form.buttonPlacePicker.setOnClickListener(view -> { - boolean success = locationPicker.launchPicker(getActivity()); - if (locationPicker.shouldShowLocationLayout() || !success) - showLocationLayouts(); + @Override + public void onCancel() { + getFragmentManager().popBackStack(); + } + }); }); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == PLACE_PICKER_REQUEST && resultCode == RESULT_OK) { - //once place is picked from map, make location fields visible for confirmation by user - showLocationLayouts(); - //set event attributes - - Location location = locationPicker.getPlace(getActivity(), data); - - Event event = binding.getEvent(); - event.latitude = location.getLatitude(); - event.longitude = location.getLongitude(); - //auto-complete location fields for confirmation by user - - binding.form.locationName.setText(location.getAddress()); - binding.form.searchableLocationName.setText( - createEventViewModel.getSearchableLocationName(location.getAddress().toString()) - ); - } else if (requestCode == RICH_TEXT_REQUEST && resultCode == RESULT_OK) { + if (requestCode == RICH_TEXT_REQUEST && resultCode == RESULT_OK) { String description = data.getStringExtra(TAG_RICH_TEXT); if (!TextUtils.isEmpty(description)) { createEventViewModel.getEvent().setDescription(description); @@ -365,11 +377,6 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } } - private void showLocationLayouts() { - binding.form.layoutSearchableLocation.setVisibility(View.VISIBLE); - binding.form.layoutLocationName.setVisibility(View.VISIBLE); - } - @Override public void setEvent(Event event) { binding.setEvent(event); diff --git a/app/src/fdroid/res/layout/event_create_form.xml b/app/src/fdroid/res/layout/event_create_form.xml index 9eef94f38..2df09edb0 100644 --- a/app/src/fdroid/res/layout/event_create_form.xml +++ b/app/src/fdroid/res/layout/event_create_form.xml @@ -236,51 +236,21 @@ android:textStyle="bold" />