Skip to content

Commit 2a98bd2

Browse files
neslihanturanmisaochan
authored andcommitted
Add nearby tutorial (#1467)
* Add dependency for MaterialShowcase * Add actionview class to get a reference to material showcase * Create a NearbyMaterialShowcaseSequence class * Apply sequence steps * Add first three steps of nearby showcase * Add sequence id constants to make sure they will be displayed only once * Add last step of sequence to explain plus fab * Create an object to prevent customize all sequences every time * Fix typo * Code cleanup * Add strings to strings.xml * Code cleanup * Revert irrelevant change * Revert irrelevant change * Remove showcaseview for recenter button * Use single showcaseView instead of sequence * Add single showcase view insted of sequence to be able to edit text style * Make sure it will be displayed only once * Cleanup * Update strings * Change dismiss text style
1 parent 4f6b791 commit 2a98bd2

File tree

6 files changed

+151
-4
lines changed

6 files changed

+151
-4
lines changed

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ dependencies {
2525
transitive=true
2626
}
2727

28+
implementation "com.github.deano2390:MaterialShowcaseView:1.2.0"
29+
2830
implementation "com.android.support:support-v4:$SUPPORT_LIB_VERSION"
2931
implementation "com.android.support:appcompat-v7:$SUPPORT_LIB_VERSION"
3032
implementation "com.android.support:design:$SUPPORT_LIB_VERSION"

app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@
44
import android.content.Context;
55
import android.content.Intent;
66
import android.content.IntentFilter;
7+
import android.content.SharedPreferences;
78
import android.content.pm.PackageManager;
9+
import android.graphics.Typeface;
810
import android.net.Uri;
911
import android.os.Build;
1012
import android.os.Bundle;
13+
import android.os.Handler;
1114
import android.support.annotation.NonNull;
1215
import android.support.design.widget.BottomSheetBehavior;
1316
import android.support.v4.app.FragmentTransaction;
1417
import android.support.v7.app.AlertDialog;
18+
1519
import android.view.Menu;
1620
import android.view.MenuInflater;
1721
import android.view.MenuItem;
@@ -25,6 +29,7 @@
2529
import java.util.List;
2630

2731
import javax.inject.Inject;
32+
import javax.inject.Named;
2833

2934
import butterknife.BindView;
3035
import butterknife.ButterKnife;
@@ -41,6 +46,8 @@
4146
import io.reactivex.disposables.Disposable;
4247
import io.reactivex.schedulers.Schedulers;
4348
import timber.log.Timber;
49+
import uk.co.deanwild.materialshowcaseview.IShowcaseListener;
50+
import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView;
4451

4552

4653
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
@@ -56,12 +63,15 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
5663
LinearLayout bottomSheetDetails;
5764
@BindView(R.id.transparentView)
5865
View transparentView;
66+
@BindView(R.id.fab_recenter)
67+
View fabRecenter;
5968

6069
@Inject
6170
LocationServiceManager locationManager;
6271
@Inject
6372
NearbyController nearbyController;
64-
73+
@Inject
74+
@Named("application_preferences") SharedPreferences applicationPrefs;
6575
private LatLng curLatLng;
6676
private Bundle bundle;
6777
private Disposable placesDisposable;
@@ -72,11 +82,18 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
7282
private NearbyListFragment nearbyListFragment;
7383
private static final String TAG_RETAINED_MAP_FRAGMENT = NearbyMapFragment.class.getSimpleName();
7484
private static final String TAG_RETAINED_LIST_FRAGMENT = NearbyListFragment.class.getSimpleName();
85+
private View listButton; // Reference to list button to use in tutorial
7586

7687
private final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
7788
private BroadcastReceiver broadcastReceiver;
89+
90+
private boolean isListShowcaseAdded = false;
91+
private boolean isMapShowCaseAdded = false;
92+
7893
private LatLng lastKnownLocation;
7994

95+
private MaterialShowcaseView secondSingleShowCaseView;
96+
8097
@Override
8198
protected void onCreate(Bundle savedInstanceState) {
8299
super.onCreate(savedInstanceState);
@@ -126,6 +143,39 @@ public boolean onCreateOptionsMenu(Menu menu) {
126143
MenuInflater inflater = getMenuInflater();
127144
inflater.inflate(R.menu.menu_nearby, menu);
128145

146+
new Handler().post(() -> {
147+
148+
listButton = findViewById(R.id.action_display_list);
149+
150+
secondSingleShowCaseView = new MaterialShowcaseView.Builder(this)
151+
.setTarget(listButton)
152+
.setDismissText(getString(R.string.showcase_view_got_it_button))
153+
.setContentText(getString(R.string.showcase_view_list_icon))
154+
.setDelay(500) // optional but starting animations immediately in onCreate can make them choppy
155+
.singleUse(ViewUtil.SHOWCASE_VIEW_ID_1) // provide a unique ID used to ensure it is only shown once
156+
.setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD))
157+
.setListener(new IShowcaseListener() {
158+
@Override
159+
public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) {
160+
161+
}
162+
163+
// If dismissed, we can inform fragment to start showcase sequence there
164+
@Override
165+
public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) {
166+
nearbyMapFragment.onNearbyMaterialShowcaseDismissed();
167+
}
168+
})
169+
.build();
170+
171+
isListShowcaseAdded = true;
172+
173+
if (isMapShowCaseAdded) { // If map showcase is also ready, start ShowcaseSequence
174+
// Probably this case is not possible. Just added to be careful
175+
setMapViewTutorialShowCase();
176+
}
177+
});
178+
129179
return super.onCreateOptionsMenu(menu);
130180
}
131181

@@ -420,6 +470,45 @@ private void populatePlaces(NearbyController.NearbyPlacesInfo nearbyPlacesInfo)
420470
updateMapFragment(false);
421471
updateListFragment();
422472
}
473+
474+
isMapShowCaseAdded = true;
475+
}
476+
477+
public void setMapViewTutorialShowCase() {
478+
/*
479+
*This showcase view will be the first step of our nearbyMaterialShowcaseSequence. The reason we use a
480+
* single item instead of adding another step to nearbyMaterialShowcaseSequence is that we are not able to
481+
* call withoutShape() method on steps. For mapView we need an showcase view without
482+
* any circle on it, it should cover the whole page.
483+
* */
484+
MaterialShowcaseView firstSingleShowCaseView = new MaterialShowcaseView.Builder(this)
485+
.setTarget(nearbyMapFragment.mapView)
486+
.setDismissText(getString(R.string.showcase_view_got_it_button))
487+
.setContentText(getString(R.string.showcase_view_whole_nearby_activity))
488+
.setDelay(500) // optional but starting animations immediately in onCreate can make them choppy
489+
.singleUse(ViewUtil.SHOWCASE_VIEW_ID_2) // provide a unique ID used to ensure it is only shown once
490+
.withoutShape() // no shape on map view since there are no view to focus on
491+
.setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD))
492+
.setListener(new IShowcaseListener() {
493+
@Override
494+
public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) {
495+
496+
}
497+
498+
@Override
499+
public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) {
500+
/* Add other nearbyMaterialShowcaseSequence here, it will make the user feel as they are a
501+
* nearbyMaterialShowcaseSequence whole together.
502+
* */
503+
secondSingleShowCaseView.show(NearbyActivity.this);
504+
}
505+
})
506+
.build();
507+
508+
if (applicationPrefs.getBoolean("firstRunNearby", true)) {
509+
applicationPrefs.edit().putBoolean("firstRunNearby", false).apply();
510+
firstSingleShowCaseView.show(this);
511+
}
423512
}
424513

425514
private void lockNearbyView(boolean lock) {

app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import android.content.SharedPreferences;
88
import android.content.pm.PackageManager;
99
import android.graphics.Color;
10+
import android.graphics.Typeface;
1011
import android.net.Uri;
1112
import android.os.Bundle;
1213
import android.support.annotation.NonNull;
@@ -58,13 +59,14 @@
5859
import fr.free.nrw.commons.utils.UriDeserializer;
5960
import fr.free.nrw.commons.utils.ViewUtil;
6061
import timber.log.Timber;
62+
import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView;
6163

6264
import static android.app.Activity.RESULT_OK;
6365
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
6466

6567
public class NearbyMapFragment extends DaggerFragment {
6668

67-
private MapView mapView;
69+
public MapView mapView;
6870
private List<NearbyBaseMarker> baseMarkerOptions;
6971
private fr.free.nrw.commons.location.LatLng curLatLng;
7072
public fr.free.nrw.commons.location.LatLng[] boundaryCoordinates;
@@ -111,6 +113,10 @@ public class NearbyMapFragment extends DaggerFragment {
111113
private final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.06;
112114
private final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.04;
113115

116+
private boolean isSecondMaterialShowcaseDismissed;
117+
private boolean isMapReady;
118+
private MaterialShowcaseView thirdSingleShowCaseView;
119+
114120
private Bundle bundleForUpdtes;// Carry information from activity about changed nearby places and current location
115121

116122
@Inject
@@ -163,7 +169,6 @@ public void onCreate(Bundle savedInstanceState) {
163169
@Override
164170
public View onCreateView(LayoutInflater inflater, ViewGroup container,
165171
Bundle savedInstanceState) {
166-
167172
Timber.d("onCreateView called");
168173
if (curLatLng != null) {
169174
Timber.d("curLatLng found, setting up map view...");
@@ -476,6 +481,7 @@ private void setupMapView(Bundle savedInstanceState) {
476481
mapView.getMapAsync(new OnMapReadyCallback() {
477482
@Override
478483
public void onMapReady(MapboxMap mapboxMap) {
484+
((NearbyActivity)getActivity()).setMapViewTutorialShowCase();
479485
NearbyMapFragment.this.mapboxMap = mapboxMap;
480486
updateMapSignificantly();
481487
}
@@ -519,6 +525,7 @@ private void addCurrentLocationMarker(MapboxMap mapboxMap) {
519525
private void addNearbyMarkerstoMapBoxMap() {
520526

521527
mapboxMap.addMarkers(baseMarkerOptions);
528+
522529
mapboxMap.setOnInfoWindowCloseListener(marker -> {
523530
if (marker == selected) {
524531
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
@@ -534,13 +541,15 @@ private void addNearbyMarkerstoMapBoxMap() {
534541
});
535542

536543
mapboxMap.setOnMarkerClickListener(marker -> {
544+
537545
if (marker instanceof NearbyMarker) {
538546
this.selected = marker;
539547
NearbyMarker nearbyMarker = (NearbyMarker) marker;
540548
Place place = nearbyMarker.getNearbyBaseMarker().getPlace();
541549
passInfoToSheet(place);
542550
bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
543551
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
552+
544553
}
545554
return false;
546555
});
@@ -634,7 +643,19 @@ private void showFAB() {
634643
addAnchorToSmallFABs(fabGallery, getActivity().findViewById(R.id.empty_view).getId());
635644

636645
addAnchorToSmallFABs(fabCamera, getActivity().findViewById(R.id.empty_view1).getId());
637-
646+
thirdSingleShowCaseView = new MaterialShowcaseView.Builder(this.getActivity())
647+
.setTarget(fabPlus)
648+
.setDismissText(getString(R.string.showcase_view_got_it_button))
649+
.setContentText(getString(R.string.showcase_view_plus_fab))
650+
.setDelay(500) // optional but starting animations immediately in onCreate can make them choppy
651+
.singleUse(ViewUtil.SHOWCASE_VIEW_ID_3) // provide a unique ID used to ensure it is only shown once
652+
.setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD))
653+
.build();
654+
655+
isMapReady = true;
656+
if (isSecondMaterialShowcaseDismissed) {
657+
thirdSingleShowCaseView.show(getActivity());
658+
}
638659
}
639660

640661

@@ -791,6 +812,13 @@ public void setBundleForUpdtes(Bundle bundleForUpdtes) {
791812
this.bundleForUpdtes = bundleForUpdtes;
792813
}
793814

815+
public void onNearbyMaterialShowcaseDismissed() {
816+
isSecondMaterialShowcaseDismissed = true;
817+
if (isMapReady) {
818+
thirdSingleShowCaseView.show(getActivity());
819+
}
820+
}
821+
794822

795823
@Override
796824
public void onStart() {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package fr.free.nrw.commons.nearby;
2+
3+
import android.app.Activity;
4+
5+
import uk.co.deanwild.materialshowcaseview.MaterialShowcaseSequence;
6+
import uk.co.deanwild.materialshowcaseview.ShowcaseConfig;
7+
8+
9+
public class NearbyMaterialShowcaseSequence extends MaterialShowcaseSequence {
10+
11+
public NearbyMaterialShowcaseSequence(Activity activity, String sequenceID) {
12+
super(activity, sequenceID);
13+
ShowcaseConfig config = new ShowcaseConfig();
14+
config.setDelay(500); // half second between each showcase view
15+
this.setConfig(config);
16+
this.singleUse(sequenceID); // Display tutorial only once
17+
}
18+
}

app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
public class ViewUtil {
1212

13+
public static final String SHOWCASE_VIEW_ID_1 = "SHOWCASE_VIEW_ID_1";
14+
public static final String SHOWCASE_VIEW_ID_2 = "SHOWCASE_VIEW_ID_2";
15+
public static final String SHOWCASE_VIEW_ID_3 = "SHOWCASE_VIEW_ID_3";
16+
1317
public static void showSnackbar(View view, int messageResourceId) {
1418
Snackbar.make(view, messageResourceId, Snackbar.LENGTH_SHORT).show();
1519
}

app/src/main/res/values/strings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,15 @@
271271
<string name="about_translate_cancel">Cancel</string>
272272
<string name="retry">Retry</string>
273273

274+
<string name="showcase_view_got_it_button">Got it!</string>
275+
<string name="showcase_view_whole_nearby_activity">These are the places near you that need pictures to illustrate their Wikipedia articles</string>
276+
<string name="showcase_view_list_icon">Tapping this button brings up a list of these places</string>
277+
<string name="showcase_view_plus_fab">You can upload a picture for any place from your gallery or camera</string>
278+
274279
<string name="no_images_found">No images found!</string>
275280
<string name="error_loading_images">Error occurred while loading images.</string>
276281
<string name="image_uploaded_by">Uploaded by: %1$s</string>
282+
277283
<string name="share_app_title">Share App</string>
278284
<string name="share_coordinates_not_present">Coordinates were not specified during image selection</string>
279285

0 commit comments

Comments
 (0)