diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a8e7af19..caa02a103 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,23 +5,30 @@ If you're not sure where to start head on to [this wiki page](https://github.com Here's a gist of the guidelines, -# Make separate commits for logically separate changes +1. Make separate commits for logically separate changes -# Describe your changes well in the commit message +1. Describe your changes well in the commit message -The first line of the commit message should be a short description of what has + The first line of the commit message should be a short description of what has changed. It is also good to prefix the first line with "area: " where the "area" is a filename or identifier for the general area of the code being modified. The body should provide a meaningful commit message. -# Write Javadocs +1. Write Javadocs -We require contributors to include Javadocs for all new methods and classes submitted via PRs (after 1 May 2018). This is aimed at making it easier for new contributors to dive into our codebase, especially those who are new to Android development. A few things to note: + We require contributors to include Javadocs for all new methods and classes + submitted via PRs (after 1 May 2018). This is aimed at making it easier for + new contributors to dive into our codebase, especially those who are new to + Android development. A few things to note: -- This should not replace the need for code that is easily-readable in and of itself -- Please make sure that your Javadocs are reasonably descriptive, not just a copy of the method name -- Please do not use `@author` tags - we aim for collective code ownership, and if needed, GitHub allows us to see who wrote something without needing to add these tags + - This should not replace the need for code that is easily-readable in + and of itself + - Please make sure that your Javadocs are reasonably descriptive, not just + a copy of the method name + - Please do not use `@author` tags - we aim for collective code ownership, + and if needed, Git allows us to see who wrote something without needing + to add these tags (`git blame`) -# Write tests for your code (if possible) +1. Write tests for your code (if possible) -# Make sure the Wiki pages don't become stale by updating them (if needed) +1. Make sure the Wiki pages don't become stale by updating them (if needed) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 34078f07e..9d7150008 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,15 @@ -## Description +## Description (required) -Fixes #{GitHub issue number} +Fixes #{GitHub issue number and title} {Describe the changes made and why they were made.} -## Tests performed +## Tests performed (required) Tested on {API level & name of device/emulator}, with {build variant, e.g. ProdDebug}. -{Please test your PR at least once before submitting.} - -## Screenshots showing what changed +## Screenshots showing what changed (optional) {Only for user interface changes, otherwise remove this section. See [how to take a screenshot](https://android.stackexchange.com/questions/1759/how-to-take-a-screenshot-with-an-android-device)} + +_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._ diff --git a/app/build.gradle b/app/build.gradle index 9c6f62fd4..5d37f8f54 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,8 @@ dependencies { transitive=true } + implementation "com.github.deano2390:MaterialShowcaseView:1.2.0" + implementation "com.android.support:support-v4:$SUPPORT_LIB_VERSION" implementation "com.android.support:appcompat-v7:$SUPPORT_LIB_VERSION" implementation "com.android.support:design:$SUPPORT_LIB_VERSION" diff --git a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java index 2d79a6c4f..affb57528 100644 --- a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java @@ -61,8 +61,8 @@ public class MediaDataExtractor { } try{ - Timber.d("Nominated for deletion: " + mediaWikiApi.pageExists("Commons:Deletion_requests/"+filename)); - deletionStatus = mediaWikiApi.pageExists("Commons:Deletion_requests/"+filename); + deletionStatus = mediaWikiApi.pageExists("Commons:Deletion_requests/" + filename); + Timber.d("Nominated for deletion: " + deletionStatus); } catch (Exception e){ Timber.d(e.getMessage()); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 2a423de93..3bf8beacf 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -4,14 +4,18 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.graphics.Typeface; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.support.design.widget.BottomSheetBehavior; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; + import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -25,6 +29,7 @@ import com.google.gson.GsonBuilder; import java.util.List; import javax.inject.Inject; +import javax.inject.Named; import butterknife.BindView; import butterknife.ButterKnife; @@ -41,6 +46,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; +import uk.co.deanwild.materialshowcaseview.IShowcaseListener; +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener { @@ -56,12 +63,15 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp LinearLayout bottomSheetDetails; @BindView(R.id.transparentView) View transparentView; + @BindView(R.id.fab_recenter) + View fabRecenter; @Inject LocationServiceManager locationManager; @Inject NearbyController nearbyController; - + @Inject + @Named("application_preferences") SharedPreferences applicationPrefs; private LatLng curLatLng; private Bundle bundle; private Disposable placesDisposable; @@ -72,11 +82,18 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp private NearbyListFragment nearbyListFragment; private static final String TAG_RETAINED_MAP_FRAGMENT = NearbyMapFragment.class.getSimpleName(); private static final String TAG_RETAINED_LIST_FRAGMENT = NearbyListFragment.class.getSimpleName(); + private View listButton; // Reference to list button to use in tutorial private final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; private BroadcastReceiver broadcastReceiver; + + private boolean isListShowcaseAdded = false; + private boolean isMapShowCaseAdded = false; + private LatLng lastKnownLocation; + private MaterialShowcaseView secondSingleShowCaseView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -126,6 +143,39 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_nearby, menu); + new Handler().post(() -> { + + listButton = findViewById(R.id.action_display_list); + + secondSingleShowCaseView = new MaterialShowcaseView.Builder(this) + .setTarget(listButton) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_list_icon)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_1) // provide a unique ID used to ensure it is only shown once + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .setListener(new IShowcaseListener() { + @Override + public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) { + + } + + // If dismissed, we can inform fragment to start showcase sequence there + @Override + public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) { + nearbyMapFragment.onNearbyMaterialShowcaseDismissed(); + } + }) + .build(); + + isListShowcaseAdded = true; + + if (isMapShowCaseAdded) { // If map showcase is also ready, start ShowcaseSequence + // Probably this case is not possible. Just added to be careful + setMapViewTutorialShowCase(); + } + }); + return super.onCreateOptionsMenu(menu); } @@ -420,6 +470,45 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp updateMapFragment(false); updateListFragment(); } + + isMapShowCaseAdded = true; + } + + public void setMapViewTutorialShowCase() { + /* + *This showcase view will be the first step of our nearbyMaterialShowcaseSequence. The reason we use a + * single item instead of adding another step to nearbyMaterialShowcaseSequence is that we are not able to + * call withoutShape() method on steps. For mapView we need an showcase view without + * any circle on it, it should cover the whole page. + * */ + MaterialShowcaseView firstSingleShowCaseView = new MaterialShowcaseView.Builder(this) + .setTarget(nearbyMapFragment.mapView) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_whole_nearby_activity)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_2) // provide a unique ID used to ensure it is only shown once + .withoutShape() // no shape on map view since there are no view to focus on + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .setListener(new IShowcaseListener() { + @Override + public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) { + + } + + @Override + public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) { + /* Add other nearbyMaterialShowcaseSequence here, it will make the user feel as they are a + * nearbyMaterialShowcaseSequence whole together. + * */ + secondSingleShowCaseView.show(NearbyActivity.this); + } + }) + .build(); + + if (applicationPrefs.getBoolean("firstRunNearby", true)) { + applicationPrefs.edit().putBoolean("firstRunNearby", false).apply(); + firstSingleShowCaseView.show(this); + } } private void lockNearbyView(boolean lock) { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java index 431132436..69041d286 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java @@ -7,6 +7,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Color; +import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; @@ -58,13 +59,14 @@ import fr.free.nrw.commons.contributions.ContributionController; import fr.free.nrw.commons.utils.UriDeserializer; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; import static android.app.Activity.RESULT_OK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class NearbyMapFragment extends DaggerFragment { - private MapView mapView; + public MapView mapView; private List baseMarkerOptions; private fr.free.nrw.commons.location.LatLng curLatLng; public fr.free.nrw.commons.location.LatLng[] boundaryCoordinates; @@ -111,6 +113,10 @@ public class NearbyMapFragment extends DaggerFragment { private final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.06; private final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.04; + private boolean isSecondMaterialShowcaseDismissed; + private boolean isMapReady; + private MaterialShowcaseView thirdSingleShowCaseView; + private Bundle bundleForUpdtes;// Carry information from activity about changed nearby places and current location @Inject @@ -163,7 +169,6 @@ public class NearbyMapFragment extends DaggerFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Timber.d("onCreateView called"); if (curLatLng != null) { Timber.d("curLatLng found, setting up map view..."); @@ -476,6 +481,7 @@ public class NearbyMapFragment extends DaggerFragment { mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(MapboxMap mapboxMap) { + ((NearbyActivity)getActivity()).setMapViewTutorialShowCase(); NearbyMapFragment.this.mapboxMap = mapboxMap; updateMapSignificantly(); } @@ -519,6 +525,7 @@ public class NearbyMapFragment extends DaggerFragment { private void addNearbyMarkerstoMapBoxMap() { mapboxMap.addMarkers(baseMarkerOptions); + mapboxMap.setOnInfoWindowCloseListener(marker -> { if (marker == selected) { bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); @@ -534,6 +541,7 @@ public class NearbyMapFragment extends DaggerFragment { }); mapboxMap.setOnMarkerClickListener(marker -> { + if (marker instanceof NearbyMarker) { this.selected = marker; NearbyMarker nearbyMarker = (NearbyMarker) marker; @@ -541,6 +549,7 @@ public class NearbyMapFragment extends DaggerFragment { passInfoToSheet(place); bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } return false; }); @@ -634,7 +643,19 @@ public class NearbyMapFragment extends DaggerFragment { addAnchorToSmallFABs(fabGallery, getActivity().findViewById(R.id.empty_view).getId()); addAnchorToSmallFABs(fabCamera, getActivity().findViewById(R.id.empty_view1).getId()); + thirdSingleShowCaseView = new MaterialShowcaseView.Builder(this.getActivity()) + .setTarget(fabPlus) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_plus_fab)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_3) // provide a unique ID used to ensure it is only shown once + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .build(); + isMapReady = true; + if (isSecondMaterialShowcaseDismissed) { + thirdSingleShowCaseView.show(getActivity()); + } } @@ -791,6 +812,13 @@ public class NearbyMapFragment extends DaggerFragment { this.bundleForUpdtes = bundleForUpdtes; } + public void onNearbyMaterialShowcaseDismissed() { + isSecondMaterialShowcaseDismissed = true; + if (isMapReady) { + thirdSingleShowCaseView.show(getActivity()); + } + } + @Override public void onStart() { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java new file mode 100644 index 000000000..c6e46611d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java @@ -0,0 +1,18 @@ +package fr.free.nrw.commons.nearby; + +import android.app.Activity; + +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseSequence; +import uk.co.deanwild.materialshowcaseview.ShowcaseConfig; + + +public class NearbyMaterialShowcaseSequence extends MaterialShowcaseSequence { + + public NearbyMaterialShowcaseSequence(Activity activity, String sequenceID) { + super(activity, sequenceID); + ShowcaseConfig config = new ShowcaseConfig(); + config.setDelay(500); // half second between each showcase view + this.setConfig(config); + this.singleUse(sequenceID); // Display tutorial only once + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 77f76fbed..6a59c8e30 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -81,8 +81,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; - - +import android.support.design.widget.FloatingActionButton; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; import static java.lang.Long.min; @@ -122,6 +121,7 @@ public class ShareActivity private Uri mediaUri; private Contribution contribution; private SimpleDraweeView backgroundImageView; + private FloatingActionButton maps_fragment; private boolean cacheFound; @@ -145,6 +145,8 @@ public class ShareActivity private long ShortAnimationDuration; private FloatingActionButton zoomInButton; private FloatingActionButton zoomOutButton; + private FloatingActionButton mainFab; + private boolean isFABOpen = false; /** @@ -284,6 +286,24 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } + + mainFab = (FloatingActionButton) findViewById(R.id.main_fab); + /* + * called when upper arrow floating button + */ + mainFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(!isFABOpen){ + showFABMenu(); + }else{ + closeFABMenu(); + } + } + }); + + + zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); try { zoomInButton.setOnClickListener(new View.OnClickListener() { @@ -358,7 +378,74 @@ public class ShareActivity .commitAllowingStateLoss(); } uploadController.prepareService(); + maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); + maps_fragment.setVisibility(View.VISIBLE); + if( imageObj == null || imageObj.imageCoordsExists != true){ + maps_fragment.setVisibility(View.INVISIBLE); + } + + + maps_fragment.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( imageObj != null && imageObj.imageCoordsExists == true) { + Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); + Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); + mapIntent.setPackage("com.google.android.apps.maps"); + startActivity(mapIntent); + } + } + }); } + /* + * Function to display the zoom and map FAB + */ + private void showFABMenu(){ + isFABOpen=true; + + if( imageObj != null && imageObj.imageCoordsExists == true) + maps_fragment.setVisibility(View.VISIBLE); + zoomInButton.setVisibility(View.VISIBLE); + + mainFab.animate().rotationBy(180); + maps_fragment.animate().translationY(-getResources().getDimension(R.dimen.second_fab)); + zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab)); + } + + /* + * function to close the zoom and map FAB + */ + private void closeFABMenu(){ + isFABOpen=false; + mainFab.animate().rotationBy(-180); + maps_fragment.animate().translationY(0); + zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + + } + + @Override + public void onAnimationEnd(Animator animator) { + if(!isFABOpen){ + maps_fragment.setVisibility(View.GONE); + zoomInButton.setVisibility(View.GONE); + } + + } + + @Override + public void onAnimationCancel(Animator animator) { + + } + + @Override + public void onAnimationRepeat(Animator animator) { + + } + }); + } + @Override public void onRequestPermissionsResult(int requestCode, @@ -461,6 +548,9 @@ public class ShareActivity detectUnwantedPicturesAsync.execute(); } + /* + * to display permission snackbar in share activity + */ private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, final int code) { @@ -693,7 +783,9 @@ public class ShareActivity return super.onOptionsItemSelected(item); } - // Get SHA1 of file from input stream + /* + * Get SHA1 of file from input stream + */ private String getSHA1(InputStream is) { MessageDigest digest; @@ -730,6 +822,9 @@ public class ShareActivity } } + /* + * function to provide pinch zoom + */ private void zoomImageFromThumb(final View thumbView, Uri imageuri ) { // If there's an animation in progress, cancel it // immediately and proceed with this one. @@ -737,6 +832,8 @@ public class ShareActivity CurrentAnimator.cancel(); } ViewUtil.hideKeyboard(ShareActivity.this.findViewById(R.id.titleEdit | R.id.descEdit)); + closeFABMenu(); + mainFab.setVisibility(View.GONE); InputStream input = null; Bitmap scaled = null; try { @@ -866,7 +963,7 @@ public class ShareActivity CurrentAnimator.cancel(); } zoomOutButton.setVisibility(View.GONE); - zoomInButton.setVisibility(View.VISIBLE); + mainFab.setVisibility(View.VISIBLE); // Animate the four positioning/sizing properties in parallel, // back to their original values. diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java b/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java index 1c45e8178..0c22a40a2 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java @@ -10,6 +10,10 @@ import android.widget.Toast; public class ViewUtil { + public static final String SHOWCASE_VIEW_ID_1 = "SHOWCASE_VIEW_ID_1"; + public static final String SHOWCASE_VIEW_ID_2 = "SHOWCASE_VIEW_ID_2"; + public static final String SHOWCASE_VIEW_ID_3 = "SHOWCASE_VIEW_ID_3"; + public static void showSnackbar(View view, int messageResourceId) { Snackbar.make(view, messageResourceId, Snackbar.LENGTH_SHORT).show(); } diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml new file mode 100644 index 000000000..bc010396b --- /dev/null +++ b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml index ca8097495..b6e523239 100644 --- a/app/src/main/res/layout/activity_share.xml +++ b/app/src/main/res/layout/activity_share.xml @@ -41,8 +41,6 @@ - - + + + + + + + 20sp 16sp 14sp + 15dp + 25dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3e83fefd1..dcea51bcc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -271,8 +271,16 @@ Cancel Retry + Got it! + These are the places near you that need pictures to illustrate their Wikipedia articles + Tapping this button brings up a list of these places + You can upload a picture for any place from your gallery or camera + No images found! Error occurred while loading images. Uploaded by: %1$s + Share App + Coordinates were not specified during image selection +