diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java index 027989b26..bc0360c52 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java @@ -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; @@ -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(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java index c46ab1956..863020fd8 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java @@ -1,8 +1,10 @@ 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; @@ -10,13 +12,14 @@ 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; @@ -24,56 +27,34 @@ public class LanguagesAdapter extends RecyclerView.Adapter { private List 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 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) { @@ -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); } } @@ -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); } } @@ -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 getItems() { @@ -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(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LanguageItemCallback.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LanguageItemCallback.java index df25e3025..aebb0aa9d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LanguageItemCallback.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LanguageItemCallback.java @@ -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); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/FadingFrameLayout.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/FadingFrameLayout.java new file mode 100644 index 000000000..040bfe515 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/FadingFrameLayout.java @@ -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); + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java index 98ef21b75..14abef0ef 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java @@ -41,13 +41,12 @@ private void initialize(Context aContext) { LayoutInflater inflater = LayoutInflater.from(aContext); // Preferred languages adapter - mPreferredAdapter = new LanguagesAdapter(mLanguageItemCallback); - mPreferredAdapter.setPreferred(LocaleUtils.getCurrentLocaleLanguage()); + mPreferredAdapter = new LanguagesAdapter(mLanguageItemCallback, true); mPreferredAdapter.setLanguageList(LocaleUtils.getPreferredLanguages(getContext())); // Available languages adapter - mAvailableAdapter = new LanguagesAdapter(mLanguageItemCallback); - mAvailableAdapter.setLanguageList(LocaleUtils.getAvailableLanguages(getContext())); + mAvailableAdapter = new LanguagesAdapter(mLanguageItemCallback, false); + mAvailableAdapter.setLanguageList(LocaleUtils.getAvailableLanguages()); // Inflate this data binding layout mBinding = DataBindingUtil.inflate(inflater, R.layout.options_language_content, this, true); @@ -84,9 +83,17 @@ public Point getDimensions() { private LanguageItemCallback mLanguageItemCallback = new LanguageItemCallback() { @Override - public void onClick(View view, Language language) { - mPreferredAdapter.onCLick(language); - mAvailableAdapter.onCLick(language); + public void onAdd(View view, Language language) { + mPreferredAdapter.onAdd(language); + mAvailableAdapter.onAdd(language); + + saveCurrentLanguages(); + } + + @Override + public void onRemove(View view, Language language) { + mPreferredAdapter.onRemove(language); + mAvailableAdapter.onRemove(language); saveCurrentLanguages(); } @@ -116,7 +123,7 @@ private void saveCurrentLanguages() { private void refreshLanguages() { ThreadUtils.postToUiThread(() -> { mPreferredAdapter.setLanguageList(LocaleUtils.getPreferredLanguages(getContext())); - mAvailableAdapter.setLanguageList(LocaleUtils.getAvailableLanguages(getContext())); + mAvailableAdapter.setLanguageList(LocaleUtils.getAvailableLanguages()); }); } @@ -124,6 +131,7 @@ private void refreshLanguages() { protected boolean reset() { SettingsStore.getInstance(getContext()).setContentLocales(Arrays.asList(LocaleUtils.getSystemLocale())); SessionStore.get().setLocales(Arrays.asList(LocaleUtils.getSystemLocale())); + LocaleUtils.resetLanguages(); refreshLanguages(); return false; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java index 83c120f73..6621eb90f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java @@ -48,24 +48,32 @@ public static String getCurrentLocale() { return Locale.getDefault().toLanguageTag(); } - public static HashMap getAllLanguages() { + private static HashMap getAllLanguages() { if (mLanguagesCache != null) return mLanguagesCache; + String currentLocale = getCurrentLocale(); Locale[] locales = Locale.getAvailableLocales(); mLanguagesCache = new HashMap<>(); for(Locale temp : locales) { String languageId = temp.toLanguageTag(); String displayName = temp.getDisplayName().substring(0, 1).toUpperCase() + temp.getDisplayName().substring(1); - if (languageId.equals(getCurrentLocale())) - displayName = "(Default) " + displayName; - Log.d("Locale", " [" + languageId + "]"); - mLanguagesCache.put(languageId, new Language(languageId, displayName + " [" + languageId + "]")); + Language locale = new Language(languageId, displayName + " [" + languageId + "]"); + if (languageId.equals(currentLocale)) + locale.setDefault(true); + mLanguagesCache.put(languageId, locale); } return mLanguagesCache; } + public static void resetLanguages() { + String currentLocale = getCurrentLocale(); + mLanguagesCache.values().stream().forEach((language) -> { + language.setPreferred(language.getId().equals(currentLocale)); + }); + } + public static Language getCurrentLocaleLanguage() { return mLanguagesCache.get(getCurrentLocale()); } @@ -83,8 +91,11 @@ public static List getPreferredLanguages(@NonNull Context aContext) { HashMap languages = getAllLanguages(); List savedLanguages = SettingsStore.getInstance(aContext).getContentLocales(); List preferredLanguages = new ArrayList<>(); - for (String language : savedLanguages) - preferredLanguages.add(languages.get(language)); + for (String language : savedLanguages) { + Language lang = languages.get(language); + lang.setPreferred(true); + preferredLanguages.add(lang); + } if (!savedLanguages.stream().anyMatch(str -> str.trim().equals(getCurrentLocale()))) preferredLanguages.add(getCurrentLocaleLanguage()); @@ -92,13 +103,9 @@ public static List getPreferredLanguages(@NonNull Context aContext) { return preferredLanguages; } - public static List getAvailableLanguages(@NonNull Context aContext) { + public static List getAvailableLanguages() { HashMap languages = getAllLanguages(); - List savedLanguages = SettingsStore.getInstance(aContext).getContentLocales(); List availableLanguages = languages.values().stream() - .filter((language) -> - !(language.getId().equals(getCurrentLocale()) || - savedLanguages.stream().anyMatch(str -> str.trim().equals(language.getId())))) .sorted((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())) .collect(Collectors.toList()); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java index 254d9e33a..595641f57 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java @@ -15,6 +15,7 @@ import androidx.annotation.NonNull; import androidx.core.text.HtmlCompat; +import org.jetbrains.annotations.NotNull; import org.mozilla.vrbrowser.ui.widgets.UIWidget; public class ViewUtils { @@ -99,4 +100,18 @@ public static boolean isChildrenOf(@NonNull View parent, @NonNull View view) { return parent.findViewById(view.getId()) != null; } + public static boolean isInsideView(@NotNull View view, int rx, int ry) { + int[] location = new int[2]; + view.getLocationOnScreen(location); + int x = location[0]; + int y = location[1]; + int w = view.getWidth(); + int h = view.getHeight(); + + if (rx < x || rx > x + w || ry < y || ry > y + h) { + return false; + } + return true; + } + } diff --git a/app/src/main/res/drawable/ic_icon_empty.xml b/app/src/main/res/drawable/ic_icon_empty.xml new file mode 100644 index 000000000..a853dcfd8 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_empty.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_icon_language_add.xml b/app/src/main/res/drawable/ic_icon_language_add.xml index 3a8aebf1d..6856d9dc5 100644 --- a/app/src/main/res/drawable/ic_icon_language_add.xml +++ b/app/src/main/res/drawable/ic_icon_language_add.xml @@ -1,9 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_icon_language_delete.xml b/app/src/main/res/drawable/ic_icon_language_delete.xml index 5c4538fa1..ee6b06795 100644 --- a/app/src/main/res/drawable/ic_icon_language_delete.xml +++ b/app/src/main/res/drawable/ic_icon_language_delete.xml @@ -1,12 +1,5 @@ - - - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_icon_language_move_down.xml b/app/src/main/res/drawable/ic_icon_language_move_down.xml new file mode 100644 index 000000000..7b8832c12 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_language_move_down.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_icon_language_move_up.xml b/app/src/main/res/drawable/ic_icon_language_move_up.xml new file mode 100644 index 000000000..07bdc2552 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_language_move_up.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/language_item.xml b/app/src/main/res/layout/language_item.xml index 87bbd2404..060d29411 100644 --- a/app/src/main/res/layout/language_item.xml +++ b/app/src/main/res/layout/language_item.xml @@ -17,27 +17,20 @@ name="isLast" type="boolean" /> - - - - - + + android:requiresFadingEdge="horizontal" + android:fadingEdgeLength="@dimen/language_row_fade_size"> + + + diff --git a/app/src/main/res/layout/options_controller.xml b/app/src/main/res/layout/options_controller.xml index e1ab45e65..219a5b4a5 100644 --- a/app/src/main/res/layout/options_controller.xml +++ b/app/src/main/res/layout/options_controller.xml @@ -8,9 +8,7 @@ android:layout_height="match_parent" android:background="@drawable/dialog_background" android:paddingStart="30dp" - android:paddingTop="10dp" - android:paddingEnd="30dp" - android:paddingBottom="10dp"> + android:paddingEnd="30dp"> + android:paddingEnd="30dp"> + android:paddingEnd="30dp"> + android:paddingEnd="30dp"> diff --git a/app/src/main/res/layout/options_header.xml b/app/src/main/res/layout/options_header.xml index 1f9680446..048386290 100644 --- a/app/src/main/res/layout/options_header.xml +++ b/app/src/main/res/layout/options_header.xml @@ -34,6 +34,7 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" + android:paddingTop="10dp" android:paddingBottom="10dp"> diff --git a/app/src/main/res/layout/options_language.xml b/app/src/main/res/layout/options_language.xml index ba68162be..03fb0366b 100644 --- a/app/src/main/res/layout/options_language.xml +++ b/app/src/main/res/layout/options_language.xml @@ -23,9 +23,7 @@ android:layout_height="match_parent" android:background="@drawable/dialog_background" android:paddingStart="30dp" - android:paddingTop="10dp" - android:paddingEnd="30dp" - android:paddingBottom="10dp"> + android:paddingEnd="30dp"> + android:paddingEnd="30dp"> + android:paddingEnd="30dp"> + android:paddingEnd="30dp"> + android:paddingEnd="30dp"> 22dp 20dp + 120dp 360dp diff --git a/app/src/main/res/values/non_L10n.xml b/app/src/main/res/values/non_L10n.xml index c1c3f8a47..4e4e69f4e 100644 --- a/app/src/main/res/values/non_L10n.xml +++ b/app/src/main/res/values/non_L10n.xml @@ -54,6 +54,8 @@ view position view_id + up_id + down_id https://support.mozilla.org/kb/crash-reporting-firefox-reality org.mozilla.vrbrowser.CRASH_RECEIVER_PERMISSION x diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5409c2f4d..8dbc30d52 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,7 +169,7 @@ - Web pages are sometimes offered in more than one language. Choose the languages you want to dispaly in order of preference to the left side. + Web pages are sometimes offered in more than one language. Choose the languages you want to display in order of preference to the left side. @@ -867,4 +867,7 @@ Don’t Allow + + + Default