From 24b93e9481058ca2b5e0e5a4e3ee7f5fd47988e0 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Thu, 1 Aug 2019 20:35:57 +0200 Subject: [PATCH 1/2] Add support for display/voice/content languages --- .../mozilla/vrbrowser/VRBrowserActivity.java | 8 + .../vrbrowser/VRBrowserApplication.java | 15 ++ .../vrbrowser/browser/SessionStore.java | 6 + .../vrbrowser/browser/SettingsStore.java | 53 ++++- .../ui/adapters/BindingAdapters.java | 20 ++ .../vrbrowser/ui/adapters/Language.java | 25 +++ .../ui/adapters/LanguagesAdapter.java | 203 ++++++++++++++++++ .../ui/callbacks/BookmarkClickCallback.java | 2 - .../ui/callbacks/LanguageItemCallback.java | 11 + .../ui/views/settings/ButtonSetting.java | 14 +- .../ui/views/settings/SwitchSetting.java | 2 +- .../widgets/dialogs/RestartDialogWidget.java | 28 ++- .../ui/widgets/dialogs/UIDialog.java | 1 + .../ui/widgets/dialogs/VoiceSearchWidget.java | 5 +- .../settings/ContentLanguageOptionsView.java | 139 ++++++++++++ .../settings/DisplayLanguageOptionsView.java | 95 ++++++++ .../widgets/settings/LanguageOptionsView.java | 123 +++++++++++ .../widgets/settings/PrivacyOptionsView.java | 2 +- .../ui/widgets/settings/SettingsFooter.java | 68 ++++++ .../ui/widgets/settings/SettingsHeader.java | 82 +++++++ .../ui/widgets/settings/SettingsView.java | 5 + .../ui/widgets/settings/SettingsWidget.java | 24 ++- .../VoiceSearchLanguageOptionsView.java | 56 +++-- .../mozilla/vrbrowser/utils/LocaleUtils.java | 196 ++++++++++++++++- .../mozilla/vrbrowser/utils/SystemUtils.java | 32 +++ .../mozilla/vrbrowser/utils/ViewUtils.java | 13 +- app/src/main/AndroidManifest.xml | 2 +- app/src/main/res/anim/button_click_scale.xml | 12 ++ app/src/main/res/drawable/ic_icon_add.xml | 9 + app/src/main/res/drawable/ic_icon_check.xml | 12 ++ app/src/main/res/drawable/ic_icon_delete.xml | 12 ++ .../res/drawable/ic_icon_language_add.xml | 9 + .../res/drawable/ic_icon_language_delete.xml | 12 ++ .../main/res/drawable/ic_icon_move_down.xml | 9 + app/src/main/res/drawable/ic_icon_move_up.xml | 9 + .../res/drawable/ic_icon_settings_help.xml | 15 ++ .../ic_keyboard_arrow_down_black_24px.xml | 12 +- .../settings_list_row_background_color.xml | 25 +++ app/src/main/res/layout/language_item.xml | 135 ++++++++++++ app/src/main/res/layout/options_footer.xml | 32 +++ app/src/main/res/layout/options_header.xml | 95 ++++++++ app/src/main/res/layout/options_language.xml | 126 ++++++----- .../res/layout/options_language_content.xml | 106 +++++++++ .../res/layout/options_language_display.xml | 70 ++++++ .../res/layout/options_language_voice.xml | 69 ++++++ app/src/main/res/layout/options_privacy.xml | 2 +- app/src/main/res/layout/setting_button.xml | 2 +- app/src/main/res/layout/setting_edit.xml | 4 +- .../main/res/layout/setting_radio_group.xml | 2 +- app/src/main/res/layout/setting_switch.xml | 2 +- app/src/main/res/values/attrs.xml | 15 ++ app/src/main/res/values/dimen.xml | 11 + app/src/main/res/values/non_L10n.xml | 8 +- app/src/main/res/values/options_values.xml | 31 ++- app/src/main/res/values/strings.xml | 69 ++++++ app/src/main/res/values/styles.xml | 45 +++- 56 files changed, 2034 insertions(+), 156 deletions(-) create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LanguageItemCallback.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/LanguageOptionsView.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java create mode 100644 app/src/main/res/anim/button_click_scale.xml create mode 100644 app/src/main/res/drawable/ic_icon_add.xml create mode 100644 app/src/main/res/drawable/ic_icon_check.xml create mode 100644 app/src/main/res/drawable/ic_icon_delete.xml create mode 100644 app/src/main/res/drawable/ic_icon_language_add.xml create mode 100644 app/src/main/res/drawable/ic_icon_language_delete.xml create mode 100644 app/src/main/res/drawable/ic_icon_move_down.xml create mode 100644 app/src/main/res/drawable/ic_icon_move_up.xml create mode 100644 app/src/main/res/drawable/ic_icon_settings_help.xml create mode 100644 app/src/main/res/drawable/settings_list_row_background_color.xml create mode 100644 app/src/main/res/layout/language_item.xml create mode 100644 app/src/main/res/layout/options_footer.xml create mode 100644 app/src/main/res/layout/options_header.xml create mode 100644 app/src/main/res/layout/options_language_content.xml create mode 100644 app/src/main/res/layout/options_language_display.xml create mode 100644 app/src/main/res/layout/options_language_voice.xml diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index f1f6e5611..17e3764af 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -63,6 +63,7 @@ import org.mozilla.vrbrowser.ui.widgets.WindowWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.CrashDialogWidget; import org.mozilla.vrbrowser.utils.ConnectivityReceiver; +import org.mozilla.vrbrowser.utils.LocaleUtils; import org.mozilla.vrbrowser.utils.ServoUtils; import java.io.IOException; @@ -173,6 +174,11 @@ public void onGlobalFocusChanged(View oldFocus, View newFocus) { } }; + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(LocaleUtils.setLocale(base)); + } + @Override protected void onCreate(Bundle savedInstanceState) { // Fix for infinite restart on startup crashes. @@ -189,6 +195,8 @@ protected void onCreate(Bundle savedInstanceState) { // Set a global exception handler as soon as possible GlobalExceptionHandler.register(this.getApplicationContext()); + LocaleUtils.init(this); + if (DeviceType.isOculusBuild()) { workaroundGeckoSigAction(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserApplication.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserApplication.java index e99d8b0b2..cdb1648a6 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserApplication.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserApplication.java @@ -6,10 +6,13 @@ package org.mozilla.vrbrowser; import android.app.Application; +import android.content.Context; +import android.content.res.Configuration; import org.mozilla.vrbrowser.browser.Places; import org.mozilla.vrbrowser.db.AppDatabase; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; +import org.mozilla.vrbrowser.utils.LocaleUtils; public class VRBrowserApplication extends Application { @@ -26,6 +29,18 @@ public void onCreate() { TelemetryWrapper.init(this); } + @Override + protected void attachBaseContext(Context base) { + LocaleUtils.saveSystemLocale(); + super.attachBaseContext(LocaleUtils.setLocale(base)); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + LocaleUtils.setLocale(this); + } + public AppDatabase getDatabase() { return AppDatabase.getInstance(this, mAppExecutors); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java index 49912c3c4..05e9acfd9 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java @@ -1000,6 +1000,12 @@ public boolean getAutoplayEnabled() { return false; } + public void setLocales(List locales) { + if (mRuntime != null) { + mRuntime.getSettings().setLocales(locales.stream().toArray(String[]::new)); + } + } + // NavigationDelegate @Override diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java index 6393701f8..796982a12 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java @@ -6,9 +6,10 @@ import android.os.StrictMode; import android.preference.PreferenceManager; +import org.json.JSONArray; +import org.json.JSONException; import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.telemetry.TelemetryHolder; -import org.mozilla.vrbrowser.BuildConfig; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.utils.DeviceType; @@ -17,6 +18,8 @@ import androidx.annotation.NonNull; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import static org.mozilla.vrbrowser.utils.ServoUtils.isServoAvailable; @@ -409,21 +412,63 @@ public void setAudioEnabled(boolean isEnabled) { editor.commit(); } - public String getVoiceSearchLanguage() { + public String getVoiceSearchLocale() { String language = mPrefs.getString( mContext.getString(R.string.settings_key_voice_search_language), null); if (language == null) { - return LocaleUtils.getDefaultVoiceSearchLanguage(mContext); + return LocaleUtils.getDefaultVoiceSearchLocale(mContext); } return language; } - public void setVoiceSearchLanguage(String language) { + public void setVoiceSearchLocale(String language) { SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(mContext.getString(R.string.settings_key_voice_search_language), language); editor.commit(); } + public String getDisplayLocale() { + String language = mPrefs.getString( + mContext.getString(R.string.settings_key_display_language), null); + if (language == null) { + return LocaleUtils.getDefaultDisplayLocale(mContext); + } + return language; + } + + public void setDisplayLocale(String language) { + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putString(mContext.getString(R.string.settings_key_display_language), language); + editor.commit(); + } + + public ArrayList getContentLocales() { + ArrayList result = new ArrayList<>(); + + String json = mPrefs.getString( + mContext.getString(R.string.settings_key_content_languages), + new JSONArray().toString()); + + try { + JSONArray jsonArray = new JSONArray(json); + for (int i=0; i languages) { + JSONArray json = new JSONArray(languages); + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putString(mContext.getString(R.string.settings_key_content_languages), json.toString()); + editor.commit(); + } + public float getCylinderDensity() { return mPrefs.getFloat(mContext.getString(R.string.settings_key_cylinder_density), 0); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java index 199d71803..c2b27870b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java @@ -1,13 +1,33 @@ package org.mozilla.vrbrowser.ui.adapters; +import android.graphics.Typeface; import android.view.View; +import android.widget.TextView; import androidx.databinding.BindingAdapter; public class BindingAdapters { + @BindingAdapter("visibleGone") public static void showHide(View view, boolean show) { view.setVisibility(show ? View.VISIBLE : View.GONE); } + + @BindingAdapter("visibleInvisible") + public static void showInvisible(View view, boolean show) { + view.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + } + + @BindingAdapter("typeface") + public static void setTypeface(TextView v, String style) { + switch (style) { + case "bold": + v.setTypeface(null, Typeface.BOLD); + break; + default: + v.setTypeface(null, Typeface.NORMAL); + break; + } + } } \ No newline at end of file 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 new file mode 100644 index 000000000..027989b26 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java @@ -0,0 +1,25 @@ +package org.mozilla.vrbrowser.ui.adapters; + +public class Language { + + public Language(String id, String name) { + this.id = id; + this.name = name; + } + + private String name; + private String id; + + public String getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + @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 new file mode 100644 index 000000000..c46ab1956 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java @@ -0,0 +1,203 @@ +package org.mozilla.vrbrowser.ui.adapters; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +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.vrbrowser.R; +import org.mozilla.vrbrowser.databinding.LanguageItemBinding; +import org.mozilla.vrbrowser.ui.callbacks.LanguageItemCallback; + +import java.util.Collections; +import java.util.List; + +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) { + mLanguageItemCallback = clickCallback; + + 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); + } + } + + public void addItem(Language language) { + mLanguagesList.add(0, language); + notifyDataSetChanged(); + } + + public void addItemAlphabetical(Language language) { + int index = Collections.binarySearch(mLanguagesList, language, + (ob1, ob2) -> ob1.getName().compareToIgnoreCase(ob2.getName())); + + if (index < 0) { + index = (index * -1) - 1; + } + + mLanguagesList.add(index, language); + notifyDataSetChanged(); + } + + public void removeItem(Language language) { + int position = mLanguagesList.indexOf(language); + if (position >= 0) { + mLanguagesList.remove(position); + notifyDataSetChanged(); + } + } + + public void moveItemUp(View view, Language language) { + int position = mLanguagesList.indexOf(language); + if (position > 0) { + Collections.swap(mLanguagesList, position, position - 1); + view.startAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.button_click_scale)); + notifyDataSetChanged(); + } + } + + public void moveItemDown(View view, Language language) { + int position = mLanguagesList.indexOf(language); + if (position < mLanguagesList.size()-1) { + Collections.swap(mLanguagesList, position, position + 1); + view.startAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.button_click_scale)); + notifyDataSetChanged(); + } + } + + public void onCLick(Language language) { + if (mLanguagesList.indexOf(language) < 0) { + if (mIsPreferred) + addItem(language); + else + addItemAlphabetical(language); + + } else { + removeItem(language); + } + } + + public void setPreferred(Language language) { + mIsPreferred = true; + mDefaultLanguage = language; + } + + public List getItems() { + return mLanguagesList; + } + + @Override + public LanguageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LanguageItemBinding binding = DataBindingUtil + .inflate(LayoutInflater.from(parent.getContext()), R.layout.language_item, + parent, false); + binding.setCallback(mLanguageItemCallback); + binding.setIsPreferred(mIsPreferred); + + return new LanguageViewHolder(binding); + } + + @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); + holder.binding.executePendingBindings(); + } + + @Override + public int getItemCount() { + return mLanguagesList == null ? 0 : mLanguagesList.size(); + } + + static class LanguageViewHolder extends RecyclerView.ViewHolder { + + final LanguageItemBinding binding; + + LanguageViewHolder(@NonNull LanguageItemBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } + + @Override + public long getItemId(int position) { + return position; + } + + // There seems to be a bug int he RecyclerView adapter that makes it crash sometimes when updating the dataset + // This seems to be the only thing that works until there is a ew RecyclerView version > 1.0.0 + public static class CustLinearLayoutManager extends LinearLayoutManager { + + public CustLinearLayoutManager(Context context) { + super(context); + } + + public CustLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { + super(context, orientation, reverseLayout); + } + + public CustLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + // Something is happening here + + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkClickCallback.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkClickCallback.java index 3a0b5d33e..9028fbc1e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkClickCallback.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkClickCallback.java @@ -1,7 +1,5 @@ package org.mozilla.vrbrowser.ui.callbacks; -import org.mozilla.vrbrowser.model.Bookmark; - import mozilla.components.concept.storage.BookmarkNode; public interface BookmarkClickCallback { 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 new file mode 100644 index 000000000..df25e3025 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LanguageItemCallback.java @@ -0,0 +1,11 @@ +package org.mozilla.vrbrowser.ui.callbacks; + +import android.view.View; + +import org.mozilla.vrbrowser.ui.adapters.Language; + +public interface LanguageItemCallback { + void onClick(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/settings/ButtonSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ButtonSetting.java index 3035eabf5..853cc555d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ButtonSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ButtonSetting.java @@ -3,6 +3,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; +import android.text.Spanned; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; @@ -17,6 +18,7 @@ public class ButtonSetting extends LinearLayout { private String mDescription; private String mButtonText; private TextView mButton; + private TextView mDescriptionView; private OnClickListener mListener; private Drawable mButtonBackground; private Drawable mButtonForeground; @@ -41,8 +43,8 @@ private void initialize(Context aContext) { mAudio = AudioEngine.fromContext(aContext); - TextView description = findViewById(R.id.setting_description); - description.setText(mDescription); + mDescriptionView = findViewById(R.id.setting_description); + mDescriptionView.setText(mDescription); mButton = findViewById(R.id.button); mButton.setText(mButtonText); @@ -84,6 +86,14 @@ public void setButtonText(String aText) { mButton.setText(aText); } + public void setDescription(String description) { + mDescriptionView.setText(description); + } + + public void setDescription(Spanned description) { + mDescriptionView.setText(description); + } + public String getDescription() { return mDescription; } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SwitchSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SwitchSetting.java index 7919b1fff..eabf4b821 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SwitchSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SwitchSetting.java @@ -77,7 +77,7 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean b) { } }; - public void setLinkClickListner(@NonNull ViewUtils.LinkClickableSpan listener) { + public void setLinkClickListener(@NonNull ViewUtils.LinkClickableSpan listener) { ViewUtils.setTextViewHTML(mSwitchDescription, mText, (widget, url) -> listener.onClick(widget, url)); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/RestartDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/RestartDialogWidget.java index f655dfaf4..0fe3d7dbd 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/RestartDialogWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/RestartDialogWidget.java @@ -5,19 +5,16 @@ package org.mozilla.vrbrowser.ui.widgets.dialogs; -import android.app.AlarmManager; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; import android.util.AttributeSet; import android.widget.Button; import android.widget.TextView; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.VRBrowserActivity; import org.mozilla.vrbrowser.audio.AudioEngine; -import org.mozilla.vrbrowser.ui.widgets.UIWidget; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; +import org.mozilla.vrbrowser.utils.SystemUtils; public class RestartDialogWidget extends UIDialog { @@ -52,7 +49,7 @@ private void initialize(Context aContext) { mAudio.playSound(AudioEngine.Sound.CLICK); } - postDelayed(() -> handleRestartApp(), 500); + postDelayed(() -> SystemUtils.scheduleRestart(getContext(), 100), 500); }); cancelButton.setOnClickListener(view -> { if (mAudio != null) { @@ -79,16 +76,17 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.restart_dialog_world_z); } - private void handleRestartApp() { - Intent i = new Intent(getContext(), VRBrowserActivity.class); - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + @Override + public void show(int aShowFlags) { + super.show(aShowFlags); + + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); + } - PendingIntent mPendingIntent = PendingIntent.getActivity(getContext(), 0, i, - PendingIntent.FLAG_CANCEL_CURRENT); - AlarmManager mgr = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); - mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); + @Override + public void hide(int aHideFlags) { + super.hide(aHideFlags); - System.exit(0); + mWidgetManager.popWorldBrightness(this); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/UIDialog.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/UIDialog.java index 37c31b285..dba843f6f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/UIDialog.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/UIDialog.java @@ -45,4 +45,5 @@ public void onGlobalFocusChanged(View oldFocus, View newFocus) { onDismiss(); } } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java index 6f08efa4e..b0d707019 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/VoiceSearchWidget.java @@ -30,6 +30,7 @@ import org.mozilla.vrbrowser.ui.views.UIButton; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; +import org.mozilla.vrbrowser.utils.LocaleUtils; import androidx.core.app.ActivityCompat; @@ -227,8 +228,8 @@ public void startVoiceSearch() { ActivityCompat.requestPermissions((Activity)getContext(), new String[]{Manifest.permission.RECORD_AUDIO}, VOICESEARCH_AUDIO_REQUEST_CODE); } else { - String language = SettingsStore.getInstance(getContext()).getVoiceSearchLanguage(); - mMozillaSpeechService.setLanguage(language); + String locale = LocaleUtils.getVoiceSearchLocale(getContext()); + mMozillaSpeechService.setLanguage(LocaleUtils.mapToMozillaSpeechLocales(locale)); mMozillaSpeechService.start(getContext().getApplicationContext()); mIsSpeechRecognitionRunning = true; } 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 new file mode 100644 index 000000000..2d085de24 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java @@ -0,0 +1,139 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.vrbrowser.ui.widgets.settings; + +import android.content.Context; +import android.graphics.Point; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.databinding.DataBindingUtil; + +import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.browser.SessionStore; +import org.mozilla.vrbrowser.browser.SettingsStore; +import org.mozilla.vrbrowser.databinding.OptionsLanguageContentBinding; +import org.mozilla.vrbrowser.ui.adapters.Language; +import org.mozilla.vrbrowser.ui.adapters.LanguagesAdapter; +import org.mozilla.vrbrowser.ui.callbacks.LanguageItemCallback; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; +import org.mozilla.vrbrowser.utils.LocaleUtils; + +import java.util.Arrays; + +class ContentLanguageOptionsView extends SettingsView { + + private OptionsLanguageContentBinding mBinding; + private LanguagesAdapter mPreferredAdapter; + private LanguagesAdapter mAvailableAdapter; + + public ContentLanguageOptionsView(Context aContext, WidgetManagerDelegate aWidgetManager) { + super(aContext, aWidgetManager); + initialize(aContext); + } + + private void initialize(Context aContext) { + LayoutInflater inflater = LayoutInflater.from(aContext); + + // Preferred languages adapter + mPreferredAdapter = new LanguagesAdapter(mLanguageItemCallback); + mPreferredAdapter.setPreferred(LocaleUtils.getCurrentLocaleLanguage()); + mPreferredAdapter.setLanguageList(LocaleUtils.getPreferredLanguages(getContext())); + + // Available languages adapter + mAvailableAdapter = new LanguagesAdapter(mLanguageItemCallback); + mAvailableAdapter.setLanguageList(LocaleUtils.getAvailableLanguages(getContext())); + + // Inflate this data binding layout + mBinding = DataBindingUtil.inflate(inflater, R.layout.options_language_content, this, true); + + // Header + mBinding.headerLayout.setBackClickListener(view -> { + mDelegate.showView(new LanguageOptionsView(getContext(), mWidgetManager)); + }); + mBinding.headerLayout.setHelpClickListener(view -> { + SessionStore.get().loadUri(getResources().getString(R.string.sumo_language_content_url)); + mDelegate.exitWholeSettings(); + }); + + // Adapters + mBinding.preferredList.setAdapter(mPreferredAdapter); + mBinding.availableList.setAdapter(mAvailableAdapter); + + // Footer + mBinding.footerLayout.setResetClickListener(mResetListener); + + mBinding.executePendingBindings(); + } + + private OnClickListener mResetListener = (view) -> { + reset(); + }; + + @Override + public Point getDimensions() { + return new Point( WidgetPlacement.dpDimension(getContext(), R.dimen.language_options_width), + WidgetPlacement.dpDimension(getContext(), R.dimen.language_options_height)); + } + + private LanguageItemCallback mLanguageItemCallback = new LanguageItemCallback() { + + @Override + public void onClick(View view, Language language) { + mPreferredAdapter.onCLick(language); + mAvailableAdapter.onCLick(language); + + saveCurrentLanguages(); + } + + @Override + public void onMoveUp(View view, Language language) { + mPreferredAdapter.moveItemUp(view, language); + + saveCurrentLanguages(); + } + + @Override + public void onMoveDown(View view, Language language) { + mPreferredAdapter.moveItemDown(view, language); + + saveCurrentLanguages(); + } + }; + + private void saveCurrentLanguages() { + SettingsStore.getInstance(getContext()).setContentLocales( + LocaleUtils.getLanguageIdsFromList(mPreferredAdapter.getItems())); + SessionStore.get().setLocales( + LocaleUtils.getLanguageIdsFromList(mPreferredAdapter.getItems())); + } + + private void refreshLanguages() { + ThreadUtils.postToUiThread(() -> { + mPreferredAdapter.setLanguageList(LocaleUtils.getPreferredLanguages(getContext())); + mAvailableAdapter.setLanguageList(LocaleUtils.getAvailableLanguages(getContext())); + }); + } + + @Override + protected boolean reset() { + SettingsStore.getInstance(getContext()).setContentLocales(Arrays.asList(LocaleUtils.getSystemLocale())); + SessionStore.get().setLocales(Arrays.asList(LocaleUtils.getSystemLocale())); + refreshLanguages(); + + return false; + } + + @Override + public void onShown() { + super.onShown(); + + refreshLanguages(); + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java new file mode 100644 index 000000000..8f579b43e --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java @@ -0,0 +1,95 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.vrbrowser.ui.widgets.settings; + +import android.content.Context; +import android.graphics.Point; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.browser.SessionStore; +import org.mozilla.vrbrowser.ui.views.settings.RadioGroupSetting; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; +import org.mozilla.vrbrowser.utils.LocaleUtils; + +class DisplayLanguageOptionsView extends SettingsView { + + private RadioGroupSetting mLanguage; + + public DisplayLanguageOptionsView(Context aContext, WidgetManagerDelegate aWidgetManager) { + super(aContext, aWidgetManager); + initialize(aContext); + } + + private void initialize(Context aContext) { + inflate(aContext, R.layout.options_language_display, this); + + // Header + SettingsHeader header = findViewById(R.id.header_layout); + header.setBackClickListener(view -> { + mDelegate.showView(new LanguageOptionsView(getContext(), mWidgetManager)); + }); + header.setHelpClickListener(view -> { + SessionStore.get().loadUri(getResources().getString(R.string.sumo_language_display_url)); + mDelegate.exitWholeSettings(); + }); + + // Footer + SettingsFooter footer = findViewById(R.id.footer_layout); + footer.setResetClickListener(mResetListener); + + String language = LocaleUtils.getDisplayLocale(getContext()); + mLanguage = findViewById(R.id.languageRadio); + mLanguage.setOnCheckedChangeListener(mLanguageListener); + setLanguage(mLanguage.getIdForValue(language), false); + } + + @Override + protected boolean reset() { + String systemLocale = LocaleUtils.getSystemLocale(); + String currentLocale = LocaleUtils.getCurrentLocale(); + if (!currentLocale.equalsIgnoreCase(systemLocale)) { + setLanguage(mLanguage.getIdForValue(systemLocale), true); + return true; + + } else { + setLanguage(mLanguage.getIdForValue(systemLocale), false); + return false; + } + } + + private RadioGroupSetting.OnCheckedChangeListener mLanguageListener = (radioGroup, checkedId, doApply) -> { + String currentLocale = LocaleUtils.getCurrentLocale(); + String locale = mLanguage.getValueForId(mLanguage.getCheckedRadioButtonId()).toString(); + + if (!locale.equalsIgnoreCase(currentLocale)) + setLanguage(checkedId, true); + }; + + private OnClickListener mResetListener = (view) -> { + reset(); + }; + + private void setLanguage(int checkedId, boolean doApply) { + mLanguage.setOnCheckedChangeListener(null); + mLanguage.setChecked(checkedId, doApply); + mLanguage.setOnCheckedChangeListener(mLanguageListener); + + if (doApply) { + String language = mLanguage.getValueForId(checkedId).toString(); + LocaleUtils.setDisplayLocale(getContext(), language); + + if (mDelegate != null) + mDelegate.showRestartDialog(); + } + } + + @Override + public Point getDimensions() { + return new Point( WidgetPlacement.dpDimension(getContext(), R.dimen.language_options_width), + WidgetPlacement.dpDimension(getContext(), R.dimen.language_options_height)); + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/LanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/LanguageOptionsView.java new file mode 100644 index 000000000..f7a0827fc --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/LanguageOptionsView.java @@ -0,0 +1,123 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.vrbrowser.ui.widgets.settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; + +import androidx.databinding.DataBindingUtil; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.databinding.OptionsLanguageBinding; +import org.mozilla.vrbrowser.ui.adapters.Language; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; +import org.mozilla.vrbrowser.utils.LocaleUtils; +import org.mozilla.vrbrowser.utils.ViewUtils; + +import java.util.List; + +class LanguageOptionsView extends SettingsView { + + private SharedPreferences mPrefs; + private OptionsLanguageBinding mBinding; + private SettingsView mContentLanguage; + private SettingsView mVoiceLanguage; + private SettingsView mDisplayLanguage; + + public LanguageOptionsView(Context aContext, WidgetManagerDelegate aWidgetManager) { + super(aContext, aWidgetManager); + initialize(aContext); + } + + private void initialize(Context aContext) { + LayoutInflater inflater = LayoutInflater.from(aContext); + + // Inflate this data binding layout + mBinding = DataBindingUtil.inflate(inflater, R.layout.options_language, this, true); + + // Header + mBinding.headerLayout.setBackClickListener(view -> onDismiss()); + + // Footer + mBinding.footerLayout.setResetClickListener(mResetListener); + + // Set listeners + mBinding.setContentClickListener(mContentListener); + mBinding.setDisplayClickListener(mDisplayListener); + mBinding.setVoiceSearchClickListener(mVoiceSearchListener); + + // Set descriptions + setVoiceLanguage(); + setContentLanguage(); + setDisplayLanguage(); + + mContentLanguage = new ContentLanguageOptionsView(getContext(), mWidgetManager); + mVoiceLanguage = new VoiceSearchLanguageOptionsView(getContext(), mWidgetManager); + mDisplayLanguage = new DisplayLanguageOptionsView(getContext(), mWidgetManager); + + mPrefs = PreferenceManager.getDefaultSharedPreferences(aContext); + } + + @Override + public void onShown() { + super.onShown(); + + mPrefs.registerOnSharedPreferenceChangeListener(mPreferencesListener); + } + + @Override + protected void onDismiss() { + super.onDismiss(); + + mPrefs.unregisterOnSharedPreferenceChangeListener(mPreferencesListener); + } + + private OnClickListener mResetListener = (view) -> { + if (mContentLanguage.reset() | + mDisplayLanguage.reset() | + mVoiceLanguage.reset()) + showRestartDialog(); + }; + + private void setVoiceLanguage() { + String voiceLanguageString = LocaleUtils.getVoiceSearchLanguageString(getContext()); + String text = getContext().getResources().getString(R.string.language_options_voice_search_language, voiceLanguageString); + mBinding.voiceSearchLanguageButton.setDescription(ViewUtils.getSpannedText(text)); + } + + private void setContentLanguage() { + List preferredLanguages = LocaleUtils.getPreferredLanguages(getContext()); + String text = getContext().getResources().getString(R.string.language_options_content_language, preferredLanguages.get(0).getName()); + mBinding.contentLanguageButton.setDescription(ViewUtils.getSpannedText(text)); + } + + private void setDisplayLanguage() { + String displayLanguageString = LocaleUtils.getDisplayCurrentLanguageString(); + String text = getContext().getResources().getString(R.string.language_options_display_language, displayLanguageString); + mBinding.displayLanguageButton.setDescription(ViewUtils.getSpannedText(text)); + } + + private OnClickListener mContentListener = v -> mDelegate.showView(mContentLanguage); + + private OnClickListener mVoiceSearchListener = v -> mDelegate.showView(mVoiceLanguage); + + private OnClickListener mDisplayListener = v -> mDelegate.showView(mDisplayLanguage); + + private SharedPreferences.OnSharedPreferenceChangeListener mPreferencesListener = (sharedPreferences, key) -> { + if (key.equals(getContext().getString(R.string.settings_key_content_languages))) { + setContentLanguage(); + + } else if (key.equals(getContext().getString(R.string.settings_key_voice_search_language))) { + setVoiceLanguage(); + + } else if(key.equals(getContext().getString(R.string.settings_key_display_language))) { + setDisplayLanguage(); + } + }; + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java index 16cd87a60..9d767e5b4 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java @@ -83,7 +83,7 @@ private void initialize(Context aContext) { SettingsStore.getInstance(getContext()).setDrmContentPlaybackEnabled(enabled); // TODO Enable/Disable DRM content playback }); - mDrmContentPlaybackSwitch.setLinkClickListner((widget, url) -> { + mDrmContentPlaybackSwitch.setLinkClickListener((widget, url) -> { SessionStore.get().loadUri(url); exitWholeSettings(); }); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java new file mode 100644 index 000000000..60fb8c51c --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java @@ -0,0 +1,68 @@ +package org.mozilla.vrbrowser.ui.widgets.settings; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.databinding.OptionsFooterBinding; + +public class SettingsFooter extends FrameLayout { + + private OptionsFooterBinding mBinding; + + public SettingsFooter(@NonNull Context context) { + super(context); + + initialize(context, null); + } + + public SettingsFooter(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingsHeader, 0, 0); + + initialize(context, attributes); + } + + public SettingsFooter(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingsFooter, defStyleAttr, 0); + + initialize(context, attributes); + } + + public SettingsFooter(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingsFooter, defStyleAttr, 0); + + initialize(context, attributes); + } + + private void initialize(@NonNull Context aContext, @NonNull TypedArray attributes) { + LayoutInflater inflater = LayoutInflater.from(aContext); + + // Inflate this data binding layout + mBinding = DataBindingUtil.inflate(inflater, R.layout.options_footer, this, true); + + if (attributes != null) { + String description = attributes.getString(R.styleable.SettingsFooter_description); + + if (description != null) + mBinding.setDescription(description); + } + } + + public void setResetClickListener(@NonNull View.OnClickListener listener) { + mBinding.setResetClickListener(listener); + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java new file mode 100644 index 000000000..9a7966ff9 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java @@ -0,0 +1,82 @@ +package org.mozilla.vrbrowser.ui.widgets.settings; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.databinding.OptionsHeaderBinding; + +public class SettingsHeader extends FrameLayout { + + private OptionsHeaderBinding mBinding; + + public SettingsHeader(@NonNull Context context) { + super(context); + + initialize(context, null); + } + + public SettingsHeader(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingsHeader, 0, 0); + + initialize(context, attributes); + } + + public SettingsHeader(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingsHeader, defStyleAttr, 0); + + initialize(context, attributes); + } + + public SettingsHeader(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingsHeader, defStyleAttr, defStyleRes); + + initialize(context, attributes); + } + + private void initialize(@NonNull Context aContext, @NonNull TypedArray attributes) { + LayoutInflater inflater = LayoutInflater.from(aContext); + + // Inflate this data binding layout + mBinding = DataBindingUtil.inflate(inflater, R.layout.options_header, this, true); + + if (attributes != null) { + String title = attributes.getString(R.styleable.SettingsHeader_title); + String description = attributes.getString(R.styleable.SettingsHeader_description); + int helpVisibility = attributes.getInt(R.styleable.SettingsHeader_helpVisibility, VISIBLE); + + if (title != null) + mBinding.setTitle(title); + + if (description != null) + mBinding.setDescription(description); + else + mBinding.displayLanguageDescription.setVisibility(View.GONE); + + mBinding.setHelpVisibility(helpVisibility); + } + } + + public void setBackClickListener(@NonNull View.OnClickListener listener) { + mBinding.setBackClickListener(listener); + } + + public void setHelpClickListener(@NonNull View.OnClickListener listener) { + mBinding.setHelpClickListener(listener); + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsView.java index 84fde3a78..955e0956e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsView.java @@ -23,6 +23,7 @@ public interface Delegate { void exitWholeSettings(); void showRestartDialog(); void showAlert(String aTitle, String aMessage); + void showView(SettingsView view); } public SettingsView(@NonNull Context context, WidgetManagerDelegate aWidgetManager) { @@ -84,4 +85,8 @@ public Point getDimensions() { return new Point( WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_width), WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_height)); } + + protected boolean reset() { + return false; + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsWidget.java index e1581070f..f38c6b67f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsWidget.java @@ -295,7 +295,7 @@ private String versionCodeToDate(final int aVersionCode) { return formatted; } - private void showView(SettingsView aView) { + public void showView(SettingsView aView) { if (mCurrentView != null) { mCurrentView.onHidden(); this.removeView(mCurrentView); @@ -329,7 +329,9 @@ private void showControllerOptionsDialog() { } private void showLanguageOptionsDialog() { - showView(new VoiceSearchLanguageOptionsView(getContext(), mWidgetManager)); + LanguageOptionsView view = new LanguageOptionsView(getContext(), mWidgetManager); + view.setDelegate(this); + showView(view); } private void showDisplayOptionsDialog() { @@ -374,8 +376,12 @@ public void onWorldClick() { @Override public void onDismiss() { if (mCurrentView != null) { - if (!mCurrentView.isEditing()) - showView(null); + if (!mCurrentView.isEditing()) { + if (isLanguagesSubView(mCurrentView)) + showLanguageOptionsDialog(); + else + showView(null); + } } else { super.onDismiss(); } @@ -418,4 +424,14 @@ public void showAlert(String aTitle, String aMessage) { widget.show(REQUEST_FOCUS); } + + private boolean isLanguagesSubView(View view) { + if (view instanceof DisplayLanguageOptionsView || + view instanceof ContentLanguageOptionsView || + view instanceof VoiceSearchLanguageOptionsView) { + return true; + } + + return false; + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java index 87548881c..f6775e73d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java @@ -7,25 +7,18 @@ import android.content.Context; import android.graphics.Point; -import android.view.View; -import android.widget.ScrollView; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.audio.AudioEngine; +import org.mozilla.vrbrowser.browser.SessionStore; import org.mozilla.vrbrowser.browser.SettingsStore; -import org.mozilla.vrbrowser.ui.views.UIButton; -import org.mozilla.vrbrowser.ui.views.settings.ButtonSetting; import org.mozilla.vrbrowser.ui.views.settings.RadioGroupSetting; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.LocaleUtils; class VoiceSearchLanguageOptionsView extends SettingsView { - private AudioEngine mAudio; - private UIButton mBackButton; + private RadioGroupSetting mLanguage; - private ButtonSetting mResetButton; - private ScrollView mScrollbar; public VoiceSearchLanguageOptionsView(Context aContext, WidgetManagerDelegate aWidgetManager) { super(aContext, aWidgetManager); @@ -33,30 +26,36 @@ public VoiceSearchLanguageOptionsView(Context aContext, WidgetManagerDelegate aW } private void initialize(Context aContext) { - inflate(aContext, R.layout.options_language, this); - - mAudio = AudioEngine.fromContext(aContext); - - mScrollbar = findViewById(R.id.scrollbar); + inflate(aContext, R.layout.options_language_voice, this); - mBackButton = findViewById(R.id.backButton); - mBackButton.setOnClickListener(view -> { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } - - onDismiss(); + // Header + SettingsHeader header = findViewById(R.id.header_layout); + header.setBackClickListener(view -> { + mDelegate.showView(new LanguageOptionsView(getContext(), mWidgetManager)); + }); + header.setHelpClickListener(view -> { + SessionStore.get().loadUri(getResources().getString(R.string.sumo_language_voice_url)); + mDelegate.exitWholeSettings(); }); - String language = SettingsStore.getInstance(getContext()).getVoiceSearchLanguage(); + // Footer + SettingsFooter footer = findViewById(R.id.footer_layout); + footer.setResetClickListener(mResetListener); + + String language = LocaleUtils.getVoiceSearchLocale(getContext()); mLanguage = findViewById(R.id.languageRadio); mLanguage.setOnCheckedChangeListener(mLanguageListener); setLanguage(mLanguage.getIdForValue(language), false); + } - mResetButton = findViewById(R.id.resetButton); - mResetButton.setOnClickListener(mResetListener); + @Override + protected boolean reset() { + String value = mLanguage.getValueForId(mLanguage.getCheckedRadioButtonId()).toString(); + if (!value.equals(LocaleUtils.getSystemLocale())) { + setLanguage(mLanguage.getIdForValue(LocaleUtils.getSystemLocale()), true); + } - mScrollbar = findViewById(R.id.scrollbar); + return false; } private RadioGroupSetting.OnCheckedChangeListener mLanguageListener = (radioGroup, checkedId, doApply) -> { @@ -64,10 +63,7 @@ private void initialize(Context aContext) { }; private OnClickListener mResetListener = (view) -> { - String value = mLanguage.getValueForId(mLanguage.getCheckedRadioButtonId()).toString(); - if (!value.equals(LocaleUtils.getCurrentLocale())) { - setLanguage(mLanguage.getIdForValue(LocaleUtils.getCurrentLocale()), true); - } + reset(); }; private void setLanguage(int checkedId, boolean doApply) { @@ -75,7 +71,7 @@ private void setLanguage(int checkedId, boolean doApply) { mLanguage.setChecked(checkedId, doApply); mLanguage.setOnCheckedChangeListener(mLanguageListener); - SettingsStore.getInstance(getContext()).setVoiceSearchLanguage(mLanguage.getValueForId(checkedId).toString()); + LocaleUtils.setVoiceSearchLocale(getContext(), mLanguage.getValueForId(checkedId).toString()); } @Override 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 f9c00ec6f..0371c1e10 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java @@ -1,39 +1,217 @@ package org.mozilla.vrbrowser.utils; import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.NonNull; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.browser.SessionStore; +import org.mozilla.vrbrowser.browser.SettingsStore; +import org.mozilla.vrbrowser.ui.adapters.Language; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; -import androidx.annotation.NonNull; +import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; +import static android.os.Build.VERSION_CODES.N; public class LocaleUtils { + private static Locale mSystemLocale; + private static HashMap mLanguagesCache; + + public static void init(Context context) { + getAllLanguages(); + SessionStore.get().setLocales( + LocaleUtils.getLanguageIdsFromList(getPreferredLanguages(context))); + } + + public static void saveSystemLocale() { + mSystemLocale = Locale.getDefault(); + } + + @NonNull + public static String getSystemLocale() { + return mSystemLocale.toLanguageTag(); + } + @NonNull public static String getCurrentLocale() { - String cCode = Locale.getDefault().getCountry(); - String lCode = Locale.getDefault().getLanguage(); - return lCode + "-" + cCode; + return Locale.getDefault().toLanguageTag(); + } + + public static HashMap getAllLanguages() { + if (mLanguagesCache != null) + return mLanguagesCache; + + 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 + "]")); + } + + return mLanguagesCache; + } + + public static Language getCurrentLocaleLanguage() { + return mLanguagesCache.get(getCurrentLocale()); + } + + public static List getLanguageIdsFromList(@NonNull final List languages) { + List result = new ArrayList<>(); + for (Language language : languages) { + result.add(language.getId()); + } + + return result; + } + + 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)); + + if (!savedLanguages.stream().anyMatch(str -> str.trim().equals(getCurrentLocale()))) + preferredLanguages.add(getCurrentLocaleLanguage()); + + return preferredLanguages; + } + + public static List getAvailableLanguages(@NonNull Context aContext) { + 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()); + + return availableLanguages; } @NonNull - public static List getSupportedVoiceLanguages(Context aContext) { + public static List getSupportedVoiceLanguages(@NonNull Context aContext) { return Arrays.asList(aContext.getResources().getStringArray( R.array.developer_options_voice_search_languages_values)); } @NonNull - public static String getDefaultVoiceSearchLanguage(Context aContext) { - String language = getCurrentLocale(); + public static List getSupportedDisplayLanguages(@NonNull Context aContext) { + return Arrays.asList(aContext.getResources().getStringArray( + R.array.developer_options_display_languages_values)); + } + + @NonNull + public static String getDefaultVoiceSearchLocale(@NonNull Context aContext) { + String locale = getCurrentLocale(); List supportedLanguages = getSupportedVoiceLanguages(aContext); - if (!supportedLanguages.contains(language)) { + if (!supportedLanguages.contains(locale)) { return supportedLanguages.get(0); } - return language; + return locale; + } + + @NonNull + public static String getVoiceSearchLocale(@NonNull Context aContext) { + String locale = SettingsStore.getInstance(aContext).getVoiceSearchLocale(); + return mapOldLocaleToNew(locale); + } + + public static void setVoiceSearchLocale(@NonNull Context context, @NonNull String locale) { + SettingsStore.getInstance(context).setVoiceSearchLocale(locale); + } + + @NonNull + public static String getVoiceSearchLanguageString(@NonNull Context aContext) { + String language = LocaleUtils.getVoiceSearchLocale(aContext); + return getAllLanguages().get(language).getName(); + } + + @NonNull + public static String getDefaultDisplayLocale(@NonNull Context aContext) { + String locale = getCurrentLocale(); + List supportedLanguages = getSupportedDisplayLanguages(aContext); + if (!supportedLanguages.contains(locale)) { + return supportedLanguages.get(0); + } + + return locale; + } + + @NonNull + public static String getDisplayLocale(Context context) { + String locale = SettingsStore.getInstance(context).getDisplayLocale(); + return mapOldLocaleToNew((locale)); + } + + public static void setDisplayLocale(@NonNull Context context, @NonNull String locale) { + SettingsStore.getInstance(context).setDisplayLocale(locale); + } + + @NonNull + public static String getDisplayCurrentLanguageString() { + return getAllLanguages().get(getCurrentLocale()).getName(); + } + + public static Context setLocale(@NonNull Context context) { + return updateResources(context, SettingsStore.getInstance(context).getDisplayLocale()); + } + + private static Context updateResources(@NonNull Context context, @NonNull String language) { + String[] localeStr = language.split("-"); + Locale locale = new Locale(localeStr[0], localeStr.length > 1 ? localeStr[1] : ""); + Locale.setDefault(locale); + + Resources res = context.getResources(); + Configuration config = new Configuration(res.getConfiguration()); + if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) { + config.setLocale(locale); + context = context.createConfigurationContext(config); + } else { + config.locale = locale; + res.updateConfiguration(config, res.getDisplayMetrics()); + } + return context; + } + + public static Locale getLocale(@NonNull Resources res) { + Configuration config = res.getConfiguration(); + return (Build.VERSION.SDK_INT >= N) ? config.getLocales().get(0) : config.locale; + } + + public static String mapOldLocaleToNew(@NonNull String locale) { + if (locale.equalsIgnoreCase("cmn-Hant-TW")) + locale = "zh-Hant-TW"; + else if (locale.equalsIgnoreCase("cmn-Hans-CN")) + locale = "zh-Hans-CN"; + + return locale; + } + + public static String mapToMozillaSpeechLocales(@NonNull String locale) { + if (locale.equalsIgnoreCase("zh-Hant-TW")) + locale = "cmn-Hant-TW"; + else if (locale.equalsIgnoreCase("zh-Hans-CN")) + locale = "cmn-Hans-CN"; + + return locale; } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java new file mode 100644 index 000000000..f84eb92ea --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java @@ -0,0 +1,32 @@ +package org.mozilla.vrbrowser.utils; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; + +import androidx.annotation.NonNull; + +import org.mozilla.vrbrowser.VRBrowserActivity; + +public class SystemUtils { + + public static final void restart(@NonNull Context context) { + Intent i = new Intent(context, VRBrowserActivity.class); + context.startActivity(i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)); + } + + public static final void scheduleRestart(@NonNull Context context, long delay) { + Intent i = new Intent(context, VRBrowserActivity.class); + i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + PendingIntent mPendingIntent = PendingIntent.getActivity(context, 0, i, + PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + delay, mPendingIntent); + + System.exit(0); + } + +} 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 efe89c221..dd7a0d91b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/ViewUtils.java @@ -1,6 +1,9 @@ package org.mozilla.vrbrowser.utils; +import android.os.Build; +import android.text.Html; import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.text.style.URLSpan; @@ -33,7 +36,7 @@ public void onClick(View view) { strBuilder.removeSpan(span); } - public static void setTextViewHTML(@NonNull TextView text, @NonNull String html, @NonNull LinkClickableSpan listener) + public static void setTextViewHTML(@NonNull TextView text, @NonNull String html, LinkClickableSpan listener) { CharSequence sequence = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY); SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence); @@ -77,4 +80,12 @@ public static UIWidget getParentWidget(@NonNull View view) { } } + public static Spanned getSpannedText(String text) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT); + } else { + return Html.fromHtml(text); + } + } + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d2bff2bea..f59ecec04 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,7 +25,7 @@ diff --git a/app/src/main/res/anim/button_click_scale.xml b/app/src/main/res/anim/button_click_scale.xml new file mode 100644 index 000000000..3322629f9 --- /dev/null +++ b/app/src/main/res/anim/button_click_scale.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_icon_add.xml b/app/src/main/res/drawable/ic_icon_add.xml new file mode 100644 index 000000000..246e2639b --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_icon_check.xml b/app/src/main/res/drawable/ic_icon_check.xml new file mode 100644 index 000000000..0e1dc7944 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_check.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_icon_delete.xml b/app/src/main/res/drawable/ic_icon_delete.xml new file mode 100644 index 000000000..35f384174 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_delete.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_icon_language_add.xml b/app/src/main/res/drawable/ic_icon_language_add.xml new file mode 100644 index 000000000..3a8aebf1d --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_language_add.xml @@ -0,0 +1,9 @@ + + + + + \ 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 new file mode 100644 index 000000000..5c4538fa1 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_language_delete.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_icon_move_down.xml b/app/src/main/res/drawable/ic_icon_move_down.xml new file mode 100644 index 000000000..19281d661 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_move_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_icon_move_up.xml b/app/src/main/res/drawable/ic_icon_move_up.xml new file mode 100644 index 000000000..4f16e43fa --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_move_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_icon_settings_help.xml b/app/src/main/res/drawable/ic_icon_settings_help.xml new file mode 100644 index 000000000..e2ce5832e --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_settings_help.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_down_black_24px.xml b/app/src/main/res/drawable/ic_keyboard_arrow_down_black_24px.xml index eae960530..8dcfd358b 100644 --- a/app/src/main/res/drawable/ic_keyboard_arrow_down_black_24px.xml +++ b/app/src/main/res/drawable/ic_keyboard_arrow_down_black_24px.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:fillColor="#000000" + android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z" /> diff --git a/app/src/main/res/drawable/settings_list_row_background_color.xml b/app/src/main/res/drawable/settings_list_row_background_color.xml new file mode 100644 index 000000000..15ac09af7 --- /dev/null +++ b/app/src/main/res/drawable/settings_list_row_background_color.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..87bbd2404 --- /dev/null +++ b/app/src/main/res/layout/language_item.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_footer.xml b/app/src/main/res/layout/options_footer.xml new file mode 100644 index 000000000..41025ab39 --- /dev/null +++ b/app/src/main/res/layout/options_footer.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_header.xml b/app/src/main/res/layout/options_header.xml new file mode 100644 index 000000000..1f9680446 --- /dev/null +++ b/app/src/main/res/layout/options_header.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_language.xml b/app/src/main/res/layout/options_language.xml index 1f540b405..77b47b6c5 100644 --- a/app/src/main/res/layout/options_language.xml +++ b/app/src/main/res/layout/options_language.xml @@ -1,9 +1,20 @@ - + - + + + + + + - - - - - + android:layout_height="wrap_content" + android:layout_marginEnd="35dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:title="@string/settings_language_settings" + app:helpVisibility="gone"/> - - - - - - - - + android:layout_height="wrap_content" + android:layout_marginEnd="35dp" + android:paddingTop="10dp" + android:layout_alignParentBottom="true" + app:layout_constraintTop_toBottomOf="@id/header_layout" + app:buttonText="@string/settings_button_edit" + app:description="@string/language_options_voice_search_language" + android:onClick="@{(view) -> voiceSearchClickListener.onClick(view)}" /> - + android:layout_height="wrap_content" + android:layout_marginEnd="35dp" + android:layout_alignParentBottom="true" + app:layout_constraintTop_toBottomOf="@id/voiceSearchLanguageButton" + app:buttonText="@string/settings_button_edit" + app:description="@string/language_options_content_language" + android:onClick="@{(view) -> contentClickListener.onClick(view)}" /> - - + app:layout_constraintTop_toBottomOf="@id/contentLanguageButton" + app:buttonText="@string/settings_button_edit" + app:description="@string/language_options_display_language" + android:onClick="@{(view) -> displayClickListener.onClick(view)}" /> + + + + + diff --git a/app/src/main/res/layout/options_language_content.xml b/app/src/main/res/layout/options_language_content.xml new file mode 100644 index 000000000..a69f8bc40 --- /dev/null +++ b/app/src/main/res/layout/options_language_content.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_language_display.xml b/app/src/main/res/layout/options_language_display.xml new file mode 100644 index 000000000..4dc2663bc --- /dev/null +++ b/app/src/main/res/layout/options_language_display.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_language_voice.xml b/app/src/main/res/layout/options_language_voice.xml new file mode 100644 index 000000000..b7fe8fa4e --- /dev/null +++ b/app/src/main/res/layout/options_language_voice.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_privacy.xml b/app/src/main/res/layout/options_privacy.xml index a87e928b8..d59dbdae4 100644 --- a/app/src/main/res/layout/options_privacy.xml +++ b/app/src/main/res/layout/options_privacy.xml @@ -66,7 +66,7 @@ diff --git a/app/src/main/res/layout/setting_button.xml b/app/src/main/res/layout/setting_button.xml index b0e691449..74115993b 100644 --- a/app/src/main/res/layout/setting_button.xml +++ b/app/src/main/res/layout/setting_button.xml @@ -13,7 +13,7 @@ @@ -43,7 +43,7 @@ diff --git a/app/src/main/res/layout/setting_radio_group.xml b/app/src/main/res/layout/setting_radio_group.xml index 58eb7b851..545a1453a 100644 --- a/app/src/main/res/layout/setting_radio_group.xml +++ b/app/src/main/res/layout/setting_radio_group.xml @@ -13,7 +13,7 @@ diff --git a/app/src/main/res/layout/setting_switch.xml b/app/src/main/res/layout/setting_switch.xml index cb2ad2d0a..57f21b239 100644 --- a/app/src/main/res/layout/setting_switch.xml +++ b/app/src/main/res/layout/setting_switch.xml @@ -21,7 +21,7 @@ android:gravity="left|center"> + @@ -61,4 +62,18 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 94c95cb72..b44de03a4 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -174,6 +174,10 @@ 12sp 11sp + 20sp + 14sp + 10sp + 4 250dp @@ -184,9 +188,16 @@ 10dp 8dp + 2dp + 0dp + 64dp + + 22dp + 20dp + 2dp 1dp diff --git a/app/src/main/res/values/non_L10n.xml b/app/src/main/res/values/non_L10n.xml index a9ad26d23..a1c531817 100644 --- a/app/src/main/res/values/non_L10n.xml +++ b/app/src/main/res/values/non_L10n.xml @@ -30,6 +30,8 @@ settings_gfx_msaa settings_audio settings_voice_search_language + settings_display_language + settings_content_languages settings_cylinder_density settings_foveated_in_app settings_foveated_webvr @@ -44,6 +46,9 @@ https://www.mozilla.org/privacy/firefox/ https://mixedreality.mozilla.org/fxr/report?src=browser-fxr&label=browser-firefox-reality&url=%1$s https://support.mozilla.org/products/firefox-reality + https://support.mozilla.org/en-US/kb/using-voice-search-firefox-reality + https://support.mozilla.org/en-US/kb/use-firefox-another-language?as=u&utm_source=inproduct + https://support.mozilla.org/en-US/kb/choose-display-languages-multilingual-web-pages?as=u&utm_source=inproduct view position view_id @@ -63,7 +68,8 @@ 日本語 Français Deutsch - Español + Español (España) + Español 한국어 Italiano diff --git a/app/src/main/res/values/options_values.xml b/app/src/main/res/values/options_values.xml index 36e9b8ffd..512c0d762 100644 --- a/app/src/main/res/values/options_values.xml +++ b/app/src/main/res/values/options_values.xml @@ -99,8 +99,8 @@ en-US - cmn-Hant-TW - cmn-Hans-CN + zh-Hant-TW + zh-Hans-CN ja-JP fr-FR de-DE @@ -108,4 +108,31 @@ ko-KR it-IT + + + + @string/language_en_US + @string/language_cmn_Hant_TW + @string/language_cmn_Hans_CN + @string/language_ja_JP + @string/language_fr_FR + @string/language_de_DE + @string/language_es_ES + @string/language_es + @string/language_ko_KR + @string/language_it_IT + + + + en-US + zh-Hant-TW + zh-Hans-CN + ja-JP + fr-FR + de-DE + es-ES + es + ko-KR + it-IT + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b31605269..4aefa5852 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -118,6 +118,11 @@ opens a dialog box that contains language-related settings. --> Language + + Language Settings + Choose your preferred language for voice search + + Firefox Display Language + + + Choose the language used to display menus, messages, and notifications. + + + Voice Search Language + + + Choose your preferred language for voice search. + + + Preferred Website Languages + + + 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. + English (US) @@ -341,6 +380,9 @@ Reset + + Edit + Environment @@ -616,6 +658,30 @@ Reset Voice Search Language Settings + + Reset Display Language + + + Reset Preferred Website Language + + + Reset All Language Settings + + + Voice Search Language:   %1$s]]> + + + Preferred Language for Displaying Websites:   %1$s]]> + + + Display Language for App:   %1$s]]> + + + Preferred Language(s) + + + Available Languages + Name @@ -775,4 +841,7 @@ Poor Performance Detected The current web page is affecting performance and has been suspended. Reducing the window size can help improve a web page with poor performance. + + + Default diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 21138f698..722176f08 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -171,7 +171,7 @@ + + + + + + + + @@ -324,4 +359,4 @@