diff --git a/app/src/main/java/fr/free/nrw/commons/ui/widget/CustomEditText.java b/app/src/main/java/fr/free/nrw/commons/ui/widget/CustomEditText.java new file mode 100644 index 000000000..632eef081 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/ui/widget/CustomEditText.java @@ -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); + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java b/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java index 8c2432ee1..5c5fe8877 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java @@ -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 { private Title title; @@ -119,7 +113,7 @@ class DescriptionsAdapter extends RecyclerView.Adapter { + 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 { - description.setDescriptionText(descriptionText); - })); + descItemEditText.addTextChangedListener(new AbstractTextWatcher(description::setDescriptionText)); descItemEditText.setOnFocusChangeListener((v, hasFocus) -> { if (!hasFocus) { ViewUtil.hideKeyboard(v); @@ -179,95 +183,75 @@ class DescriptionsAdapter extends RecyclerView.Adapter 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) { + 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); } } - @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 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); - private Drawable getInfoIcon() { - return context.getResources() - .getDrawable(R.drawable.mapbox_info_icon_default); + if (description.getSelectedLanguageIndex() == -1) { + if (position == 1) { + int defaultLocaleIndex = languagesAdapter.getIndexOfUserDefaultLocale(context); + spinnerDescriptionLanguages.setSelection(defaultLocaleIndex); + } else { + 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; + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + } + + /** + * Extracted out the method to get the icon drawable + * @return + */ + private Drawable getInfoIcon() { + return context.getResources().getDrawable(R.drawable.mapbox_info_icon_default); + } } public interface Callback { diff --git a/app/src/main/res/layout/row_item_description.xml b/app/src/main/res/layout/row_item_description.xml index f626abcfa..3282a9a2f 100644 --- a/app/src/main/res/layout/row_item_description.xml +++ b/app/src/main/res/layout/row_item_description.xml @@ -1,6 +1,5 @@ - diff --git a/app/src/main/res/layout/row_item_title.xml b/app/src/main/res/layout/row_item_title.xml index 3a2262067..b67a99711 100644 --- a/app/src/main/res/layout/row_item_title.xml +++ b/app/src/main/res/layout/row_item_title.xml @@ -7,7 +7,7 @@ android:layout_height="wrap_content" tools:showIn="@layout/activity_upload"> -