Merge branch '2.9-release'

This commit is contained in:
maskara 2018-11-27 22:38:20 +05:30
commit 4a2cdb70b9
21 changed files with 552 additions and 172 deletions

View file

@ -89,8 +89,8 @@ android {
defaultConfig {
applicationId 'fr.free.nrw.commons'
versionCode 92
versionName '2.8.5'
versionCode 94
versionName '2.9.0'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion 15

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.contributions;
import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@ -20,12 +21,14 @@ import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.app.LoaderManager;
import android.support.v4.widget.CursorAdapter;
import android.util.Log;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@ -50,6 +53,8 @@ import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.notification.UnreadNotificationsCheckAsync;
import fr.free.nrw.commons.settings.Prefs;
import fr.free.nrw.commons.upload.UploadService;
import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
@ -102,10 +107,11 @@ public class ContributionsFragment
private LatLng curLatLng;
private boolean firstLocationUpdate = true;
private LocationServiceManager locationManager;
public LocationServiceManager locationManager;
private boolean isFragmentAttachedBefore = false;
private View checkBoxView;
private CheckBox checkBox;
/**
* Since we will need to use parent activity on onAuthCookieAcquired, we have to wait
@ -137,6 +143,18 @@ public class ContributionsFragment
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contributions, container, false);
nearbyNoificationCardView = view.findViewById(R.id.card_view_nearby);
checkBoxView = View.inflate(getActivity(), R.layout.nearby_permission_dialog, null);
checkBox = (CheckBox) checkBoxView.findViewById(R.id.never_ask_again);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// Do not ask for permission on activity start again
prefs.edit().putBoolean("displayLocationPermissionForCardView",false).apply();
}
}
});
if (savedInstanceState != null) {
mediaDetailPagerFragment = (MediaDetailPagerFragment)getChildFragmentManager().findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG);
@ -186,7 +204,9 @@ public class ContributionsFragment
// show nearby card view on contributions list is visible
if (nearbyNoificationCardView != null) {
if (prefs.getBoolean("displayNearbyCardView", true)) {
nearbyNoificationCardView.setVisibility(View.VISIBLE);
if (nearbyNoificationCardView.cardViewVisibilityState == NearbyNoificationCardView.CardViewVisibilityState.READY) {
nearbyNoificationCardView.setVisibility(View.VISIBLE);
}
} else {
nearbyNoificationCardView.setVisibility(View.GONE);
}
@ -312,16 +332,24 @@ public class ContributionsFragment
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult");
switch (requestCode) {
case LOCATION_REQUEST: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Timber.d("Location permission granted, refreshing view");
// No need to display permission request button anymore
nearbyNoificationCardView.displayPermissionRequestButton(false);
locationManager.registerLocationManager();
} else {
// Still ask for permission
nearbyNoificationCardView.displayPermissionRequestButton(true);
if (prefs.getBoolean("displayLocationPermissionForCardView", true)) {
// Still ask for permission
DialogUtil.showAlertDialog(getActivity(),
getString(R.string.nearby_card_permission_title),
getString(R.string.nearby_card_permission_explanation),
() -> displayYouWontSeeNearbyMessage(),
() -> enableLocationPermission(),
checkBoxView,
false);
}
}
}
break;
@ -499,9 +527,10 @@ public class ContributionsFragment
if (prefs.getBoolean("displayNearbyCardView", true)) {
nearbyNoificationCardView.cardViewVisibilityState = NearbyNoificationCardView.CardViewVisibilityState.LOADING;
nearbyNoificationCardView.setVisibility(View.VISIBLE);
checkGPS();
if (nearbyNoificationCardView.cardViewVisibilityState == NearbyNoificationCardView.CardViewVisibilityState.READY) {
nearbyNoificationCardView.setVisibility(View.VISIBLE);
}
} else {
// Hide nearby notification card view if related shared preferences is false
@ -511,7 +540,6 @@ public class ContributionsFragment
}
/**
* Check GPS to decide displaying request permission button or not.
*/
@ -519,7 +547,15 @@ public class ContributionsFragment
if (!locationManager.isProviderEnabled()) {
Timber.d("GPS is not enabled");
nearbyNoificationCardView.permissionType = NearbyNoificationCardView.PermissionType.ENABLE_GPS;
nearbyNoificationCardView.displayPermissionRequestButton(true);
if (prefs.getBoolean("displayLocationPermissionForCardView", true)) {
DialogUtil.showAlertDialog(getActivity(),
getString(R.string.nearby_card_permission_title),
getString(R.string.nearby_card_permission_explanation),
() -> displayYouWontSeeNearbyMessage(),
() -> enableGPS(),
checkBoxView,
false);
}
} else {
Timber.d("GPS is enabled");
checkLocationPermission();
@ -530,20 +566,57 @@ public class ContributionsFragment
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (locationManager.isLocationPermissionGranted()) {
nearbyNoificationCardView.permissionType = NearbyNoificationCardView.PermissionType.NO_PERMISSION_NEEDED;
nearbyNoificationCardView.displayPermissionRequestButton(false);
locationManager.registerLocationManager();
} else {
nearbyNoificationCardView.permissionType = NearbyNoificationCardView.PermissionType.ENABLE_LOCATION_PERMISSON;
nearbyNoificationCardView.displayPermissionRequestButton(true);
// If user didn't selected Don't ask again
if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
&& prefs.getBoolean("displayLocationPermissionForCardView", true)) {
DialogUtil.showAlertDialog(getActivity(),
getString(R.string.nearby_card_permission_title),
getString(R.string.nearby_card_permission_explanation),
() -> displayYouWontSeeNearbyMessage(),
() -> enableLocationPermission(),
checkBoxView,
false);
}
}
} else {
// If device is under Marshmallow, we already checked for GPS
nearbyNoificationCardView.permissionType = NearbyNoificationCardView.PermissionType.NO_PERMISSION_NEEDED;
nearbyNoificationCardView.displayPermissionRequestButton(false);
locationManager.registerLocationManager();
}
}
private void enableLocationPermission() {
if (!getActivity().isFinishing()) {
((MainActivity) getActivity()).locationManager.requestPermissions(getActivity());
}
}
private void enableGPS() {
new AlertDialog.Builder(getActivity())
.setMessage(R.string.gps_disabled)
.setCancelable(false)
.setPositiveButton(R.string.enable_gps,
(dialog, id) -> {
Intent callGPSSettingIntent = new Intent(
android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
Timber.d("Loaded settings page");
((MainActivity) getActivity()).startActivityForResult(callGPSSettingIntent, 1);
})
.setNegativeButton(R.string.menu_cancel_upload, (dialog, id) -> {
dialog.cancel();
displayYouWontSeeNearbyMessage();
})
.create()
.show();
}
private void displayYouWontSeeNearbyMessage() {
ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.unable_to_display_nearest_place));
}
private void updateClosestNearbyCardViewInfo() {
curLatLng = locationManager.getLastLocation();

View file

@ -31,6 +31,7 @@ import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.nearby.NearbyFragment;
import fr.free.nrw.commons.nearby.NearbyMapFragment;
import fr.free.nrw.commons.nearby.NearbyNoificationCardView;
import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.upload.UploadService;
@ -253,7 +254,9 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
showTabs();
// Nearby Notification Card View was invisible when Media Details Fragment is active, make it visible again on Contrib List Fragment active, according to preferences
if (prefs.getBoolean("displayNearbyCardView", true)) {
contributionsFragment.nearbyNoificationCardView.setVisibility(View.VISIBLE);
if (contributionsFragment.nearbyNoificationCardView.cardViewVisibilityState == NearbyNoificationCardView.CardViewVisibilityState.READY) {
contributionsFragment.nearbyNoificationCardView.setVisibility(View.VISIBLE);
}
} else {
contributionsFragment.nearbyNoificationCardView.setVisibility(View.GONE);
}
@ -468,6 +471,8 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Timber.d("Location permission given");
((ContributionsFragment)contributionsActivityPagerAdapter
.getItem(0)).locationManager.registerLocationManager();
} else {
// If nearby fragment is visible and location permission is not given, send user back to contrib fragment
if (!isContributionsFragmentVisible) {

View file

@ -157,8 +157,11 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider) (getParentFragment().getParentFragment());
if (getParentFragment() != null
&& getParentFragment() instanceof MediaDetailPagerFragment) {
detailProvider =
((MediaDetailPagerFragment) getParentFragment()).getMediaDetailProvider();
}
if (savedInstanceState != null) {
editable = savedInstanceState.getBoolean("editable");
isCategoryImage = savedInstanceState.getBoolean("isCategoryImage");
@ -230,7 +233,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
@Override
public void onResume() {
super.onResume();
((ContributionsFragment)(getParentFragment().getParentFragment())).nearbyNoificationCardView.setVisibility(View.GONE);
if(getParentFragment()!=null && getParentFragment().getParentFragment()!=null) {
//Added a check because, not necessarily, the parent fragment will have a parent fragment, say
// in the case when MediaDetailPagerFragment is directly started by the CategoryImagesActivity
((ContributionsFragment) (getParentFragment().getParentFragment())).nearbyNoificationCardView
.setVisibility(View.GONE);
}
media = detailProvider.getMediaAtPosition(index);
if (media == null) {
// Ask the detail provider to ping us when we're ready

View file

@ -69,6 +69,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
private boolean isFeaturedImage;
MediaDetailAdapter adapter;
private Bookmark bookmark;
private MediaDetailProvider provider;
public MediaDetailPagerFragment() {
this(false, false);
@ -128,6 +129,23 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
isFeaturedImage = savedInstanceState.getBoolean("isFeaturedImage");
}
setHasOptionsMenu(true);
initProvider();
}
/**
* initialise the provider, based on from where the fragment was started, as in from an activity
* or a fragment
*/
private void initProvider() {
if (getParentFragment() != null) {
provider = (MediaDetailProvider) getParentFragment();
} else {
provider = (MediaDetailProvider) getActivity();
}
}
public MediaDetailProvider getMediaDetailProvider() {
return provider;
}
@Override
@ -136,7 +154,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
Timber.d("Returning as activity is destroyed!");
return true;
}
MediaDetailProvider provider = (MediaDetailProvider) getParentFragment();
Media m = provider.getMediaAtPosition(pager.getCurrentItem());
switch (item.getItemId()) {
case R.id.menu_bookmark_current_image:
@ -346,7 +364,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
e.printStackTrace();
}
}
getParentFragment().getActivity().supportInvalidateOptionsMenu();
getActivity().invalidateOptionsMenu();
}
@Override
@ -384,7 +402,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
Timber.d("Skipping getItem. Returning as activity is destroyed!");
return null;
}
pager.postDelayed(() -> getParentFragment().getActivity().supportInvalidateOptionsMenu(), 5);
pager.postDelayed(() -> getActivity().invalidateOptionsMenu(), 5);
}
return MediaDetailFragment.forMedia(i, editable, isFeaturedImage);
}
@ -395,7 +413,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
Timber.d("Skipping getCount. Returning as activity is destroyed!");
return 0;
}
return ((MediaDetailProvider) getParentFragment()).getTotalMediaCount();
return provider.getTotalMediaCount();
}
}
}

View file

@ -135,6 +135,8 @@ public class NearbyMapFragment extends DaggerFragment {
@Inject
BookmarkLocationsDao bookmarkLocationDao;
private static final double ZOOM_LEVEL = 14f;
public NearbyMapFragment() {
}
@ -304,7 +306,7 @@ public class NearbyMapFragment extends DaggerFragment {
curMapBoxLatLng.getLongitude())
: curMapBoxLatLng ) // Sets the new camera position
.zoom(isBottomListSheetExpanded ?
11 // zoom level is fixed to 11 when bottom sheet is expanded
ZOOM_LEVEL // zoom level is fixed when bottom sheet is expanded
:mapboxMap.getCameraPosition().zoom) // Same zoom level
.build();
}else {
@ -314,7 +316,7 @@ public class NearbyMapFragment extends DaggerFragment {
curMapBoxLatLng.getLongitude())
: curMapBoxLatLng ) // Sets the new camera position
.zoom(isBottomListSheetExpanded ?
11 // zoom level is fixed to 11 when bottom sheet is expanded
ZOOM_LEVEL // zoom level is fixed when bottom sheet is expanded
:mapboxMap.getCameraPosition().zoom) // Same zoom level
.build();
}
@ -325,47 +327,6 @@ public class NearbyMapFragment extends DaggerFragment {
}
}
/**
* Updates camera position according to list sheet status. If list sheet is collapsed, camera
* focus should be in the center. If list sheet is expanded, camera focus should be visible
* on the gap between list sheet and tab layout.
* @param isBottomListSheetExpanded
*/
private void updateMapCameraAccordingToBottomSheet(boolean isBottomListSheetExpanded) {
CameraPosition position;
this.isBottomListSheetExpanded = isBottomListSheetExpanded;
if (mapboxMap != null && curLatLng != null) {
if (isBottomListSheetExpanded) {
// Make camera to follow user on location change
if (ViewUtil.isPortrait(getActivity())) {
position = new CameraPosition.Builder()
.target(new LatLng(curLatLng.getLatitude() - CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT,
curLatLng.getLongitude())) // Sets the new camera target above
// current to make it visible when sheet is expanded
.zoom(11) // Fixed zoom level
.build();
} else {
position = new CameraPosition.Builder()
.target(new LatLng(curLatLng.getLatitude() - CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE,
curLatLng.getLongitude())) // Sets the new camera target above
// current to make it visible when sheet is expanded
.zoom(11) // Fixed zoom level
.build();
}
} else {
// Make camera to follow user on location change
position = new CameraPosition.Builder()
.target(new LatLng(curLatLng.getLatitude(),
curLatLng.getLongitude())) // Sets the new camera target to curLatLng
.zoom(mapboxMap.getCameraPosition().zoom) // Same zoom level
.build();
}
mapboxMap.animateCamera(CameraUpdateFactory
.newCameraPosition(position), 1000);
}
}
private void initViews() {
Timber.d("initViews called");
bottomSheetList = ((NearbyFragment)getParentFragment()).view.findViewById(R.id.bottom_sheet);
@ -446,7 +407,7 @@ public class NearbyMapFragment extends DaggerFragment {
curLatLng.getLongitude())
: new LatLng(curLatLng.getLatitude(), curLatLng.getLongitude(), 0)) // Sets the new camera position
.zoom(isBottomListSheetExpanded ?
11 // zoom level is fixed to 11 when bottom sheet is expanded
ZOOM_LEVEL
:mapboxMap.getCameraPosition().zoom) // Same zoom level
.build();
}else {
@ -456,7 +417,7 @@ public class NearbyMapFragment extends DaggerFragment {
curLatLng.getLongitude())
: new LatLng(curLatLng.getLatitude(), curLatLng.getLongitude(), 0)) // Sets the new camera position
.zoom(isBottomListSheetExpanded ?
11 // zoom level is fixed to 11 when bottom sheet is expanded
ZOOM_LEVEL
:mapboxMap.getCameraPosition().zoom) // Same zoom level
.build();
}
@ -494,9 +455,6 @@ public class NearbyMapFragment extends DaggerFragment {
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
updateMapCameraAccordingToBottomSheet(true);
} else {
updateMapCameraAccordingToBottomSheet(false);
}
}
@ -526,7 +484,7 @@ public class NearbyMapFragment extends DaggerFragment {
.attributionEnabled(false)
.camera(new CameraPosition.Builder()
.target(new LatLng(curLatLng.getLatitude(), curLatLng.getLongitude()))
.zoom(11)
.zoom(ZOOM_LEVEL)
.build());
if (!getParentFragment().getActivity().isFinishing()) {

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons.nearby;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.CoordinatorLayout;
@ -10,6 +11,7 @@ import android.support.design.widget.SwipeDismissBehavior;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
@ -19,6 +21,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.utils.ViewUtil;
@ -86,7 +89,7 @@ public class NearbyNoificationCardView extends CardView{
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// If you don't setVisibility after getting layout params, then you will se an empty space in place of nerabyNotificationCardView
if (((MainActivity)context).prefs.getBoolean("displayNearbyCardView", true)) {
if (((MainActivity)context).prefs.getBoolean("displayNearbyCardView", true) && this.cardViewVisibilityState == NearbyNoificationCardView.CardViewVisibilityState.READY) {
this.setVisibility(VISIBLE);
} else {
this.setVisibility(GONE);
@ -183,7 +186,7 @@ public class NearbyNoificationCardView extends CardView{
} else {
cardViewVisibilityState = CardViewVisibilityState.LOADING;
permissionRequestButton.setVisibility(GONE);
/*permissionRequestButton.setVisibility(GONE);
contentLayout.setVisibility(VISIBLE);
// Set visibility of elements in content layout once it become visible
progressBar.setVisibility(VISIBLE);
@ -191,10 +194,35 @@ public class NearbyNoificationCardView extends CardView{
notificationDistance.setVisibility(GONE);
notificationIcon.setVisibility(GONE);
permissionRequestButton.setVisibility(GONE);
permissionRequestButton.setVisibility(GONE);*/
this.setVisibility(GONE);
Handler nearbyNotificationHandler = new Handler();
Runnable nearbyNotificationRunnable = new Runnable() {
@Override
public void run() {
if (cardViewVisibilityState != NearbyNoificationCardView.CardViewVisibilityState.READY
&& cardViewVisibilityState != NearbyNoificationCardView.CardViewVisibilityState.ASK_PERMISSION
&& cardViewVisibilityState != NearbyNoificationCardView.CardViewVisibilityState.INVISIBLE) {
// If after 30 seconds, card view is not ready
errorOcured();
} else {
suceeded();
}
}
};
nearbyNotificationHandler.postDelayed(nearbyNotificationRunnable, 30000);
}
}
private void errorOcured() {
this.setVisibility(GONE);
}
private void suceeded() {
this.setVisibility(VISIBLE);
}
/**
* Pass place information to views.
* @param isClosestNearbyPlaceFound false if there are no close place
@ -202,6 +230,7 @@ public class NearbyNoificationCardView extends CardView{
*/
public void updateContent(boolean isClosestNearbyPlaceFound, Place place) {
Timber.d("Update nearby card notification content");
this.setVisibility(VISIBLE);
cardViewVisibilityState = CardViewVisibilityState.READY;
permissionRequestButton.setVisibility(GONE);
contentLayout.setVisibility(VISIBLE);
@ -269,6 +298,7 @@ public class NearbyNoificationCardView extends CardView{
READY,
INVISIBLE,
ASK_PERMISSION,
ERROR_OCURED
}
/**

View file

@ -0,0 +1,204 @@
package fr.free.nrw.commons.ui.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.EditText;
/**
* Custom edit text with a drawable click listener
* https://stackoverflow.com/questions/13135447/setting-onclicklistener-for-the-drawable-right-of-an-edittext
*/
@SuppressLint("AppCompatCustomView")
public class CustomEditText extends EditText {
private Drawable drawableRight;
private Drawable drawableLeft;
private Drawable drawableTop;
private Drawable drawableBottom;
int actionX, actionY;
private DrawableClickListener clickListener;
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
// this Contructure required when you are using this view in xml
}
public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
public void setCompoundDrawables(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
if (left != null) {
drawableLeft = left;
}
if (right != null) {
drawableRight = right;
}
if (top != null) {
drawableTop = top;
}
if (bottom != null) {
drawableBottom = bottom;
}
super.setCompoundDrawables(left, top, right, bottom);
}
/**
* Fires the appropriate drawable click listener on touching the icon
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
Rect bounds;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
actionX = (int) event.getX();
actionY = (int) event.getY();
if (drawableBottom != null
&& drawableBottom.getBounds().contains(actionX, actionY)) {
clickListener.onClick(DrawableClickListener.DrawablePosition.BOTTOM);
return super.onTouchEvent(event);
}
if (drawableTop != null
&& drawableTop.getBounds().contains(actionX, actionY)) {
clickListener.onClick(DrawableClickListener.DrawablePosition.TOP);
return super.onTouchEvent(event);
}
// this works for left since container shares 0,0 origin with bounds
if (drawableLeft != null) {
bounds = null;
bounds = drawableLeft.getBounds();
int x, y;
int extraTapArea = (int) (13 * getResources().getDisplayMetrics().density + 0.5);
x = actionX;
y = actionY;
if (!bounds.contains(actionX, actionY)) {
/** Gives the +20 area for tapping. */
x = (int) (actionX - extraTapArea);
y = (int) (actionY - extraTapArea);
if (x <= 0)
x = actionX;
if (y <= 0)
y = actionY;
/** Creates square from the smallest value */
if (x < y) {
y = x;
}
}
if (bounds.contains(x, y) && clickListener != null) {
clickListener
.onClick(DrawableClickListener.DrawablePosition.LEFT);
event.setAction(MotionEvent.ACTION_CANCEL);
return false;
}
}
if (drawableRight != null) {
bounds = null;
bounds = drawableRight.getBounds();
int x, y;
int extraTapArea = 13;
/**
* IF USER CLICKS JUST OUT SIDE THE RECTANGLE OF THE DRAWABLE
* THAN ADD X AND SUBTRACT THE Y WITH SOME VALUE SO THAT AFTER
* CALCULATING X AND Y CO-ORDINATE LIES INTO THE DRAWBABLE
* BOUND. - this process help to increase the tappable area of
* the rectangle.
*/
x = (int) (actionX + extraTapArea);
y = (int) (actionY - extraTapArea);
/**Since this is right drawable subtract the value of x from the width
* of view. so that width - tappedarea will result in x co-ordinate in drawable bound.
*/
x = getWidth() - x;
/*x can be negative if user taps at x co-ordinate just near the width.
* e.g views width = 300 and user taps 290. Then as per previous calculation
* 290 + 13 = 303. So subtract X from getWidth() will result in negative value.
* So to avoid this add the value previous added when x goes negative.
*/
if (x <= 0) {
x += extraTapArea;
}
/* If result after calculating for extra tappable area is negative.
* assign the original value so that after subtracting
* extratapping area value doesn't go into negative value.
*/
if (y <= 0)
y = actionY;
/**If drawble bounds contains the x and y points then move ahead.*/
if (bounds.contains(x, y) && clickListener != null) {
clickListener
.onClick(DrawableClickListener.DrawablePosition.RIGHT);
event.setAction(MotionEvent.ACTION_CANCEL);
return false;
}
return super.onTouchEvent(event);
}
}
return super.onTouchEvent(event);
}
@Override
protected void finalize() throws Throwable {
drawableRight = null;
drawableBottom = null;
drawableLeft = null;
drawableTop = null;
super.finalize();
}
/**
* Attaches the drawable click listener to the custom edit text
* @param listener
*/
public void setDrawableClickListener(DrawableClickListener listener) {
this.clickListener = listener;
}
/**
* Interface for drawable click listener
*/
public interface DrawableClickListener {
enum DrawablePosition {TOP, BOTTOM, LEFT, RIGHT}
void onClick(DrawablePosition target);
}
}

View file

@ -4,26 +4,22 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.AppCompatSpinner;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.EditText;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnTouch;
import butterknife.Optional;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.ui.widget.CustomEditText;
import fr.free.nrw.commons.utils.AbstractTextWatcher;
import fr.free.nrw.commons.utils.BiMap;
import fr.free.nrw.commons.utils.ViewUtil;
@ -31,8 +27,6 @@ import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.Subject;
import timber.log.Timber;
import static android.view.MotionEvent.ACTION_UP;
class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewHolder> {
private Title title;
@ -119,7 +113,7 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
AppCompatSpinner spinnerDescriptionLanguages;
@BindView(R.id.description_item_edit_text)
EditText descItemEditText;
CustomEditText descItemEditText;
private View view;
@ -154,6 +148,18 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
}
});
descItemEditText.setDrawableClickListener(target -> {
switch (target) {
case RIGHT:
if (getAdapterPosition() == 0) {
callback.showAlert(R.string.media_detail_title, R.string.title_info);
}
break;
default:
break;
}
});
} else {
Description description = descriptions.get(position - 1);
Timber.d("Description is " + description);
@ -168,9 +174,7 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
descItemEditText.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
descItemEditText.addTextChangedListener(new AbstractTextWatcher(descriptionText -> {
description.setDescriptionText(descriptionText);
}));
descItemEditText.addTextChangedListener(new AbstractTextWatcher(description::setDescriptionText));
descItemEditText.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus) {
ViewUtil.hideKeyboard(v);
@ -179,97 +183,77 @@ class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapter.ViewH
}
});
SpinnerLanguagesAdapter languagesAdapter = new SpinnerLanguagesAdapter(context,
R.layout.row_item_languages_spinner, selectedLanguages);
languagesAdapter.notifyDataSetChanged();
spinnerDescriptionLanguages.setAdapter(languagesAdapter);
if (description.getSelectedLanguageIndex() == -1) {
if (position == 1) {
int defaultLocaleIndex = languagesAdapter.getIndexOfUserDefaultLocale(context);
spinnerDescriptionLanguages.setSelection(defaultLocaleIndex);
} else {
spinnerDescriptionLanguages.setSelection(0);
descItemEditText.setDrawableClickListener(target -> {
switch (target) {
case RIGHT:
if (getAdapterPosition() == 1) {
callback.showAlert(R.string.media_detail_description,
R.string.description_info);
}
break;
default:
break;
}
});
initLanguageSpinner(position, description);
}
}
/**
* Extracted out the function to init the language spinner with different system supported languages
* @param position
* @param description
*/
private void initLanguageSpinner(int position, Description description) {
SpinnerLanguagesAdapter languagesAdapter = new SpinnerLanguagesAdapter(context,
R.layout.row_item_languages_spinner, selectedLanguages);
languagesAdapter.notifyDataSetChanged();
spinnerDescriptionLanguages.setAdapter(languagesAdapter);
if (description.getSelectedLanguageIndex() == -1) {
if (position == 1) {
int defaultLocaleIndex = languagesAdapter.getIndexOfUserDefaultLocale(context);
spinnerDescriptionLanguages.setSelection(defaultLocaleIndex);
} else {
spinnerDescriptionLanguages.setSelection(description.getSelectedLanguageIndex());
selectedLanguages.put(spinnerDescriptionLanguages, description.getLanguageCode());
spinnerDescriptionLanguages.setSelection(0);
}
} else {
spinnerDescriptionLanguages.setSelection(description.getSelectedLanguageIndex());
selectedLanguages.put(spinnerDescriptionLanguages, description.getLanguageCode());
}
//TODO do it the butterknife way
spinnerDescriptionLanguages.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
long l) {
description.setSelectedLanguageIndex(position);
String languageCode = ((SpinnerLanguagesAdapter) adapterView.getAdapter()).getLanguageCode(position);
description.setLanguageCode(languageCode);
selectedLanguages.remove(adapterView);
selectedLanguages.put(adapterView, languageCode);
((SpinnerLanguagesAdapter) adapterView.getAdapter()).selectedLangCode = languageCode;
}
//TODO do it the butterknife way
spinnerDescriptionLanguages.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
long l) {
description.setSelectedLanguageIndex(position);
String languageCode = ((SpinnerLanguagesAdapter) adapterView.getAdapter()).getLanguageCode(position);
description.setLanguageCode(languageCode);
selectedLanguages.remove(adapterView);
selectedLanguages.put(adapterView, languageCode);
((SpinnerLanguagesAdapter) adapterView.getAdapter()).selectedLangCode = languageCode;
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
}
});
}
@Optional
@OnTouch(R.id.description_item_edit_text)
boolean descriptionInfo(View view, MotionEvent motionEvent) {
//Title info is visible only for the title
if (getAdapterPosition() == 0) {
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
final int value = view.getRight() - descItemEditText
.getCompoundDrawables()[2]
.getBounds().width();
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
callback.showAlert(R.string.media_detail_title, R.string.title_info);
return true;
}
} else {
final int value = descItemEditText.getLeft() + descItemEditText
.getCompoundDrawables()[0]
.getBounds().width();
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() <= value) {
callback.showAlert(R.string.media_detail_title, R.string.title_info);
return true;
}
}
//Description info is visible only for the first description
} else if (getAdapterPosition() == 1) {
final int value;
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
value = view.getRight() - descItemEditText.getCompoundDrawables()[2].getBounds().width();
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
callback.showAlert(R.string.media_detail_description,
R.string.description_info);
return true;
}
} else {
value = descItemEditText.getLeft() + descItemEditText
.getCompoundDrawables()[0]
.getBounds().width();
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() <= value) {
callback.showAlert(R.string.media_detail_description,
R.string.description_info);
return true;
}
}
}
return false;
/**
* Extracted out the method to get the icon drawable
* @return
*/
private Drawable getInfoIcon() {
return context.getResources().getDrawable(R.drawable.mapbox_info_icon_default);
}
}
private Drawable getInfoIcon() {
return context.getResources()
.getDrawable(R.drawable.mapbox_info_icon_default);
}
public interface Callback {
void showAlert(int mediaDetailDescription, int descriptionInfo);
}

View file

@ -223,6 +223,9 @@ public class UploadActivity extends AuthenticatedActivity implements UploadView,
bottomCardSubtitle.setText(cardSubTitle);
categoryTitle.setText(cardTitle);
licenseTitle.setText(cardTitle);
if (currentStep == stepCount) {
dismissKeyboard();
}
if(isShowingItem) {
descriptionsAdapter.setItems(uploadItem.title, uploadItem.descriptions);
rvDescriptions.setAdapter(descriptionsAdapter);

View file

@ -10,6 +10,7 @@ import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import fr.free.nrw.commons.R;
import timber.log.Timber;
@ -160,6 +161,63 @@ public class DialogUtil {
showSafely(activity, dialog);
}
/*
Shows alert dialog with custom view
*/
public static void showAlertDialog(Activity activity,
String title,
String message,
final Runnable onPositiveBtnClick,
final Runnable onNegativeBtnClick,
View customView,
boolean cancelable) {
showAlertDialog(activity,
title,
message,
activity.getString(R.string.no),
activity.getString(R.string.yes),
onPositiveBtnClick,
onNegativeBtnClick,
customView,
false);
}
/*
Shows alert dialog with custom view
*/
private static void showAlertDialog(Activity activity,
String title,
String message,
String positiveButtonText,
String negativeButtonText,
final Runnable onPositiveBtnClick,
final Runnable onNegativeBtnClick,
View customView,
boolean cancelable) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(title);
builder.setMessage(message);
builder.setView(customView);
builder.setCancelable(cancelable);
builder.setPositiveButton(positiveButtonText, (dialogInterface, i) -> {
dialogInterface.dismiss();
if (onPositiveBtnClick != null) {
onPositiveBtnClick.run();
}
});
builder.setNegativeButton(negativeButtonText, (DialogInterface dialogInterface, int i) -> {
dialogInterface.dismiss();
if (onNegativeBtnClick != null) {
onNegativeBtnClick.run();
}
});
AlertDialog dialog = builder.create();
showSafely(activity, dialog);
}
public interface Callback {
void onPositiveButtonClicked();

View file

@ -0,0 +1,13 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<CheckBox
android:id="@+id/never_ask_again"
style="?android:attr/textAppearanceMedium"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/never_ask_again"
android:layout_margin="5dp" />
</FrameLayout>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@ -20,14 +19,16 @@
android:layout_height="wrap_content"
android:layout_weight="8">
<EditText
<fr.free.nrw.commons.ui.widget.CustomEditText
android:id="@+id/description_item_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/mapbox_info_icon_default"
android:drawableRight="@drawable/mapbox_info_icon_default"
android:hint="@string/share_description_hint"
android:imeOptions="flagNoExtractUi"
android:drawablePadding="4dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:drawableRight="@drawable/mapbox_info_icon_default"
android:inputType="textMultiLine" />
</android.support.design.widget.TextInputLayout>

View file

@ -7,7 +7,7 @@
android:layout_height="wrap_content"
tools:showIn="@layout/activity_upload">
<android.support.design.widget.TextInputEditText
<fr.free.nrw.commons.ui.widget.CustomEditText
android:id="@+id/description_item_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -44,7 +44,7 @@
<string name="provider_contributions">การอัปโหลดของฉัน</string>
<string name="menu_share">แชร์</string>
<string name="menu_open_in_browser">ดูในเบราว์เซอร์</string>
<string name="share_title_hint" fuzzy="true">ชื่อเรื่อง</string>
<string name="share_title_hint">ชื่อเรื่อง</string>
<string name="add_title_toast">กรุณาระบุชืิ่อเรื่องของไฟล์นี้</string>
<string name="share_description_hint">คำอธิบาย</string>
<string name="login_failed_network">ไม่สามารถเข้าสู่ระบบได้ - ความล้มเหลวของเครือข่าย</string>
@ -63,7 +63,7 @@
<string name="gps_disabled">GPS ถูกปิดใช้งานในอุปกรณ์ของคุณอยู่ คุณต้องการเปิดใช้งานหรือไม่?</string>
<string name="enable_gps">เปิดใช้งาน GPS</string>
<string name="contributions_subtitle_zero">ยังไม่มีการอัปโหลด</string>
<string name="contributions_subtitle" fuzzy="true">การอัปโหลด %1$d รายการ</string>
<string name="contributions_subtitle">การอัปโหลด %1$d รายการ</string>
<plurals name="starting_multiple_uploads">
<item quantity="one">กำลังเริ่มอัปโหลด %1$d รายการ</item>
<item quantity="other">กำลังเริ่มอัปโหลด %1$d รายการ</item>
@ -155,6 +155,7 @@
<string name="title_activity_nearby">สถานที่ใกล้เคียง</string>
<string name="no_nearby">ไม่พบสถานที่ใกล้เคียง</string>
<string name="warning">คำเตือน</string>
<string name="upload_image_problem_duplicate">ไฟล์นี้มีอยู่แล้วบนคอมมอนส์ คุณแน่ใจหรือว่าคุณต้องการดำเนินการต่อ?</string>
<string name="yes">ใช่</string>
<string name="no">ไม่</string>
<string name="media_detail_title">ชื่อเรื่อง</string>
@ -212,7 +213,6 @@
<string name="error_while_cache">ข้อผิดพลาดขณะแคชภาพ</string>
<string name="title_info">ชื่อเรื่องที่อธิบายลักษณะเฉพาะของไฟล์ ซึ่งจะใช้เป็นชื่อไฟล์ คุณอาจใช้ภาษาธรรมดาที่มีเว้นวรรคก็ได้ อย่ารวมนามสกุลไฟล์</string>
<string name="description_info">โปรดอธิบายสื่อดังกล่าวให้มากที่สุดเท่าที่จะได้: สื่อนี้ถูกถ่ายที่ไหน? สื่อนี้แสดงถึงอะไร? บริบทคืออะไร? โปรดอธิบายถึงวัตถุหรือบุคคล เปิดเผยข้อมูลที่ไม่อาจคาดเดาได้อย่างง่ายดาย เช่น เวลาที่ถ่าย หากเป็นภาพทิวทัศน์ หากสื่อแสดงถึงสิ่งที่ไม่ธรรมดา โปรดอธิบายว่าอะไรทำให้สื่อดังกล่าวไม่ธรรมดา</string>
<string name="upload_image_problem_duplicate">ไฟล์นี้มีอยู่แล้วบนคอมมอนส์ คุณแน่ใจหรือว่าคุณต้องการดำเนินการต่อ?</string>
<string name="upload_image_problem_dark">ภาพนี้มืดเกินไป คุณแน่ใจหรือว่าคุณต้องการอัปโหลดภาพนี้? วิกิมีเดียคอมมอนส์นั้นมีไว้สำหรับรูปภาพที่มีคุณค่าในทางสารานุกรมเท่านั้น</string>
<string name="upload_image_problem_blurry">ภาพนี้มัว คุณแน่ใจหรือว่าคุณต้องการอัปโหลดภาพนี้? วิกิมีเดียคอมมอนส์นั้นมีไว้สำหรับรูปภาพที่มีคุณค่าในทางสารานุกรมเท่านั้น</string>
<string name="give_permission">ให้สิทธิ์</string>

View file

@ -185,6 +185,7 @@
<string name="title_activity_nearby">Місця поблизу</string>
<string name="no_nearby">Не знайдено місць поблизу</string>
<string name="warning">Попередження</string>
<string name="file_exists">Цей файл вже існує на Вікісховищі. Ви впевнені, що хочете продовжити?</string>
<string name="yes">Так</string>
<string name="no">Ні</string>
<string name="media_detail_title">Назва</string>

View file

@ -165,6 +165,7 @@
<string name="title_activity_nearby">Nơi Lân cận</string>
<string name="no_nearby">Không tìm thấy nơi lân cận</string>
<string name="warning">Cảnh báo</string>
<string name="upload_image_problem_duplicate">Tập tin này đã tồn tại ở Commons. Bạn có chắc chắn muốn tiếp tục?</string>
<string name="yes"></string>
<string name="no">Không</string>
<string name="media_detail_title">Tiêu đề</string>

View file

@ -179,6 +179,7 @@
<string name="title_activity_nearby">附近地点</string>
<string name="no_nearby">找不到附近地点</string>
<string name="warning">警告</string>
<string name="upload_image_problem_duplicate">此文件在共享资源已存在。您确定要继续么?</string>
<string name="yes"></string>
<string name="no"></string>
<string name="media_detail_title">标题</string>

View file

@ -425,5 +425,11 @@ Upload your first media by touching the camera or gallery icon above.</string>
<string name="no_go_back">No, Go Back</string>
<string name="upload_flow_all_images_in_set">(For all images in set)</string>
<string name="nearby_card_permission_title">Permission Request</string>
<string name="nearby_card_permission_explanation">Would you like us to use your current location to display the nearest place that needs pictures?</string>
<string name="unable_to_display_nearest_place">Unable to display nearest place that needs pictures without location permissions</string>
<string name="never_ask_again">Never ask this again</string>
<string name="display_location_permission_title">Display location permission</string>
<string name="display_location_permission_explanation">Ask for location permission when needed for nearby notification card view feature.</string>
</resources>

View file

@ -42,6 +42,12 @@
android:defaultValue="true"
android:summary="@string/display_nearby_notification_summary" />
<fr.free.nrw.commons.ui.LongTitlePreferences.LongTitleSwitchPreference
android:key="displayLocationPermissionForCardView"
android:title="@string/display_location_permission_title"
android:defaultValue="true"
android:summary="@string/display_location_permission_explanation" />
</fr.free.nrw.commons.ui.LongTitlePreferences.LongTitlePreferenceCategory>
<!-- The key 'allowGps' was used before and has since been removed based on the discussion at #1599.