Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Preferred Languages fixes (#1551)
Browse files Browse the repository at this point in the history
* Preferred Languages fixes

* Fix last item not being refreshed after and addition
  • Loading branch information
keianhzo authored and bluemarvin committed Aug 12, 2019
1 parent 2ccdfe4 commit 0bf3cb7
Show file tree
Hide file tree
Showing 27 changed files with 331 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ public class Language {
public Language(String id, String name) {
this.id = id;
this.name = name;
this.isPreferred = false;
this.isDefault = false;
}

private String name;
private String id;
private boolean isPreferred;
private boolean isDefault;

public String getId() {
return this.id;
Expand All @@ -18,6 +22,22 @@ public String getName() {
return this.name;
}

public void setPreferred(boolean isPreferred) {
this.isPreferred = isPreferred;
}

public boolean isPreferred() {
return isPreferred;
}

public void setDefault(boolean isDefault) {
this.isDefault = isDefault;
}

public boolean isDefault() {
return isDefault;
}

@Override
public int hashCode() {
return id.hashCode();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,79 +1,60 @@
package org.mozilla.vrbrowser.ui.adapters;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.vrbrowser.R;
import org.mozilla.vrbrowser.databinding.LanguageItemBinding;
import org.mozilla.vrbrowser.ui.callbacks.LanguageItemCallback;
import org.mozilla.vrbrowser.utils.ViewUtils;

import java.util.Collections;
import java.util.List;

public class LanguagesAdapter extends RecyclerView.Adapter<LanguagesAdapter.LanguageViewHolder> {

private List<Language> mLanguagesList;
private Language mDefaultLanguage;
private boolean mIsPreferred;

@Nullable
private final LanguageItemCallback mLanguageItemCallback;

public LanguagesAdapter(@Nullable LanguageItemCallback clickCallback) {
public LanguagesAdapter(@Nullable LanguageItemCallback clickCallback, boolean isPreferred) {
mLanguageItemCallback = clickCallback;
mIsPreferred = isPreferred;

setHasStableIds(true);
}

public void setLanguageList(final List<Language> languagesList) {
if (mLanguagesList == null) {
mLanguagesList = languagesList;
notifyItemRangeInserted(0, languagesList.size());

} else {
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return mLanguagesList.size();
}

@Override
public int getNewListSize() {
return languagesList.size();
}

@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mLanguagesList.get(oldItemPosition).getId().equals(languagesList.get(newItemPosition).getId());
}

@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
Language newBookmark = languagesList.get(newItemPosition);
Language oldBookmark = mLanguagesList.get(oldItemPosition);
return newBookmark.getId().equals(oldBookmark.getId());
}
});

mLanguagesList = languagesList;
result.dispatchUpdatesTo(this);
}
// Ideally we would use the DiffTools here as we do in the Bookmarks adapter but as we are
// using elements from the shared local languages list from LocaleUtils and get get the
// preferred and available Language items from the global list, the diff is always void.
// We save some memory though.
mLanguagesList = languagesList;
notifyItemRangeInserted(0, languagesList.size());
notifyDataSetChanged();
}

public void addItem(Language language) {
mLanguagesList.add(0, language);
notifyDataSetChanged();
notifyItemInserted(mLanguagesList.indexOf(language));
// This shouldn't be necessary but for some reason the last list item is not refreshed
// if we don't do a full refresh. Might be another RecyclerView bug.
ThreadUtils.postToUiThread(() -> notifyDataSetChanged());
}

public void addItemAlphabetical(Language language) {
Expand All @@ -85,14 +66,14 @@ public void addItemAlphabetical(Language language) {
}

mLanguagesList.add(index, language);
notifyDataSetChanged();
notifyItemInserted(index);
}

public void removeItem(Language language) {
int position = mLanguagesList.indexOf(language);
if (position >= 0) {
mLanguagesList.remove(position);
notifyDataSetChanged();
notifyItemRemoved(position);
}
}

Expand All @@ -101,7 +82,7 @@ public void moveItemUp(View view, Language language) {
if (position > 0) {
Collections.swap(mLanguagesList, position, position - 1);
view.startAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.button_click_scale));
notifyDataSetChanged();
notifyItemRangeChanged(position - 1, 2);
}
}

Expand All @@ -110,25 +91,30 @@ public void moveItemDown(View view, Language language) {
if (position < mLanguagesList.size()-1) {
Collections.swap(mLanguagesList, position, position + 1);
view.startAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.button_click_scale));
notifyDataSetChanged();
notifyItemRangeChanged(position, 2);
}
}

public void onCLick(Language language) {
if (mLanguagesList.indexOf(language) < 0) {
if (mIsPreferred)
addItem(language);
else
addItemAlphabetical(language);
public void onAdd(Language language) {
if (mIsPreferred) {
addItem(language);

} else {
removeItem(language);
language.setPreferred(true);
}

notifyDataSetChanged();
}

public void setPreferred(Language language) {
mIsPreferred = true;
mDefaultLanguage = language;
public void onRemove(Language language) {
if (mIsPreferred) {
removeItem(language);

} else {
language.setPreferred(false);
}

notifyDataSetChanged();
}

public List<Language> getItems() {
Expand All @@ -140,19 +126,45 @@ public LanguageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int view
LanguageItemBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(parent.getContext()), R.layout.language_item,
parent, false);
binding.setCallback(mLanguageItemCallback);
binding.setIsPreferred(mIsPreferred);

return new LanguageViewHolder(binding);
}

@SuppressLint("ClickableViewAccessibility")
@Override
public void onBindViewHolder(@NonNull LanguageViewHolder holder, int position) {
Language language = mLanguagesList.get(position);
holder.binding.setLanguage(language);
holder.binding.setIsFirst(position == 0);
holder.binding.setIsLast(position == mLanguagesList.size()-1);
holder.binding.setIsDefault(mIsPreferred ? language.equals(mDefaultLanguage) : false);
// We can't use duplicateParentState to change the state drawables of child views if they
// handling click events as when they get focus they stop propagating their own state changes
// so we use duplicateParentState but we handle the events here for add/remove/moveup/movedown.
holder.binding.layout.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
if (holder.binding.up.getVisibility() == View.VISIBLE &&
ViewUtils.isInsideView(holder.binding.up, (int)event.getRawX(), (int)event.getRawY())) {
mLanguageItemCallback.onMoveUp(holder.binding.up, language);

} else if (holder.binding.down.getVisibility() == View.VISIBLE &&
ViewUtils.isInsideView(holder.binding.down, (int)event.getRawX(), (int)event.getRawY())) {
mLanguageItemCallback.onMoveDown(holder.binding.down, language);

} else if (holder.binding.add.getVisibility() == View.VISIBLE &&
ViewUtils.isInsideView(holder.binding.add, (int)event.getRawX(), (int)event.getRawY())) {
if (!language.isDefault() && !language.isPreferred())
mLanguageItemCallback.onAdd(holder.binding.add, language);

} else if (holder.binding.delete.getVisibility() == View.VISIBLE &&
ViewUtils.isInsideView(holder.binding.delete, (int)event.getRawX(), (int)event.getRawY())) {
if (!language.isDefault() && language.isPreferred())
mLanguageItemCallback.onRemove(holder.binding.delete, language);
}

}
return false;
});
holder.binding.executePendingBindings();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import org.mozilla.vrbrowser.ui.adapters.Language;

public interface LanguageItemCallback {
void onClick(View view, Language language);
void onAdd(View view, Language language);
void onRemove(View view, Language language);
void onMoveUp(View view, Language language);
void onMoveDown(View view, Language language);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.mozilla.vrbrowser.ui.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.widget.FrameLayout;

public class FadingFrameLayout extends FrameLayout {

private static final int[] FADE_COLORS_REVERSE = new int[]{Color.BLACK, Color.TRANSPARENT};

private Paint mPaint;
private Rect mRect;
private boolean mDirty;

public FadingFrameLayout(Context context) {
super(context);
init();
}

public FadingFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public FadingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init() {
PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setXfermode(mode);

mRect = new Rect();
}

@Override
public void setPadding(int left, int top, int right, int bottom) {
if (getPaddingRight() != right) {
mDirty = true;
}
super.setPadding(left, top, right, bottom);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w != oldw) {
mDirty = true;
}
}

@Override
protected void dispatchDraw(Canvas canvas) {
int newWidth = getWidth(), newHeight = getHeight();
if (getVisibility() == GONE || newWidth == 0 || newHeight == 0) {
super.dispatchDraw(canvas);
return;
}

if (mDirty) {
int actualWidth = getWidth() - getPaddingLeft() - getPaddingRight();
int size = Math.min(getHorizontalFadingEdgeLength(), actualWidth);
int l = getPaddingLeft() + actualWidth - size;
int t = getPaddingTop();
int r = l + size;
int b = getHeight() - getPaddingBottom();
mRect.set(l, t, r, b);
LinearGradient gradient = new LinearGradient(l, t, r, t, FADE_COLORS_REVERSE, null, Shader.TileMode.CLAMP);
mPaint.setShader(gradient);
}

int count = canvas.saveLayer(0.0f, 0.0f, (float) getWidth(), (float) getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);

if (isHorizontalFadingEdgeEnabled() && getHorizontalFadingEdgeLength() > 0) {
canvas.drawRect(mRect, mPaint);
}
canvas.restoreToCount(count);
}

}
Loading

0 comments on commit 0bf3cb7

Please sign in to comment.