diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index 727b9eb05..5e41646eb 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -131,6 +131,7 @@ public void run() { private LinkedList> mBrightnessQueue; private Pair mCurrentBrightness; private SearchEngineWrapper mSearchEngineWrapper; + private SettingsStore mSettings; @Override protected void onCreate(Bundle savedInstanceState) { @@ -181,6 +182,8 @@ protected void onCreate(Bundle savedInstanceState) { }); mAudioUpdateRunnable = () -> mAudioEngine.update(); + mSettings = SettingsStore.getInstance(this); + loadFromIntent(getIntent()); queueRunnable(() -> createOffscreenDisplay()); final String tempPath = getCacheDir().getAbsolutePath(); @@ -531,7 +534,8 @@ void handleScrollEvent(final int aHandle, final int aDevice, final float aX, fin runOnUiThread(() -> { Widget widget = mWidgets.get(aHandle); if (widget != null) { - MotionEventGenerator.dispatchScroll(widget, aDevice, aX, aY); + float scrollDirection = mSettings.getScrollDirection() == 0 ? 1.0f : -1.0f; + MotionEventGenerator.dispatchScroll(widget, aDevice, aX * scrollDirection, aY * scrollDirection); } else { Log.e(LOGTAG, "Failed to find widget for scroll event: " + aHandle); } @@ -965,12 +969,12 @@ public boolean isPermissionGranted(@NonNull String permission) { } @Override - public void requestPermission(@NonNull String uri, @NonNull String permission, GeckoSession.PermissionDelegate.Callback aCallback) { - mPermissionDelegate.onAppPermissionRequest( - SessionStore.get().getCurrentSession(), - uri, - permission, - aCallback); + public void requestPermission(String uri, @NonNull String permission, GeckoSession.PermissionDelegate.Callback aCallback) { + if (uri != null && !uri.isEmpty()) { + mPermissionDelegate.onAppPermissionRequest(SessionStore.get().getCurrentSession(), uri, permission, aCallback); + } else { + mPermissionDelegate.onAndroidPermissionsRequest(SessionStore.get().getCurrentSession(), new String[]{permission}, aCallback); + } } @Override 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 197c7252a..27ad9d9b2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SessionStore.java @@ -89,7 +89,7 @@ public interface SessionChangeListener { class SessionSettings { boolean multiprocess = SettingsStore.getInstance(mContext).isMultiprocessEnabled(); boolean privateMode = false; - boolean trackingProtection = true; + boolean trackingProtection = SettingsStore.getInstance(mContext).isTrackingProtectionEnabled(); boolean suspendMediaWhenInactive = true; int userAgentMode = SettingsStore.getInstance(mContext).getUaMode(); boolean servo = false; @@ -872,7 +872,7 @@ public void setServo(final boolean enabled) { } } - public void setMultiprocess(final boolean enabled) { + private void recreateSession(SessionStore.SessionSettings aSettings) { if (mCurrentSession != null) { final GeckoResult state = mCurrentSession.saveState(); state.then(new GeckoResult.OnValueListener() { @@ -884,9 +884,7 @@ public GeckoResult onValue(@Nullable GeckoSession.SessionState value) th mCurrentSession.close(); int oldSessionId = getCurrentSessionId(); - SessionStore.SessionSettings settings = new SessionStore.SessionSettings(); - settings.multiprocess = enabled; - int sessionId = createSession(settings); + int sessionId = createSession(aSettings); GeckoSession session = getSession(sessionId); session.restoreState(value); setCurrentSession(sessionId); @@ -906,6 +904,29 @@ public GeckoResult onException(@NonNull Throwable exception) throws Thro } } + private State getCurrentState() { + if (mCurrentSession != null) { + return mSessions.get(mCurrentSession.hashCode()); + } + return null; + } + + public void setMultiprocess(final boolean aEnabled) { + State state = getCurrentState(); + if (state != null && state.mSettings.multiprocess != aEnabled) { + state.mSettings.multiprocess = aEnabled; + recreateSession(state.mSettings); + } + } + + public void setTrackingProtection(final boolean aEnabled) { + State state = getCurrentState(); + if (state != null && state.mSettings.trackingProtection != aEnabled) { + state.mSettings.trackingProtection = aEnabled; + recreateSession(state.mSettings); + } + } + public void setRemoteDebugging(final boolean enabled) { if (mRuntime != null) { mRuntime.getSettings().setRemoteDebuggingEnabled(enabled); 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 cdfea3c26..115cf79cf 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java @@ -40,6 +40,7 @@ SettingsStore getInstance(final @NonNull Context aContext) { public final static boolean CONSOLE_LOGS_DEFAULT = false; public final static boolean ENV_OVERRIDE_DEFAULT = false; public final static boolean MULTIPROCESS_DEFAULT = false; + public final static boolean TRACKING_DEFAULT = true; public final static boolean SERVO_DEFAULT = false; public final static int UA_MODE_DEFAULT = GeckoSessionSettings.USER_AGENT_MODE_VR; public final static int INPUT_MODE_DEFAULT = 1; @@ -50,6 +51,7 @@ SettingsStore getInstance(final @NonNull Context aContext) { public final static int MAX_WINDOW_WIDTH_DEFAULT = 1200; public final static int MAX_WINDOW_HEIGHT_DEFAULT = 1200; public final static int POINTER_COLOR_DEFAULT_DEFAULT = Color.parseColor("#FFFFFF"); + public final static int SCROLL_DIRECTION_DEFAULT = 0; public final static String ENV_DEFAULT = "cave"; public final static float BROWSER_WORLD_WIDTH_DEFAULT = 4.0f; public final static float BROWSER_WORLD_HEIGHT_DEFAULT = 2.25f; @@ -61,6 +63,8 @@ SettingsStore getInstance(final @NonNull Context aContext) { private final static boolean enableCrashReportingByDefault = false; private final static boolean enableTelemetryByDefault = true; + private int mCachedScrollDirection = -1; + public SettingsStore(Context aContext) { mContext = aContext; mPrefs = PreferenceManager.getDefaultSharedPreferences(aContext); @@ -134,6 +138,17 @@ public void setConsoleLogsEnabled(boolean isEnabled) { editor.commit(); } + public boolean isTrackingProtectionEnabled() { + return mPrefs.getBoolean( + mContext.getString(R.string.settings_key_tracking_protection), TRACKING_DEFAULT); + } + + public void setTrackingProtectionEnabled(boolean isEnabled) { + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putBoolean(mContext.getString(R.string.settings_key_tracking_protection), isEnabled); + editor.commit(); + } + public boolean isEnvironmentOverrideEnabled() { return mPrefs.getBoolean( mContext.getString(R.string.settings_key_environment_override), ENV_OVERRIDE_DEFAULT); @@ -145,6 +160,7 @@ public void setEnvironmentOverrideEnabled(boolean isEnabled) { editor.commit(); } + public boolean isMultiprocessEnabled() { return mPrefs.getBoolean( mContext.getString(R.string.settings_key_multiprocess), MULTIPROCESS_DEFAULT); @@ -314,6 +330,21 @@ public void setPointerColor(int color) { editor.commit(); } + public int getScrollDirection() { + if (mCachedScrollDirection < 0) { + mCachedScrollDirection = mPrefs.getInt(mContext.getString(R.string.settings_key_scroll_direction), SCROLL_DIRECTION_DEFAULT); + } + return mCachedScrollDirection; + } + + public void setScrollDirection(int aScrollDirection) { + mCachedScrollDirection = aScrollDirection; + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putInt(mContext.getString(R.string.settings_key_scroll_direction), aScrollDirection); + editor.commit(); + } + + public int getMSAALevel() { return mPrefs.getInt( mContext.getString(R.string.settings_key_msaa), MSAA_DEFAULT_LEVEL); 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 127e1fc48..3035eabf5 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 @@ -2,6 +2,7 @@ import android.content.Context; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; @@ -17,6 +18,8 @@ public class ButtonSetting extends LinearLayout { private String mButtonText; private TextView mButton; private OnClickListener mListener; + private Drawable mButtonBackground; + private Drawable mButtonForeground; public ButtonSetting(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -44,6 +47,8 @@ private void initialize(Context aContext) { mButton = findViewById(R.id.button); mButton.setText(mButtonText); mButton.setOnClickListener(mInternalClickListener); + mButtonBackground = mButton.getBackground(); + mButtonForeground = mButton.getForeground(); } private View.OnClickListener mInternalClickListener = v -> onClickListener(v); @@ -64,4 +69,23 @@ public void setOnClickListener(OnClickListener aListener) { mListener = aListener; } + public void setShowAsLabel(boolean aShowAsLabel) { + if (aShowAsLabel) { + mButton.setBackground(null); + mButton.setForeground(null); + mButton.setTextColor(getContext().getColor(R.color.fog)); + } else { + mButton.setBackground(mButtonBackground); + mButton.setForeground(mButtonForeground); + } + } + + public void setButtonText(String aText) { + mButton.setText(aText); + } + + public String getDescription() { + return mDescription; + } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ImageRadioGroupSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ImageRadioGroupSetting.java new file mode 100644 index 000000000..48ec28eb9 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ImageRadioGroupSetting.java @@ -0,0 +1,168 @@ +package org.mozilla.vrbrowser.ui.views.settings; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.text.InputType; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RadioButton; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.audio.AudioEngine; + +import androidx.annotation.IdRes; + +public class ImageRadioGroupSetting extends LinearLayout { + + public interface OnCheckedChangeListener { + void onCheckedChanged(@IdRes int checkedId, boolean apply); + } + + private AudioEngine mAudio; + private CharSequence[] mOptions; + private Object[] mValues; + private Drawable[] mImages; + private OnCheckedChangeListener mRadioGroupListener; + private ImageRadioButton[] mItems; + + public ImageRadioGroupSetting(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ImageRadioGroupSetting(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.RadioGroupSetting, defStyleAttr, 0); + mOptions = attributes.getTextArray(R.styleable.RadioGroupSetting_options); + int id = attributes.getResourceId(R.styleable.RadioGroupSetting_values, 0); + TypedArray array = context.getResources().obtainTypedArray(id); + if (array.getType(0) == TypedValue.TYPE_STRING) { + mValues = getResources().getStringArray(id); + + } else if (array.getType(0) == TypedValue.TYPE_INT_HEX || + array.getType(0) == TypedValue.TYPE_INT_DEC) { + int [] values = getResources().getIntArray(id); + mValues = new Integer[values.length]; + for (int i=0; i { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + setChecked(checkedId, true); + + }); + addView(button); + mItems[i] = button; + } + } + + static class ImageRadioButton extends LinearLayout { + private ImageView mImage; + private RadioButton mRadioButton; + public ImageRadioButton(Context context) { + super(context); + initialize(context); + } + + private void initialize(Context aContext) { + inflate(aContext, R.layout.setting_radio_item, this); + mImage = findViewById(R.id.radioItemImage); + mRadioButton = findViewById(R.id.radioItemButton); + mRadioButton.setInputType(InputType.TYPE_NULL); + mRadioButton.setSoundEffectsEnabled(false); + } + + public void setValues(int aItemId, String aText, Drawable aImage) { + mRadioButton.setId(aItemId); + mRadioButton.setText(aText); + if (aImage != null) { + mImage.setImageDrawable(aImage); + } + } + + public void setChecked(boolean aChecked) { + mRadioButton.setChecked(aChecked); + } + + public boolean isChecked() { + return mRadioButton.isChecked(); + } + + @Override + public void setOnClickListener(View.OnClickListener aListener) { + super.setOnClickListener(aListener); + mRadioButton.setOnClickListener(aListener); + mImage.setOnClickListener(aListener); + } + } + + public void setChecked(@IdRes int checkedId, boolean doApply) { + for (int i = 0; i < mItems.length; i++) { + mItems[i].setChecked(i == checkedId); + } + + if (mRadioGroupListener != null && doApply) { + mRadioGroupListener.onCheckedChanged(checkedId, doApply); + } + } + + public void setOnCheckedChangeListener(OnCheckedChangeListener aListener) { + mRadioGroupListener = aListener; + } + + public Object getValueForId(@IdRes int checkId) { + return mValues[checkId]; + } + + public int getIdForValue(Object value) { + for (int i = 0; i < mValues.length; i++) { + if (mValues[i].equals(value)) { + return i; + } + } + + return 0; + } + + public int getCheckedRadioButtonId() { + for (int i = 0; i < mItems.length; ++i) { + if (mItems[i].isChecked()) { + return i; + } + } + return -1; + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java index e87b25e18..2a13cf947 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java @@ -58,10 +58,10 @@ public RadioGroupSetting(Context context, AttributeSet attrs, int defStyleAttr) } attributes.recycle(); - initialize(context, mLayout); + initialize(context, attrs, defStyleAttr, mLayout); } - protected void initialize(Context aContext, @LayoutRes int layout) { + protected void initialize(Context aContext, AttributeSet attrs, int defStyleAttr, @LayoutRes int layout) { inflate(aContext, layout, this); mAudio = AudioEngine.fromContext(aContext); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupVSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupVSetting.java index 7cfad8d17..39eec48eb 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupVSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupVSetting.java @@ -1,10 +1,13 @@ package org.mozilla.vrbrowser.ui.views.settings; import android.content.Context; +import android.content.res.TypedArray; import android.text.InputType; import android.util.AttributeSet; import android.widget.RadioButton; +import org.mozilla.vrbrowser.R; + import androidx.annotation.LayoutRes; public class RadioGroupVSetting extends RadioGroupSetting { @@ -18,15 +21,18 @@ public RadioGroupVSetting(Context context, AttributeSet attrs, int defStyleAttr) } @Override - protected void initialize(Context aContext, @LayoutRes int layout) { - super.initialize(aContext, layout); + protected void initialize(Context aContext, AttributeSet attrs, int defStyleAttr, @LayoutRes int layout) { + super.initialize(aContext, attrs, defStyleAttr, layout); + + TypedArray attributes = aContext.obtainStyledAttributes(attrs, R.styleable.RadioGroupSetting, defStyleAttr, 0); + int margin = attributes.getInteger(R.styleable.RadioGroupSetting_itemMargin, 20); for (int i=0; i aDelegate.run()); + } else { + mHelpButton.setVisibility(View.GONE); + } + } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java index ee10943a2..103c1697d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java @@ -53,5 +53,5 @@ interface WorldClickListener { void addWorldClickListener(WorldClickListener aListener); void removeWorldClickListener(WorldClickListener aListener); boolean isPermissionGranted(@NonNull String permission); - void requestPermission(@NonNull String uri, @NonNull String permission, GeckoSession.PermissionDelegate.Callback aCallback); + void requestPermission(String uri, @NonNull String permission, GeckoSession.PermissionDelegate.Callback aCallback); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/EnvironmentOptionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/EnvironmentOptionsWidget.java new file mode 100644 index 000000000..97cd1c441 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/EnvironmentOptionsWidget.java @@ -0,0 +1,201 @@ +/* -*- 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.dialogs; + +import android.content.Context; +import android.util.AttributeSet; +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.ImageRadioGroupSetting; +import org.mozilla.vrbrowser.ui.views.settings.RadioGroupSetting; +import org.mozilla.vrbrowser.ui.views.settings.SwitchSetting; +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.LocaleUtils; + +public class EnvironmentOptionsWidget extends UIWidget implements + WidgetManagerDelegate.WorldClickListener, + WidgetManagerDelegate.FocusChangeListener { + + private AudioEngine mAudio; + private UIButton mBackButton; + + private SwitchSetting mEnvOverrideSwitch; + private ImageRadioGroupSetting mEnvironmentsRadio; + private int mRestartDialogHandle = -1; + + private ButtonSetting mResetButton; + + private ScrollView mScrollbar; + + public EnvironmentOptionsWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + public EnvironmentOptionsWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public EnvironmentOptionsWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + private void initialize(Context aContext) { + inflate(aContext, R.layout.options_environment, this); + + mAudio = AudioEngine.fromContext(aContext); + + mWidgetManager.addFocusChangeListener(this); + mWidgetManager.addWorldClickListener(this); + + mBackButton = findViewById(R.id.backButton); + mBackButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + onDismiss(); + }); + + String env = SettingsStore.getInstance(getContext()).getEnvironment(); + mEnvironmentsRadio = findViewById(R.id.environmentRadio); + mEnvironmentsRadio.setOnCheckedChangeListener(mEnvsListener); + setEnv(mEnvironmentsRadio.getIdForValue(env), false); + + mEnvOverrideSwitch = findViewById(R.id.envOverrideSwitch); + mEnvOverrideSwitch.setOnCheckedChangeListener(mEnvOverrideListener); + setEnvOverride(SettingsStore.getInstance(getContext()).isEnvironmentOverrideEnabled()); + mEnvOverrideSwitch.setHelpDelegate(() -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + SessionStore.get().loadUri(getContext().getString(R.string.environment_override_help_url)); + hide(REMOVE_WIDGET); + }); + + mResetButton = findViewById(R.id.resetButton); + mResetButton.setOnClickListener(mResetListener); + + mScrollbar = findViewById(R.id.scrollbar); + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + aPlacement.visible = false; + aPlacement.width = WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_width); + aPlacement.height = WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_height); + aPlacement.parentAnchorX = 0.5f; + aPlacement.parentAnchorY = 0.5f; + aPlacement.anchorX = 0.5f; + aPlacement.anchorY = 0.5f; + aPlacement.translationY = WidgetPlacement.unitFromMeters(getContext(), R.dimen.restart_dialog_world_y); + aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.restart_dialog_world_z); + } + + @Override + public void releaseWidget() { + mWidgetManager.removeFocusChangeListener(this); + mWidgetManager.removeWorldClickListener(this); + + super.releaseWidget(); + } + + @Override + public void show() { + super.show(); + + mScrollbar.scrollTo(0, 0); + } + + private void setEnvOverride(boolean value) { + mEnvOverrideSwitch.setOnCheckedChangeListener(null); + mEnvOverrideSwitch.setValue(value, false); + mEnvOverrideSwitch.setOnCheckedChangeListener(mEnvOverrideListener); + + SettingsStore.getInstance(getContext()).setEnvironmentOverrideEnabled(value); + } + + private OnClickListener mResetListener = (view) -> { + boolean restart = false; + if (mEnvOverrideSwitch.isChecked() != SettingsStore.ENV_OVERRIDE_DEFAULT) { + setEnvOverride(SettingsStore.ENV_OVERRIDE_DEFAULT); + restart = true; + } + + if (!mEnvironmentsRadio.getValueForId(mEnvironmentsRadio.getCheckedRadioButtonId()).equals(SettingsStore.ENV_DEFAULT)) { + setEnv(mEnvironmentsRadio.getIdForValue(SettingsStore.ENV_DEFAULT), true); + } + + if (restart) + showRestartDialog(); + }; + + private SwitchSetting.OnCheckedChangeListener mEnvOverrideListener = (compoundButton, value, doApply) -> { + setEnvOverride(value); + showRestartDialog(); + }; + + private ImageRadioGroupSetting.OnCheckedChangeListener mEnvsListener = (checkedId, doApply) -> { + setEnv(checkedId, doApply); + }; + + private void setEnv(int checkedId, boolean doApply) { + mEnvironmentsRadio.setOnCheckedChangeListener(null); + mEnvironmentsRadio.setChecked(checkedId, doApply); + mEnvironmentsRadio.setOnCheckedChangeListener(mEnvsListener); + + SettingsStore.getInstance(getContext()).setEnvironment((String) mEnvironmentsRadio.getValueForId(checkedId)); + + if (doApply) { + mWidgetManager.updateEnvironment(); + } + } + + private void showRestartDialog() { + hide(UIWidget.REMOVE_WIDGET); + + UIWidget widget = getChild(mRestartDialogHandle); + if (widget == null) { + widget = createChild(RestartDialogWidget.class, false); + mRestartDialogHandle = widget.getHandle(); + widget.setDelegate(() -> onRestartDialogDismissed()); + } + + widget.show(); + } + + private void onRestartDialogDismissed() { + show(); + } + + // WindowManagerDelegate.FocusChangeListener + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (oldFocus == this && isVisible() && findViewById(newFocus.getId()) == null) { + onDismiss(); + } + } + + // WorldClickListener + @Override + public void onWorldClick() { + if (isVisible()) { + onDismiss(); + } + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SettingsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SettingsWidget.java index 44d16b797..e18f3adaa 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SettingsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SettingsWidget.java @@ -28,8 +28,10 @@ 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.ui.widgets.options.ControllerOptionsWidget; import org.mozilla.vrbrowser.ui.widgets.options.DeveloperOptionsWidget; import org.mozilla.vrbrowser.ui.widgets.options.DisplayOptionsWidget; +import org.mozilla.vrbrowser.ui.widgets.options.PrivacyOptionsWidget; import org.mozilla.vrbrowser.ui.widgets.options.VoiceSearchLanguageOptionsWidget; import java.io.UnsupportedEncodingException; @@ -46,6 +48,9 @@ public class SettingsWidget extends UIWidget implements WidgetManagerDelegate.Fo private int mDeveloperOptionsDialogHandle = -1; private int mLanguageOptionsDialogHandle = -1; private int mDisplayOptionsDialogHandle = -1; + private int mControllerOptionsDialogHandle = -1; + private int mPrivacyOptionsDialogHandle = -1; + private int mEnvironmentOptionsDialogHandle = -1; private TextView mBuildText; class VersionGestureListener extends GestureDetector.SimpleOnGestureListener { @@ -147,6 +152,15 @@ private void initialize(Context aContext) { onDisplayOptionsClick(); }); + HoneycombButton environmentButton = findViewById(R.id.environmentButton); + environmentButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + showEnvironmentOptionsDialog(); + }); + TextView versionText = findViewById(R.id.versionText); try { PackageInfo pInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), 0); @@ -186,6 +200,15 @@ private void initialize(Context aContext) { onDeveloperOptionsClick(); }); + HoneycombButton controllerOptionsButton = findViewById(R.id.controllerOptionsButton); + controllerOptionsButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + showControllerOptionsDialog(); + }); + mAudio = AudioEngine.fromContext(aContext); } @@ -219,15 +242,16 @@ private void onSettingsTelemetryChange(boolean isEnabled) { } private void onSettingsPrivacyClick() { - GeckoSession session = SessionStore.get().getCurrentSession(); - if (session == null) { - int sessionId = SessionStore.get().createSession(); - SessionStore.get().setCurrentSession(sessionId); + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); + hide(REMOVE_WIDGET); + UIWidget widget = getChild(mPrivacyOptionsDialogHandle); + if (widget == null) { + widget = createChild(PrivacyOptionsWidget.class, false); + mPrivacyOptionsDialogHandle = widget.getHandle(); + widget.setDelegate(() -> onOptionsDialogDismissed()); } - SessionStore.get().loadUri(getContext().getString(R.string.private_policy_url)); - - hide(REMOVE_WIDGET); + widget.show(); } private void onSettingsReportClick() { @@ -322,6 +346,19 @@ private void showDeveloperOptionsDialog() { widget.show(); } + private void showControllerOptionsDialog() { + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); + hide(REMOVE_WIDGET); + UIWidget widget = getChild(mControllerOptionsDialogHandle); + if (widget == null) { + widget = createChild(ControllerOptionsWidget.class, false); + mControllerOptionsDialogHandle = widget.getHandle(); + widget.setDelegate(() -> onOptionsDialogDismissed()); + } + + widget.show(); + } + private void showLanguageOptionsDialog() { mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); hide(UIWidget.REMOVE_WIDGET); @@ -348,6 +385,19 @@ private void showDisplayOptionsDialog() { widget.show(); } + private void showEnvironmentOptionsDialog() { + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); + hide(UIWidget.REMOVE_WIDGET); + UIWidget widget = getChild(mEnvironmentOptionsDialogHandle); + if (widget == null) { + widget = createChild(EnvironmentOptionsWidget.class, false); + mEnvironmentOptionsDialogHandle = widget.getHandle(); + widget.setDelegate(() -> onOptionsDialogDismissed()); + } + + widget.show(); + } + private void onOptionsDialogDismissed() { mWidgetManager.popWorldBrightness(this); show(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/ControllerOptionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/ControllerOptionsWidget.java new file mode 100644 index 000000000..0ef8147fa --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/ControllerOptionsWidget.java @@ -0,0 +1,160 @@ +/* -*- 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.options; + +import android.content.Context; +import android.util.AttributeSet; +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.DoubleEditSetting; +import org.mozilla.vrbrowser.ui.views.settings.RadioGroupSetting; +import org.mozilla.vrbrowser.ui.views.settings.SingleEditSetting; +import org.mozilla.vrbrowser.ui.views.settings.SwitchSetting; +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.ui.widgets.dialogs.RestartDialogWidget; + +public class ControllerOptionsWidget extends UIWidget implements WidgetManagerDelegate.WorldClickListener { + private AudioEngine mAudio; + private UIButton mBackButton; + + private RadioGroupSetting mPointerColorRadio; + private RadioGroupSetting mScrollDirectionRadio; + + + private ButtonSetting mResetButton; + + private ScrollView mScrollbar; + + public ControllerOptionsWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + public ControllerOptionsWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public ControllerOptionsWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + private void initialize(Context aContext) { + inflate(aContext, R.layout.options_controller, this); + mAudio = AudioEngine.fromContext(aContext); + + mBackButton = findViewById(R.id.backButton); + mBackButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + hide(REMOVE_WIDGET); + if (mDelegate != null) { + mDelegate.onDismiss(); + } + }); + + mScrollbar = findViewById(R.id.scrollbar); + + int color = SettingsStore.getInstance(getContext()).getPointerColor(); + mPointerColorRadio = findViewById(R.id.pointer_color_radio); + mPointerColorRadio.setOnCheckedChangeListener(mPointerColorListener); + setPointerColor(mPointerColorRadio.getIdForValue(color), false); + + int scrollDirection = SettingsStore.getInstance(getContext()).getScrollDirection(); + mScrollDirectionRadio = findViewById(R.id.scroll_direction_radio); + mScrollDirectionRadio.setOnCheckedChangeListener(mScrollDirectionListener); + setScrollDirection(mScrollDirectionRadio.getIdForValue(scrollDirection), false); + + mResetButton = findViewById(R.id.resetButton); + mResetButton.setOnClickListener(v -> resetOptions()); + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + aPlacement.visible = false; + aPlacement.width = WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_width); + aPlacement.height = WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_height); + aPlacement.parentAnchorX = 0.5f; + aPlacement.parentAnchorY = 0.5f; + aPlacement.anchorX = 0.5f; + aPlacement.anchorY = 0.5f; + aPlacement.translationY = WidgetPlacement.unitFromMeters(getContext(), R.dimen.restart_dialog_world_y); + aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.restart_dialog_world_z); + } + + @Override + public void show() { + super.show(); + + mWidgetManager.addWorldClickListener(this); + mScrollbar.scrollTo(0, 0); + } + + @Override + public void hide(@HideFlags int aHideFlags) { + super.hide(aHideFlags); + + mWidgetManager.removeWorldClickListener(this); + } + + private void resetOptions() { + if (!mPointerColorRadio.getValueForId(mPointerColorRadio.getCheckedRadioButtonId()).equals(SettingsStore.POINTER_COLOR_DEFAULT_DEFAULT)) { + setPointerColor(mPointerColorRadio.getIdForValue(SettingsStore.POINTER_COLOR_DEFAULT_DEFAULT), true); + } + if (!mScrollDirectionRadio.getValueForId(mScrollDirectionRadio.getCheckedRadioButtonId()).equals(SettingsStore.SCROLL_DIRECTION_DEFAULT)) { + setScrollDirection(mScrollDirectionRadio.getIdForValue(SettingsStore.SCROLL_DIRECTION_DEFAULT), true); + } + } + + private void setPointerColor(int checkedId, boolean doApply) { + mPointerColorRadio.setOnCheckedChangeListener(null); + mPointerColorRadio.setChecked(checkedId, doApply); + mPointerColorRadio.setOnCheckedChangeListener(mPointerColorListener); + + if (doApply) { + SettingsStore.getInstance(getContext()).setPointerColor((int)mPointerColorRadio.getValueForId(checkedId)); + mWidgetManager.updatePointerColor(); + } + } + + private void setScrollDirection(int checkedId, boolean doApply) { + mScrollDirectionRadio.setOnCheckedChangeListener(null); + mScrollDirectionRadio.setChecked(checkedId, doApply); + mScrollDirectionRadio.setOnCheckedChangeListener(mScrollDirectionListener); + + if (doApply) { + SettingsStore.getInstance(getContext()).setScrollDirection((int)mScrollDirectionRadio.getValueForId(checkedId)); + } + } + + private RadioGroupSetting.OnCheckedChangeListener mPointerColorListener = (radioGroup, checkedId, doApply) -> { + setPointerColor(checkedId, doApply); + }; + + private RadioGroupSetting.OnCheckedChangeListener mScrollDirectionListener = (radioGroup, checkedId, doApply) -> { + setScrollDirection(checkedId, doApply); + }; + + // WorldClickListener + @Override + public void onWorldClick() { + if (isVisible()) { + onDismiss(); + } + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DeveloperOptionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DeveloperOptionsWidget.java index e6f7f32e0..d89748f70 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DeveloperOptionsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/DeveloperOptionsWidget.java @@ -35,13 +35,9 @@ public class DeveloperOptionsWidget extends UIWidget implements private SwitchSetting mRemoteDebuggingSwitch; private SwitchSetting mConsoleLogsSwitch; - private SwitchSetting mEnvOverrideSwitch; private SwitchSetting mMultiprocessSwitch; private SwitchSetting mServoSwitch; - private RadioGroupSetting mEnvironmentsRadio; - private RadioGroupSetting mPointerColorRadio; - private SingleEditSetting mHomepageEdit; private String mDefaultHomepageUrl; @@ -99,10 +95,6 @@ private void initialize(Context aContext) { mConsoleLogsSwitch.setOnCheckedChangeListener(mConsoleLogsListener); setConsoleLogs(SettingsStore.getInstance(getContext()).isConsoleLogsEnabled(), false); - mEnvOverrideSwitch = findViewById(R.id.env_override_switch); - mEnvOverrideSwitch.setOnCheckedChangeListener(mEnvOverrideListener); - setEnvOverride(SettingsStore.getInstance(getContext()).isEnvironmentOverrideEnabled()); - mMultiprocessSwitch = findViewById(R.id.multiprocess_switch); mMultiprocessSwitch.setOnCheckedChangeListener(mMultiprocessListener); setMultiprocess(SettingsStore.getInstance(getContext()).isMultiprocessEnabled(), false); @@ -115,16 +107,6 @@ private void initialize(Context aContext) { setServo(SettingsStore.getInstance(getContext()).isServoEnabled(), false); } - String env = SettingsStore.getInstance(getContext()).getEnvironment(); - mEnvironmentsRadio = findViewById(R.id.environment_radio); - mEnvironmentsRadio.setOnCheckedChangeListener(mEnvsListener); - setEnv(mEnvironmentsRadio.getIdForValue(env), false); - - int color = SettingsStore.getInstance(getContext()).getPointerColor(); - mPointerColorRadio = findViewById(R.id.pointer_radio); - mPointerColorRadio.setOnCheckedChangeListener(mPointerColorListener); - setPointerColor(mPointerColorRadio.getIdForValue(color), false); - mResetButton = findViewById(R.id.resetButton); mResetButton.setOnClickListener(mResetListener); @@ -207,11 +189,6 @@ private void onRestartDialogDismissed() { setConsoleLogs(value, doApply); }; - private SwitchSetting.OnCheckedChangeListener mEnvOverrideListener = (compoundButton, value, doApply) -> { - setEnvOverride(value); - showRestartDialog(); - }; - private SwitchSetting.OnCheckedChangeListener mMultiprocessListener = (compoundButton, value, doApply) -> { setMultiprocess(value, doApply); }; @@ -220,14 +197,6 @@ private void onRestartDialogDismissed() { setServo(b, true); }; - private RadioGroupSetting.OnCheckedChangeListener mEnvsListener = (radioGroup, checkedId, doApply) -> { - setEnv(checkedId, doApply); - }; - - private RadioGroupSetting.OnCheckedChangeListener mPointerColorListener = (radioGroup, checkedId, doApply) -> { - setPointerColor(checkedId, doApply); - }; - private OnClickListener mResetListener = (view) -> { boolean restart = false; if (mRemoteDebuggingSwitch.isChecked() != SettingsStore.REMOTE_DEBUGGING_DEFAULT) { @@ -246,15 +215,6 @@ private void onRestartDialogDismissed() { } setHomepage(mDefaultHomepageUrl); - if (mEnvOverrideSwitch.isChecked() != SettingsStore.ENV_OVERRIDE_DEFAULT) { - setEnvOverride(SettingsStore.ENV_OVERRIDE_DEFAULT); - restart = true; - } - - if (!mEnvironmentsRadio.getValueForId(mEnvironmentsRadio.getCheckedRadioButtonId()).equals(SettingsStore.ENV_DEFAULT)) { - setEnv(mEnvironmentsRadio.getIdForValue(SettingsStore.ENV_DEFAULT), true); - } - if (restart) showRestartDialog(); }; @@ -290,14 +250,6 @@ private void setConsoleLogs(boolean value, boolean doApply) { } } - private void setEnvOverride(boolean value) { - mEnvOverrideSwitch.setOnCheckedChangeListener(null); - mEnvOverrideSwitch.setValue(value, false); - mEnvOverrideSwitch.setOnCheckedChangeListener(mEnvOverrideListener); - - SettingsStore.getInstance(getContext()).setEnvironmentOverrideEnabled(value); - } - private void setMultiprocess(boolean value, boolean doApply) { mMultiprocessSwitch.setOnCheckedChangeListener(null); mMultiprocessSwitch.setValue(value, false); @@ -322,30 +274,6 @@ private void setServo(boolean value, boolean doApply) { } } - private void setEnv(int checkedId, boolean doApply) { - mEnvironmentsRadio.setOnCheckedChangeListener(null); - mEnvironmentsRadio.setChecked(checkedId, doApply); - mEnvironmentsRadio.setOnCheckedChangeListener(mEnvsListener); - - SettingsStore.getInstance(getContext()).setEnvironment((String) mEnvironmentsRadio.getValueForId(checkedId)); - - if (doApply) { - mWidgetManager.updateEnvironment(); - } - } - - private void setPointerColor(int checkedId, boolean doApply) { - mPointerColorRadio.setOnCheckedChangeListener(null); - mPointerColorRadio.setChecked(checkedId, doApply); - mPointerColorRadio.setOnCheckedChangeListener(mPointerColorListener); - - SettingsStore.getInstance(getContext()).setPointerColor((int)mPointerColorRadio.getValueForId(checkedId)); - - if (doApply) { - mWidgetManager.updatePointerColor(); - } - } - // WindowManagerDelegate.FocusChangeListener @Override diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/PrivacyOptionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/PrivacyOptionsWidget.java new file mode 100644 index 000000000..d19b6f93d --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/options/PrivacyOptionsWidget.java @@ -0,0 +1,197 @@ +/* -*- 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.options; + +import android.Manifest; +import android.content.Context; +import android.util.AttributeSet; +import android.util.Pair; +import android.widget.ScrollView; + +import org.mozilla.geckoview.GeckoSession; +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.SwitchSetting; +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.ui.widgets.prompts.AlertPromptWidget; + +import java.util.ArrayList; + +public class PrivacyOptionsWidget extends UIWidget implements WidgetManagerDelegate.WorldClickListener { + private AudioEngine mAudio; + private UIButton mBackButton; + + private SwitchSetting mTrackingSetting; + private ButtonSetting mResetButton; + private ArrayList> mPermissionButtons; + private int mAlertDialogHandle; + + private ScrollView mScrollbar; + + public PrivacyOptionsWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + public PrivacyOptionsWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public PrivacyOptionsWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + private void initialize(Context aContext) { + inflate(aContext, R.layout.options_privacy, this); + mAudio = AudioEngine.fromContext(aContext); + + mBackButton = findViewById(R.id.backButton); + mBackButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + hide(REMOVE_WIDGET); + if (mDelegate != null) { + mDelegate.onDismiss(); + } + }); + + mScrollbar = findViewById(R.id.scrollbar); + + ButtonSetting privacyPolicy = findViewById(R.id.showPrivacyButton); + privacyPolicy.setOnClickListener(v -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + GeckoSession session = SessionStore.get().getCurrentSession(); + if (session == null) { + int sessionId = SessionStore.get().createSession(); + SessionStore.get().setCurrentSession(sessionId); + } + + SessionStore.get().loadUri(getContext().getString(R.string.private_policy_url)); + + hide(REMOVE_WIDGET); + }); + + mTrackingSetting = findViewById(R.id.trackingProtectionButton); + mTrackingSetting.setChecked(SettingsStore.getInstance(getContext()).isTrackingProtectionEnabled()); + mTrackingSetting.setOnCheckedChangeListener((compoundButton, enabled, apply) -> { + SettingsStore.getInstance(getContext()).setTrackingProtectionEnabled(enabled); + SessionStore.get().setTrackingProtection(enabled); + }); + + mPermissionButtons = new ArrayList<>(); + mPermissionButtons.add(Pair.create(findViewById(R.id.cameraPermissionButton), Manifest.permission.CAMERA)); + mPermissionButtons.add(Pair.create(findViewById(R.id.microphonePermissionButton), Manifest.permission.RECORD_AUDIO)); + mPermissionButtons.add(Pair.create(findViewById(R.id.locationPermissionButton), Manifest.permission.ACCESS_FINE_LOCATION)); + mPermissionButtons.add(Pair.create(findViewById(R.id.storagePermissionButton), Manifest.permission.READ_EXTERNAL_STORAGE)); + + for (Pair button: mPermissionButtons) { + if (mWidgetManager.isPermissionGranted(button.second)) { + button.first.setShowAsLabel(true); + button.first.setButtonText(getContext().getString(R.string.permission_enabled)); + } + button.first.setOnClickListener(v -> { + togglePermission(button.first, button.second); + }); + } + + + mResetButton = findViewById(R.id.resetButton); + mResetButton.setOnClickListener(v -> resetOptions()); + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + aPlacement.visible = false; + aPlacement.width = WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_width); + aPlacement.height = WidgetPlacement.dpDimension(getContext(), R.dimen.developer_options_height); + aPlacement.parentAnchorX = 0.5f; + aPlacement.parentAnchorY = 0.5f; + aPlacement.anchorX = 0.5f; + aPlacement.anchorY = 0.5f; + aPlacement.translationY = WidgetPlacement.unitFromMeters(getContext(), R.dimen.restart_dialog_world_y); + aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.restart_dialog_world_z); + } + + @Override + public void show() { + super.show(); + + mWidgetManager.addWorldClickListener(this); + mScrollbar.scrollTo(0, 0); + } + + @Override + public void hide(@HideFlags int aHideFlags) { + super.hide(aHideFlags); + + mWidgetManager.removeWorldClickListener(this); + } + + private void togglePermission(ButtonSetting aButton, String aPermission) { + if (mWidgetManager.isPermissionGranted(aPermission)) { + showAlert(aButton.getDescription(), getContext().getString(R.string.security_options_permissions_reject_message)); + } else { + mWidgetManager.requestPermission("", aPermission, new GeckoSession.PermissionDelegate.Callback() { + @Override + public void grant() { + aButton.setShowAsLabel(true); + aButton.setButtonText(getContext().getString(R.string.permission_enabled)); + } + @Override + public void reject() { + + } + }); + } + } + + private void resetOptions() { + if (mTrackingSetting.isChecked() != SettingsStore.TRACKING_DEFAULT) { + mTrackingSetting.setChecked(SettingsStore.TRACKING_DEFAULT); + } + } + + private void showAlert(String aTitle, String aMessage) { + hide(UIWidget.KEEP_WIDGET); + + AlertPromptWidget widget = getChild(mAlertDialogHandle); + if (widget == null) { + widget = createChild(AlertPromptWidget.class, false); + mAlertDialogHandle = widget.getHandle(); + widget.setDelegate(() -> onAlertDismissed()); + } + widget.getPlacement().translationZ = 0; + widget.getPlacement().parentHandle = mHandle; + widget.setTitle(aTitle); + widget.setMessage(aMessage); + + widget.show(); + } + + private void onAlertDismissed() { + show(); + } + + // WorldClickListener + @Override + public void onWorldClick() { + if (isVisible()) { + onDismiss(); + } + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java index 60e82f6a8..dbe9e039f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java @@ -62,6 +62,10 @@ protected void onDismiss() { if (mCallback != null) { mCallback.dismiss(); } + + if (mDelegate != null) { + mDelegate.onDismiss(); + } } public void setDelegate(GeckoSession.PromptDelegate.AlertCallback delegate) { diff --git a/app/src/main/res/drawable/environment_cave.png b/app/src/main/res/drawable/environment_cave.png new file mode 100644 index 000000000..8c24f1d20 Binary files /dev/null and b/app/src/main/res/drawable/environment_cave.png differ diff --git a/app/src/main/res/drawable/environment_meadow.png b/app/src/main/res/drawable/environment_meadow.png new file mode 100644 index 000000000..6321db983 Binary files /dev/null and b/app/src/main/res/drawable/environment_meadow.png differ diff --git a/app/src/main/res/drawable/ic_settings_controlleroptions.xml b/app/src/main/res/drawable/ic_settings_controlleroptions.xml new file mode 100644 index 000000000..626734b94 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_controlleroptions.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_environment.xml b/app/src/main/res/drawable/ic_settings_environment.xml new file mode 100644 index 000000000..6f5cebceb --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_environment.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/drawable/setting_help_button_background.xml b/app/src/main/res/drawable/setting_help_button_background.xml new file mode 100644 index 000000000..7dc5098f2 --- /dev/null +++ b/app/src/main/res/drawable/setting_help_button_background.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/honeycomb_button.xml b/app/src/main/res/layout/honeycomb_button.xml index d2444d06d..15e0675ad 100644 --- a/app/src/main/res/layout/honeycomb_button.xml +++ b/app/src/main/res/layout/honeycomb_button.xml @@ -19,11 +19,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:paddingStart="3dp" + android:paddingEnd="3dp" android:layout_marginStart="15dp" android:layout_marginEnd="15dp" - android:fontFamily="sans-serif-light" + android:fontFamily="sans-serif" android:gravity="center" - android:text="@string/settings_privacy" + android:text="@string/settings_privacy_policy" android:textSize="@dimen/text_smaller_size" /> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_developer.xml b/app/src/main/res/layout/options_developer.xml index 6a901a315..feb9fc79f 100644 --- a/app/src/main/res/layout/options_developer.xml +++ b/app/src/main/res/layout/options_developer.xml @@ -66,12 +66,6 @@ android:layout_height="wrap_content" app:description="@string/developer_options_show_console" /> - - - - - diff --git a/app/src/main/res/layout/options_environment.xml b/app/src/main/res/layout/options_environment.xml new file mode 100644 index 000000000..23b8e742d --- /dev/null +++ b/app/src/main/res/layout/options_environment.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/options_privacy.xml b/app/src/main/res/layout/options_privacy.xml new file mode 100644 index 000000000..b174a0f11 --- /dev/null +++ b/app/src/main/res/layout/options_privacy.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/setting_radio_group.xml b/app/src/main/res/layout/setting_radio_group.xml index 688bc6c66..ff1c2a236 100644 --- a/app/src/main/res/layout/setting_radio_group.xml +++ b/app/src/main/res/layout/setting_radio_group.xml @@ -16,7 +16,7 @@ style="@style/settingsDescription" android:layout_alignParentStart="true" android:layout_centerVertical="true" - android:layout_toStartOf="@id/environment_radio" + android:layout_toStartOf="@id/environmentRadio" tools:text="Radio Setting Description" /> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/setting_switch.xml b/app/src/main/res/layout/setting_switch.xml index a21f7a895..cb2ad2d0a 100644 --- a/app/src/main/res/layout/setting_switch.xml +++ b/app/src/main/res/layout/setting_switch.xml @@ -12,13 +12,24 @@ android:layout_height="match_parent" android:gravity="center_vertical"> - + android:gravity="left|center"> + + + + + - - + app:honeycombButtonTextSize="@dimen/settings_main_button_text_width"/> + app:honeycombSwitchTextSize="@dimen/settings_main_button_text_width" /> + + + app:honeycombSwitchTextSize="@dimen/settings_main_button_text_width"/> + + + @@ -129,16 +138,25 @@ style="?attr/honeycombButtonStyle" android:layout_marginRight="10dp" app:honeycombButtonIcon="@drawable/ic_settings_privacypolicy" - app:honeycombButtonText="@string/settings_privacy" - app:honeycombSwitchTextSize="@dimen/settings_button_text_width" /> + app:honeycombButtonText="@string/settings_privacy_security" + app:honeycombSwitchTextSize="@dimen/settings_main_button_text_width" /> + app:honeycombButtonIcon="@drawable/ic_settings_display" + app:honeycombButtonText="@string/settings_display" + app:honeycombButtonTextSize="@dimen/settings_main_button_text_width" /> + + + + app:honeycombSwitchTextSize="@dimen/settings_main_button_text_width" /> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 38a21e00e..d0e544ccb 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -65,7 +65,7 @@ - Datenschutzrichtlinie + Datenschutzrichtlinie diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 34bb548f2..f83d18bbf 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -65,7 +65,7 @@ - Política de privacidad + Política de privacidad diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0dc178441..3e4013a18 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -65,7 +65,7 @@ - Politique de confidentialité + Politique de confidentialité diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 0da85836e..8ccb7d0db 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -65,7 +65,7 @@ - Informativa sulla privacy + Informativa sulla privacy diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 9e866b283..ac04dd11c 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -65,7 +65,7 @@ - 個人情報保護方針 + 個人情報保護方針 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index eee444713..5877ac150 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -65,7 +65,7 @@ - 개인정보보호정책 + 개인정보보호정책 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index a764e076b..741016b3d 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -65,7 +65,7 @@ - 隐私政策 + 隐私政策 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a48756030..a56a51db9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -65,7 +65,7 @@ - 隱私權保護政策 + 隱私權保護政策 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index faa420370..5b5c06887 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -28,8 +28,10 @@ + + diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index b16a0b69e..5bd5f294f 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -49,11 +49,12 @@ 1.6 - -1.8 - 580dp + -2.0 + 730dp 520dp 320dp 100dp + 125dp 88dp diff --git a/app/src/main/res/values/non_L10n.xml b/app/src/main/res/values/non_L10n.xml index c2a08dca7..5f77e0dc4 100644 --- a/app/src/main/res/values/non_L10n.xml +++ b/app/src/main/res/values/non_L10n.xml @@ -12,6 +12,7 @@ settings_environment_override settings_environment_multiprocess settings_environment_servo + settings_tracking_protection settings_user_agent_version settings_touch_mode settings_display_density @@ -22,6 +23,7 @@ settings_max_window_height settings_env settings_pointer_color + settings_scroll_direction settings_gfx_msaa settings_audio settings_voice_search_language @@ -29,6 +31,7 @@ https://support.mozilla.org/kb/private-mode-firefox-reality settings_browser_world_width settings_browser_world_height + https://github.com/MozillaReality/FirefoxReality/wiki/Environments 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 diff --git a/app/src/main/res/values/options_values.xml b/app/src/main/res/values/options_values.xml index 7abb44dfa..8d8bc32d6 100644 --- a/app/src/main/res/values/options_values.xml +++ b/app/src/main/res/values/options_values.xml @@ -13,6 +13,12 @@ meadow + + @color/black + @drawable/environment_cave + @drawable/environment_meadow + + @string/developer_options_pointer_white @@ -24,6 +30,16 @@ 0xC27FFCFF + + @string/developer_options_scroll_direction_natural + @string/developer_options_scroll_direction_reversed + + + + 0 + 1 + + @string/developer_options_ua_mobile diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1d9223818..8d3a18fa0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,12 @@ permission requested for in the permission dialog box. --> Don’t Allow + + Enable + + + Enabled + @@ -63,9 +69,13 @@ On/Off switch which toggles whether browser-level Telemetry is reported back to Mozilla. --> Telemetry + + Privacy & Security + - Privacy Policy + Privacy Policy @@ -205,6 +215,11 @@ indicates that, when the button is pressed, the user's edits will be saved. --> Save + + Show + Display DPI: @@ -239,6 +254,16 @@ VR + + Scroll Direction + + + Natural + + + Reversed + Reset Developer Settings @@ -273,6 +298,24 @@ for the home page. --> Homepage + + Tracking Protection + + Permissions (Allow Firefox to access) + + This permission can only be disabled in system permissions + + Camera + + Microphone + + Location + + Notifications + + Read External Storage + On @@ -431,6 +474,15 @@ Reset Display Settings + + Reset Privacy & Security Settings + + + Reset Controller Settings + + + Reset Environment Settings + Reset Voice Search Language Settings diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4011a032c..28e30fd6f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -214,6 +214,17 @@ @drawable/switch_track + +