diff --git a/.gitignore b/.gitignore index 6f69b0260c..3c08e227b8 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ infer-out/ fastlane/ app/.externalNativeBuild +app/.cxx openwnn/.externalNativeBuild *.swp diff --git a/.taskcluster.yml b/.taskcluster.yml index 8f9454ef9d..27fcbea666 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -2,9 +2,17 @@ # 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/. -version: 0 -allowPullRequests: public +version: 1 +policy: + pullRequests: public tasks: + $let: + repository: + $if: 'tasks_for == "github-pull-request"' + then: ${event.pull_request.head.repo.clone_url} + else: ${event.repository.clone_url} + + in: ############################################################################### # Task: Pull requests # @@ -12,34 +20,31 @@ tasks: # # - Builds NoAPI and GoogleVR flavors ############################################################################### - - provisionerId: '{{ taskcluster.docker.provisionerId }}' - workerType: '{{ taskcluster.docker.workerType }}' - extra: - github: - events: - - pull_request.opened - - pull_request.edited - - pull_request.synchronize - - pull_request.reopened - payload: - maxRunTime: 14400 - image: 'mozillamixedreality/firefoxreality:190312' - command: - - /bin/bash - - '--login' - - '-cx' - - >- - git fetch {{ event.head.repo.url }} {{ event.head.repo.branch }} - && git config advice.detachedHead false - && git checkout {{event.head.sha}} - && rm -rf gvr-android-sdk && git clone https://github.com/MozillaReality/FirefoxReality-gvr-android-sdk.git gvr-android-sdk - && git submodule update - && ./gradlew --no-daemon --console=plain clean assembleNoapi assembleGooglevr - metadata: - name: Firefox Reality for Android - Build - Pull Request - description: Building Firefox Reality for Android (via Gradle) - triggered by a pull request. - owner: '{{ event.head.user.email }}' - source: '{{ event.head.repo.url }}' + - $if: 'tasks_for == "github-pull-request" && event["action"] in ["opened", "edited", "reopened", "synchronize"]' + then: + provisionerId: 'aws-provisioner-v1' + workerType: 'github-worker' + deadline: {$fromNow: 1 day} + expires: {$fromNow: 1 year} + payload: + maxRunTime: 14400 + image: 'mozillamixedreality/firefoxreality:190312' + command: + - /bin/bash + - '--login' + - '-cx' + - >- + git fetch ${repository} ${event.pull_request.head.ref} + && git config advice.detachedHead false + && git checkout ${event.pull_request.head.sha} + && rm -rf gvr-android-sdk && git clone https://github.com/MozillaReality/FirefoxReality-gvr-android-sdk.git gvr-android-sdk + && git submodule update + && ./gradlew --no-daemon --console=plain clean assembleNoapi assembleGooglevr + metadata: + name: Firefox Reality for Android - Build - Pull Request + description: Building Firefox Reality for Android (via Gradle) - triggered by a pull request. + owner: noreply@mozilla.com + source: ${repository} ############################################################################### # Task: Master builds # @@ -48,103 +53,98 @@ tasks: # testing only and should not be uploaded to App Stores. # ############################################################################### - - provisionerId: '{{ taskcluster.docker.provisionerId }}' - workerType: '{{ taskcluster.docker.workerType }}' - extra: - github: - env: true - events: - - push - branches: - - master - scopes: - - "secrets:get:project/firefoxreality/github-deploy-key" - - "secrets:get:project/firefoxreality/staging-signing-token" - - "secrets:get:project/firefoxreality/symbols-token" - routes: - - notify.email.fxr-releng@mozilla.com.on-any - payload: - maxRunTime: 14400 - image: 'mozillamixedreality/firefoxreality:190312' - features: - taskclusterProxy: true - command: - - /bin/bash - - '--login' - - '-cx' - - >- - git fetch origin - && git config advice.detachedHead false - && git rebase origin/master - && rm -rf gvr-android-sdk && git clone https://github.com/MozillaReality/FirefoxReality-gvr-android-sdk.git gvr-android-sdk - && git submodule update - && . tools/taskcluster/get_third_party.sh - && cp tools/gradle/taskcluster.properties ./user.properties - && ./gradlew --no-daemon --console=plain clean `python tools/taskcluster/build_targets.py =all` - && python tools/taskcluster/fetch_secret.py -s project/firefoxreality/staging-signing-token -o token -n token - && python tools/taskcluster/sign_apk.py -t token - && python tools/taskcluster/archive_debug_apk.py - && . tools/taskcluster/upload_symbols.sh - artifacts: - 'public': - type: 'directory' - path: '/opt/FirefoxReality/builds/' - expires: "{{ '1 month' | $fromNow }}" - metadata: - name: Firefox Reality for Android - Build - Master update - description: Building Firefox Reality for Android (via Gradle) - triggered by update to master - owner: '{{ event.head.user.email }}' - source: '{{ event.head.repo.url }}' + - $if: 'tasks_for == "github-push" && event["ref"] == "refs/heads/master"' + then: + provisionerId: 'aws-provisioner-v1' + workerType: 'github-worker' + deadline: {$fromNow: 1 day} + expires: {$fromNow: 1 year} + scopes: + - "secrets:get:project/firefoxreality/github-deploy-key" + - "secrets:get:project/firefoxreality/staging-signing-token" + - "secrets:get:project/firefoxreality/symbols-token" + routes: + - notify.email.fxr-releng@mozilla.com.on-any + payload: + maxRunTime: 14400 + image: 'mozillamixedreality/firefoxreality:190312' + features: + taskclusterProxy: true + command: + - /bin/bash + - '--login' + - '-cx' + - >- + git fetch origin + && git config advice.detachedHead false + && git rebase origin/master + && rm -rf gvr-android-sdk && git clone https://github.com/MozillaReality/FirefoxReality-gvr-android-sdk.git gvr-android-sdk + && git submodule update + && . tools/taskcluster/get_third_party.sh + && cp tools/gradle/taskcluster.properties ./user.properties + && ./gradlew --no-daemon --console=plain clean `python tools/taskcluster/build_targets.py =all` + && python tools/taskcluster/fetch_secret.py -s project/firefoxreality/staging-signing-token -o token -n token + && python tools/taskcluster/sign_apk.py -t token + && python tools/taskcluster/archive_debug_apk.py + && . tools/taskcluster/upload_symbols.sh + artifacts: + 'public': + type: 'directory' + path: '/opt/FirefoxReality/builds/' + expires: {$fromNow: '1 month'} + metadata: + name: Firefox Reality for Android - Build - Master update + description: Building Firefox Reality for Android (via Gradle) - triggered by update to master + owner: noreply@mozilla.com + source: ${repository} ############################################################################### # Task: Release builds # # Triggered when a new release is tagged. Produces signed release APKs. # -# NOTE: {{ event.version }} is the string used to tag the release. -# ############################################################################### - - provisionerId: '{{ taskcluster.docker.provisionerId }}' - workerType: '{{ taskcluster.docker.workerType }}' - extra: - github: - events: - - release - scopes: - - "secrets:get:project/firefoxreality/github-deploy-key" - - "secrets:get:project/firefoxreality/release-signing-token" - - "secrets:get:project/firefoxreality/symbols-token" - routes: - - notify.email.fxr-releng@mozilla.com.on-any - payload: - maxRunTime: 14400 - image: 'mozillamixedreality/firefoxreality:190312' - features: - taskclusterProxy: true - command: - - /bin/bash - - '--login' - - '-cx' - - >- - git fetch origin - && git config advice.detachedHead false - && git checkout {{ event.version }} - && rm -rf gvr-android-sdk && git clone https://github.com/MozillaReality/FirefoxReality-gvr-android-sdk.git gvr-android-sdk - && git submodule update - && . tools/taskcluster/get_third_party.sh - && cp tools/gradle/taskcluster.properties ./user.properties - && ./gradlew --no-daemon --console=plain clean `python tools/taskcluster/build_targets.py {{ event.version }}` - && python tools/taskcluster/fetch_secret.py -s project/firefoxreality/release-signing-token -o token -n token - && python tools/taskcluster/sign_apk.py -t token -r - && python tools/taskcluster/archive_debug_apk.py - && . tools/taskcluster/upload_symbols.sh - artifacts: - 'public': - type: 'directory' - path: '/opt/FirefoxReality/builds/' - expires: "{{ '1 year' | $fromNow }}" - metadata: - name: Firefox Reality for Android - Release Build ({{ event.version }}) - description: Building Firefox Reality for Android (via Gradle) - triggered by release - owner: '{{ event.head.user.email }}' - source: '{{ event.head.repo.url }}' + - $if: 'tasks_for == "github-release"' + then: + provisionerId: 'aws-provisioner-v1' + workerType: 'github-worker' + deadline: {$fromNow: 1 day} + expires: {$fromNow: 1 year} + scopes: + - "secrets:get:project/firefoxreality/github-deploy-key" + - "secrets:get:project/firefoxreality/release-signing-token" + - "secrets:get:project/firefoxreality/symbols-token" + routes: + - notify.email.fxr-releng@mozilla.com.on-any + payload: + maxRunTime: 14400 + image: 'mozillamixedreality/firefoxreality:190312' + features: + taskclusterProxy: true + command: + - /bin/bash + - '--login' + - '-cx' + - >- + git fetch origin + && git config advice.detachedHead false + && git checkout ${event.release.tag_name} + && rm -rf gvr-android-sdk && git clone https://github.com/MozillaReality/FirefoxReality-gvr-android-sdk.git gvr-android-sdk + && git submodule update + && . tools/taskcluster/get_third_party.sh + && cp tools/gradle/taskcluster.properties ./user.properties + && ./gradlew --no-daemon --console=plain clean `python tools/taskcluster/build_targets.py ${event.release.tag_name}` + && python tools/taskcluster/fetch_secret.py -s project/firefoxreality/release-signing-token -o token -n token + && python tools/taskcluster/sign_apk.py -t token -r + && python tools/taskcluster/archive_debug_apk.py + && . tools/taskcluster/upload_symbols.sh + artifacts: + 'public': + type: 'directory' + path: '/opt/FirefoxReality/builds/' + expires: {$fromNow: '1 year'} + metadata: + name: Firefox Reality for Android - Release Build (${event.release.tag_name}) + description: Building Firefox Reality for Android (via Gradle) - triggered by release + owner: noreply@mozilla.com + source: ${repository} diff --git a/README.md b/README.md index fcbf52d67c..e76431cfce 100644 --- a/README.md +++ b/README.md @@ -100,14 +100,19 @@ cp ./extra/wavesdk/build.gradle ./third_party/wavesdk Make certain to set the build flavor to `wavevrDebug` in Android Studio before building the project. -## Using a custom GeckoView +## Local Development -Create a file called `user.properties` in the top-level project directory. Add a variable called `geckoViewLocalArm` and `geckoViewLocalX86` and set it to the location of your locally built AAR: +### Dependency substitutions + +You might be interested in building this project against local versions of some of the dependencies. +This could be done either by using a [local maven repository](https://mozilla-mobile.github.io/android-components/contributing/testing-components-inside-app) (quite cumbersome), or via Gradle's [dependency substitutions](https://docs.gradle.org/current/userguide/customizing_dependency_resolution_behavior.html) (not at all cumbersome!). + +Currently, the substitution flow is streamlined for some of the core dependencies via configuration flags in `local.properties`. You can build against a local checkout of the following dependencies by specifying their local paths: +- [GeckoView](https://hg.mozilla.org/mozilla-central), specifying its path via `dependencySubstitutions.geckoviewTopsrcdir=/path/to/mozilla-central` (and, optionally, `dependencySubstitutions.geckoviewTopobjdir=/path/to/topobjdir`). See [Bug 1533465](https://bugzilla.mozilla.org/show_bug.cgi?id=1533465). + - This assumes that you have built, packaged, and published your local GeckoView -- but don't worry, the dependency substitution script has the latest instructions for doing that. + +Do not forget to run a Gradle sync in Android Studio after changing `local.properties`. If you specified any substitutions, they will be reflected in the modules list, and you'll be able to modify them from a single Android Studio window. -```ini -geckoViewLocalArm=/path/to/your/build/geckoview-nightly-armeabi-v7a-64.0.20180924100359.aar -geckoViewLocalX86=/path/to/your/build/geckoview-nightly-x86-64.0.20180924100359.aar -``` ## Install dev and production builds on device simultaneously @@ -142,16 +147,6 @@ npm run compress Enable [USB Remote Debugging](https://github.com/MozillaReality/FirefoxReality/wiki/Developer-Info#remote-debugging) on the device. -### `Could not get unknown property 'geckoViewLocal' for build 'FirefoxReality'[...]` - -```bash -./mach build -./mach package -./mach android archive-geckoview -find $objdir -name *.aar -echo "geckoViewLocalArm=$objdir/gradle/build/mobile/android/geckoview/outputs/aar/geckoview-official-withGeckoBinaries-noMinApi-release.aar" > $FirefoxReality/user.properties -``` - ### **`Firefox > Web Developer > WebIDE > Performance`** gets stuck with greyed out "stop and show profile" Restart FxR and close and re-open the WebIDE page. diff --git a/app/build.gradle b/app/build.gradle index 73317fd5c5..212411d7bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,22 +12,22 @@ def getGitHash = { -> } def getCrashRestartDisabled = { -> - if (gradle.hasProperty("disableCrashRestart")) { - return gradle.disableCrashRestart + if (gradle.hasProperty("userProperties.disableCrashRestart")) { + return gradle."userProperties.disableCrashRestart" } return "false" } def getDevApplicationIdSuffix = { -> - if (gradle.hasProperty("simultaneousDevProduction")) { - return gradle.simultaneousDevProduction == "true" ? ".dev" : "" + if (gradle.hasProperty("userProperties.simultaneousDevProduction")) { + return gradle."userProperties.simultaneousDevProduction" == "true" ? ".dev" : "" } return "" } def getUseDebugSigningOnRelease = { -> - if (gradle.hasProperty("useDebugSigningOnRelease")) { - return gradle.useDebugSigningOnRelease == "true" + if (gradle.hasProperty("userProperties.useDebugSigningOnRelease")) { + return gradle."userProperties.useDebugSigningOnRelease" == "true" } return false } @@ -39,7 +39,7 @@ android { minSdkVersion build_versions.min_sdk targetSdkVersion build_versions.target_sdk versionCode 1 - versionName "1.4" + versionName "1.5" buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\"" buildConfigField "Boolean", "DISABLE_CRASH_RESTART", getCrashRestartDisabled() testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -59,7 +59,7 @@ android { } } - if (gradle.hasProperty('taskclusterBuild')) { + if (gradle.hasProperty('userProperties.taskclusterBuild')) { project.archivesBaseName = "FirefoxReality-$defaultConfig.versionName-$generatedVersionCode" defaultConfig.versionCode = generatedVersionCode } else { @@ -476,21 +476,25 @@ if (findProject(':wavesdk')) { } } -if (findProject(':geckoview-local')) { - dependencies { - implementation project(':geckoview-local') - implementation deps.snakeyaml - } -} else { - dependencies { - // To see what the latest geckoview-nightly version is go here: - // https://maven.mozilla.org/?prefix=maven2/org/mozilla/geckoview/geckoview-nightly-armeabi-v7a/ - armImplementation deps.gecko_view.nightly_armv7a - arm64Implementation deps.gecko_view.nightly_arm64 - x86_64Implementation deps.gecko_view.nightly_x86_64 - } +dependencies { + // To see what the latest geckoview-nightly version is go here: + // https://maven.mozilla.org/?prefix=maven2/org/mozilla/geckoview/geckoview-nightly-armeabi-v7a/ + armImplementation deps.gecko_view.nightly_armv7a + arm64Implementation deps.gecko_view.nightly_arm64 + x86_64Implementation deps.gecko_view.nightly_x86_64 } +if (gradle.hasProperty('geckoViewLocalArm') || gradle.hasProperty('geckoViewLocalX86')) { + throw new GradleException("geckoViewLocal{Arm,X86} are deprecated: use geckoViewLocalTopsrcdir and geckoViewLocalTopobjdir") +} + +if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopsrcdir')) { + if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopobjdir')) { + ext.topobjdir = gradle."localProperties.dependencySubstitutions.geckoviewTopobjdir" + } + ext.topsrcdir = gradle."localProperties.dependencySubstitutions.geckoviewTopsrcdir" + apply from: "${topsrcdir}/substitute-local-geckoview.gradle" +} // ------------------------------------------------------------------------------------------------- // Dynamically set versionCode (See tools/build/versionCode.gradle @@ -498,7 +502,7 @@ if (findProject(':geckoview-local')) { android.applicationVariants.all { variant -> def buildType = variant.buildType.name - if (gradle.hasProperty('taskclusterBuild')) { + if (gradle.hasProperty('userProperties.taskclusterBuild')) { def versionCode = generatedVersionCode // The Google Play Store does not allow multiple APKs for the same app that all have the diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index 327ce644fe..d6dcdad297 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.Process; import android.util.Log; import android.util.Pair; import android.view.KeyEvent; @@ -69,6 +70,7 @@ import org.mozilla.vrbrowser.utils.ConnectivityReceiver; import org.mozilla.vrbrowser.utils.LocaleUtils; import org.mozilla.vrbrowser.utils.ServoUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.io.IOException; import java.net.URISyntaxException; @@ -112,7 +114,7 @@ public void run() { static final int SwipeDelay = 1000; // milliseconds static final long RESET_CRASH_COUNT_DELAY = 5000; - static final String LOGTAG = "VRB"; + static final String LOGTAG = SystemUtils.createLogtag(VRBrowserActivity.class); HashMap mWidgets; private int mWidgetHandleIndex = 1; AudioEngine mAudioEngine; @@ -178,6 +180,7 @@ protected void attachBaseContext(Context base) { @Override protected void onCreate(Bundle savedInstanceState) { + SettingsStore.getInstance(getBaseContext()).setPid(Process.myPid()); // Fix for infinite restart on startup crashes. long count = SettingsStore.getInstance(getBaseContext()).getCrashRestartCount(); boolean cancelRestart = count > CrashReporterService.MAX_RESTART_COUNT; @@ -270,6 +273,11 @@ public void onWindowBorderChanged(@NonNull WindowWidget aChangeWindow) { public void onWindowsMoved() { updateWidget(mTray); } + + @Override + public void onWindowClosed() { + updateWidget(mTray); + } }); // Create Browser navigation widget @@ -312,12 +320,14 @@ private void attachToWindow(@NonNull WindowWidget aWindow, @Nullable WindowWidge @Override protected void onStart() { + SettingsStore.getInstance(getBaseContext()).setPid(Process.myPid()); super.onStart(); TelemetryWrapper.start(); } @Override protected void onStop() { + SettingsStore.getInstance(getBaseContext()).setPid(0); super.onStop(); if (SettingsStore.getInstance(this).getCylinderDensity() > 0.0f) { @@ -336,8 +346,6 @@ protected void onPause() { } mAudioEngine.pauseEngine(); - SessionStore.get().onPause(); - mWindows.onPause(); for (Widget widget: mWidgets.values()) { @@ -355,12 +363,13 @@ protected void onPause() { @Override protected void onResume() { + MotionEventGenerator.clearDevices(); mWidgetContainer.getViewTreeObserver().addOnGlobalFocusChangeListener(globalFocusListener); if (mOffscreenDisplay != null) { mOffscreenDisplay.onResume(); } - SessionStore.get().onResume(); + mWindows.onResume(); mAudioEngine.resumeEngine(); for (Widget widget: mWidgets.values()) { @@ -373,6 +382,7 @@ protected void onResume() { @Override protected void onDestroy() { + SettingsStore.getInstance(getBaseContext()).setPid(0); // Unregister the crash service broadcast receiver unregisterReceiver(mCrashReceiver); mSearchEngineWrapper.unregisterForUpdates(); @@ -392,8 +402,6 @@ protected void onDestroy() { } // Remove all widget listeners - mTray.removeListeners(mWindows); - mTray.onDestroy(); mWindows.onDestroy(); SessionStore.get().onDestroy(); @@ -796,6 +804,7 @@ void pauseGeckoViewCompositor() { return; } mIsPresentingImmersive = true; + mWindows.enterImmersiveMode(); TelemetryWrapper.startImmersive(); PauseCompositorRunnable runnable = new PauseCompositorRunnable(); @@ -818,6 +827,7 @@ void resumeGeckoViewCompositor() { return; } mIsPresentingImmersive = false; + mWindows.exitImmersiveMode(); // Show the window in front of you when you exit immersive mode. resetUIYaw(); @@ -1204,7 +1214,9 @@ public void popWorldBrightness(Object aKey) { @Override public void setTrayVisible(boolean visible) { - mTray.setTrayVisible(visible); + if (mTray != null && !mTray.isReleased()) { + mTray.setTrayVisible(visible); + } } @Override diff --git a/app/src/common/shared/org/mozilla/vrbrowser/audio/AudioEngine.java b/app/src/common/shared/org/mozilla/vrbrowser/audio/AudioEngine.java index 5b23575c57..2f437a30cd 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/audio/AudioEngine.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/audio/AudioEngine.java @@ -6,6 +6,9 @@ package org.mozilla.vrbrowser.audio; import android.content.Context; + +import org.mozilla.vrbrowser.utils.SystemUtils; + import java.util.concurrent.ConcurrentHashMap; public class AudioEngine { @@ -15,7 +18,7 @@ public class AudioEngine { private float mMasterVolume = 1.0f; private static ConcurrentHashMap mEngines = new ConcurrentHashMap<>(); private boolean mEnabled; - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(AudioEngine.class); public enum SoundType { STEREO, diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt b/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt index aaa7ecfbfb..93c994df2d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt @@ -21,6 +21,7 @@ class BookmarksStore constructor(val context: Context) { interface BookmarkListener { fun onBookmarksUpdated() + fun onBookmarkAdded() } fun addListener(aListener: BookmarkListener) { @@ -44,6 +45,7 @@ class BookmarksStore constructor(val context: Context) { fun addBookmark(aURL: String, aTitle: String) = GlobalScope.future { storage.addItem(BookmarkRoot.Mobile.id, aURL, aTitle, null) notifyListeners() + notifyAddedListeners() } fun deleteBookmarkByURL(aURL: String) = GlobalScope.future { @@ -89,4 +91,15 @@ class BookmarksStore constructor(val context: Context) { } } } + + private fun notifyAddedListeners() { + if (listeners.size > 0) { + val listenersCopy = ArrayList(listeners) + Handler(Looper.getMainLooper()).post { + for (listener in listenersCopy) { + listener.onBookmarkAdded() + } + } + } + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/Media.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/Media.java index 2b1c49ba0a..8bef8e632f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/Media.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/Media.java @@ -1,12 +1,14 @@ package org.mozilla.vrbrowser.browser; +import androidx.annotation.NonNull; + import org.mozilla.geckoview.MediaElement; +import org.mozilla.vrbrowser.utils.SystemUtils; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import java.util.concurrent.CopyOnWriteArrayList; public class Media implements MediaElement.Delegate { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(Media.class); private boolean mIsFullscreen = false; private double mCurrentTime = 0.0f; private MediaElement.Metadata mMetaData; @@ -17,17 +19,22 @@ public class Media implements MediaElement.Delegate { private double mVolume = 1.0f; private boolean mIsMuted = false; private boolean mIsUnloaded = false; - private org.mozilla.geckoview.MediaElement mMedia; - private MediaElement.Delegate mDelegate; + private MediaElement mMedia; + private CopyOnWriteArrayList mMediaListeners; private ResizeDelegate mResizeDelegate; public Media(@NonNull MediaElement aMediaElement) { mMedia = aMediaElement; + mMediaListeners = new CopyOnWriteArrayList<>(); aMediaElement.setDelegate(this); } - public void setDelegate(@Nullable MediaElement.Delegate aDelegate) { - mDelegate = aDelegate; + public void addMediaListener(MediaElement.Delegate aListener) { + mMediaListeners.add(aListener); + } + + public void removeMediaListener(MediaElement.Delegate aListener) { + mMediaListeners.remove(aListener); } public double getDuration() { @@ -103,7 +110,7 @@ public void setMuted(boolean aIsMuted) { public void unload() { mIsUnloaded = true; - mDelegate = null; + mMediaListeners.clear(); } public int getWidth() { @@ -132,17 +139,13 @@ public void onPlaybackStateChange(MediaElement mediaElement, int playbackState) } else if (playbackState == MediaElement.MEDIA_STATE_ENDED) { mEnded = true; } - if (mDelegate != null) { - mDelegate.onPlaybackStateChange(mediaElement, playbackState); - } + mMediaListeners.forEach(listener -> listener.onPlaybackStateChange(mediaElement, playbackState)); } @Override public void onReadyStateChange(MediaElement mediaElement, int readyState) { mReadyState = readyState; - if (mDelegate != null) { - mDelegate.onReadyStateChange(mediaElement, readyState); - } + mMediaListeners.forEach(listener -> listener.onReadyStateChange(mediaElement, readyState)); } @Override @@ -150,9 +153,7 @@ public void onMetadataChange(MediaElement mediaElement, MediaElement.Metadata me final int oldWidth = getWidth(); final int oldHeight = getHeight(); mMetaData = metaData; - if (mDelegate != null) { - mDelegate.onMetadataChange(mediaElement, metaData); - } + mMediaListeners.forEach(listener -> listener.onMetadataChange(mediaElement, metaData)); if (mResizeDelegate!= null && metaData != null) { final int w = getWidth(); @@ -165,18 +166,14 @@ public void onMetadataChange(MediaElement mediaElement, MediaElement.Metadata me @Override public void onLoadProgress(MediaElement mediaElement, MediaElement.LoadProgressInfo progressInfo) { - if (mDelegate != null) { - mDelegate.onLoadProgress(mediaElement, progressInfo); - } + mMediaListeners.forEach(listener -> listener.onLoadProgress(mediaElement, progressInfo)); } @Override public void onVolumeChange(MediaElement mediaElement, double volume, boolean muted) { mVolume = volume; mIsMuted = muted; - if (mDelegate != null) { - mDelegate.onVolumeChange(mediaElement, volume, muted); - } + mMediaListeners.forEach(listener -> listener.onVolumeChange(mediaElement, volume, muted)); } @Override @@ -186,31 +183,23 @@ public void onTimeChange(MediaElement mediaElement, double time) { if (duration <= 0 || mCurrentTime < getDuration()) { mEnded = false; } - if (mDelegate != null) { - mDelegate.onTimeChange(mediaElement, time); - } + mMediaListeners.forEach(listener -> listener.onTimeChange(mediaElement, time)); } @Override public void onPlaybackRateChange(MediaElement mediaElement, double rate) { mPlaybackRate = rate; - if (mDelegate != null) { - mDelegate.onPlaybackRateChange(mediaElement, rate); - } + mMediaListeners.forEach(listener -> listener.onPlaybackRateChange(mediaElement, rate)); } @Override public void onFullscreenChange(MediaElement mediaElement, boolean fullscreen) { mIsFullscreen = fullscreen; - if (mDelegate != null) { - mDelegate.onFullscreenChange(mediaElement, fullscreen); - } + mMediaListeners.forEach(listener -> listener.onFullscreenChange(mediaElement, fullscreen)); } @Override public void onError(MediaElement mediaElement, int code) { - if (mDelegate != null) { - mDelegate.onError(mediaElement, code); - } + mMediaListeners.forEach(listener -> listener.onError(mediaElement, code)); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/PermissionDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/PermissionDelegate.java index 5dceb8f893..d3d8b0b273 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/PermissionDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/PermissionDelegate.java @@ -14,6 +14,7 @@ import org.mozilla.vrbrowser.browser.engine.SessionStore; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.dialogs.PermissionWidget; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.ArrayList; import java.util.Arrays; @@ -22,7 +23,7 @@ public class PermissionDelegate implements GeckoSession.PermissionDelegate, Widg static final int PERMISSION_REQUEST_CODE = 1143; - static final String LOGTAG = "VRB"; + static final String LOGTAG = SystemUtils.createLogtag(PermissionDelegate.class); private Context mContext; private int mParentWidgetHandle; private WidgetManagerDelegate mWidgetManager; 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 28af9e1304..eb74987b88 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java @@ -15,6 +15,7 @@ import org.mozilla.vrbrowser.utils.DeviceType; import org.mozilla.vrbrowser.utils.LocaleUtils; import org.mozilla.vrbrowser.utils.StringUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import androidx.annotation.NonNull; @@ -26,7 +27,7 @@ public class SettingsStore { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(SettingsStore.class); private static SettingsStore mSettingsInstance; @@ -265,25 +266,11 @@ public void setDisplayDensity(float aDensity) { } public int getWindowWidth() { - return mPrefs.getInt( - mContext.getString(R.string.settings_key_window_width), WINDOW_WIDTH_DEFAULT); - } - - public void setWindowWidth(int aWindowWidth) { - SharedPreferences.Editor editor = mPrefs.edit(); - editor.putInt(mContext.getString(R.string.settings_key_window_width), aWindowWidth); - editor.commit(); + return WINDOW_WIDTH_DEFAULT; } public int getWindowHeight() { - return mPrefs.getInt( - mContext.getString(R.string.settings_key_window_height), WINDOW_HEIGHT_DEFAULT); - } - - public void setWindowHeight(int aWindowHeight) { - SharedPreferences.Editor editor = mPrefs.edit(); - editor.putInt(mContext.getString(R.string.settings_key_window_height), aWindowHeight); - editor.commit(); + return WINDOW_HEIGHT_DEFAULT; } public float getWindowAspect() { @@ -302,25 +289,11 @@ public void setDisplayDpi(int aDpi) { } public int getMaxWindowWidth() { - return mPrefs.getInt( - mContext.getString(R.string.settings_key_max_window_width), MAX_WINDOW_WIDTH_DEFAULT); - } - - public void setMaxWindowWidth(int aMaxWindowWidth) { - SharedPreferences.Editor editor = mPrefs.edit(); - editor.putInt(mContext.getString(R.string.settings_key_max_window_width), aMaxWindowWidth); - editor.commit(); + return MAX_WINDOW_WIDTH_DEFAULT; } public int getMaxWindowHeight() { - return mPrefs.getInt( - mContext.getString(R.string.settings_key_max_window_height), MAX_WINDOW_HEIGHT_DEFAULT); - } - - public void setMaxWindowHeight(int aMaxWindowHeight) { - SharedPreferences.Editor editor = mPrefs.edit(); - editor.putInt(mContext.getString(R.string.settings_key_max_window_height), aMaxWindowHeight); - editor.commit(); + return MAX_WINDOW_HEIGHT_DEFAULT; } public String getEnvironment() { @@ -426,7 +399,7 @@ public ArrayList getContentLocales() { String json = mPrefs.getString( mContext.getString(R.string.settings_key_content_languages), - new JSONArray().toString()); + null); try { JSONArray jsonArray = new JSONArray(json); @@ -434,11 +407,11 @@ public ArrayList getContentLocales() { result.add(jsonArray.getString(i)); } - } catch (JSONException e) { - e.printStackTrace(); - } + return result; - return result; + } catch (Exception e) { + return null; + } } public void setContentLocales(List languages) { @@ -566,5 +539,25 @@ public void setDebugLoggingEnabled(boolean isEnabled) { editor.putBoolean(mContext.getString(R.string.settings_key_debug_logging), isEnabled); editor.commit(); } + + public boolean isAutoplayEnabled() { + return mPrefs.getBoolean(mContext.getString(R.string.settings_key_autoplay), AUTOPLAY_ENABLED); + } + + public void setAutoplayEnabled(boolean isEnabled) { + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putBoolean(mContext.getString(R.string.settings_key_autoplay), isEnabled); + editor.commit(); + } + + public void setPid(int aPid) { + SharedPreferences.Editor editor = mPrefs.edit(); + editor.putInt(mContext.getString(R.string.settings_key_pid), aPid); + editor.commit(); + } + + public int getPid() { + return mPrefs.getInt(mContext.getString(R.string.settings_key_pid), 0); + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/UserAgentOverride.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/UserAgentOverride.java index aa5de0661b..db2b693881 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/UserAgentOverride.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/UserAgentOverride.java @@ -7,6 +7,7 @@ import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.io.IOException; import java.io.InputStream; @@ -19,7 +20,7 @@ import java.util.List; public class UserAgentOverride { - private final static String LOGTAG = "VRB"; + private final static String LOGTAG = SystemUtils.createLogtag(UserAgentOverride.class); private static final String NO_OVERRIDE_FOUND = "NO OVERRIDE USER AGENT FOUND"; private ArrayMap mOverrideMap; private ArrayMap mOverrideCache; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStack.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStack.java index 6381478504..843bbf0b5f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStack.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStack.java @@ -35,6 +35,7 @@ import org.mozilla.vrbrowser.geolocation.GeolocationData; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.utils.InternalPages; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.net.URI; import java.net.URISyntaxException; @@ -55,8 +56,7 @@ public class SessionStack implements ContentBlocking.Delegate, GeckoSession.Navi GeckoSession.PromptDelegate, GeckoSession.MediaDelegate, GeckoSession.HistoryDelegate, SharedPreferences.OnSharedPreferenceChangeListener { - private static final String LOGTAG = "VRB"; - + private static final String LOGTAG = SystemUtils.createLogtag(SessionStack.class); // You can test a local file using: "resource://android/assets/webvr/index.html" public static final int NO_SESSION = -1; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionState.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionState.java index dd4466b3a3..16eb585f40 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionState.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionState.java @@ -73,15 +73,17 @@ public void write(JsonWriter out, T value) throws IOException { out.name("mTitle").value(session.mTitle); out.name("mFullScreen").value(session.mFullScreen); out.name("mSettings").jsonValue(gson.toJson(session.mSettings)); - if (session.mSession.getSettings().getUsePrivateMode()) { - out.name("mSessionState").jsonValue(null); - - } else { - if (session.mSessionState != null) { - out.name("mSessionState").jsonValue(gsDelegate.toJson(session.mSessionState)); + if (session.mSession != null) { + if (session.mSession.getSettings().getUsePrivateMode()) { + out.name("mSessionState").jsonValue(null); } else { - out.name("mSessionState").jsonValue(null); + if (session.mSessionState != null) { + out.name("mSessionState").jsonValue(gsDelegate.toJson(session.mSessionState)); + + } else { + out.name("mSessionState").jsonValue(null); + } } } out.endObject(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java index 63fde33227..e5f6c97d53 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java @@ -72,6 +72,7 @@ public void setContext(Context context, Bundle aExtras) { runtimeSettingsBuilder.displayDpiOverride(SettingsStore.getInstance(context).getDisplayDpi()); runtimeSettingsBuilder.screenSizeOverride(SettingsStore.getInstance(context).getMaxWindowWidth(), SettingsStore.getInstance(context).getMaxWindowHeight()); + runtimeSettingsBuilder.autoplayDefault(SettingsStore.getInstance(mContext).isAutoplayEnabled() ? GeckoRuntimeSettings.AUTOPLAY_DEFAULT_ALLOWED : GeckoRuntimeSettings.AUTOPLAY_DEFAULT_BLOCKED); if (SettingsStore.getInstance(context).getTransparentBorderWidth() > 0) { runtimeSettingsBuilder.useMaxScreenDepth(true); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/CrashReporterService.java b/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/CrashReporterService.java index de0c715963..eecc5c799e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/CrashReporterService.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/CrashReporterService.java @@ -5,7 +5,6 @@ import android.content.Intent; import android.os.Build; import android.os.Process; -import android.preference.PreferenceManager; import android.util.Log; import org.mozilla.geckoview.GeckoRuntime; @@ -13,13 +12,14 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.VRBrowserActivity; import org.mozilla.vrbrowser.browser.SettingsStore; +import org.mozilla.vrbrowser.utils.SystemUtils; import androidx.annotation.NonNull; import androidx.core.app.JobIntentService; public class CrashReporterService extends JobIntentService { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(CrashReporterService.class); public static final String CRASH_ACTION = BuildConfig.APPLICATION_ID + ".CRASH_ACTION"; public static final String DATA_TAG = "intent"; @@ -31,11 +31,9 @@ public class CrashReporterService extends JobIntentService { public static final long MAX_RESTART_COUNT = 2; private static final int MAX_PID_CHECK_COUNT = 5; - private int mPidCheckCount = 0; - @Override public int onStartCommand(Intent intent, int flags, int startId) { - Log.d(LOGTAG, "======> onStartCommand"); + Log.d(LOGTAG, "onStartCommand"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { enqueueWork(this, CrashReporterService.class, JOB_ID, intent); } @@ -47,34 +45,39 @@ public int onStartCommand(Intent intent, int flags, int startId) { protected void onHandleWork(@NonNull Intent intent) { String action = intent.getAction(); if (GeckoRuntime.ACTION_CRASHED.equals(action)) { + final int activityPid = SettingsStore.getInstance(getBaseContext()).getPid(); boolean fatal = intent.getBooleanExtra(GeckoRuntime.EXTRA_CRASH_FATAL, false); long count = SettingsStore.getInstance(getBaseContext()).getCrashRestartCount(); boolean cancelRestart = count > MAX_RESTART_COUNT; if (cancelRestart || BuildConfig.DISABLE_CRASH_RESTART) { - Log.e(LOGTAG, "CANCEL CRASH HANDLER"); + Log.e(LOGTAG, "Too many restarts. Abort crash reporter service."); return; } if (fatal) { - Log.d(LOGTAG, "======> NATIVE CRASH PARENT " + intent); - final int pid = Process.myPid(); + Log.d(LOGTAG, "Main process crash " + intent); + if (activityPid == 0) { + Log.e(LOGTAG, "Application was quitting. Crash reporter will not trigger a restart."); + return; + } final ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); if (activityManager == null) { return; } + int pidCheckCount = 0; do { - boolean otherProcessesFound = false; + boolean activityFound = false; for (final ActivityManager.RunningAppProcessInfo info : activityManager.getRunningAppProcesses()) { - if (pid != info.pid) { - otherProcessesFound = true; - Log.e(LOGTAG, "======> Found PID " + info.pid); + if (activityPid == info.pid) { + activityFound = true; + Log.e(LOGTAG, "Main activity still running: " + activityPid); break; } } - if (!otherProcessesFound || (mPidCheckCount > MAX_PID_CHECK_COUNT)) { + if (!activityFound || (pidCheckCount > MAX_PID_CHECK_COUNT)) { intent.setClass(CrashReporterService.this, VRBrowserActivity.class); intent.setPackage(BuildConfig.APPLICATION_ID); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -82,7 +85,7 @@ protected void onHandleWork(@NonNull Intent intent) { break; } else { - mPidCheckCount++; + pidCheckCount++; try { Thread.sleep(PID_CHECK_INTERVAL); } catch (InterruptedException e) { @@ -93,14 +96,14 @@ protected void onHandleWork(@NonNull Intent intent) { } while (true); } else { - Log.d(LOGTAG, "======> NATIVE CRASH CONTENT" + intent); + Log.d(LOGTAG, "Content process crash " + intent); Intent broadcastIntent = new Intent(CRASH_ACTION); broadcastIntent.putExtra(DATA_TAG, intent); sendBroadcast(broadcastIntent, getString(R.string.app_permission_name)); } } - Log.d(LOGTAG, "======> Crash reporter job finished"); + Log.d(LOGTAG, "Crash reporter job finished"); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/GlobalExceptionHandler.java b/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/GlobalExceptionHandler.java index 1842411e1f..fad3d76356 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/GlobalExceptionHandler.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/crashreporting/GlobalExceptionHandler.java @@ -5,12 +5,13 @@ import android.util.Log; import org.mozilla.gecko.CrashHandler; +import org.mozilla.vrbrowser.utils.SystemUtils; import androidx.annotation.NonNull; public class GlobalExceptionHandler { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(GlobalExceptionHandler.class); private static GlobalExceptionHandler mInstance; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/geolocation/GeolocationData.java b/app/src/common/shared/org/mozilla/vrbrowser/geolocation/GeolocationData.java index 12e1f71b40..aa1e96b814 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/geolocation/GeolocationData.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/geolocation/GeolocationData.java @@ -4,6 +4,7 @@ import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.vrbrowser.utils.SystemUtils; import androidx.annotation.NonNull; @@ -12,7 +13,7 @@ */ public class GeolocationData { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(GeolocationData.class); private JSONObject mData; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/input/CustomKeyboard.java b/app/src/common/shared/org/mozilla/vrbrowser/input/CustomKeyboard.java index 29f9f3e82c..5a44cab6bf 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/input/CustomKeyboard.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/input/CustomKeyboard.java @@ -114,7 +114,7 @@ private int getParentFieldInt(Object obj, String fieldName) { Field mField = getField(obj.getClass().getSuperclass(), fieldName); mField.setAccessible(true); return mField.getInt(obj); - } catch (IllegalAccessException e) { + } catch (IllegalAccessException | RuntimeException e) { e.printStackTrace(); return 0; } @@ -125,7 +125,7 @@ private Object getFieldObject(Object obj, String fieldName) { Field mField = getField(obj.getClass(), fieldName); mField.setAccessible(true); return mField.get(obj); - } catch (IllegalAccessException e) { + } catch (IllegalAccessException | RuntimeException e) { e.printStackTrace(); return null; } @@ -135,7 +135,7 @@ private Object getParentFieldObject(Object obj, String fieldName) { Field mField = getField(obj.getClass().getSuperclass(), fieldName); mField.setAccessible(true); return mField.get(this); - } catch (IllegalAccessException e) { + } catch (IllegalAccessException| RuntimeException e) { e.printStackTrace(); return null; } @@ -157,14 +157,15 @@ public static Field getField(Class clazz, String fieldName) { } public static void setParentField(Object obj, String fieldName, Object value) { + if (obj.getClass().getSuperclass() == null) { + return; + } try { Field privateField = obj.getClass().getSuperclass().getDeclaredField(fieldName); privateField.setAccessible(true); privateField.set(obj, value); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { + } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/input/MotionEventGenerator.java b/app/src/common/shared/org/mozilla/vrbrowser/input/MotionEventGenerator.java index ff577801a4..b8f2554fe6 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/input/MotionEventGenerator.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/input/MotionEventGenerator.java @@ -12,9 +12,10 @@ import android.util.SparseArray; import org.mozilla.vrbrowser.ui.widgets.Widget; +import org.mozilla.vrbrowser.utils.SystemUtils; public class MotionEventGenerator { - static final String LOGTAG = "VRB"; + static final String LOGTAG = SystemUtils.createLogtag(MotionEventGenerator.class); static class Device { int mDevice; Widget mPreviousWidget = null; @@ -131,4 +132,8 @@ public static void dispatchScroll(Widget aWidget, int aDevice, float aX, float a device.mCoords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, 0.0f); device.mCoords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, 0.0f); } + + public static void clearDevices() { + devices.clear(); + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/telemetry/TelemetryWrapper.java b/app/src/common/shared/org/mozilla/vrbrowser/telemetry/TelemetryWrapper.java index e37aaf0a71..6632085738 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/telemetry/TelemetryWrapper.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/telemetry/TelemetryWrapper.java @@ -24,6 +24,7 @@ import org.mozilla.vrbrowser.browser.SettingsStore; import org.mozilla.vrbrowser.search.SearchEngineWrapper; import org.mozilla.vrbrowser.utils.DeviceType; +import org.mozilla.vrbrowser.utils.SystemUtils; import org.mozilla.vrbrowser.utils.UrlUtils; import java.net.URI; @@ -40,7 +41,7 @@ public class TelemetryWrapper { private final static String APP_NAME = "FirefoxReality"; - private final static String LOGTAG = TelemetryWrapper.class.getSimpleName(); + private final static String LOGTAG = SystemUtils.createLogtag(TelemetryWrapper.class); private final static int MIN_LOAD_TIME = 40; private final static int LOADING_BUCKET_SIZE_MS = 100; private final static int MIN_IMMERSIVE_TIME = 1000; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/OffscreenDisplay.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/OffscreenDisplay.java index c42efdc3e5..f861938340 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/OffscreenDisplay.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/OffscreenDisplay.java @@ -17,8 +17,10 @@ import android.view.View; import android.view.ViewGroup; +import org.mozilla.vrbrowser.utils.SystemUtils; + public class OffscreenDisplay { - final String LOGTAG = "VRB"; + final String LOGTAG = SystemUtils.createLogtag(OffscreenDisplay.class); private Context mContext; private int mWidth; private int mHeight; 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 17cabbf1c6..208405cb0c 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,17 +1,20 @@ package org.mozilla.vrbrowser.ui.adapters; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ImageSpan; import android.view.View; import android.widget.TextView; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.databinding.BindingAdapter; -import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -import java.util.TimeZone; public class BindingAdapters { @@ -53,4 +56,13 @@ public static void bindDate(@NonNull TextView textView, long timestamp) { } textView.setText(androidDateTime.concat(AmPm)); } + + @BindingAdapter(value={"textDrawable", "textString"}) + public static void setSpannableString(@NonNull TextView textView, Drawable drawable, String text) { + SpannableString spannableString = new SpannableString(text); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM); + spannableString.setSpan(span, spannableString.toString().indexOf("@"), spannableString.toString().indexOf("@")+1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + textView.setText(spannableString); + } } \ No newline at end of file diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java index a96bada138..2f1bafa1a2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java @@ -16,9 +16,10 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.databinding.BookmarkItemBinding; -import org.mozilla.vrbrowser.ui.callbacks.BookmarkClickCallback; +import org.mozilla.vrbrowser.ui.callbacks.BookmarkItemCallback; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.AnimationHelper; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.List; import java.util.Objects; @@ -27,7 +28,7 @@ public class BookmarkAdapter extends RecyclerView.Adapter { - static final String LOGTAG = BookmarkAdapter.class.getSimpleName(); + static final String LOGTAG = SystemUtils.createLogtag(BookmarkAdapter.class); private static final int ICON_ANIMATION_DURATION = 200; @@ -39,13 +40,13 @@ public class BookmarkAdapter extends RecyclerView.Adapter { + int ev = motionEvent.getActionMasked(); + switch (ev) { + case MotionEvent.ACTION_HOVER_ENTER: + binding.setIsHovered(true); + return false; + + case MotionEvent.ACTION_HOVER_EXIT: + binding.setIsHovered(false); + return false; + } + + return false; + }); + binding.layout.setOnTouchListener((view, motionEvent) -> { + int ev = motionEvent.getActionMasked(); + switch (ev) { + case MotionEvent.ACTION_UP: + return false; + + case MotionEvent.ACTION_DOWN: + binding.setIsHovered(true); + return false; + + case MotionEvent.ACTION_CANCEL: + binding.setIsHovered(false); + return false; + } + return false; + }); + binding.more.setOnHoverListener(mIconHoverListener); + binding.more.setOnTouchListener((view, motionEvent) -> { + int ev = motionEvent.getActionMasked(); + switch (ev) { + case MotionEvent.ACTION_UP: + mBookmarkItemCallback.onMore(view, binding.getItem()); + return true; + + case MotionEvent.ACTION_DOWN: + return true; + } + return false; + }); binding.trash.setOnHoverListener(mIconHoverListener); binding.trash.setOnTouchListener((view, motionEvent) -> { int ev = motionEvent.getActionMasked(); switch (ev) { case MotionEvent.ACTION_UP: - mBookmarkClickCallback.onDelete(binding.getBookmark()); + mBookmarkItemCallback.onDelete(view, binding.getItem()); return true; case MotionEvent.ACTION_DOWN: @@ -127,7 +179,7 @@ public BookmarkViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int view @Override public void onBindViewHolder(@NonNull BookmarkViewHolder holder, int position) { - holder.binding.setBookmark(mBookmarkList.get(position)); + holder.binding.setItem(mBookmarkList.get(position)); holder.binding.executePendingBindings(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java index abb2822959..bd8dbff32d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java @@ -19,15 +19,17 @@ import org.mozilla.vrbrowser.ui.callbacks.HistoryItemCallback; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.AnimationHelper; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.List; import java.util.Objects; import mozilla.components.concept.storage.VisitInfo; +import mozilla.components.concept.storage.VisitType; public class HistoryAdapter extends RecyclerView.Adapter { - static final String LOGTAG = HistoryAdapter.class.getSimpleName(); + static final String LOGTAG = SystemUtils.createLogtag(HistoryAdapter.class); private static final int ICON_ANIMATION_DURATION = 200; @@ -44,8 +46,8 @@ public class HistoryAdapter extends RecyclerView.Adapter + item.getVisitType() == VisitType.NOT_A_VISIT) ? + 0 : + mHistoryList.size(); + } + + return 0; } public int getItemPosition(long id) { @@ -140,6 +149,10 @@ public HistoryItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int v case MotionEvent.ACTION_DOWN: binding.setIsHovered(true); return false; + + case MotionEvent.ACTION_CANCEL: + binding.setIsHovered(false); + return false; } return false; }); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java index bc0360c521..3f606633d1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/Language.java @@ -6,13 +6,11 @@ public Language(String id, String name) { this.id = id; this.name = name; this.isPreferred = false; - this.isDefault = false; } private String name; private String id; private boolean isPreferred; - private boolean isDefault; public String getId() { return this.id; @@ -30,14 +28,6 @@ public boolean isPreferred() { return isPreferred; } - public void setDefault(boolean isDefault) { - this.isDefault = isDefault; - } - - public boolean isDefault() { - return isDefault; - } - @Override public int hashCode() { return id.hashCode(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java index 8e5a3ee274..807dfc5b91 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/LanguagesAdapter.java @@ -153,13 +153,13 @@ public void onBindViewHolder(@NonNull LanguageViewHolder holder, int position) { } else if (holder.binding.add.getVisibility() == View.VISIBLE && ViewUtils.isInsideView(holder.binding.add, (int)event.getRawX(), (int)event.getRawY())) { - if (!language.isDefault() && !language.isPreferred()) { + if (!language.isPreferred()) { mLanguageItemCallback.onAdd(holder.binding.add, language); } } else if (holder.binding.delete.getVisibility() == View.VISIBLE && ViewUtils.isInsideView(holder.binding.delete, (int)event.getRawX(), (int)event.getRawY())) { - if (!language.isDefault() && language.isPreferred()) { + if (language.isPreferred()) { mLanguageItemCallback.onRemove(holder.binding.delete, language); } } 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 deleted file mode 100644 index 9028fbc1e0..0000000000 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkClickCallback.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.mozilla.vrbrowser.ui.callbacks; - -import mozilla.components.concept.storage.BookmarkNode; - -public interface BookmarkClickCallback { - void onClick(BookmarkNode bookmark); - void onDelete(BookmarkNode bookmark); -} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkItemCallback.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkItemCallback.java new file mode 100644 index 0000000000..930876b323 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarkItemCallback.java @@ -0,0 +1,11 @@ +package org.mozilla.vrbrowser.ui.callbacks; + +import android.view.View; + +import mozilla.components.concept.storage.BookmarkNode; + +public interface BookmarkItemCallback { + void onClick(View view, BookmarkNode item); + void onDelete(View view, BookmarkNode item); + void onMore(View view, BookmarkNode item); +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarksCallback.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarksCallback.java new file mode 100644 index 0000000000..f33e1a576d --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/BookmarksCallback.java @@ -0,0 +1,12 @@ +package org.mozilla.vrbrowser.ui.callbacks; + +import android.view.View; + +import androidx.annotation.NonNull; + +import mozilla.components.concept.storage.BookmarkNode; + +public interface BookmarksCallback { + void onClearBookmarks(@NonNull View view); + void onShowContextMenu(@NonNull View view, BookmarkNode item, boolean isLastVisibleItem); +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/HistoryCallback.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/HistoryCallback.java index df1250e4c8..7877c02650 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/HistoryCallback.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/HistoryCallback.java @@ -2,9 +2,11 @@ import android.view.View; +import androidx.annotation.NonNull; + import mozilla.components.concept.storage.VisitInfo; public interface HistoryCallback { - void onClearHistory(View view); - void onShowContextMenu(View view, VisitInfo item, boolean isLastVisibleItem); + void onClearHistory(@NonNull View view); + void onShowContextMenu(@NonNull View view, @NonNull VisitInfo item, boolean isLastVisibleItem); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/HistoryItemContextMenuClickCallback.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/HistoryItemContextMenuClickCallback.java deleted file mode 100644 index 05088a63be..0000000000 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/HistoryItemContextMenuClickCallback.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.mozilla.vrbrowser.ui.callbacks; - -import mozilla.components.concept.storage.VisitInfo; - -public interface HistoryItemContextMenuClickCallback { - void onOpenInNewWindowClick(VisitInfo item); - void onAddToBookmarks(VisitInfo item); - void onRemoveFromBookmarks(VisitInfo item); -} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LibraryItemContextMenuClickCallback.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LibraryItemContextMenuClickCallback.java new file mode 100644 index 0000000000..4174c43eab --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/callbacks/LibraryItemContextMenuClickCallback.java @@ -0,0 +1,9 @@ +package org.mozilla.vrbrowser.ui.callbacks; + +import org.mozilla.vrbrowser.ui.views.LibraryItemContextMenu; + +public interface LibraryItemContextMenuClickCallback { + void onOpenInNewWindowClick(LibraryItemContextMenu.LibraryContextMenuItem item); + void onAddToBookmarks(LibraryItemContextMenu.LibraryContextMenuItem item); + void onRemoveFromBookmarks(LibraryItemContextMenu.LibraryContextMenuItem item); +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java index f38f9b17fe..77e2a37a4c 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChinesePinyinKeyboard.java @@ -10,6 +10,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.input.CustomKeyboard; import org.mozilla.vrbrowser.utils.StringUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.ArrayList; import java.util.Arrays; @@ -23,7 +24,7 @@ import androidx.annotation.Nullable; public class ChinesePinyinKeyboard extends BaseKeyboard { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(ChinesePinyinKeyboard.class); private CustomKeyboard mKeyboard; private DBHelper mDB; private HashMap mKeymaps = new HashMap<>(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChineseZhuyinKeyboard.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChineseZhuyinKeyboard.java index 5b9ebba956..005f2122ba 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChineseZhuyinKeyboard.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/ChineseZhuyinKeyboard.java @@ -11,6 +11,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.input.CustomKeyboard; import org.mozilla.vrbrowser.utils.StringUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.ArrayList; import java.util.Collections; @@ -24,7 +25,7 @@ import androidx.annotation.Nullable; public class ChineseZhuyinKeyboard extends BaseKeyboard { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(ChineseZhuyinKeyboard.class); private static final String nonZhuyinReg = "[^ㄅ-ㄩ˙ˊˇˋˉ]"; private CustomKeyboard mKeyboard; private DBWordHelper mWordDB; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/JapaneseKeyboard.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/JapaneseKeyboard.java index 92fe1058e3..6341cfe295 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/JapaneseKeyboard.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/JapaneseKeyboard.java @@ -8,6 +8,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.input.CustomKeyboard; import org.mozilla.vrbrowser.utils.StringUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.ArrayList; import java.util.Arrays; @@ -25,7 +26,7 @@ public class JapaneseKeyboard extends BaseKeyboard { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(JapaneseKeyboard.class); private CustomKeyboard mKeyboard; private CustomKeyboard mSymbolsKeyboard; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/BookmarksView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/BookmarksView.java index 71668caded..d568bd163a 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/BookmarksView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/BookmarksView.java @@ -5,12 +5,17 @@ package org.mozilla.vrbrowser.ui.views; +import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.view.View; import android.widget.FrameLayout; +import androidx.annotation.NonNull; import androidx.databinding.DataBindingUtil; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.audio.AudioEngine; @@ -19,7 +24,8 @@ import org.mozilla.vrbrowser.browser.engine.SessionStore; import org.mozilla.vrbrowser.databinding.BookmarksBinding; import org.mozilla.vrbrowser.ui.adapters.BookmarkAdapter; -import org.mozilla.vrbrowser.ui.callbacks.BookmarkClickCallback; +import org.mozilla.vrbrowser.ui.callbacks.BookmarkItemCallback; +import org.mozilla.vrbrowser.ui.callbacks.BookmarksCallback; import org.mozilla.vrbrowser.utils.UIThreadExecutor; import java.util.List; @@ -48,6 +54,7 @@ public BookmarksView(Context aContext, AttributeSet aAttrs, int aDefStyle) { initialize(aContext); } + @SuppressLint("ClickableViewAccessibility") private void initialize(Context aContext) { mAudio = AudioEngine.fromContext(aContext); @@ -55,48 +62,77 @@ private void initialize(Context aContext) { // Inflate this data binding layout mBinding = DataBindingUtil.inflate(inflater, R.layout.bookmarks, this, true); - mBookmarkAdapter = new BookmarkAdapter(mBookmarkClickCallback, aContext); + mBookmarkAdapter = new BookmarkAdapter(mBookmarkItemCallback, aContext); mBinding.bookmarksList.setAdapter(mBookmarkAdapter); + mBinding.bookmarksList.setOnTouchListener((v, event) -> { + v.requestFocusFromTouch(); + return false; + }); mBinding.setIsLoading(true); mBinding.executePendingBindings(); syncBookmarks(); SessionStore.get().getBookmarkStore().addListener(this); setVisibility(GONE); + + setOnTouchListener((v, event) -> { + v.requestFocusFromTouch(); + return false; + }); } public void onDestroy() { SessionStore.get().getBookmarkStore().removeListener(this); } - private final BookmarkClickCallback mBookmarkClickCallback = new BookmarkClickCallback() { + private final BookmarkItemCallback mBookmarkItemCallback = new BookmarkItemCallback() { @Override - public void onClick(BookmarkNode bookmark) { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } + public void onClick(View view, BookmarkNode item) { + mBinding.bookmarksList.requestFocusFromTouch(); SessionStack sessionStack = SessionStore.get().getActiveStore(); - sessionStack.loadUri(bookmark.getUrl()); + sessionStack.loadUri(item.getUrl()); } @Override - public void onDelete(BookmarkNode bookmark) { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } + public void onDelete(View view, BookmarkNode item) { + mBinding.bookmarksList.requestFocusFromTouch(); mIgnoreNextListener = true; - SessionStore.get().getBookmarkStore().deleteBookmarkById(bookmark.getGuid()); - mBookmarkAdapter.removeItem(bookmark); + SessionStore.get().getBookmarkStore().deleteBookmarkById(item.getGuid()); + mBookmarkAdapter.removeItem(item); if (mBookmarkAdapter.itemCount() == 0) { mBinding.setIsEmpty(true); mBinding.setIsLoading(false); mBinding.executePendingBindings(); } } + + @Override + public void onMore(View view, BookmarkNode item) { + mBinding.bookmarksList.requestFocusFromTouch(); + + int rowPosition = mBookmarkAdapter.getItemPosition(item.getGuid()); + RecyclerView.ViewHolder row = mBinding.bookmarksList.findViewHolderForLayoutPosition(rowPosition); + boolean isLastVisibleItem = false; + if (mBinding.bookmarksList.getLayoutManager() instanceof LinearLayoutManager) { + LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.bookmarksList.getLayoutManager(); + int lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition(); + if (rowPosition == layoutManager.findLastVisibleItemPosition() && rowPosition != lastVisibleItem) { + isLastVisibleItem = true; + } + } + + mBinding.getCallback().onShowContextMenu( + row.itemView, + item, + isLastVisibleItem); + } }; + public void setBookmarksCallback(@NonNull BookmarksCallback callback) { + mBinding.setCallback(callback); + } private void syncBookmarks() { SessionStore.get().getBookmarkStore().getBookmarks().thenAcceptAsync(this::showBookmarks, new UIThreadExecutor()); @@ -113,6 +149,8 @@ private void showBookmarks(List aBookmarks) { mBookmarkAdapter.setBookmarkList(aBookmarks); } mBinding.executePendingBindings(); + mBinding.bookmarksList.post(() -> mBinding.bookmarksList.smoothScrollToPosition( + mBookmarkAdapter.getItemCount() > 0 ? mBookmarkAdapter.getItemCount() - 1 : 0)); } // BookmarksStore.BookmarksViewListener @@ -124,4 +162,9 @@ public void onBookmarksUpdated() { } syncBookmarks(); } + + @Override + public void onBookmarkAdded() { + // Nothing to do + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomListView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomListView.java index 3fb794d12c..2780a540b1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomListView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/CustomListView.java @@ -45,18 +45,18 @@ public boolean isInTouchMode() { protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); - boolean scrollVisible = false; - - if (getChildCount() > 0) { - View first = getChildAt(0); - View last = getChildAt(getChildCount() - 1); - if (first.getTop() < 0 || last.getBottom() > getHeight()) { - scrollVisible = true; - } + boolean fits = getCount() == 0; + + // Check if all the items fit on the ListView + int last = getLastVisiblePosition(); + int first = getFirstVisiblePosition(); + if (!fits && first == 0 && last == getCount() - 1) { + fits = getChildAt(first).getTop() >= 0 && getChildAt(last).getBottom() <= getHeight(); } - setVerticalScrollBarEnabled(scrollVisible); - setFastScrollAlwaysVisible(scrollVisible); + // Hide scrollbar is all item fit. + setVerticalScrollBarEnabled(!fits); + setFastScrollAlwaysVisible(!fits); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryItemContextMenu.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryItemContextMenu.java deleted file mode 100644 index 47bc9e585d..0000000000 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryItemContextMenu.java +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- 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.views; - -import android.content.Context; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.widget.FrameLayout; - -import androidx.databinding.DataBindingUtil; - -import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.browser.engine.SessionStore; -import org.mozilla.vrbrowser.databinding.HistoryItemContextMenuBinding; -import org.mozilla.vrbrowser.ui.callbacks.HistoryItemContextMenuClickCallback; - -import mozilla.components.concept.storage.VisitInfo; - -public class HistoryItemContextMenu extends FrameLayout { - - private static final String LOGTAG = HistoryItemContextMenu.class.getSimpleName(); - - private HistoryItemContextMenuBinding mBinding; - - public HistoryItemContextMenu(Context aContext) { - super(aContext); - initialize(aContext); - } - - public HistoryItemContextMenu(Context aContext, AttributeSet aAttrs) { - super(aContext, aAttrs); - initialize(aContext); - } - - public HistoryItemContextMenu(Context aContext, AttributeSet aAttrs, int aDefStyle) { - super(aContext, aAttrs, aDefStyle); - initialize(aContext); - } - - private void initialize(Context aContext) { - LayoutInflater inflater = LayoutInflater.from(aContext); - - mBinding = DataBindingUtil.inflate(inflater, R.layout.history_item_context_menu, this, true); - } - - public void setItem(VisitInfo item) { - SessionStore.get().getBookmarkStore().isBookmarked(item.getUrl()).thenAccept((isBookmarked -> { - mBinding.setItem(item); - mBinding.setIsBookmarked(isBookmarked); - mBinding.bookmark.setText(isBookmarked ? R.string.history_context_remove_bookmarks : R.string.history_context_add_bookmarks); - invalidate(); - - })).exceptionally(throwable -> { - Log.d(LOGTAG, "Couldn't get the bookmarked status of the history item"); - return null; - }); - } - - public void setContextMenuClickCallback(HistoryItemContextMenuClickCallback callback) { - mBinding.setCallback(callback); - } - -} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java index e9e333b210..770cef76d1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java @@ -39,7 +39,7 @@ public class HistoryView extends FrameLayout implements HistoryStore.HistoryListener { - private static final String LOGTAG = HistoryView.class.getSimpleName(); + private static final String LOGTAG = SystemUtils.createLogtag(HistoryView.class); private HistoryBinding mBinding; private HistoryAdapter mHistoryAdapter; @@ -92,7 +92,8 @@ public void onDestroy() { private final HistoryItemCallback mHistoryItemCallback = new HistoryItemCallback() { @Override public void onClick(View view, VisitInfo item) { - view.requestFocusFromTouch(); + mBinding.historyList.requestFocusFromTouch(); + SessionStack sessionStack = SessionStore.get().getActiveStore(); sessionStack.loadUri(item.getUrl()); } @@ -100,6 +101,7 @@ public void onClick(View view, VisitInfo item) { @Override public void onDelete(View view, VisitInfo item) { mBinding.historyList.requestFocusFromTouch(); + mIgnoreNextListener = true; SessionStore.get().getHistoryStore().deleteHistory(item.getUrl(), item.getVisitTime()); mHistoryAdapter.removeItem(item); @@ -112,14 +114,15 @@ public void onDelete(View view, VisitInfo item) { @Override public void onMore(View view, VisitInfo item) { - view.requestFocusFromTouch(); + mBinding.historyList.requestFocusFromTouch(); int rowPosition = mHistoryAdapter.getItemPosition(item.getVisitTime()); RecyclerView.ViewHolder row = mBinding.historyList.findViewHolderForLayoutPosition(rowPosition); boolean isLastVisibleItem = false; if (mBinding.historyList.getLayoutManager() instanceof LinearLayoutManager) { LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.historyList.getLayoutManager(); - if (rowPosition == layoutManager.findLastVisibleItemPosition()) { + int lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition(); + if (rowPosition == layoutManager.findLastVisibleItemPosition() && rowPosition != lastVisibleItem) { isLastVisibleItem = true; } } @@ -155,7 +158,7 @@ private void syncHistory() { addSection(orderedItems, getResources().getString(R.string.history_section_today), currentTime, todayLimit); addSection(orderedItems, getResources().getString(R.string.history_section_yesterday), todayLimit, yesterdayLimit); addSection(orderedItems, getResources().getString(R.string.history_section_last_week), yesterdayLimit, oneWeekLimit); - addSection(orderedItems, getResources().getString(R.string.history_section_today), oneWeekLimit, 0); + addSection(orderedItems, getResources().getString(R.string.history_section_older), oneWeekLimit, 0); showHistory(orderedItems); @@ -193,6 +196,7 @@ private void showHistory(List historyItems) { mHistoryAdapter.setHistoryList(historyItems); } mBinding.executePendingBindings(); + mBinding.historyList.post(() -> mBinding.historyList.smoothScrollToPosition(0)); } // HistoryStore.HistoryListener diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HoneycombButton.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HoneycombButton.java index 344ad2f683..0aa357bf31 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HoneycombButton.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HoneycombButton.java @@ -14,12 +14,13 @@ import org.mozilla.vrbrowser.utils.DeviceType; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.utils.SystemUtils; import androidx.annotation.Nullable; public class HoneycombButton extends LinearLayout { - private static final String LOGTAG = "VRB"; + private static final String LOGTAG = SystemUtils.createLogtag(HoneycombButton.class); private ImageView mIcon; private TextView mText; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LibraryItemContextMenu.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LibraryItemContextMenu.java new file mode 100644 index 0000000000..4cd5fd593a --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LibraryItemContextMenu.java @@ -0,0 +1,117 @@ +/* -*- 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.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.databinding.DataBindingUtil; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.browser.engine.SessionStore; +import org.mozilla.vrbrowser.databinding.LibraryItemContextMenuBinding; +import org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; + +public class LibraryItemContextMenu extends FrameLayout { + + private static final String LOGTAG = LibraryItemContextMenu.class.getSimpleName(); + + public enum LibraryItemType { + BOOKMARKS, + HISTORY + } + + public static class LibraryContextMenuItem { + + private String url; + private String title; + private LibraryItemType type; + + public LibraryContextMenuItem(@NonNull String url, String title, LibraryItemType type) { + this.url = url; + this.title = title; + this.type = type; + } + + public String getUrl() { + return url; + } + + public String getTitle() { + return title; + } + + public LibraryItemType getType() { + return type; + } + + } + + private LibraryItemContextMenuBinding mBinding; + + public LibraryItemContextMenu(Context aContext) { + super(aContext); + initialize(aContext); + } + + public LibraryItemContextMenu(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public LibraryItemContextMenu(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + private void initialize(Context aContext) { + LayoutInflater inflater = LayoutInflater.from(aContext); + + mBinding = DataBindingUtil.inflate(inflater, R.layout.library_item_context_menu, this, true); + } + + public void setItem(@NonNull LibraryContextMenuItem item) { + mBinding.setItem(item); + SessionStore.get().getBookmarkStore().isBookmarked(item.getUrl()).thenAccept((isBookmarked -> { + mBinding.setIsBookmarked(isBookmarked); + mBinding.bookmark.setText(isBookmarked ? R.string.history_context_remove_bookmarks : R.string.history_context_add_bookmarks); + invalidate(); + + })).exceptionally(throwable -> { + Log.d(LOGTAG, "Couldn't get the bookmarked status of the history item"); + return null; + }); + } + + public void setContextMenuClickCallback(LibraryItemContextMenuClickCallback callback) { + mBinding.setCallback(callback); + } + + public int getMenuHeight() { + if (mBinding.getItem() == null) { + throw new IllegalStateException("setItem must be called before getMenuHeight"); + + } + switch (mBinding.getItem().getType()) { + case BOOKMARKS: + mBinding.bookmarkLayout.setVisibility(GONE); + mBinding.newWindowLayout.setBackgroundResource(R.drawable.library_context_menu_item_background_single); + return WidgetPlacement.dpDimension(getContext(), R.dimen.library_item_row_height); + case HISTORY: + mBinding.bookmarkLayout.setVisibility(VISIBLE); + mBinding.newWindowLayout.setBackgroundResource(R.drawable.library_context_menu_item_background_top); + return WidgetPlacement.dpDimension(getContext(), R.dimen.library_item_row_height) * 2; + } + + return WidgetPlacement.dpDimension(getContext(), R.dimen.library_item_row_height) * 2; + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/NavigationURLBar.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/NavigationURLBar.java index 0b9e834813..9883a4e38b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/NavigationURLBar.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/NavigationURLBar.java @@ -39,6 +39,7 @@ import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.StringUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import org.mozilla.vrbrowser.utils.UIThreadExecutor; import org.mozilla.vrbrowser.utils.UrlUtils; @@ -54,7 +55,7 @@ public class NavigationURLBar extends FrameLayout { - private static final String LOGTAG = NavigationURLBar.class.getSimpleName(); + private static final String LOGTAG = SystemUtils.createLogtag(NavigationURLBar.class); private InlineAutocompleteEditText mURL; private UIButton mMicrophoneButton; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java index 594bb9b8be..5ccd270241 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java @@ -9,6 +9,8 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; +import android.graphics.PointF; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; @@ -22,6 +24,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.ui.widgets.TooltipWidget; import org.mozilla.vrbrowser.ui.widgets.UIWidget; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.ViewUtils; public class UIButton extends AppCompatImageButton implements CustomUIButton { @@ -29,7 +32,8 @@ public class UIButton extends AppCompatImageButton implements CustomUIButton { private enum State { NORMAL, PRIVATE, - ACTIVE + ACTIVE, + NOTIFICATION } private ColorStateList mTintColorList; @@ -39,11 +43,13 @@ private enum State { private @IdRes int mTintColorListRes; private @IdRes int mPrivateModeTintColorListRes; private @IdRes int mActiveModeTintColorListRes; + private @IdRes int mNotificationModeTintColorListRes; private TooltipWidget mTooltipView; private String mTooltipText; private State mState; private int mTooltipDelay; private float mTooltipDensity; + private boolean mCurvedTooltip = true; private ViewUtils.TooltipPosition mTooltipPosition; public UIButton(Context context, AttributeSet attrs) { @@ -62,6 +68,7 @@ public UIButton(Context context, AttributeSet attrs, int defStyleAttr) { mActiveModeBackground = attributes.getDrawable(R.styleable.UIButton_activeModeBackground); mPrivateModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_privateModeTintColorList, 0); mActiveModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_activeModeTintColorList, 0); + mNotificationModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_notificationModeTintColorList, 0); mTooltipDelay = attributes.getInt(R.styleable.UIButton_tooltipDelay, getResources().getInteger(R.integer.tooltip_delay)); mTooltipPosition = ViewUtils.TooltipPosition.fromId(attributes.getInt(R.styleable.UIButton_tooltipPosition, ViewUtils.TooltipPosition.BOTTOM.ordinal())); mTooltipDensity = attributes.getFloat(R.styleable.UIButton_tooltipDensity, getContext().getResources().getDisplayMetrics().density); @@ -98,6 +105,13 @@ public void setTooltip(String text) { } } + public void setCurvedTooltip(boolean aEnabled) { + mCurvedTooltip = aEnabled; + if (mTooltipView != null) { + mTooltipView.setCurvedMode(aEnabled); + } + } + public void setTooltipText(@NonNull String text) { mTooltipText = text; } @@ -155,6 +169,15 @@ public void setActiveMode(boolean isActive) { } } + public void setNotificationMode(boolean isNotification) { + if (isNotification) { + setNotification(); + + } else { + setNormal(); + } + } + public boolean isActive() { return mState == State.ACTIVE; } @@ -196,6 +219,13 @@ private void setActive() { } } + private void setNotification() { + mState = State.NOTIFICATION; + if (mActiveModeTintColorListRes != 0) { + setTintColorList(mNotificationModeTintColorListRes); + } + } + private Runnable mShowTooltipRunnable = new Runnable() { @Override public void run() { @@ -203,9 +233,36 @@ public void run() { return; } - mTooltipView = new TooltipWidget(getContext()); + if (mTooltipView == null) { + mTooltipView = new TooltipWidget(getContext()); + } + mTooltipView.setCurvedMode(mCurvedTooltip); mTooltipView.setText(getTooltip()); - mTooltipView.setLayoutParams(UIButton.this, mTooltipPosition, mTooltipDensity); + + Rect offsetViewBounds = new Rect(); + getDrawingRect(offsetViewBounds); + UIWidget parent = ViewUtils.getParentWidget(UIButton.this); + parent.offsetDescendantRectToMyCoords(UIButton.this, offsetViewBounds); + + float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), parent); + + mTooltipView.getPlacement().parentHandle = parent.getHandle(); + mTooltipView.getPlacement().density = mTooltipDensity; + // At the moment we only support showing tooltips on top or bottom of the target view + if (mTooltipPosition == ViewUtils.TooltipPosition.BOTTOM) { + mTooltipView.getPlacement().anchorY = 1.0f; + mTooltipView.getPlacement().parentAnchorY = 0.0f; + mTooltipView.getPlacement().translationX = (offsetViewBounds.left + UIButton.this.getWidth() / 2.0f) * ratio; + mTooltipView.getPlacement().translationY = -offsetViewBounds.top * ratio; + + } else { + mTooltipView.getPlacement().anchorY = 0.0f; + mTooltipView.getPlacement().parentAnchorY = 1.0f; + mTooltipView.getPlacement().translationX = (offsetViewBounds.left + UIButton.this.getHeight() / 2.0f) * ratio; + mTooltipView.getPlacement().translationY = offsetViewBounds.top * ratio; + } + + mTooltipView.setCurvedMode(false); mTooltipView.show(UIWidget.CLEAR_FOCUS); } }; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrightnessMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrightnessMenuWidget.java index fc03baeca6..57dbf42edb 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrightnessMenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrightnessMenuWidget.java @@ -23,7 +23,7 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.anchorX = 0.0f; aPlacement.anchorY = 0.0f; aPlacement.translationY = WidgetPlacement.dpDimension(getContext(), R.dimen.video_projection_menu_translation_y); - aPlacement.translationZ = 2.0f; + aPlacement.translationZ = 30.0f; } public void setParentWidget(UIWidget aParent) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java index c25c9df6b9..9f53b92823 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java @@ -19,6 +19,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.widget.ImageButton; @@ -49,6 +50,7 @@ import org.mozilla.vrbrowser.ui.keyboards.ChinesePinyinKeyboard; import org.mozilla.vrbrowser.ui.keyboards.EnglishKeyboard; import org.mozilla.vrbrowser.utils.StringUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.ArrayList; import java.util.Locale; @@ -60,8 +62,6 @@ public class KeyboardWidget extends UIWidget implements CustomKeyboardView.OnKeyboardActionListener, AutoCompletionView.Delegate, GeckoSession.TextInputDelegate, WidgetManagerDelegate.FocusChangeListener, VoiceSearchWidget.VoiceSearchDelegate, TextWatcher { - private static final String LOGTAG = "VRB"; - private static int MAX_CHARS_PER_POPUP_LINE = 10; private CustomKeyboardView mKeyboardView; @@ -388,7 +388,7 @@ protected void onDismiss() { @Override public void onPress(int primaryCode) { - Log.d("VRB", "Keyboard onPress " + primaryCode); + Log.d(LOGTAG, "Keyboard onPress " + primaryCode); } @Override @@ -448,7 +448,7 @@ public void onRelease(int primaryCode) { @Override public void onKey(int primaryCode, int[] keyCodes, boolean hasPopup) { - Log.d("VRB", "Keyboard onPress++ " + primaryCode); + Log.d(LOGTAG, "Keyboard onPress++ " + primaryCode); switch (primaryCode) { case Keyboard.KEYCODE_MODE_CHANGE: handleModeChange(); @@ -717,8 +717,9 @@ private void handleDone() { final int action = mEditorInfo.imeOptions & EditorInfo.IME_MASK_ACTION; postInputCommand(() -> connection.performEditorAction(action)); - boolean hide = (action & (EditorInfo.IME_ACTION_DONE | EditorInfo.IME_ACTION_GO | - EditorInfo.IME_ACTION_SEARCH | EditorInfo.IME_ACTION_SEND)) != 0; + boolean hide = (action == EditorInfo.IME_ACTION_DONE) || (action == EditorInfo.IME_ACTION_GO) || + (action == EditorInfo.IME_ACTION_SEARCH) || (action == EditorInfo.IME_ACTION_SEND); + if (hide && mFocusedView != null) { mFocusedView.clearFocus(); } @@ -799,7 +800,12 @@ private String getTextBeforeCursor(InputConnection aConnection) { return ""; } - String fullText = aConnection.getExtractedText(new ExtractedTextRequest(),0).text.toString(); + ExtractedText extracted = aConnection.getExtractedText(new ExtractedTextRequest(),0); + if ((extracted == null) || extracted.text == null) { + return ""; + } + + String fullText = extracted.text.toString(); return aConnection.getTextBeforeCursor(fullText.length(),0).toString(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java index 5e2fcde104..670517dd4f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MediaControlsWidget.java @@ -20,10 +20,10 @@ import org.mozilla.vrbrowser.ui.views.MediaSeekBar; import org.mozilla.vrbrowser.ui.views.UIButton; import org.mozilla.vrbrowser.ui.views.VolumeControl; +import org.mozilla.vrbrowser.utils.SystemUtils; public class MediaControlsWidget extends UIWidget implements MediaElement.Delegate { - private static final String LOGTAG = "VRB"; private Media mMedia; private MediaSeekBar mSeekBar; private VolumeControl mVolumeControl; @@ -244,7 +244,7 @@ public void setMedia(Media aMedia) { return; } if (mMedia != null) { - mMedia.setDelegate(null); + mMedia.removeMediaListener(this); } mMedia = aMedia; boolean enabled = mMedia != null; @@ -266,7 +266,7 @@ public void setMedia(Media aMedia) { onPlaybackStateChange(mMedia.getMediaElement(), MediaElement.MEDIA_STATE_PLAY); } - mMedia.setDelegate(this); + mMedia.addMediaListener(this); } public void setProjectionSelectorEnabled(boolean aEnabled) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java index 03b99834e9..4fd36e2600 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/NavigationBarWidget.java @@ -37,6 +37,7 @@ import org.mozilla.vrbrowser.ui.widgets.dialogs.VoiceSearchWidget; import org.mozilla.vrbrowser.utils.AnimationHelper; import org.mozilla.vrbrowser.utils.ServoUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import org.mozilla.vrbrowser.utils.UIThreadExecutor; import java.util.ArrayList; @@ -50,8 +51,6 @@ public class NavigationBarWidget extends UIWidget implements GeckoSession.Naviga SharedPreferences.OnSharedPreferenceChangeListener, SuggestionsWidget.URLBarPopupDelegate, WindowWidget.BookmarksViewDelegate, WindowWidget.HistoryViewDelegate, TrayListener { - private static final String LOGTAG = NavigationBarWidget.class.getSimpleName(); - private AudioEngine mAudio; private UIButton mBackButton; private UIButton mForwardButton; @@ -64,12 +63,8 @@ public class NavigationBarWidget extends UIWidget implements GeckoSession.Naviga private ViewGroup mResizeModeContainer; private WindowWidget mAttachedWindow; private boolean mIsLoading; - private boolean mIsInFullScreenMode; - private boolean mIsResizing; private boolean mIsInVRVideo; private boolean mAutoEnteredVRVideo; - private WidgetPlacement mPlacementBeforeResize; - private WidgetPlacement mPlacementBeforeFullscreen; private Runnable mResizeBackHandler; private Runnable mFullScreenBackHandler; private Runnable mVRVideoBackHandler; @@ -131,9 +126,6 @@ private void initialize(@NonNull Context aContext) { mBrightnessButton = findViewById(R.id.brightnessButton); mFullScreenResizeButton = findViewById(R.id.fullScreenResizeEnterButton); mProjectionButton = findViewById(R.id.projectionButton); - mPlacementBeforeResize = new WidgetPlacement(aContext); - mPlacementBeforeFullscreen = new WidgetPlacement(aContext); - mResizeBackHandler = () -> exitResizeMode(ResizeAction.RESTORE_SIZE); @@ -309,7 +301,7 @@ private void initialize(@NonNull Context aContext) { mButtons = new ArrayList<>(); mButtons.addAll(Arrays.asList( mBackButton, mForwardButton, mReloadButton, mHomeButton, mResizeEnterButton, mResizeExitButton, - mServoButton, mPreset0, mPreset1, mPreset2, mPreset3)); + mServoButton, mPreset0, mPreset1, mPreset15, mPreset2, mPreset3)); mURLBar.setDelegate(this); @@ -342,6 +334,18 @@ public void releaseWidget() { mWidgetManager.removeUpdateListener(this); mWidgetManager.removeWorldClickListener(this); mPrefs.unregisterOnSharedPreferenceChangeListener(this); + + if (mAttachedWindow != null && mAttachedWindow.isFullScreen()) { + // Workaround for https://issuetracker.google.com/issues/37123764 + // exitFullScreenMode() may animate some views that are then released + // so use a custom way to exit fullscreen here without triggering view updates. + if (mSessionStack.isInFullScreen()) { + mSessionStack.exitFullScreen(); + } + mAttachedWindow.restoreBeforeFullscreenPlacement(); + mAttachedWindow.setIsFullScreen(false); + mWidgetManager.popBackHandler(mFullScreenBackHandler); + } detachFromWindow(); @@ -365,10 +369,10 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { @Override public void detachFromWindow() { - if (mIsResizing) { + if (mAttachedWindow != null && mAttachedWindow.isResizing()) { exitResizeMode(ResizeAction.RESTORE_SIZE); } - if (mIsInFullScreenMode) { + if (mAttachedWindow != null && mAttachedWindow.isFullScreen()) { exitFullScreenMode(); } @@ -415,6 +419,8 @@ public void attachToWindow(@NonNull WindowWidget aWindow) { } } + clearFocus(); + mSessionStack = aWindow.getSessionStack(); if (mSessionStack != null) { mURLBar.setSessionStack(mSessionStack); @@ -433,31 +439,13 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); } - private void setFullScreenSize() { - mPlacementBeforeFullscreen.copyFrom(mAttachedWindow.getPlacement()); - final float minScale = WidgetPlacement.floatDimension(getContext(), R.dimen.window_fullscreen_min_scale); - // Set browser fullscreen size - float aspect = SettingsStore.getInstance(getContext()).getWindowAspect(); - Media media = mSessionStack.getFullScreenVideo(); - if (media != null && media.getWidth() > 0 && media.getHeight() > 0) { - aspect = (float)media.getWidth() / (float)media.getHeight(); - } - float scale = mAttachedWindow.getCurrentScale(); - // Enforce min fullscreen size. - // If current window area is larger only resize if the aspect changes (e.g. media). - if (scale < minScale || aspect != mAttachedWindow.getCurrentAspect()) { - mAttachedWindow.resizeByMultiplier(aspect, Math.max(scale, minScale)); - } - } - private void enterFullScreenMode() { - if (mIsInFullScreenMode) { + if (mAttachedWindow.isFullScreen()) { return; } - setFullScreenSize(); mWidgetManager.pushBackHandler(mFullScreenBackHandler); - mIsInFullScreenMode = true; + mAttachedWindow.setIsFullScreen(true); AnimationHelper.fadeIn(mFullScreenModeContainer, AnimationHelper.FADE_ANIMATION_DURATION, null); AnimationHelper.fadeOut(mNavigationContainer, 0, null); @@ -471,7 +459,7 @@ private void enterFullScreenMode() { mProjectionMenu.setParentWidget(this); mProjectionMenuPlacement = new WidgetPlacement(getContext()); mWidgetManager.addWidget(mProjectionMenu); - mProjectionMenu.setDelegate((projection )-> { + mProjectionMenu.setDelegate((projection)-> { if (mIsInVRVideo) { // Reproject while reproducing VRVideo mWidgetManager.showVRVideo(mAttachedWindow.getHandle(), projection); @@ -491,7 +479,8 @@ private void enterFullScreenMode() { } private void exitFullScreenMode() { - if (!mIsInFullScreenMode) { + if (!mAttachedWindow.isFullScreen()) { + mWidgetManager.setTrayVisible(true); return; } @@ -503,10 +492,9 @@ private void exitFullScreenMode() { } }, 50); - mAttachedWindow.getPlacement().copyFrom(mPlacementBeforeFullscreen); mWidgetManager.updateWidget(mAttachedWindow); - mIsInFullScreenMode = false; + mAttachedWindow.setIsFullScreen(false); mWidgetManager.popBackHandler(mFullScreenBackHandler); AnimationHelper.fadeIn(mNavigationContainer, AnimationHelper.FADE_ANIMATION_DURATION, null); @@ -520,14 +508,14 @@ private void exitFullScreenMode() { } private void enterResizeMode() { - if (mIsResizing) { + if (mAttachedWindow.isResizing()) { return; } - mIsResizing = true; - mPlacementBeforeResize.copyFrom(mAttachedWindow.getPlacement()); + mAttachedWindow.setIsResizing(true); + mAttachedWindow.saveBeforeResizePlacement(); startWidgetResize(); AnimationHelper.fadeIn(mResizeModeContainer, AnimationHelper.FADE_ANIMATION_DURATION, null); - if (mIsInFullScreenMode) { + if (mAttachedWindow.isFullScreen()) { AnimationHelper.fadeOut(mFullScreenModeContainer, 0, null); } else { AnimationHelper.fadeOut(mNavigationContainer, 0, null); @@ -573,24 +561,24 @@ enum ResizeAction { } private void exitResizeMode(ResizeAction aResizeAction) { - if (!mIsResizing) { + if (!mAttachedWindow.isResizing()) { return; } if (aResizeAction == ResizeAction.RESTORE_SIZE) { - mAttachedWindow.getPlacement().copyFrom(mPlacementBeforeResize); + mAttachedWindow.restoreBeforeResizePlacement(); mWidgetManager.updateWidget(mAttachedWindow); mWidgetManager.updateVisibleWidgets(); } - mIsResizing = false; + mAttachedWindow.setIsResizing(false); finishWidgetResize(); - if (mIsInFullScreenMode) { + if (mAttachedWindow.isFullScreen()) { AnimationHelper.fadeIn(mFullScreenModeContainer, AnimationHelper.FADE_ANIMATION_DURATION, null); } else { AnimationHelper.fadeIn(mNavigationContainer, AnimationHelper.FADE_ANIMATION_DURATION, null); } AnimationHelper.fadeOut(mResizeModeContainer, 0, () -> updateWidget()); mWidgetManager.popBackHandler(mResizeBackHandler); - mWidgetManager.setTrayVisible(!mIsInFullScreenMode); + mWidgetManager.setTrayVisible(!mAttachedWindow.isFullScreen()); closeFloatingMenus(); if (aResizeAction == ResizeAction.KEEP_SIZE) { @@ -636,6 +624,7 @@ private void enterVRVideo(@VideoProjectionMenuWidget.VideoProjectionFlags int aP } mMediaControlsWidget.setProjectionMenuWidget(mProjectionMenu); mMediaControlsWidget.setMedia(mFullScreenMedia); + mMediaControlsWidget.setParentWidget(mAttachedWindow.getHandle()); mMediaControlsWidget.setProjectionSelectorEnabled(true); mWidgetManager.updateWidget(mMediaControlsWidget); mWidgetManager.showVRVideo(mAttachedWindow.getHandle(), aProjection); @@ -651,7 +640,10 @@ private void exitVRVideo() { mIsInVRVideo = false; mWidgetManager.popBackHandler(mVRVideoBackHandler); mWidgetManager.hideVRVideo(); + boolean firstDraw = mProjectionMenu.getPlacement().firstDraw; mProjectionMenu.getPlacement().copyFrom(mProjectionMenuPlacement); + mProjectionMenu.getPlacement().firstDraw = firstDraw; + mWidgetManager.updateWidget(mProjectionMenu); closeFloatingMenus(); mWidgetManager.setControllersVisible(true); @@ -821,10 +813,10 @@ public void onSecurityChange(GeckoSession geckoSession, SecurityInformation secu @Override public void onFullScreen(GeckoSession session, boolean aFullScreen) { if (aFullScreen) { - if (!mIsInFullScreenMode) { + if (!mAttachedWindow.isFullScreen()) { enterFullScreenMode(); } - if (mIsResizing) { + if (mAttachedWindow.isResizing()) { exitResizeMode(ResizeAction.KEEP_SIZE); } AtomicBoolean autoEnter = new AtomicBoolean(false); @@ -846,7 +838,7 @@ public void onFullScreen(GeckoSession session, boolean aFullScreen) { // WidgetManagerDelegate.UpdateListener @Override public void onWidgetUpdate(Widget aWidget) { - if (aWidget == mAttachedWindow && !mIsResizing) { + if (aWidget == mAttachedWindow && !mAttachedWindow.isResizing()) { handleWindowResize(); } } @@ -874,9 +866,9 @@ public void onCurrentSessionChange(GeckoSession aSession, int aId) { handleSessionState(); boolean isFullScreen = mSessionStack.isInFullScreen(aSession); - if (isFullScreen && !mIsInFullScreenMode) { + if (isFullScreen && !mAttachedWindow.isFullScreen()) { enterFullScreenMode(); - } else if (!isFullScreen && mIsInFullScreenMode) { + } else if (!isFullScreen && mAttachedWindow.isFullScreen()) { exitVRVideo(); exitFullScreenMode(); } @@ -1025,10 +1017,10 @@ public void onHistoryViewHidden(WindowWidget aWindow) { @Override public void onBookmarksClicked() { - if (mIsResizing) { + if (mAttachedWindow.isResizing()) { exitResizeMode(ResizeAction.RESTORE_SIZE); - } else if (mIsInFullScreenMode) { + } else if (mAttachedWindow.isFullScreen()) { exitFullScreenMode(); } else if (mIsInVRVideo) { @@ -1043,10 +1035,10 @@ public void onPrivateBrowsingClicked() { @Override public void onHistoryClicked() { - if (mIsResizing) { + if (mAttachedWindow.isResizing()) { exitResizeMode(ResizeAction.RESTORE_SIZE); - } else if (mIsInFullScreenMode) { + } else if (mAttachedWindow.isFullScreen()) { exitFullScreenMode(); } else if (mIsInVRVideo) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java index f6f414fc94..aba8ab9148 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/SuggestionsWidget.java @@ -23,13 +23,14 @@ import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.audio.AudioEngine; +import org.mozilla.vrbrowser.ui.views.CustomListView; import java.util.ArrayList; import java.util.List; public class SuggestionsWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { - private ListView mList; + private CustomListView mList; private SuggestionsAdapter mAdapter; private Animation mScaleUpAnimation; private Animation mScaleDownAnimation; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TitleBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TitleBarWidget.java index 21b018c5e7..8228d07ea2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TitleBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TitleBarWidget.java @@ -20,6 +20,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.browser.Media; import org.mozilla.vrbrowser.databinding.TitleBarBinding; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.net.MalformedURLException; import java.net.URI; @@ -27,8 +28,6 @@ public class TitleBarWidget extends UIWidget { - private static final String LOGTAG = TitleBarWidget.class.getSimpleName(); - public interface Delegate { void onTitleClicked(@NonNull TitleBarWidget titleBar); void onMediaPlayClicked(@NonNull TitleBarWidget titleBar); @@ -37,8 +36,9 @@ public interface Delegate { private TitleBarBinding mBinding; private WindowWidget mAttachedWindow; - private boolean mVisible; + private boolean mVisible = false; private Media mMedia; + private boolean mWidgetAdded = false; public TitleBarWidget(Context aContext) { super(aContext); @@ -123,11 +123,11 @@ public void setVisible(boolean aIsVisible) { } mVisible = aIsVisible; getPlacement().visible = aIsVisible; - - if (aIsVisible) { + if (!mWidgetAdded) { mWidgetManager.addWidget(this); + mWidgetAdded = true; } else { - mWidgetManager.removeWidget(this); + mWidgetManager.updateWidget(this); } } @@ -154,7 +154,7 @@ public void setURL(String urlString) { ""); mBinding.url.setText(url.toString()); - } catch (MalformedURLException e) { + } catch (MalformedURLException | IllegalArgumentException e) { mBinding.url.setText(""); } @@ -181,7 +181,8 @@ public void mediaAvailabilityChanged(boolean available) { mMedia = mAttachedWindow.getSessionStack().getFullScreenVideo(); if (mMedia != null) { mBinding.setIsMediaPlaying(mMedia.isPlaying()); - mMedia.setDelegate(mMediaDelegate); + mMedia.removeMediaListener(mMediaDelegate); + mMedia.addMediaListener(mMediaDelegate); } } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java index b86f37a853..bd0cb56563 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java @@ -1,32 +1,37 @@ package org.mozilla.vrbrowser.ui.widgets; import android.content.Context; -import android.graphics.PointF; -import android.graphics.Rect; -import android.view.View; +import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.utils.ViewUtils; +import org.mozilla.vrbrowser.utils.AnimationHelper; public class TooltipWidget extends UIWidget { - private View mTargetView; - private UIWidget mParentWidget; protected TextView mText; - private PointF mTranslation; - private float mRatio; - private float mDensityRatio; + protected ViewGroup mLayout; + + public TooltipWidget(@NonNull Context aContext, @NonNull @LayoutRes int layoutRes) { + super(aContext); + + initialize(layoutRes); + } public TooltipWidget(Context aContext) { super(aContext); - initialize(); + initialize(R.layout.tooltip); } - private void initialize() { - inflate(getContext(), R.layout.tooltip, this); + private void initialize(@NonNull @LayoutRes int layoutRes) { + inflate(getContext(), layoutRes, this); + mLayout = findViewById(R.id.layout); mText = findViewById(R.id.tooltipText); } @@ -46,52 +51,26 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { public void show(@ShowFlags int aShowFlags) { measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mWidgetPlacement.translationX = mTranslation.x * (mRatio / mWidgetPlacement.density); - mWidgetPlacement.translationY = mTranslation.y * (mRatio / mWidgetPlacement.density); int paddingH = getPaddingStart() + getPaddingEnd(); int paddingV = getPaddingTop() + getPaddingBottom(); - mWidgetPlacement.width = (int)(WidgetPlacement.convertPixelsToDp(getContext(), getMeasuredWidth() + paddingH)/mDensityRatio); - mWidgetPlacement.height = (int)(WidgetPlacement.convertPixelsToDp(getContext(), getMeasuredHeight() + paddingV)/mDensityRatio); + mWidgetPlacement.width = (int)((getMeasuredWidth() + paddingH)/mWidgetPlacement.density); + mWidgetPlacement.height = (int)((getMeasuredHeight() + paddingV)/mWidgetPlacement.density); super.show(aShowFlags); + AnimationHelper.scaleIn(mLayout, 100, 0, null); } - public void setLayoutParams(View targetView) { - this.setLayoutParams(targetView, ViewUtils.TooltipPosition.BOTTOM); + @Override + public void hide(int aHideFlags) { + AnimationHelper.scaleOut(mLayout, 100, 0, () -> TooltipWidget.super.hide(aHideFlags)); } - public void setLayoutParams(View targetView, ViewUtils.TooltipPosition position) { - this.setLayoutParams(targetView, position, mWidgetPlacement.density); + public void setCurvedMode(boolean enabled) { + mWidgetPlacement.cylinder = enabled; } - public void setLayoutParams(View targetView, ViewUtils.TooltipPosition position, float density) { - mTargetView = targetView; - mParentWidget = ViewUtils.getParentWidget(mTargetView); - if (mParentWidget != null) { - mRatio = WidgetPlacement.worldToWidgetRatio(mParentWidget); - mWidgetPlacement.density = density; - mDensityRatio = mWidgetPlacement.density / getContext().getResources().getDisplayMetrics().density; - - Rect offsetViewBounds = new Rect(); - getDrawingRect(offsetViewBounds); - mParentWidget.offsetDescendantRectToMyCoords(mTargetView, offsetViewBounds); - - mWidgetPlacement.parentHandle = mParentWidget.getHandle(); - // At the moment we only support showing tooltips on top or bottom of the target view - if (position == ViewUtils.TooltipPosition.BOTTOM) { - mWidgetPlacement.anchorY = 1.0f; - mWidgetPlacement.parentAnchorY = 0.0f; - mTranslation = new PointF( - (offsetViewBounds.left + mTargetView.getWidth() / 2) * mDensityRatio, - -offsetViewBounds.top * mDensityRatio); - } else { - mWidgetPlacement.anchorY = 0.0f; - mWidgetPlacement.parentAnchorY = 1.0f; - mTranslation = new PointF( - (offsetViewBounds.left + mTargetView.getWidth() / 2) * mDensityRatio, - offsetViewBounds.top * mDensityRatio); - } - } + public void setText(@StringRes int stringRes) { + mText.setText(stringRes); } public void setText(String text) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java index d9fdb59bb3..4368fa83c1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java @@ -29,7 +29,8 @@ public class TopBarWidget extends UIWidget implements SessionChangeListener, Wid private WindowWidget mAttachedWindow; private TopBarWidget.Delegate mDelegate; private LinearLayout mMultiWindowControlsContainer; - private boolean mVisible; + private boolean mVisible = false; + private boolean mWidgetAdded = false; public TopBarWidget(Context aContext) { super(aContext); @@ -166,16 +167,16 @@ public void onCurrentSessionChange(GeckoSession aSession, int aId) { @Override public void setVisible(boolean aIsVisible) { - if (mVisible == aIsVisible) { + if (mVisible == aIsVisible || mWidgetManager == null) { return; } mVisible = aIsVisible; getPlacement().visible = aIsVisible; - - if (aIsVisible) { + if (!mWidgetAdded) { mWidgetManager.addWidget(this); + mWidgetAdded = true; } else { - mWidgetManager.removeWidget(this); + mWidgetManager.updateWidget(this); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java index ad7635d0b8..0e2bac2253 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java @@ -8,6 +8,7 @@ import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; @@ -16,14 +17,18 @@ import androidx.annotation.NonNull; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.audio.AudioEngine; +import org.mozilla.vrbrowser.browser.BookmarksStore; import org.mozilla.vrbrowser.browser.SessionChangeListener; import org.mozilla.vrbrowser.browser.engine.SessionStack; +import org.mozilla.vrbrowser.browser.engine.SessionStore; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.ui.views.UIButton; import org.mozilla.vrbrowser.ui.widgets.settings.SettingsWidget; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.util.ArrayList; import java.util.Arrays; @@ -32,9 +37,8 @@ public class TrayWidget extends UIWidget implements SessionChangeListener, WindowWidget.BookmarksViewDelegate, WindowWidget.HistoryViewDelegate, WidgetManagerDelegate.UpdateListener { - static final String LOGTAG = TrayWidget.class.getSimpleName(); - private static final int ICON_ANIMATION_DURATION = 200; + private static final int LIBRARY_NOTIFICATION_DURATION = 3000; private UIButton mSettingsButton; private UIButton mPrivateButton; @@ -50,6 +54,7 @@ public class TrayWidget extends UIWidget implements SessionChangeListener, Windo private boolean mTrayVisible = true; private SessionStack mSessionStack; private WindowWidget mAttachedWindow; + private TooltipWidget mLibraryNotification; public TrayWidget(Context aContext) { super(aContext); @@ -84,6 +89,7 @@ private void initialize(Context aContext) { notifyPrivateBrowsingClicked(); view.requestFocusFromTouch(); }); + mPrivateButton.setCurvedTooltip(false); mSettingsButton = findViewById(R.id.settingsButton); mSettingsButton.setOnHoverListener(mButtonScaleHoverListener); @@ -97,6 +103,7 @@ private void initialize(Context aContext) { view.requestFocusFromTouch(); } }); + mSettingsButton.setCurvedTooltip(false); mBookmarksButton = findViewById(R.id.bookmarksButton); mBookmarksButton.setOnHoverListener(mButtonScaleHoverListener); @@ -108,6 +115,7 @@ private void initialize(Context aContext) { notifyBookmarksClicked(); view.requestFocusFromTouch(); }); + mBookmarksButton.setCurvedTooltip(false); mHistoryButton = findViewById(R.id.historyButton); mHistoryButton.setOnHoverListener(mButtonScaleHoverListener); @@ -119,6 +127,7 @@ private void initialize(Context aContext) { notifyHistoryClicked(); view.requestFocusFromTouch(); }); + mHistoryButton.setCurvedTooltip(false); UIButton addWindowButton = findViewById(R.id.addwindowButton); addWindowButton.setOnHoverListener(mButtonScaleHoverListener); @@ -133,6 +142,7 @@ private void initialize(Context aContext) { notifyAddWindowClicked(); }); + addWindowButton.setCurvedTooltip(false); mAudio = AudioEngine.fromContext(aContext); @@ -207,10 +217,6 @@ public void removeListeners(TrayListener... listeners) { mTrayListeners.removeAll(Arrays.asList(listeners)); } - public void onDestroy() { - mTrayListeners.clear(); - } - private void notifyBookmarksClicked() { mTrayListeners.forEach(TrayListener::onBookmarksClicked); } @@ -255,6 +261,7 @@ public void releaseWidget() { } mWidgetManager.removeUpdateListener(this); + mTrayListeners.clear(); super.releaseWidget(); } @@ -269,6 +276,8 @@ public void show(@ShowFlags int aShowFlags) { @Override public void hide(@HideFlags int aHideFlags) { + hideBookmarkNotification.run(); + if (mWidgetPlacement.visible) { mWidgetPlacement.visible = false; if (aHideFlags == REMOVE_WIDGET) { @@ -281,11 +290,14 @@ public void hide(@HideFlags int aHideFlags) { @Override public void detachFromWindow() { + hideBookmarkNotification.run(); + if (mSessionStack != null) { mSessionStack.removeSessionChangeListener(this); mSessionStack = null; } if (mAttachedWindow != null) { + SessionStore.get().getBookmarkStore().addListener(mBookmarksListener); mAttachedWindow.removeBookmarksViewListener(this); mAttachedWindow.removeHistoryViewListener(this); } @@ -305,6 +317,8 @@ public void attachToWindow(@NonNull WindowWidget aWindow) { mAttachedWindow.addBookmarksViewListener(this); mAttachedWindow.addHistoryViewListener(this); + SessionStore.get().getBookmarkStore().addListener(mBookmarksListener); + mSessionStack = aWindow.getSessionStack(); if (mSessionStack != null) { mSessionStack.addSessionChangeListener(this); @@ -374,6 +388,9 @@ public void setTrayVisible(boolean aVisible) { if (mTrayVisible != aVisible) { mTrayVisible = aVisible; updateVisibility(); + + } else { + mWidgetManager.updateWidget(this); } } @@ -436,4 +453,57 @@ public void onWidgetUpdate(Widget aWidget) { } } + private BookmarksStore.BookmarkListener mBookmarksListener = new BookmarksStore.BookmarkListener() { + @Override + public void onBookmarksUpdated() { + // Nothing to do + } + + @Override + public void onBookmarkAdded() { + mBookmarksButton.setNotificationMode(true); + ThreadUtils.postToUiThread(showBookmarkNotification); + } + }; + + private Runnable showBookmarkNotification = new Runnable() { + @Override + public void run() { + if (mLibraryNotification != null && mLibraryNotification.isVisible()) { + return; + } + + Rect offsetViewBounds = new Rect(); + getDrawingRect(offsetViewBounds); + offsetDescendantRectToMyCoords(mBookmarksButton, offsetViewBounds); + + float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), TrayWidget.this); + + mLibraryNotification = new TooltipWidget(getContext(), R.layout.library_notification); + mLibraryNotification.getPlacement().parentHandle = getHandle(); + mLibraryNotification.getPlacement().anchorY = 0.0f; + mLibraryNotification.getPlacement().translationX = (offsetViewBounds.left + mBookmarksButton.getWidth() / 2.0f) * ratio; + mLibraryNotification.getPlacement().translationY = ((offsetViewBounds.top - 66) * ratio); + mLibraryNotification.getPlacement().translationZ = 38.0f; + mLibraryNotification.getPlacement().density = 3.0f; + mLibraryNotification.setText(R.string.bookmarks_saved_notification); + mLibraryNotification.setCurvedMode(false); + mLibraryNotification.show(UIWidget.CLEAR_FOCUS); + + ThreadUtils.postDelayedToUiThread(hideBookmarkNotification, LIBRARY_NOTIFICATION_DURATION); + } + }; + + private Runnable hideBookmarkNotification = new Runnable() { + @Override + public void run() { + if (mLibraryNotification != null) { + mLibraryNotification.hide(UIWidget.REMOVE_WIDGET); + mLibraryNotification = null; + mBookmarksButton.setNotificationMode(false); + } + } + }; + + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java index ef6de0d5a9..477a663186 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java @@ -19,6 +19,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.browser.SettingsStore; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.lang.reflect.Constructor; import java.util.HashMap; @@ -28,7 +29,7 @@ public abstract class UIWidget extends FrameLayout implements Widget { - private static final String LOGTAG = "VRB"; + protected final String LOGTAG = SystemUtils.createLogtag(this.getClass()); public interface Delegate { void onDismiss(); @@ -49,6 +50,7 @@ public interface Delegate { protected int mBorderWidth; private Runnable mFirstDrawCallback; protected boolean mResizing = false; + protected boolean mReleased = false; public UIWidget(Context aContext) { super(aContext); @@ -69,6 +71,7 @@ private void initialize() { mBorderWidth = SettingsStore.getInstance(getContext()).getTransparentBorderWidth(); mWidgetManager = (WidgetManagerDelegate) getContext(); mWidgetPlacement = new WidgetPlacement(getContext()); + mWidgetPlacement.name = getClass().getSimpleName(); mHandle = mWidgetManager.newWidgetHandle(); mWorldWidth = WidgetPlacement.pixelDimension(getContext(), R.dimen.world_width); initializeWidgetPlacement(mWidgetPlacement); @@ -191,14 +194,23 @@ public void handleMoveEvent(float aDeltaX, float aDeltaY, float aDeltaZ, float a mWidgetPlacement.rotation = aRotation; } - @Override - public void releaseWidget() { + private void releaseRenderer() { if (mRenderer != null) { mRenderer.release(); mRenderer = null; } mTexture = null; + } + + @Override + public void releaseWidget() { + releaseRenderer(); mWidgetManager = null; + mReleased = true; + } + + public boolean isReleased() { + return mReleased; } @Override @@ -327,7 +339,7 @@ public void hide(@HideFlags int aHideFlags) { mWidgetPlacement.visible = false; if (aHideFlags == REMOVE_WIDGET) { mWidgetManager.removeWidget(this); - + releaseRenderer(); } else { mWidgetManager.updateWidget(this); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VideoProjectionMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VideoProjectionMenuWidget.java index de71e721dc..b925171419 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VideoProjectionMenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VideoProjectionMenuWidget.java @@ -46,7 +46,7 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.anchorX = 0.0f; aPlacement.anchorY = 0.0f; aPlacement.translationY = WidgetPlacement.dpDimension(getContext(), R.dimen.video_projection_menu_translation_y); - aPlacement.translationZ = 2.0f; + aPlacement.translationZ = 30.0f; } public void setParentWidget(UIWidget aParent) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java index 8572337c7f..21d0146d9a 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetPlacement.java @@ -53,6 +53,7 @@ public WidgetPlacement(Context aContext) { public int borderColor = 0; // Color used to render the widget before the it's composited public int clearColor = 0; + public String name; /* * Flat surface placements are automatically mapped to curved coordinates. * If a radius is set it's used for the automatic mapping of the yaw & angle when the @@ -89,9 +90,13 @@ public void copyFrom(WidgetPlacement w) { this.showPointer = w.showPointer; this.composited = w.composited; this.layer = w.layer; + this.proxifyLayer = w.proxifyLayer; this.textureScale = w.textureScale; this.cylinder = w.cylinder; this.cylinderMapRadius = w.cylinderMapRadius; + this.borderColor = w.borderColor; + this.clearColor = w.clearColor; + this.name = w.name; } public int textureWidth() { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java index 92cea204f4..5a64fb4511 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java @@ -25,6 +25,7 @@ import androidx.annotation.StringRes; import androidx.annotation.UiThread; +import org.jetbrains.annotations.NotNull; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.GeckoDisplay; import org.mozilla.geckoview.GeckoResult; @@ -37,14 +38,16 @@ import org.mozilla.vrbrowser.browser.engine.SessionStack; import org.mozilla.vrbrowser.browser.engine.SessionStore; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; +import org.mozilla.vrbrowser.ui.callbacks.BookmarksCallback; import org.mozilla.vrbrowser.ui.callbacks.HistoryCallback; -import org.mozilla.vrbrowser.ui.callbacks.HistoryItemContextMenuClickCallback; +import org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback; import org.mozilla.vrbrowser.ui.views.BookmarksView; import org.mozilla.vrbrowser.ui.views.HistoryView; +import org.mozilla.vrbrowser.ui.views.LibraryItemContextMenu; import org.mozilla.vrbrowser.ui.widgets.dialogs.BaseAppDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.ClearCacheDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.ContextMenuWidget; -import org.mozilla.vrbrowser.ui.widgets.dialogs.HistoryItemContextMenuWidget; +import org.mozilla.vrbrowser.ui.widgets.dialogs.LibraryItemContextMenuWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.MaxWindowsWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.MessageDialogWidget; import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget; @@ -61,6 +64,7 @@ import java.util.Calendar; import java.util.GregorianCalendar; +import mozilla.components.concept.storage.BookmarkNode; import mozilla.components.concept.storage.PageObservation; import mozilla.components.concept.storage.VisitInfo; import mozilla.components.concept.storage.VisitType; @@ -72,16 +76,14 @@ public class WindowWidget extends UIWidget implements SessionChangeListener, GeckoSession.NavigationDelegate, VideoAvailabilityListener, GeckoSession.HistoryDelegate, GeckoSession.ProgressDelegate { - private static final String LOGTAG = WindowWidget.class.getSimpleName(); - public interface HistoryViewDelegate { default void onHistoryViewShown(WindowWidget aWindow) {} - default void onHistoryViewHidden(WindowWidget aWindow) {}; + default void onHistoryViewHidden(WindowWidget aWindow) {} } public interface BookmarksViewDelegate { - default void onBookmarksShown(WindowWidget aWindow) {}; - default void onBookmarksHidden(WindowWidget aWindow) {}; + default void onBookmarksShown(WindowWidget aWindow) {} + default void onBookmarksHidden(WindowWidget aWindow) {} } private int mSessionId; @@ -104,7 +106,7 @@ public interface BookmarksViewDelegate { private MessageDialogWidget mAppDialog; private ClearCacheDialogWidget mClearCacheDialog; private ContextMenuWidget mContextMenu; - private HistoryItemContextMenuWidget mHistoryContextMenu; + private LibraryItemContextMenuWidget mLibraryItemContextMenu; private int mWidthBackup; private int mHeightBackup; private int mBorderWidth; @@ -119,6 +121,7 @@ public interface BookmarksViewDelegate { private ArrayList mBookmarksViewListeners; private ArrayList mHistoryViewListeners; private Windows.WindowPlacement mWindowPlacement = Windows.WindowPlacement.FRONT; + private Windows.WindowPlacement mWindowPlacementBeforeFullscreen = Windows.WindowPlacement.FRONT; private float mMaxWindowScale = 3; private boolean mIsRestored = false; private WindowDelegate mWindowDelegate; @@ -127,6 +130,10 @@ public interface BookmarksViewDelegate { boolean mClickedAfterFocus = false; boolean mIsBookmarksVisible = false; boolean mIsHistoryVisible = false; + private WidgetPlacement mPlacementBeforeFullscreen; + private WidgetPlacement mPlacementBeforeResize; + private boolean mIsResizing; + private boolean mIsFullScreen; public interface WindowDelegate { void onFocusRequest(@NonNull WindowWidget aWindow); @@ -150,6 +157,7 @@ public WindowWidget(Context aContext, int windowId, boolean privateMode) { mSessionStack.newSession(); mBookmarksView = new BookmarksView(aContext); + mBookmarksView.setBookmarksCallback(mBookmarksCallback); mBookmarksViewListeners = new ArrayList<>(); mHistoryView = new HistoryView(aContext); @@ -158,6 +166,10 @@ public WindowWidget(Context aContext, int windowId, boolean privateMode) { mHandle = ((WidgetManagerDelegate)aContext).newWidgetHandle(); mWidgetPlacement = new WidgetPlacement(aContext); + mPlacementBeforeFullscreen = new WidgetPlacement(aContext); + mPlacementBeforeResize = new WidgetPlacement(aContext); + mIsResizing = false; + mIsFullScreen = false; initializeWidgetPlacement(mWidgetPlacement); if (mSessionStack.isPrivateMode()) { mWidgetPlacement.clearColor = ViewUtils.ARGBtoRGBA(getContext().getColor(R.color.window_private_clear_color)); @@ -188,6 +200,7 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.visible = true; aPlacement.cylinder = true; aPlacement.textureScale = 1.0f; + aPlacement.name = "Window"; // Check Windows.placeWindow method for remaining placement set-up } @@ -239,6 +252,20 @@ protected void onDismiss() { } } + @Override + public void onPause() { + super.onPause(); + mSessionStack.setActive(false); + } + + @Override + public void onResume() { + super.onResume(); + if (isVisible() || mIsInVRVideoMode) { + mSessionStack.setActive(true); + } + } + public void close() { TelemetryWrapper.closeWindowEvent(mWindowId); @@ -246,6 +273,12 @@ public void close() { mBookmarksView.onDestroy(); mHistoryView.onDestroy(); SessionStore.get().destroySessionStack(mWindowId); + if (mTopBar != null) { + mWidgetManager.removeWidget(mTopBar); + } + if (mTitleBar != null) { + mWidgetManager.removeWidget(mTitleBar); + } } public void loadHomeIfNotRestored() { @@ -267,45 +300,54 @@ protected void setRestored(boolean restored) { mIsRestored = restored; } - private void setView(View view) { - pauseCompositor(); + private void setView(View view, boolean switchSurface) { + if (switchSurface) { + pauseCompositor(); + } + mView = view; removeView(view); mView.setVisibility(VISIBLE); addView(mView); - mWidgetPlacement.density = getContext().getResources().getDisplayMetrics().density; - if (mTexture != null && mSurface != null && mRenderer == null) { - // Create the UI Renderer for the current surface. - // Surface must be released when switching back to WebView surface or the browser - // will not render it correctly. See release code in unsetView(). - mRenderer = new UISurfaceTextureRenderer(mSurface, mWidgetPlacement.textureWidth(), mWidgetPlacement.textureHeight()); + + if (switchSurface) { + mWidgetPlacement.density = getContext().getResources().getDisplayMetrics().density; + if (mTexture != null && mSurface != null && mRenderer == null) { + // Create the UI Renderer for the current surface. + // Surface must be released when switching back to WebView surface or the browser + // will not render it correctly. See release code in unsetView(). + mRenderer = new UISurfaceTextureRenderer(mSurface, mWidgetPlacement.textureWidth(), mWidgetPlacement.textureHeight()); + } + mWidgetManager.updateWidget(this); + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); + mWidgetManager.pushBackHandler(mBackHandler); + setWillNotDraw(false); + postInvalidate(); } - mWidgetManager.updateWidget(this); - mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); - mWidgetManager.pushBackHandler(mBackHandler); - setWillNotDraw(false); - postInvalidate(); } - private void unsetView(View view) { + private void unsetView(View view, boolean switchSurface) { if (mView != null && mView == view) { mView = null; removeView(view); view.setVisibility(GONE); - setWillNotDraw(true); - if (mTexture != null) { - // Surface must be recreated here when not using layers. - // When using layers the new Surface is received via the setSurface() method. - if (mRenderer != null) { - mRenderer.release(); - mRenderer = null; + + if (switchSurface) { + setWillNotDraw(true); + if (mTexture != null) { + // Surface must be recreated here when not using layers. + // When using layers the new Surface is received via the setSurface() method. + if (mRenderer != null) { + mRenderer.release(); + mRenderer = null; + } + mSurface = new Surface(mTexture); } - mSurface = new Surface(mTexture); + mWidgetPlacement.density = 1.0f; + mWidgetManager.updateWidget(this); + mWidgetManager.popWorldBrightness(this); + mWidgetManager.popBackHandler(mBackHandler); } - mWidgetPlacement.density = 1.0f; - mWidgetManager.updateWidget(this); - mWidgetManager.popWorldBrightness(this); - mWidgetManager.popBackHandler(mBackHandler); } } @@ -335,8 +377,8 @@ public void removeHistoryViewListener(@NonNull HistoryViewDelegate listener) { public void switchBookmarks() { if (isHistoryVisible()) { - hideHistory(); - showBookmarks(); + hideHistory(false); + showBookmarks(false); } else if (isBookmarksVisible()) { hideBookmarks(); @@ -347,8 +389,12 @@ public void switchBookmarks() { } public void showBookmarks() { + showBookmarks(true); + } + + public void showBookmarks(boolean switchSurface) { if (mView == null) { - setView(mBookmarksView); + setView(mBookmarksView, switchSurface); for (BookmarksViewDelegate listener : mBookmarksViewListeners) { listener.onBookmarksShown(this); } @@ -359,8 +405,12 @@ public void showBookmarks() { } public void hideBookmarks() { + hideBookmarks(true); + } + + public void hideBookmarks(boolean switchSurface) { if (mView != null) { - unsetView(mBookmarksView); + unsetView(mBookmarksView, switchSurface); for (BookmarksViewDelegate listener : mBookmarksViewListeners) { listener.onBookmarksHidden(this); } @@ -370,8 +420,8 @@ public void hideBookmarks() { public void switchHistory() { if (isBookmarksVisible()) { - hideBookmarks(); - showHistory(); + hideBookmarks(false); + showHistory(false); } else if (isHistoryVisible()) { hideHistory(); @@ -382,8 +432,12 @@ public void switchHistory() { } public void showHistory() { + showHistory(true); + } + + public void showHistory(boolean switchSurface) { if (mView == null) { - setView(mHistoryView); + setView(mHistoryView, switchSurface); for (HistoryViewDelegate listener : mHistoryViewListeners) { listener.onHistoryViewShown(this); } @@ -392,8 +446,12 @@ public void showHistory() { } public void hideHistory() { + hideHistory(true); + } + + public void hideHistory(boolean switchSurface) { if (mView != null) { - unsetView(mHistoryView); + unsetView(mHistoryView, switchSurface); for (HistoryViewDelegate listener : mHistoryViewListeners) { listener.onHistoryViewHidden(this); } @@ -465,6 +523,10 @@ public void setWindowPlacement(@NonNull Windows.WindowPlacement aPlacement) { } } + public @NonNull Windows.WindowPlacement getmWindowPlacementBeforeFullscreen() { + return mWindowPlacementBeforeFullscreen; + } + public @NonNull Windows.WindowPlacement getWindowPlacement() { return mWindowPlacement; } @@ -506,6 +568,10 @@ public void setActiveWindow(boolean active) { updateTitleBar(); } + if (mContextMenu != null) { + mContextMenu.hide(REMOVE_WIDGET); + } + TelemetryWrapper.activePlacementEvent(mWindowPlacement.getValue(), mActive); updateBorder(); } @@ -732,6 +798,48 @@ protected void updateBorder() { } } + public void saveBeforeFullscreenPlacement() { + mWindowPlacementBeforeFullscreen = mWindowPlacement; + mPlacementBeforeFullscreen.copyFrom(mWidgetPlacement); + } + + public void restoreBeforeFullscreenPlacement() { + mWindowPlacement = mWindowPlacementBeforeFullscreen; + mWidgetPlacement.copyFrom(mPlacementBeforeFullscreen); + } + + public WidgetPlacement getBeforeFullscreenPlacement() { + return mPlacementBeforeFullscreen; + } + + public void saveBeforeResizePlacement() { + mPlacementBeforeResize.copyFrom(mWidgetPlacement); + } + + public void restoreBeforeResizePlacement() { + mWidgetPlacement.copyFrom(mPlacementBeforeResize); + } + + public WidgetPlacement getBeforeResizePlacement() { + return mPlacementBeforeResize; + } + + public void setIsResizing(boolean isResizing) { + mIsResizing = isResizing; + } + + public boolean isResizing() { + return mIsResizing; + } + + public void setIsFullScreen(boolean isFullScreen) { + mIsFullScreen = isFullScreen; + } + + public boolean isFullScreen() { + return mIsFullScreen; + } + public void setWindowDelegate(WindowDelegate aDelegate) { mWindowDelegate = aDelegate; } @@ -758,15 +866,26 @@ public void releaseWidget() { mSessionStack.removeProgressListener(this); mSessionStack.setHistoryDelegate(null); GeckoSession session = mSessionStack.getSession(mSessionId); - if (session == null) { - return; - } if (mDisplay != null) { mDisplay.surfaceDestroyed(); - session.releaseDisplay(mDisplay); + if (session != null) { + session.releaseDisplay(mDisplay); + } mDisplay = null; } - session.getTextInput().setView(null); + if (session != null) { + session.getTextInput().setView(null); + } + if (mSurface != null) { + mSurface.release(); + mSurface = null; + } + if (mTexture != null && mRenderer == null) { + // Custom SurfaceTexture used for GeckoView + mTexture.release(); + mTexture = null; + } + super.releaseWidget(); } @@ -795,6 +914,9 @@ public void setVisible(boolean aVisible) { if (mWidgetPlacement.visible == aVisible) { return; } + if (!mIsInVRVideoMode) { + mSessionStack.setActive(aVisible); + } mWidgetPlacement.visible = aVisible; if (!aVisible) { if (mIsBookmarksVisible || mIsHistoryVisible) { @@ -1071,66 +1193,95 @@ private int getWindowWidth(float aWorldWidth) { return (int) Math.floor(SettingsStore.WINDOW_WIDTH_DEFAULT * aWorldWidth / WidgetPlacement.floatDimension(getContext(), R.dimen.window_world_width)); } - private HistoryCallback mHistoryCallback = new HistoryCallback() { - @Override - public void onClearHistory(View view) { - view.requestFocusFromTouch(); - showClearCacheDialog(); + private void showLibraryItemContextMenu(@NotNull View view, LibraryItemContextMenu.LibraryContextMenuItem item, boolean isLastVisibleItem) { + view.requestFocusFromTouch(); + + if (mLibraryItemContextMenu != null) { + mLibraryItemContextMenu.hide(REMOVE_WIDGET); } - @Override - public void onShowContextMenu(View view, VisitInfo item, boolean isLastVisibleItem) { - view.requestFocusFromTouch(); + float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), WindowWidget.this); - if (mHistoryContextMenu != null) { - mHistoryContextMenu.hide(REMOVE_WIDGET); - } + Rect offsetViewBounds = new Rect(); + getDrawingRect(offsetViewBounds); + offsetDescendantRectToMyCoords(view, offsetViewBounds); - float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), WindowWidget.this); + mLibraryItemContextMenu = new LibraryItemContextMenuWidget(getContext()); + mLibraryItemContextMenu.setItem(item); + mLibraryItemContextMenu.mWidgetPlacement.parentHandle = getHandle(); - Rect offsetViewBounds = new Rect(); - getDrawingRect(offsetViewBounds); - offsetDescendantRectToMyCoords(view, offsetViewBounds); + PointF position; + if (isLastVisibleItem) { + mLibraryItemContextMenu.mWidgetPlacement.anchorY = 0.0f; + position = new PointF( + (offsetViewBounds.left + view.getWidth()) * ratio, + -(offsetViewBounds.top) * ratio); - mHistoryContextMenu = new HistoryItemContextMenuWidget(getContext()); - mHistoryContextMenu.mWidgetPlacement.parentHandle = getHandle(); + } else { + mLibraryItemContextMenu.mWidgetPlacement.anchorY = 1.0f; + position = new PointF( + (offsetViewBounds.left + view.getWidth()) * ratio, + -(offsetViewBounds.top + view.getHeight()) * ratio); + } - PointF position; - if (isLastVisibleItem) { - mHistoryContextMenu.mWidgetPlacement.anchorY = 0.0f; - position = new PointF( - (offsetViewBounds.left + view.getWidth()) * ratio, - -(offsetViewBounds.top) * ratio); + mLibraryItemContextMenu.setPosition(position); + mLibraryItemContextMenu.setHistoryContextMenuItemCallback((new LibraryItemContextMenuClickCallback() { + @Override + public void onOpenInNewWindowClick(LibraryItemContextMenu.LibraryContextMenuItem item) { + mWidgetManager.openNewWindow(item.getUrl()); + mLibraryItemContextMenu.hide(REMOVE_WIDGET); + } - } else { - mHistoryContextMenu.mWidgetPlacement.anchorY = 1.0f; - position = new PointF( - (offsetViewBounds.left + view.getWidth()) * ratio, - -(offsetViewBounds.top + view.getHeight()) * ratio); + @Override + public void onAddToBookmarks(LibraryItemContextMenu.LibraryContextMenuItem item) { + SessionStore.get().getBookmarkStore().addBookmark(item.getUrl(), item.getTitle()); + mLibraryItemContextMenu.hide(REMOVE_WIDGET); } - mHistoryContextMenu.setPosition(position); - mHistoryContextMenu.setItem(item); - mHistoryContextMenu.setHistoryContextMenuItemCallback((new HistoryItemContextMenuClickCallback() { - @Override - public void onOpenInNewWindowClick(VisitInfo item) { - mWidgetManager.openNewWindow(item.getUrl()); - mHistoryContextMenu.hide(REMOVE_WIDGET); - } + @Override + public void onRemoveFromBookmarks(LibraryItemContextMenu.LibraryContextMenuItem item) { + SessionStore.get().getBookmarkStore().deleteBookmarkByURL(item.getUrl()); + mLibraryItemContextMenu.hide(REMOVE_WIDGET); + } + })); + mLibraryItemContextMenu.show(REQUEST_FOCUS); + } - @Override - public void onAddToBookmarks(VisitInfo item) { - SessionStore.get().getBookmarkStore().addBookmark(item.getUrl(), item.getTitle()); - mHistoryContextMenu.hide(REMOVE_WIDGET); - } + private BookmarksCallback mBookmarksCallback = new BookmarksCallback() { - @Override - public void onRemoveFromBookmarks(VisitInfo item) { - SessionStore.get().getBookmarkStore().deleteBookmarkByURL(item.getUrl()); - mHistoryContextMenu.hide(REMOVE_WIDGET); - } - })); - mHistoryContextMenu.show(REQUEST_FOCUS); + @Override + public void onClearBookmarks(View view) { + // Not used ATM + } + + @Override + public void onShowContextMenu(@NonNull View view, @NotNull BookmarkNode item, boolean isLastVisibleItem) { + showLibraryItemContextMenu( + view, + new LibraryItemContextMenu.LibraryContextMenuItem( + item.getUrl(), + item.getTitle(), + LibraryItemContextMenu.LibraryItemType.BOOKMARKS), + isLastVisibleItem); + } + }; + + private HistoryCallback mHistoryCallback = new HistoryCallback() { + @Override + public void onClearHistory(@NonNull View view) { + view.requestFocusFromTouch(); + showClearCacheDialog(); + } + + @Override + public void onShowContextMenu(@NonNull View view, @NonNull VisitInfo item, boolean isLastVisibleItem) { + showLibraryItemContextMenu( + view, + new LibraryItemContextMenu.LibraryContextMenuItem( + item.getUrl(), + item.getTitle(), + LibraryItemContextMenu.LibraryItemType.HISTORY), + isLastVisibleItem); } }; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Windows.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Windows.java index 261bafc620..cf39d5fbaa 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Windows.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Windows.java @@ -12,9 +12,11 @@ import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.browser.Media; import org.mozilla.vrbrowser.browser.SettingsStore; import org.mozilla.vrbrowser.browser.engine.SessionStack; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.io.File; import java.io.FileReader; @@ -28,7 +30,7 @@ public class Windows implements TrayListener, TopBarWidget.Delegate, TitleBarWidget.Delegate, GeckoSession.ContentDelegate, WindowWidget.WindowDelegate { - private static final String LOGTAG = Windows.class.getSimpleName(); + private static final String LOGTAG = SystemUtils.createLogtag(Windows.class); private static final String WINDOWS_SAVE_FILENAME = "windows_state.json"; @@ -41,12 +43,23 @@ class WindowState { float worldWidth; public void load(WindowWidget aWindow) { - placement = aWindow.getWindowPlacement(); sessionStack = aWindow.getSessionStack(); currentSessionId = aWindow.getSessionStack().getCurrentSessionId(); - textureWidth = aWindow.getPlacement().width; - textureHeight = aWindow.getPlacement().height; - worldWidth = aWindow.getPlacement().worldWidth; + WidgetPlacement widgetPlacement; + if (aWindow.isFullScreen()) { + widgetPlacement = aWindow.getBeforeFullscreenPlacement(); + placement = aWindow.getmWindowPlacementBeforeFullscreen(); + } else if (aWindow.isResizing()) { + widgetPlacement = aWindow.getBeforeResizePlacement(); + placement = aWindow.getWindowPlacement(); + } else { + widgetPlacement = aWindow.getPlacement(); + placement = aWindow.getWindowPlacement(); + } + + textureWidth = widgetPlacement.width; + textureHeight = widgetPlacement.height; + worldWidth = widgetPlacement.worldWidth; } } @@ -67,9 +80,11 @@ class WindowsState { private boolean mPrivateMode = false; public static final int MAX_WINDOWS = 3; private WindowWidget mFullscreenWindow; - private WindowPlacement mPrevWindowPlacement; + private WindowPlacement mRegularWindowPlacement; + private WindowPlacement mPrivateWindowPlacement; private boolean mStoredCurvedMode = false; private boolean mForcedCurvedMode = false; + private boolean mIsPaused = false; public enum WindowPlacement{ FRONT(0), @@ -89,6 +104,7 @@ public interface Delegate { void onFocusedWindowChanged(@NonNull WindowWidget aFocusedWindow, @Nullable WindowWidget aPrevFocusedWindow); void onWindowBorderChanged(@NonNull WindowWidget aChangeWindow); void onWindowsMoved(); + void onWindowClosed(); } public Windows(Context aContext) { @@ -97,6 +113,9 @@ public Windows(Context aContext) { mRegularWindows = new ArrayList<>(); mPrivateWindows = new ArrayList<>(); + mRegularWindowPlacement = WindowPlacement.FRONT; + mPrivateWindowPlacement = WindowPlacement.FRONT; + mStoredCurvedMode = SettingsStore.getInstance(mContext).getCylinderDensity() > 0.0f; restoreWindows(); @@ -107,8 +126,7 @@ private void saveState() { try (Writer writer = new FileWriter(file)) { WindowsState state = new WindowsState(); state.privateMode = mPrivateMode; - state.focusedWindowPlacement = mFocusedWindow.getWindowPlacement(); - state.regularWindowsState = new ArrayList<>(); + state.focusedWindowPlacement = mFocusedWindow.isFullScreen() ? mFocusedWindow.getmWindowPlacementBeforeFullscreen() : mFocusedWindow.getWindowPlacement(); for (WindowWidget window : mRegularWindows) { WindowState windowState = new WindowState(); windowState.load(window); @@ -276,6 +294,9 @@ public void closeWindow(@NonNull WindowWidget aWindow) { } updateViews(); + if (mDelegate != null) { + mDelegate.onWindowClosed(); + } } public void moveWindowRight(@NonNull WindowWidget aWindow) { @@ -359,7 +380,26 @@ public void resumeCompositor() { } public void onPause() { + mIsPaused = true; + saveState(); + for (WindowWidget window: mRegularWindows) { + window.onPause(); + } + for (WindowWidget window: mPrivateWindows) { + window.onPause(); + } + } + + public void onResume() { + mIsPaused = false; + + for (WindowWidget window: mRegularWindows) { + window.onResume(); + } + for (WindowWidget window: mPrivateWindows) { + window.onResume(); + } } public void onDestroy() { @@ -376,6 +416,43 @@ public boolean isInPrivateMode() { return mPrivateMode; } + public void enterImmersiveMode() { + if (!isInPrivateMode()) { + for (WindowWidget window: mRegularWindows) { + if (window != mFocusedWindow) { + window.onPause(); + } + } + + } else { + for (WindowWidget window: mPrivateWindows) { + if (window != mFocusedWindow) { + window.onPause(); + } + } + } + } + + public void exitImmersiveMode() { + if (mIsPaused) { + return; + } + + if (!isInPrivateMode()) { + for (WindowWidget window: mRegularWindows) { + if (window != mFocusedWindow) { + window.onResume(); + } + } + + } else { + for (WindowWidget window: mPrivateWindows) { + if (window != mFocusedWindow) { + window.onResume(); + } + } + } + } public void enterPrivateMode() { if (mPrivateMode) { @@ -383,6 +460,12 @@ public void enterPrivateMode() { } mPrivateMode = true; updateCurvedMode(true); + if (mFocusedWindow != null) { + mRegularWindowPlacement = mFocusedWindow.getWindowPlacement(); + + } else { + mRegularWindowPlacement = WindowPlacement.FRONT; + } for (WindowWidget window: mRegularWindows) { setWindowVisible(window, false); } @@ -397,7 +480,7 @@ public void enterPrivateMode() { } } else { - focusWindow(getFrontWindow()); + focusWindow(getWindowWithPlacement(mPrivateWindowPlacement)); } updateViews(); mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); @@ -409,13 +492,19 @@ public void exitPrivateMode() { } mPrivateMode = false; updateCurvedMode(true); - for (WindowWidget window: mRegularWindows) { - setWindowVisible(window, true); + if (mFocusedWindow != null) { + mPrivateWindowPlacement = mFocusedWindow.getWindowPlacement(); + + } else { + mPrivateWindowPlacement = WindowPlacement.FRONT; } for (WindowWidget window: mPrivateWindows) { setWindowVisible(window, false); } - focusWindow(getFrontWindow()); + for (WindowWidget window: mRegularWindows) { + setWindowVisible(window, true); + } + focusWindow(getWindowWithPlacement(mRegularWindowPlacement)); updateViews(); mWidgetManager.popWorldBrightness(this); } @@ -724,15 +813,19 @@ private WindowWidget createWindow() { } public void enterResizeMode() { - for (WindowWidget window : getCurrentWindows()) { - window.getTopBar().setVisible(false); + if (mFullscreenWindow == null) { + for (WindowWidget window : getCurrentWindows()) { + window.getTopBar().setVisible(false); + } } } public void exitResizeMode() { - for (WindowWidget window : getCurrentWindows()) { - if (getCurrentWindows().size() > 1 || isInPrivateMode()) { - window.getTopBar().setVisible(true); + if (mFullscreenWindow == null) { + for (WindowWidget window : getCurrentWindows()) { + if (getCurrentWindows().size() > 1 || isInPrivateMode()) { + window.getTopBar().setVisible(true); + } } } } @@ -770,7 +863,6 @@ public void onHistoryClicked() { public void onCloseClicked(TopBarWidget aWidget) { WindowWidget window = aWidget.getAttachedWindow(); if (window != null) { - focusWindow(window); closeWindow(window); } } @@ -819,6 +911,26 @@ public void onMediaPauseClicked(@NonNull TitleBarWidget titleBar) { } } + private void setFullScreenSize(WindowWidget aWindow) { + final float minScale = WidgetPlacement.floatDimension(mContext, R.dimen.window_fullscreen_min_scale); + // Set browser fullscreen size + float aspect = SettingsStore.getInstance(mContext).getWindowAspect(); + SessionStack sessionStack = mFocusedWindow.getSessionStack(); + if (sessionStack == null) { + return; + } + Media media = sessionStack.getFullScreenVideo(); + if (media != null && media.getWidth() > 0 && media.getHeight() > 0) { + aspect = (float)media.getWidth() / (float)media.getHeight(); + } + float scale = aWindow.getCurrentScale(); + // Enforce min fullscreen size. + // If current window area is larger only resize if the aspect changes (e.g. media). + if (scale < minScale || aspect != aWindow.getCurrentAspect()) { + aWindow.resizeByMultiplier(aspect, Math.max(scale, minScale)); + } + } + // Content delegate @Override public void onFullScreen(GeckoSession session, boolean aFullScreen) { @@ -829,7 +941,8 @@ public void onFullScreen(GeckoSession session, boolean aFullScreen) { if (aFullScreen) { mFullscreenWindow = window; - mPrevWindowPlacement = window.getWindowPlacement(); + window.saveBeforeFullscreenPlacement(); + setFullScreenSize(window); placeWindow(window, WindowPlacement.FRONT); focusWindow(window); for (WindowWidget win: getCurrentWindows()) { @@ -838,7 +951,7 @@ public void onFullScreen(GeckoSession session, boolean aFullScreen) { updateMaxWindowScales(); updateViews(); } else if (mFullscreenWindow != null) { - placeWindow(mFullscreenWindow, mPrevWindowPlacement); + window.restoreBeforeFullscreenPlacement(); mFullscreenWindow = null; for (WindowWidget win : getCurrentWindows()) { setWindowVisible(win, true); @@ -848,6 +961,7 @@ public void onFullScreen(GeckoSession session, boolean aFullScreen) { } } + @Nullable private WindowWidget getWindowWithSession(GeckoSession aSession) { for (WindowWidget window: getCurrentWindows()) { if (window.getSessionStack().containsSession(aSession)) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/CrashDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/CrashDialogWidget.java index abbba8a296..6fdf00eb51 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/CrashDialogWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/CrashDialogWidget.java @@ -17,11 +17,10 @@ import org.mozilla.vrbrowser.browser.SettingsStore; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; +import org.mozilla.vrbrowser.utils.SystemUtils; public class CrashDialogWidget extends UIDialog { - private static final String LOGTAG = "VRB"; - public interface CrashDialogDelegate { void onSendData(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/HistoryItemContextMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/LibraryItemContextMenuWidget.java similarity index 77% rename from app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/HistoryItemContextMenuWidget.java rename to app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/LibraryItemContextMenuWidget.java index f32d6f9920..b0d890c81e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/HistoryItemContextMenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/LibraryItemContextMenuWidget.java @@ -14,39 +14,37 @@ import androidx.annotation.NonNull; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.ui.callbacks.HistoryItemContextMenuClickCallback; -import org.mozilla.vrbrowser.ui.views.HistoryItemContextMenu; +import org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback; +import org.mozilla.vrbrowser.ui.views.LibraryItemContextMenu; 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.ViewUtils; -import mozilla.components.concept.storage.VisitInfo; +public class LibraryItemContextMenuWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { -public class HistoryItemContextMenuWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { - - private HistoryItemContextMenu mContextMenu; + private LibraryItemContextMenu mContextMenu; private int mMaxHeight; private PointF mTranslation; - public HistoryItemContextMenuWidget(Context aContext) { + public LibraryItemContextMenuWidget(Context aContext) { super(aContext); - mContextMenu = new HistoryItemContextMenu(aContext); + mContextMenu = new LibraryItemContextMenu(aContext); initialize(); } - public HistoryItemContextMenuWidget(Context aContext, AttributeSet aAttrs) { + public LibraryItemContextMenuWidget(Context aContext, AttributeSet aAttrs) { super(aContext, aAttrs); - mContextMenu = new HistoryItemContextMenu(aContext, aAttrs); + mContextMenu = new LibraryItemContextMenu(aContext, aAttrs); initialize(); } - public HistoryItemContextMenuWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + public LibraryItemContextMenuWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { super(aContext, aAttrs, aDefStyle); - mContextMenu = new HistoryItemContextMenu(aContext, aAttrs, aDefStyle); + mContextMenu = new LibraryItemContextMenu(aContext, aAttrs, aDefStyle); initialize(); } @@ -71,7 +69,7 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { @Override public void show(@ShowFlags int aShowFlags) { - mWidgetManager.addFocusChangeListener(HistoryItemContextMenuWidget.this); + mWidgetManager.addFocusChangeListener(LibraryItemContextMenuWidget.this); measure(View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); @@ -89,9 +87,9 @@ public void onGlobalLayout() { mWidgetPlacement.translationX = mTranslation.x - (getWidth()/mWidgetPlacement.density); mWidgetPlacement.translationY = mTranslation.y + getResources().getDimension(R.dimen.library_context_menu_top_margin)/mWidgetPlacement.density; - mWidgetPlacement.width = (int)(getWidth()/mWidgetPlacement.density); - mWidgetPlacement.height = (int)(getHeight()/mWidgetPlacement.density); - mWidgetManager.updateWidget(HistoryItemContextMenuWidget.this); + mWidgetPlacement.width = (int)((getWidth() + 5)/mWidgetPlacement.density); + mWidgetPlacement.height = mContextMenu.getMenuHeight() + 5; + mWidgetManager.updateWidget(LibraryItemContextMenuWidget.this); } }); } @@ -109,11 +107,11 @@ protected void onDismiss() { hide(REMOVE_WIDGET); } - public void setHistoryContextMenuItemCallback(HistoryItemContextMenuClickCallback callback) { + public void setHistoryContextMenuItemCallback(LibraryItemContextMenuClickCallback callback) { mContextMenu.setContextMenuClickCallback(callback); } - public void setItem(@NonNull VisitInfo item) { + public void setItem(@NonNull LibraryItemContextMenu.LibraryContextMenuItem item) { mContextMenu.setItem(item); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PermissionWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PermissionWidget.java index f82c1cacbf..e5e14b9be8 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PermissionWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PermissionWidget.java @@ -20,13 +20,12 @@ 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; import java.net.URI; public class PermissionWidget extends UIDialog implements WidgetManagerDelegate.FocusChangeListener { - private static final String LOGTAG = "VRB"; - private TextView mPermissionMessage; private ImageView mPermissionIcon; private GeckoSession.PermissionDelegate.Callback mPermissionCallback; 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 0fe3d7dbd7..9f4a2572a0 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 @@ -18,8 +18,6 @@ public class RestartDialogWidget extends UIDialog { - private static final String LOGTAG = "VRB"; - private AudioEngine mAudio; public RestartDialogWidget(Context aContext) { 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 92cf11c826..4155a08392 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 @@ -33,6 +33,7 @@ import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.LocaleUtils; +import org.mozilla.vrbrowser.utils.SystemUtils; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; @@ -40,7 +41,6 @@ public class VoiceSearchWidget extends UIDialog implements WidgetManagerDelegate.PermissionListener, Application.ActivityLifecycleCallbacks { - private static final String LOGTAG = "VRB"; private static final int VOICESEARCH_AUDIO_REQUEST_CODE = 7455; private static final int ANIMATION_DURATION = 1000; @@ -288,7 +288,7 @@ public void show(@ShowFlags int aShowFlags) { } else { mWidgetManager.getFocusedWindow().showAppDialog( getResources().getString(R.string.voice_samples_collect_data_dialog_title, getResources().getString(R.string.app_name)), - R.string.voice_samples_collect_dialog_description, + R.string.voice_samples_collect_dialog_description2, new int[]{ R.string.voice_samples_collect_dialog_do_not_allow, R.string.voice_samples_collect_dialog_allow}, diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayOptionsView.java index 32f8805dbd..f459383082 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayOptionsView.java @@ -93,30 +93,6 @@ private void initialize(Context aContext) { mBinding.dpiEdit.setFirstText(Integer.toString(SettingsStore.getInstance(getContext()).getDisplayDpi())); mBinding.dpiEdit.setOnClickListener(mDpiListener); setDisplayDpi(SettingsStore.getInstance(getContext()).getDisplayDpi()); - - mBinding.windowSizeEdit.setHint1(String.valueOf(SettingsStore.WINDOW_WIDTH_DEFAULT)); - mBinding.windowSizeEdit.setHint2(String.valueOf(SettingsStore.WINDOW_HEIGHT_DEFAULT)); - mBinding.windowSizeEdit.setDefaultFirstValue(String.valueOf(SettingsStore.WINDOW_WIDTH_DEFAULT)); - mBinding.windowSizeEdit.setDefaultSecondValue(String.valueOf(SettingsStore.WINDOW_HEIGHT_DEFAULT)); - mBinding.windowSizeEdit.setFirstText(Integer.toString(SettingsStore.getInstance(getContext()).getWindowWidth())); - mBinding.windowSizeEdit.setSecondText(Integer.toString(SettingsStore.getInstance(getContext()).getWindowHeight())); - mBinding.windowSizeEdit.setOnClickListener(mWindowSizeListener); - setWindowSize( - SettingsStore.getInstance(getContext()).getWindowWidth(), - SettingsStore.getInstance(getContext()).getWindowHeight(), - false); - - mBinding.maxWindowSizeEdit.setHint1(String.valueOf(SettingsStore.MAX_WINDOW_WIDTH_DEFAULT)); - mBinding.maxWindowSizeEdit.setHint2(String.valueOf(SettingsStore.MAX_WINDOW_HEIGHT_DEFAULT)); - mBinding.maxWindowSizeEdit.setDefaultFirstValue(String.valueOf(SettingsStore.MAX_WINDOW_WIDTH_DEFAULT)); - mBinding.maxWindowSizeEdit.setDefaultSecondValue(String.valueOf(SettingsStore.MAX_WINDOW_HEIGHT_DEFAULT)); - mBinding.maxWindowSizeEdit.setFirstText(Integer.toString(SettingsStore.getInstance(getContext()).getMaxWindowWidth())); - mBinding.maxWindowSizeEdit.setSecondText(Integer.toString(SettingsStore.getInstance(getContext()).getMaxWindowHeight())); - mBinding.maxWindowSizeEdit.setOnClickListener(mMaxWindowSizeListener); - setMaxWindowSize( - SettingsStore.getInstance(getContext()).getMaxWindowWidth(), - SettingsStore.getInstance(getContext()).getMaxWindowHeight(), - false); } @Override @@ -147,16 +123,6 @@ public boolean isEditing() { mBinding.dpiEdit.cancel(); } - if (mBinding.windowSizeEdit.isEditing()) { - editing = true; - mBinding.windowSizeEdit.cancel(); - } - - if (mBinding.maxWindowSizeEdit.isEditing()) { - editing = true; - mBinding.maxWindowSizeEdit.cancel(); - } - if (mBinding.homepageEdit.isEditing()) { editing = true; mBinding.homepageEdit.cancel(); @@ -214,28 +180,6 @@ public boolean isEditing() { } }; - private OnClickListener mWindowSizeListener = (view) -> { - try { - int newWindowWidth = Integer.parseInt(mBinding.windowSizeEdit.getFirstText()); - int newWindowHeight = Integer.parseInt(mBinding.windowSizeEdit.getSecondText()); - setWindowSize(newWindowWidth, newWindowHeight, true); - - } catch (NumberFormatException e) { - setWindowSize(SettingsStore.WINDOW_WIDTH_DEFAULT, SettingsStore.WINDOW_HEIGHT_DEFAULT, true); - } - }; - - private OnClickListener mMaxWindowSizeListener = (view) -> { - try { - int newMaxWindowWidth = Integer.parseInt(mBinding.maxWindowSizeEdit.getFirstText()); - int newMaxWindowHeight = Integer.parseInt(mBinding.maxWindowSizeEdit.getSecondText()); - setMaxWindowSize(newMaxWindowWidth, newMaxWindowHeight, true); - - } catch (NumberFormatException e) { - setMaxWindowSize(SettingsStore.MAX_WINDOW_WIDTH_DEFAULT, SettingsStore.MAX_WINDOW_HEIGHT_DEFAULT, true); - } - }; - private SwitchSetting.OnCheckedChangeListener mCurvedDisplayListener = (compoundButton, enabled, apply) -> setCurvedDisplay(enabled, true); @@ -260,19 +204,6 @@ public boolean isEditing() { restart = restart | setDisplayDensity(SettingsStore.DISPLAY_DENSITY_DEFAULT); restart = restart | setDisplayDpi(SettingsStore.DISPLAY_DPI_DEFAULT); - try { - if (Integer.parseInt(mBinding.windowSizeEdit.getFirstText()) != SettingsStore.WINDOW_WIDTH_DEFAULT || - Integer.parseInt(mBinding.windowSizeEdit.getSecondText()) != SettingsStore.WINDOW_HEIGHT_DEFAULT) { - setWindowSize(SettingsStore.WINDOW_WIDTH_DEFAULT, SettingsStore.WINDOW_HEIGHT_DEFAULT, true); - } - if (Integer.parseInt(mBinding.maxWindowSizeEdit.getFirstText()) != SettingsStore.MAX_WINDOW_WIDTH_DEFAULT || - Integer.parseInt(mBinding.maxWindowSizeEdit.getSecondText()) != SettingsStore.MAX_WINDOW_HEIGHT_DEFAULT) { - setMaxWindowSize(SettingsStore.MAX_WINDOW_WIDTH_DEFAULT, SettingsStore.MAX_WINDOW_HEIGHT_DEFAULT, true); - } - } catch (NumberFormatException ex) { - setWindowSize(SettingsStore.WINDOW_WIDTH_DEFAULT, SettingsStore.WINDOW_HEIGHT_DEFAULT, true); - setMaxWindowSize(SettingsStore.MAX_WINDOW_WIDTH_DEFAULT, SettingsStore.MAX_WINDOW_HEIGHT_DEFAULT, true); - } setHomepage(mDefaultHomepageUrl); setAutoplay(SettingsStore.AUTOPLAY_ENABLED, true); @@ -302,6 +233,7 @@ private void setAutoplay(boolean value, boolean doApply) { if (doApply) { SessionStore.get().setAutoplayEnabled(value); + SettingsStore.getInstance(getContext()).setAutoplayEnabled(value); } } @@ -392,81 +324,6 @@ private boolean setDisplayDpi(int newDpi) { return restart; } - private void setWindowSize(int newWindowWidth, int newWindowHeight, boolean doApply) { - int prevWindowWidth = SettingsStore.getInstance(getContext()).getWindowWidth(); - if (newWindowWidth <= 0) { - newWindowWidth = prevWindowWidth; - } - - int prevWindowHeight = SettingsStore.getInstance(getContext()).getWindowHeight(); - if (newWindowHeight <= 0) { - newWindowHeight = prevWindowHeight; - } - - int maxWindowWidth = SettingsStore.getInstance(getContext()).getMaxWindowWidth(); - if (newWindowWidth > maxWindowWidth) { - newWindowWidth = maxWindowWidth; - } - - int maxWindowHeight = SettingsStore.getInstance(getContext()).getMaxWindowHeight(); - if (newWindowHeight > maxWindowHeight) { - newWindowHeight = maxWindowHeight; - } - - if (prevWindowWidth != newWindowWidth || prevWindowHeight != newWindowHeight) { - SettingsStore.getInstance(getContext()).setWindowWidth(newWindowWidth); - SettingsStore.getInstance(getContext()).setWindowHeight(newWindowHeight); - - if (doApply) { - mWidgetManager.setWindowSize(newWindowWidth, newWindowHeight); - } - } - - String newWindowWidthStr = Integer.toString(newWindowWidth); - mBinding.windowSizeEdit.setFirstText(newWindowWidthStr); - String newWindowHeightStr = Integer.toString(newWindowHeight); - mBinding.windowSizeEdit.setSecondText(newWindowHeightStr); - } - - private void setMaxWindowSize(int newMaxWindowWidth, int newMaxWindowHeight, boolean doApply) { - int prevMaxWindowWidth = SettingsStore.getInstance(getContext()).getMaxWindowWidth(); - if (newMaxWindowWidth <= 0) { - newMaxWindowWidth = prevMaxWindowWidth; - } - - int prevMaxWindowHeight = SettingsStore.getInstance(getContext()).getMaxWindowHeight(); - if (newMaxWindowHeight <= 0) { - newMaxWindowHeight = prevMaxWindowHeight; - } - - int windowWidth = SettingsStore.getInstance(getContext()).getWindowWidth(); - if (newMaxWindowWidth < windowWidth) { - newMaxWindowWidth = windowWidth; - } - - int windowHeight = SettingsStore.getInstance(getContext()).getWindowHeight(); - if (newMaxWindowHeight < windowHeight) { - newMaxWindowHeight = windowHeight; - } - - if (newMaxWindowWidth != prevMaxWindowWidth || - newMaxWindowHeight != prevMaxWindowHeight) { - SettingsStore.getInstance(getContext()).setMaxWindowWidth(newMaxWindowWidth); - SettingsStore.getInstance(getContext()).setMaxWindowHeight(newMaxWindowHeight); - - if (doApply) { - SettingsStore.getInstance(getContext()).setMaxWindowWidth(newMaxWindowWidth); - SettingsStore.getInstance(getContext()).setMaxWindowHeight(newMaxWindowHeight); - showRestartDialog(); - } - } - - String newMaxWindowWidthStr = Integer.toString(newMaxWindowWidth); - mBinding.maxWindowSizeEdit.setFirstText(newMaxWindowWidthStr); - String newMaxWindowHeightStr = Integer.toString(newMaxWindowHeight); - mBinding.maxWindowSizeEdit.setSecondText(newMaxWindowHeightStr); - } - @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { if (oldFocus != null) { @@ -476,16 +333,6 @@ public void onGlobalFocusChanged(View oldFocus, View newFocus) { if (mBinding.dpiEdit.contains(oldFocus) && mBinding.dpiEdit.isEditing()) { mBinding.dpiEdit.cancel(); } - if (mBinding.windowSizeEdit.contains(oldFocus) && - (newFocus != null && !mBinding.windowSizeEdit.contains(newFocus)) && - mBinding.windowSizeEdit.isEditing()) { - mBinding.windowSizeEdit.cancel(); - } - if (mBinding.maxWindowSizeEdit.contains(oldFocus) && - (newFocus != null && !mBinding.maxWindowSizeEdit.contains(newFocus)) && - mBinding.maxWindowSizeEdit.isEditing()) { - mBinding.maxWindowSizeEdit.cancel(); - } if (mBinding.homepageEdit.contains(oldFocus) && mBinding.homepageEdit.isEditing()) { mBinding.homepageEdit.cancel(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/EnvironmentOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/EnvironmentOptionsView.java index 74435bf332..6d69a0d2a3 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/EnvironmentOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/EnvironmentOptionsView.java @@ -55,6 +55,14 @@ private void initialize(Context aContext) { }); } + @Override + public void onShown() { + super.onShown(); + + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_NO_DIM_BRIGHTNESS); + } + + @Override public void onHidden() { mWidgetManager.popWorldBrightness(this); 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 index f7a0827fc3..6a628ca44a 100644 --- 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 @@ -92,8 +92,11 @@ private void setVoiceLanguage() { 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)); + String text = ""; + if (preferredLanguages.size() > 0) { + text = preferredLanguages.get(0).getName(); + } + mBinding.contentLanguageButton.setDescription(ViewUtils.getSpannedText(getContext().getResources().getString(R.string.language_options_content_language, text))); } private void setDisplayLanguage() { 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 0a7e9497e7..c5081b4f9e 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 @@ -32,6 +32,7 @@ import org.mozilla.vrbrowser.ui.widgets.dialogs.RestartDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.UIDialog; import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget; +import org.mozilla.vrbrowser.utils.SystemUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -40,7 +41,6 @@ import java.util.GregorianCalendar; public class SettingsWidget extends UIDialog implements WidgetManagerDelegate.WorldClickListener, SettingsView.Delegate { - private static final String LOGTAG = "VRB"; private AudioEngine mAudio; private SettingsView mCurrentView; private TextView mBuildText; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java index fefd683f20..c5f83f4063 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java @@ -1,6 +1,7 @@ package org.mozilla.vrbrowser.utils; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.util.Log; import android.view.View; @@ -10,6 +11,10 @@ import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; +import androidx.annotation.NonNull; + +import org.mozilla.gecko.util.ThreadUtils; + public class AnimationHelper { public static final long FADE_ANIMATION_DURATION = 150; @@ -110,4 +115,30 @@ public void onAnimationRepeat(Animator animator) { }); animation.start(); } + + public static void scaleIn(@NonNull View aView, long duration, long delay, final Runnable aCallback) { + aView.setScaleX(0); + aView.setScaleY(0); + aView.animate().setStartDelay(delay).scaleX(1f).scaleY(1f).setDuration(duration).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (aCallback != null) + ThreadUtils.postToUiThread(aCallback); + } + }).setUpdateListener(animation -> aView.invalidate()); + } + + public static void scaleOut(@NonNull View aView, long duration, long delay, final Runnable aCallback) { + aView.setScaleX(1); + aView.setScaleY(1); + aView.animate().setStartDelay(delay).scaleX(0f).scaleY(0f).setDuration(duration).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (aCallback != null) + ThreadUtils.postToUiThread(aCallback); + } + }).setUpdateListener(animation -> aView.invalidate()); + } } 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 8ed0ae45f3..fca8a5bb9e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/LocaleUtils.java @@ -4,7 +4,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; -import android.util.Log; import androidx.annotation.NonNull; @@ -60,9 +59,6 @@ private static HashMap getAllLanguages() { String languageId = temp.toLanguageTag(); String displayName = temp.getDisplayName().substring(0, 1).toUpperCase() + temp.getDisplayName().substring(1); Language locale = new Language(languageId, displayName + " [" + languageId + "]"); - if (languageId.equals(currentLocale)) { - locale.setDefault(true); - } mLanguagesCache.put(languageId, locale); } @@ -93,14 +89,17 @@ public static List getPreferredLanguages(@NonNull Context aContext) { HashMap languages = getAllLanguages(); List savedLanguages = SettingsStore.getInstance(aContext).getContentLocales(); List preferredLanguages = new ArrayList<>(); - for (String language : savedLanguages) { - Language lang = languages.get(language); - lang.setPreferred(true); - preferredLanguages.add(lang); - } + if (savedLanguages != null) { + for (String language : savedLanguages) { + Language lang = languages.get(language); + lang.setPreferred(true); + preferredLanguages.add(lang); + } - if (!savedLanguages.stream().anyMatch(str -> str.trim().equals(getCurrentLocale()))) { - preferredLanguages.add(getCurrentLocaleLanguage()); + } else { + Language currentLanguage = getCurrentLocaleLanguage(); + currentLanguage.setPreferred(true); + preferredLanguages.add(currentLanguage); } return preferredLanguages; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java index 10452a4c83..e5503348c4 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/SystemUtils.java @@ -36,4 +36,8 @@ public static final void scheduleRestart(@NonNull Context context, long delay) { System.exit(0); } + @NonNull + public static String createLogtag(@NonNull Class aClass) { + return "VRB[" + aClass.getSimpleName() + "]"; + } } diff --git a/app/src/googlevr/java/org/mozilla/vrbrowser/PlatformActivity.java b/app/src/googlevr/java/org/mozilla/vrbrowser/PlatformActivity.java index b6d04e9284..085fd88a69 100644 --- a/app/src/googlevr/java/org/mozilla/vrbrowser/PlatformActivity.java +++ b/app/src/googlevr/java/org/mozilla/vrbrowser/PlatformActivity.java @@ -14,6 +14,8 @@ import android.view.View; import android.view.WindowManager; +import org.mozilla.vrbrowser.utils.SystemUtils; + import com.google.vr.ndk.base.AndroidCompat; import com.google.vr.ndk.base.GvrLayout; @@ -23,8 +25,7 @@ import javax.microedition.khronos.opengles.GL10; public class PlatformActivity extends Activity { - static String LOGTAG = "VRB"; - + static String LOGTAG = SystemUtils.createLogtag(PlatformActivity.class); public static boolean filterPermission(final String aPermission) { return false; } diff --git a/app/src/main/cpp/BrowserWorld.cpp b/app/src/main/cpp/BrowserWorld.cpp index e04b81fd43..8a1c9ee635 100644 --- a/app/src/main/cpp/BrowserWorld.cpp +++ b/app/src/main/cpp/BrowserWorld.cpp @@ -18,6 +18,7 @@ #include "Pointer.h" #include "Widget.h" #include "WidgetMover.h" +#include "WidgetResizer.h" #include "WidgetPlacement.h" #include "Cylinder.h" #include "Quad.h" @@ -182,6 +183,7 @@ struct BrowserWorld::State { VRVideoPtr vrVideo; PerformanceMonitorPtr monitor; WidgetMoverPtr movingWidget; + WidgetResizerPtr widgetResizer; State() : paused(true), glInitialized(false), modelsLoaded(false), env(nullptr), cylinderDensity(0.0f), nearClip(0.1f), farClip(300.0f), activity(nullptr), windowsInitialized(false), exitImmersiveRequested(false), loaderDelay(0) { @@ -793,6 +795,8 @@ BrowserWorld::UpdateEnvironment() { } else { VRB_ERROR("Failed to find custom skybox files."); } + } else { + VRB_ERROR("Failed to find override skybox storage path %s", storagePath.c_str()); } } @@ -937,7 +941,8 @@ BrowserWorld::StartWidgetResize(int32_t aHandle, const vrb::Vector& aMaxSize, co ASSERT_ON_RENDER_THREAD(); WidgetPtr widget = m.GetWidget(aHandle); if (widget) { - widget->StartResize(aMaxSize, aMinSize); + m.widgetResizer = widget->StartResize(aMaxSize, aMinSize); + m.rootTransparent->AddNode(m.widgetResizer->GetRoot()); } } @@ -949,6 +954,10 @@ BrowserWorld::FinishWidgetResize(int32_t aHandle) { return; } widget->FinishResize(); + if (m.widgetResizer) { + m.widgetResizer->GetRoot()->RemoveFromParents(); + m.widgetResizer = nullptr; + } } void @@ -1063,8 +1072,8 @@ BrowserWorld::LayoutWidget(int32_t aHandle) { } widget->SetTransform(parent ? parent->GetTransform().PostMultiply(transform) : transform); - if (!widget->GetCylinder()) { - widget->LayoutQuadWithCylinderParent(parent && parent->GetCylinder() ? parent->GetCylinder() : nullptr); + if (!widget->GetCylinder() && parent) { + widget->LayoutQuadWithCylinderParent(parent); } } @@ -1354,24 +1363,29 @@ BrowserWorld::CreateSkyBox(const std::string& aBasePath, const std::string& aExt float BrowserWorld::ComputeNormalizedZ(const vrb::NodePtr& aNode) const { - WidgetPtr target; - bool pointer = false; + Widget * target = nullptr; + float zDelta = 0.0f; for (const auto & widget: m.widgets) { if (widget->GetRoot() == aNode) { - target = widget; + target = widget.get(); break; } } if (!target) { for (Controller& controller: m.controllers->GetControllers()) { if (controller.pointer && controller.pointer->GetRoot() == aNode) { - target = controller.pointer->GetHitWidget(); - pointer = true; + target = controller.pointer->GetHitWidget().get(); + zDelta = 0.02f; break; } } } + if (!target && m.widgetResizer && m.widgetResizer ->GetRoot() == aNode) { + target = m.widgetResizer->GetWidget(); + zDelta = 0.01f; + } + if (!target) { return 1.0f; } @@ -1395,12 +1409,7 @@ BrowserWorld::ComputeNormalizedZ(const vrb::NodePtr& aNode) const { vrb::Vector ndc = viewProjection.MultiplyPosition(hitPoint); - float z = ndc.z(); - - if (pointer) { - z-= 0.001f; - } - return z; + return ndc.z() - zDelta; } } // namespace crow diff --git a/app/src/main/cpp/VRLayer.cpp b/app/src/main/cpp/VRLayer.cpp index d3a266fb16..26573a0792 100644 --- a/app/src/main/cpp/VRLayer.cpp +++ b/app/src/main/cpp/VRLayer.cpp @@ -27,6 +27,7 @@ struct VRLayer::State { device::EyeRect textureRect[2]; SurfaceChangedDelegate surfaceChangedDelegate; std::function pendingEvent; + std::string name; State(): initialized(false), priority(0), @@ -102,6 +103,11 @@ VRLayer::GetDrawInFront() const { return m.drawInFront; } +std::string +VRLayer::GetName() const { + return m.name; +} + bool VRLayer::ShouldDrawBefore(const VRLayer& aLayer) { if (m.layerType == VRLayer::LayerType::CUBEMAP || m.layerType == VRLayer::LayerType::EQUIRECTANGULAR) { @@ -191,6 +197,11 @@ VRLayer::SetDrawInFront(bool aDrawInFront) { m.drawInFront = aDrawInFront; } +void +VRLayer::SetName(const std::string &aName) { + m.name = aName; +} + void VRLayer::NotifySurfaceChanged(SurfaceChange aChange, const std::function& aFirstCompositeCallback) { if (m.surfaceChangedDelegate) { m.surfaceChangedDelegate(*this, aChange, aFirstCompositeCallback); diff --git a/app/src/main/cpp/VRLayer.h b/app/src/main/cpp/VRLayer.h index 1bb1928d93..2ea07971c2 100644 --- a/app/src/main/cpp/VRLayer.h +++ b/app/src/main/cpp/VRLayer.h @@ -48,6 +48,7 @@ class VRLayer { const vrb::Color& GetTintColor() const; const device::EyeRect& GetTextureRect(device::Eye aEye) const; bool GetDrawInFront() const; + std::string GetName() const; bool ShouldDrawBefore(const VRLayer& aLayer); void SetInitialized(bool aInitialized); @@ -62,6 +63,7 @@ class VRLayer { void SetTextureRect(device::Eye aEye, const device::EyeRect& aTextureRect); void SetSurfaceChangedDelegate(const SurfaceChangedDelegate& aDelegate); void SetDrawInFront(bool aDrawInFront); + void SetName(const std::string& aName); void NotifySurfaceChanged(SurfaceChange aChange, const std::function& aFirstCompositeCallback); protected: struct State; diff --git a/app/src/main/cpp/Widget.cpp b/app/src/main/cpp/Widget.cpp index 18f6576f4b..e1afa0a200 100644 --- a/app/src/main/cpp/Widget.cpp +++ b/app/src/main/cpp/Widget.cpp @@ -171,6 +171,7 @@ struct Widget::State { vrb::Matrix translation = vrb::Matrix::Translation(vrb::Vector(0.0f, 0.0f, radius * scale)); cylinder->SetTransform(translation.PostMultiply(scaleMatrix)); AdjustCylinderRotation(radius * scale); + UpdateResizerTransform(); } void AdjustCylinderRotation(const float radius) { @@ -203,6 +204,12 @@ struct Widget::State { } borders.clear(); } + + void UpdateResizerTransform() { + if (resizer) { + resizer->SetTransform(transformContainer->GetTransform().PostMultiply(transform->GetTransform())); + } + } }; WidgetPtr @@ -355,6 +362,7 @@ Widget::SetTransform(const vrb::Matrix& aTransform) { if (m.cylinder) { m.UpdateCylinderMatrix(); } + m.UpdateResizerTransform(); } void @@ -457,10 +465,11 @@ Widget::SetPlacement(const WidgetPlacementPtr& aPlacement) { VRLayerSurfacePtr layer = GetLayer(); if (layer) { layer->SetClearColor(aPlacement->clearColor); + layer->SetName(aPlacement->name); } } -void +WidgetResizerPtr Widget::StartResize(const vrb::Vector& aMaxSize, const vrb::Vector& aMinSize) { vrb::Vector worldMin, worldMax; GetWidgetMinAndMax(worldMin, worldMax); @@ -469,11 +478,10 @@ Widget::StartResize(const vrb::Vector& aMaxSize, const vrb::Vector& aMinSize) { } else { vrb::RenderContextPtr render = m.context.lock(); if (!render) { - return; + return nullptr; } vrb::CreationContextPtr create = render->GetRenderThreadCreationContext(); m.resizer = WidgetResizer::Create(create, this); - m.transform->InsertNode(m.resizer->GetRoot(), 0); } m.resizer->SetResizeLimits(aMaxSize, aMinSize); m.resizing = true; @@ -482,6 +490,8 @@ Widget::StartResize(const vrb::Vector& aMaxSize, const vrb::Vector& aMinSize) { m.quad->SetScaleMode(Quad::ScaleMode::AspectFit); m.quad->SetBackgroundColor(vrb::Color(1.0f, 1.0f, 1.0f, 1.0f)); } + m.UpdateResizerTransform(); + return m.resizer; } void @@ -611,13 +621,22 @@ Widget::SetProxifyLayer(const bool aValue) { m.layerProxy->ToggleAll(true); } -void Widget::LayoutQuadWithCylinderParent(const CylinderPtr& aCylinder) { - if (aCylinder) { - const float radius = aCylinder->GetTransformNode()->GetTransform().GetScale().x(); +void Widget::LayoutQuadWithCylinderParent(const WidgetPtr& aParent) { + CylinderPtr cylinder = aParent->GetCylinder(); + if (cylinder) { + // The widget is flat and the parent is a cylinder. + // Adjust the widget rotation based on the parent cylinder + // e.g. rotate the tray based on the parent cylindrical window. + const float radius = cylinder->GetTransformNode()->GetTransform().GetScale().x(); m.AdjustCylinderRotation(radius); } else { - m.transformContainer->SetTransform(vrb::Matrix::Identity()); + // The widget is flat and the parent is flat. Copy the parent transformContainer matrix (used for cylinder rotations) + // because the parent widget can still be recursively rotated based on a parent cylinder. + // e.g. Place the tray tooltips on the correct tray position which may be rotated based on the + // parent cylindrical window. + m.transformContainer->SetTransform(aParent->m.transformContainer->GetTransform()); } + m.UpdateResizerTransform(); } Widget::Widget(State& aState, vrb::RenderContextPtr& aContext) : m(aState) { diff --git a/app/src/main/cpp/Widget.h b/app/src/main/cpp/Widget.h index f6da744883..6e79bd43c6 100644 --- a/app/src/main/cpp/Widget.h +++ b/app/src/main/cpp/Widget.h @@ -30,6 +30,9 @@ typedef std::shared_ptr QuadPtr; class Widget; typedef std::shared_ptr WidgetPtr; +class WidgetResizer; +typedef std::shared_ptr WidgetResizerPtr; + class WidgetPlacement; typedef std::shared_ptr WidgetPlacementPtr; @@ -66,7 +69,7 @@ class Widget { vrb::TransformPtr GetTransformNode() const; const WidgetPlacementPtr& GetPlacement() const; void SetPlacement(const WidgetPlacementPtr& aPlacement); - void StartResize(const vrb::Vector& aMaxSize, const vrb::Vector& aMinSize); + WidgetResizerPtr StartResize(const vrb::Vector& aMaxSize, const vrb::Vector& aMinSize); void FinishResize(); bool IsResizing() const; bool IsResizingActive() const; @@ -76,7 +79,7 @@ class Widget { float GetCylinderDensity() const; void SetBorderColor(const vrb::Color& aColor); void SetProxifyLayer(const bool aValue); - void LayoutQuadWithCylinderParent(const CylinderPtr& aCylinder); + void LayoutQuadWithCylinderParent(const WidgetPtr& aParent); protected: struct State; Widget(State& aState, vrb::RenderContextPtr& aContext); diff --git a/app/src/main/cpp/WidgetPlacement.cpp b/app/src/main/cpp/WidgetPlacement.cpp index 04fb5b9b2f..2064d0b8fa 100644 --- a/app/src/main/cpp/WidgetPlacement.cpp +++ b/app/src/main/cpp/WidgetPlacement.cpp @@ -34,6 +34,17 @@ WidgetPlacement::FromJava(JNIEnv* aEnv, jobject& aObject) { result->name = aEnv->GetBooleanField(aObject, f); \ } +#define GET_STRING_FIELD(name) { \ + jfieldID f = aEnv->GetFieldID(clazz, #name, "Ljava/lang/String;"); \ + jstring javaString = (jstring)aEnv->GetObjectField(aObject, f); \ + if (javaString) { \ + const char* nativeString = aEnv->GetStringUTFChars(javaString, 0); \ + result->name = nativeString; \ + aEnv->ReleaseStringUTFChars(javaString, nativeString); \ + } \ +} + + GET_INT_FIELD(width); GET_INT_FIELD(height); GET_FLOAT_FIELD(anchor.x(), "anchorX"); @@ -61,6 +72,7 @@ WidgetPlacement::FromJava(JNIEnv* aEnv, jobject& aObject) { GET_FLOAT_FIELD(cylinderMapRadius, "cylinderMapRadius"); GET_INT_FIELD(borderColor); GET_INT_FIELD(clearColor); + GET_STRING_FIELD(name); return result; } diff --git a/app/src/main/cpp/WidgetPlacement.h b/app/src/main/cpp/WidgetPlacement.h index 44dcf21dca..cc296c235b 100644 --- a/app/src/main/cpp/WidgetPlacement.h +++ b/app/src/main/cpp/WidgetPlacement.h @@ -39,6 +39,8 @@ struct WidgetPlacement { int borderColor; int clearColor; + std::string name; + int32_t GetTextureWidth() const; int32_t GetTextureHeight() const; vrb::Color GetClearColor() const; diff --git a/app/src/main/cpp/WidgetResizer.cpp b/app/src/main/cpp/WidgetResizer.cpp index cf2b0e3c9e..4231333dea 100644 --- a/app/src/main/cpp/WidgetResizer.cpp +++ b/app/src/main/cpp/WidgetResizer.cpp @@ -14,6 +14,7 @@ #include "vrb/Color.h" #include "vrb/CreationContext.h" #include "vrb/Matrix.h" +#include "vrb/GLError.h" #include "vrb/Geometry.h" #include "vrb/RenderState.h" #include "vrb/SurfaceTextureFactory.h" @@ -255,6 +256,7 @@ struct WidgetResizer::State { vrb::Vector minSize; bool resizing; vrb::TogglePtr root; + vrb::TransformPtr transform; std::vector resizeHandles; std::vector resizeBars; ResizeHandlePtr activeHandle; @@ -272,6 +274,8 @@ struct WidgetResizer::State { return; } root = vrb::Toggle::Create(create); + transform = vrb::Transform::Create(create); + root->AddNode(transform); currentMin = min; currentMax = max; maxSize = kDefaultMaxResize; @@ -313,7 +317,7 @@ struct WidgetResizer::State { } ResizeBarPtr result = ResizeBar::Create(create, aCenter, aScale, aBorder, aMode); resizeBars.push_back(result); - root->AddNode(result->border->GetTransformNode()); + transform->AddNode(result->border->GetTransformNode()); return result; } @@ -325,7 +329,7 @@ struct WidgetResizer::State { ResizeHandlePtr result = ResizeHandle::Create(create, aCenter, aResizeMode, aBars); result->touchRatio = aTouchRatio; resizeHandles.push_back(result); - root->InsertNode(result->root, 0); + transform->InsertNode(result->root, 0); return result; } @@ -629,6 +633,15 @@ WidgetResizer::IsActive() const { return m.activeHandle && m.activeHandle->resizeState == ResizeState::Active; } +void +WidgetResizer::SetTransform(const vrb::Matrix &aTransform){ + m.transform->SetTransform(aTransform); +} + +Widget* +WidgetResizer::GetWidget() const { + return m.widget; +} WidgetResizer::WidgetResizer(State& aState, vrb::CreationContextPtr& aContext) : m(aState) { m.context = aContext; diff --git a/app/src/main/cpp/WidgetResizer.h b/app/src/main/cpp/WidgetResizer.h index bf7cb193f6..b7c26b381f 100644 --- a/app/src/main/cpp/WidgetResizer.h +++ b/app/src/main/cpp/WidgetResizer.h @@ -22,7 +22,7 @@ typedef std::shared_ptr WidgetResizerPtr; class WidgetResizer { public: - static WidgetResizerPtr Create(vrb::CreationContextPtr& aContext, Widget * aWidget); + static WidgetResizerPtr Create(vrb::CreationContextPtr& aContext, Widget* aWidget); vrb::NodePtr GetRoot() const; void SetSize(const vrb::Vector& aMin, const vrb::Vector& aMax); void SetResizeLimits(const vrb::Vector& aMaxSize, const vrb::Vector& aMinSize); @@ -33,6 +33,8 @@ class WidgetResizer { const vrb::Vector& GetResizeMin() const; const vrb::Vector& GetResizeMax() const; bool IsActive() const; + Widget* GetWidget() const; + void SetTransform(const vrb::Matrix& aTransform); protected: struct State; WidgetResizer(State& aState, vrb::CreationContextPtr& aContext); diff --git a/app/src/main/res/drawable/ic_icon_language_add.xml b/app/src/main/res/drawable/ic_icon_language_add.xml index 6856d9dc5b..5ca9998372 100644 --- a/app/src/main/res/drawable/ic_icon_language_add.xml +++ b/app/src/main/res/drawable/ic_icon_language_add.xml @@ -1,5 +1,6 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_icon_language_delete.xml b/app/src/main/res/drawable/ic_icon_language_delete.xml index ee6b06795e..dd429375dc 100644 --- a/app/src/main/res/drawable/ic_icon_language_delete.xml +++ b/app/src/main/res/drawable/ic_icon_language_delete.xml @@ -1,5 +1,6 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/library_panel_context_menu_item_background_color_bottom.xml b/app/src/main/res/drawable/library_context_menu_item_background_bottom.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_context_menu_item_background_color_bottom.xml rename to app/src/main/res/drawable/library_context_menu_item_background_bottom.xml diff --git a/app/src/main/res/drawable/library_panel_context_menu_item_background_color_middle.xml b/app/src/main/res/drawable/library_context_menu_item_background_middle.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_context_menu_item_background_color_middle.xml rename to app/src/main/res/drawable/library_context_menu_item_background_middle.xml diff --git a/app/src/main/res/drawable/library_context_menu_item_background_single.xml b/app/src/main/res/drawable/library_context_menu_item_background_single.xml new file mode 100644 index 0000000000..7c243ee02e --- /dev/null +++ b/app/src/main/res/drawable/library_context_menu_item_background_single.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/library_panel_context_menu_item_background_color_top.xml b/app/src/main/res/drawable/library_context_menu_item_background_top.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_context_menu_item_background_color_top.xml rename to app/src/main/res/drawable/library_context_menu_item_background_top.xml diff --git a/app/src/main/res/drawable/library_panel_item_background_color.xml b/app/src/main/res/drawable/library_item_background_color.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_item_background_color.xml rename to app/src/main/res/drawable/library_item_background_color.xml diff --git a/app/src/main/res/drawable/library_notification_background.xml b/app/src/main/res/drawable/library_notification_background.xml new file mode 100644 index 0000000000..fe67ba0a17 --- /dev/null +++ b/app/src/main/res/drawable/library_notification_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/library_notification_background_triangle.xml b/app/src/main/res/drawable/library_notification_background_triangle.xml new file mode 100644 index 0000000000..4d409349aa --- /dev/null +++ b/app/src/main/res/drawable/library_notification_background_triangle.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/main_button_icon_color_notification.xml b/app/src/main/res/drawable/main_button_icon_color_notification.xml new file mode 100644 index 0000000000..c9847983e1 --- /dev/null +++ b/app/src/main/res/drawable/main_button_icon_color_notification.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/scrollbar_thumb.xml b/app/src/main/res/drawable/scrollbar_thumb.xml index 2268c50b2f..c500866961 100644 --- a/app/src/main/res/drawable/scrollbar_thumb.xml +++ b/app/src/main/res/drawable/scrollbar_thumb.xml @@ -1,6 +1,6 @@ - + @@ -16,9 +16,9 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/layout/bookmark_item.xml b/app/src/main/res/layout/bookmark_item.xml index 2ad2b3872e..708497091d 100644 --- a/app/src/main/res/layout/bookmark_item.xml +++ b/app/src/main/res/layout/bookmark_item.xml @@ -1,73 +1,133 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + type="org.mozilla.vrbrowser.ui.callbacks.BookmarkItemCallback" /> + + - - - + android:layout_height="@dimen/library_item_row_height" + android:background="@color/void_color"> - + android:layout_height="match_parent" + android:background="@drawable/library_item_background_color" + android:clickable="true" + android:descendantFocusability="blocksDescendants" + android:focusable="true" + android:gravity="center_vertical" + android:onClick="@{(view) -> callback.onClick(view, item)}" + android:paddingStart="10dp" + android:paddingEnd="10dp" + android:soundEffectsEnabled="false"> + + + + + + + + + + + + + - - + + + + + diff --git a/app/src/main/res/layout/bookmarks.xml b/app/src/main/res/layout/bookmarks.xml index 62300df871..514cff3833 100644 --- a/app/src/main/res/layout/bookmarks.xml +++ b/app/src/main/res/layout/bookmarks.xml @@ -1,5 +1,6 @@ - @@ -11,6 +12,10 @@ + + + android:paddingStart="30dp" + android:paddingTop="30dp" + android:paddingEnd="30dp" + android:paddingBottom="30dp"> + app:visibleGone="@{!isLoading && isEmpty}"> + + - + android:layout_marginEnd="20dp" + android:layout_marginBottom="20dp" + app:visibleGone="@{!isLoading && !isEmpty}"> + + + + + @@ -47,7 +47,7 @@ android:layout_height="wrap_content" android:layout_marginTop="35dp" android:fontFamily="sans-serif" - android:text="@string/history_title" + android:text="@string/history_empty" android:textAlignment="center" android:textAllCaps="false" android:textColor="@color/fog" diff --git a/app/src/main/res/layout/history_item.xml b/app/src/main/res/layout/history_item.xml index 863a1af66c..55d7b87534 100644 --- a/app/src/main/res/layout/history_item.xml +++ b/app/src/main/res/layout/history_item.xml @@ -21,7 +21,7 @@ + tools:text="Item Title" /> @@ -94,7 +94,7 @@ android:singleLine="true" android:text="@{item.url}" android:textColor="@color/library_panel_description_color" - android:textSize="@dimen/history_url_text_size" + android:textSize="@dimen/library_item_url_text_size" tools:text="http://mozilla.org" /> @@ -116,7 +116,7 @@ android:scrollHorizontally="true" android:singleLine="true" android:textColor="@color/library_panel_description_color" - android:textSize="@dimen/history_date_text_size" + android:textSize="@dimen/library_item_date_text_size" app:bindDate="@{item.visitTime}" app:visibleInvisible="@{!isHovered}" tools:text="8/2/19, 2:18 PM" /> diff --git a/app/src/main/res/layout/language_item.xml b/app/src/main/res/layout/language_item.xml index 060d29411e..440cd3cd49 100644 --- a/app/src/main/res/layout/language_item.xml +++ b/app/src/main/res/layout/language_item.xml @@ -67,7 +67,7 @@ android:contentDescription="Preferred language delete button" android:duplicateParentState="true" android:soundEffectsEnabled="false" - android:src="@{language.isDefault ? @drawable/ic_icon_empty : @drawable/ic_icon_language_delete}" + android:src="@{@drawable/ic_icon_language_delete}" android:tint="@color/fog" app:srcCompat="@drawable/ic_icon_language_delete" app:visibleGone="@{isPreferred}" /> @@ -95,8 +95,7 @@ android:gravity="center_vertical" android:paddingStart="5dp" android:singleLine="true" - android:text="@{language.isDefault && isPreferred ? `(` + @string/language_default + `) ` + language.name : language.name}" - app:typeface='@{language.isDefault ? "bold" : "normal"}' + android:text="@{language.name}" tool:text="Language Item" /> diff --git a/app/src/main/res/layout/history_item_context_menu.xml b/app/src/main/res/layout/library_item_context_menu.xml similarity index 81% rename from app/src/main/res/layout/history_item_context_menu.xml rename to app/src/main/res/layout/library_item_context_menu.xml index c5fafc2468..f61f2eb835 100644 --- a/app/src/main/res/layout/history_item_context_menu.xml +++ b/app/src/main/res/layout/library_item_context_menu.xml @@ -3,13 +3,15 @@ xmlns:tools="http://schemas.android.com/tools"> + + + type="org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback" /> + type="org.mozilla.vrbrowser.ui.views.LibraryItemContextMenu.LibraryContextMenuItem" /> diff --git a/app/src/main/res/layout/library_notification.xml b/app/src/main/res/layout/library_notification.xml new file mode 100644 index 0000000000..e6a2302dad --- /dev/null +++ b/app/src/main/res/layout/library_notification.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_popup_window.xml b/app/src/main/res/layout/list_popup_window.xml index 88b4198b9e..b32c18d82e 100644 --- a/app/src/main/res/layout/list_popup_window.xml +++ b/app/src/main/res/layout/list_popup_window.xml @@ -4,13 +4,15 @@ android:layout_height="match_parent" android:layout_width="match_parent"> - - + - + - + - - - - + diff --git a/app/src/main/res/layout/navigation_bar_fullscreen.xml b/app/src/main/res/layout/navigation_bar_fullscreen.xml index 1dc2f3f9d3..e53bdb6df3 100644 --- a/app/src/main/res/layout/navigation_bar_fullscreen.xml +++ b/app/src/main/res/layout/navigation_bar_fullscreen.xml @@ -1,37 +1,50 @@ - - + - + - + - - + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/navigation_bar_resize.xml b/app/src/main/res/layout/navigation_bar_resize.xml index 06be11ced3..57d4da1c52 100644 --- a/app/src/main/res/layout/navigation_bar_resize.xml +++ b/app/src/main/res/layout/navigation_bar_resize.xml @@ -3,12 +3,10 @@ - - - diff --git a/app/src/main/res/layout/options_language_content.xml b/app/src/main/res/layout/options_language_content.xml index 59140a31d1..d3c40c58bb 100644 --- a/app/src/main/res/layout/options_language_content.xml +++ b/app/src/main/res/layout/options_language_content.xml @@ -42,9 +42,6 @@ android:layout_height="wrap_content" android:paddingBottom="5dp" style="@style/settingsText" - android:layout_alignParentStart="true" - android:layout_centerVertical="true" - android:layout_toStartOf="@+id/button" android:gravity="center_vertical" android:text="@string/language_options_preferred_languages" tools:text="@string/language_options_preferred_languages" /> @@ -69,9 +66,6 @@ android:layout_height="wrap_content" android:paddingBottom="5dp" style="@style/settingsText" - android:layout_alignParentStart="true" - android:layout_centerVertical="true" - android:layout_toStartOf="@+id/button" android:gravity="center_vertical" android:text="@string/language_options_available_languages" tools:text="@string/language_options_available_languages" /> diff --git a/app/src/main/res/layout/setting_edit.xml b/app/src/main/res/layout/setting_edit.xml index 1b2ae91d44..10ed01d6d2 100644 --- a/app/src/main/res/layout/setting_edit.xml +++ b/app/src/main/res/layout/setting_edit.xml @@ -25,7 +25,7 @@ android:id="@+id/setting_description" style="@style/settingsText" android:layout_marginEnd="5dp" - android:text="@string/developer_options_window_size" + android:text="@string/developer_options_display_dpi" tools:text="Setting Description" /> Elige el idioma en el que quieras para las búsquedas por voz + + Idioma de búsqueda por voz + + + Elige tu idioma favorito para la búsqueda por voz. + - Inglés (US) + Inglés (EE. UU.) @@ -194,6 +206,10 @@ a developer-build version of the app. --> Compilación del desarrollador + + Ayuda + Se requiere reiniciar @@ -233,6 +249,12 @@ A dialog should appear with a field labeled with the text, 'Enable Multiprocess'. --> Habilitar multiproceso + + Activar el monitor de rendimiento + Habilitar Servo @@ -332,6 +354,9 @@ Restablecer + + Editar + Entorno @@ -640,7 +665,7 @@ Abrir en una nueva ventana. Marcadores @@ -700,4 +725,5 @@ %1$s no tiene permiso para ejecutarse en este dispositivo y se cerrará. - + + diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index e65332a62c..868b649235 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -412,6 +412,9 @@   %1$s]]> + +   %1$s]]> + Ensisijainen kieli/kielet diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 653b799b0a..5aa3f180e2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -119,6 +119,11 @@ opens a dialog box that contains language-related settings. --> Langues + + Paramètres de langue + Choix de la langue préférée pour la commande vocale + + Langue d\'affichage de %1$s + + + Choisissez la langue utilisée pour afficher les menus, les messages et les notifications. + + + Langue utilisée pour la recherche vocale + + + Choisissez votre langue préférée pour la recherche vocale. + + + Langues préférées pour les sites web + + + Les pages web sont parfois proposées dans plusieurs langues. Choisissez les langues que vous souhaitez afficher par ordre de préférence sur le côté gauche. + Anglais (US) @@ -237,15 +276,32 @@ A dialog should appear with a field labeled with the text, 'Enable Multiprocess'. --> Activer le mode multiprocessus - Activer le moniteur de performances + + Activer la journalisation du débogage + Activer Servo + + Cookies et données de sites + + + Contenu web en cache + + + Effacer les données + Densité de l’écran : @@ -342,6 +398,9 @@ Réinitialiser + + Modifier + Environnement @@ -429,19 +488,20 @@ - Collecte et utilisation de données par Firefox + Collecte et utilisation de données par $1$s - Autoriser Firefox à collecter des données vocales + Autoriser %1$s à collecter des données vocales - Autoriser Firefox à envoyer des données techniques et d’interaction à Mozilla + Autoriser %1$s à envoyer des données techniques et d’interaction à Mozilla + - Autoriser Firefox à envoyer pour vous les rapports de plantages en attente + Autoriser %1$s à envoyer pour vous les rapports de plantages en attente Activé @@ -615,6 +675,30 @@ Réinitialiser les paramètres des choix de la langue préférée pour la commande vocale + + Réinitialiser la langue d’affichage + + + Réinitialiser la langue préférée pour les sites web + + + Réinitialiser tous les paramètres de langue + + +   %1$s]]> + + +   %1$s]]> + + +   %1$s]]> + + + Langue(s) préférée(s) + + + Langues disponibles + Nom @@ -658,9 +742,9 @@ speech-to-text recognition. --> Commande vocale - - Changer d’agent utilisateur + Afficher la version du site pour ordinateurs @@ -766,6 +850,10 @@ that can be opened (initially 3) --> Vous ne pouvez pas ouvrir plus de %1$s fenêtres à la fois. Veuillez en fermer une avant d’en ouvrir une autre. + + OK + Exécution impossible La page web en cours affecte les performances et a été suspendue. Réduire la taille de la fenêtre peut aider à améliorer une page web avec des performances médiocres. + + + Autoriser %1$s à collecter des échantillons de voix pour la recherche ? + + + Vos données sont toujours sécurisées et ne sont jamais partagées avec des tiers. Nous recueillons des échantillons de voix pour améliorer l’expérience et les performances de nos produits. Vous pourrez toujours utiliser la recherche vocale, même si vous n’autorisez pas la collecte. <a href="privacy">En savoir plus</a> + + + Autoriser + + + Ne pas autoriser + + + Par défaut diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index ebdb6a0581..9400700116 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -119,6 +119,11 @@ opens a dialog box that contains language-related settings. --> Lingua + + Impostazioni lingua + Scegliere la lingua in cui condurre ricerche vocali + + Lingua dell’interfaccia di %1$s + + + Scegli la lingua utilizzata per mostrare menu, messaggi e notifiche. + + + Lingua per la ricerca vocale + + + Scegli la lingua da utilizzare per le ricerche vocali. + + + Lingua preferita per la visualizzazione dei siti web + + + Le pagine web sono spesso disponibili in più lingue. È possibile scegliere le lingue predefinite per queste pagine, in ordine di preferenza, nella sezione a sinistra. + Italiano (IT) @@ -237,15 +276,32 @@ A dialog should appear with a field labeled with the text, 'Enable Multiprocess'. --> Abilita multiprocesso - Attiva monitoraggio prestazioni + + Attiva i log di debug + Abilita Servo + + Cookie e dati dei siti web + + + Contenuti web memorizzati nella cache + + + Elimina dati + Densità display: @@ -342,6 +398,9 @@ Ripristina + + Modifica + Ambiente @@ -429,19 +488,19 @@ - Raccolta e utilizzo dati di Firefox + Raccolta e utilizzo dati di %1$s - Consenti a Firefox di raccogliere dati vocali + Consenti a %1$s di raccogliere dati vocali - Consenti a Firefox di inviare a Mozilla dati tecnici e relativi all’interazione con il browser + Consenti a %1$s di inviare a Mozilla dati tecnici e relativi all’interazione con il browser - Consenti a Firefox di inviare segnalazioni di arresto anomalo in sospeso + Consenti a %1$s di inviare segnalazioni di arresto anomalo in sospeso Attivato @@ -615,6 +674,30 @@ Ripristina impostazioni lingua ricerca vocale + + Reimposta lingua dell’interfaccia + + + Reimposta lingue preferite per i siti web + + + Ripristina tutte le impostazioni della lingua + + +   %1$s]]> + + +   %1$s]]> + + +   %1$s]]> + + + Lingue preferite + + + Lingue disponibili + Nome @@ -658,9 +741,9 @@ speech-to-text recognition. --> Ricerca vocale - - Cambia User Agent + Richiedi sito desktop @@ -769,6 +852,10 @@ that can be opened (initially 3) --> Non puoi avere più di %1$s finestre aperte contemporaneamente. Chiudine una prima di aprirne una nuova. + + OK + Impossibile eseguire La pagina web corrente è stata sospesa a causa del suo impatto negativo sulle prestazioni. Per migliorare le prestazioni della pagina web prova a ridurre le dimensioni della finestra. + + + Consenti a %1$s di raccogliere campioni vocali per scopi di ricerca? + + + I tuoi dati sono sempre al sicuro e non vengono mai condivisi con terze parti. Raccogliamo campioni vocali per migliorare l’esperienza e le prestazioni dei nostri prodotti. Sarai sempre in grado di utilizzare la ricerca vocale, anche se non dai il consenso alla raccolta. <a href="privacy">Ulteriori informazioni</a> + + + Consenti + + + Non consentire + + + Predefinito diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 502cf9989e..c693ce4ab2 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -237,7 +237,7 @@ A dialog should appear with a field labeled with the text, 'Enable Multiprocess'. --> マルチプロセスを有効化 - @@ -427,22 +427,6 @@ of this storage even though the storage may not reside on external removable media. --> 外部ストレージの読み取り - - Firefox のデータ収集と利用について - - - Firefox が音声データを収集することを許可する - - - Firefox が技術的な対話データを Mozilla へ送信することを許可する - - - Firefox があなたに代わって未送信のクラッシュレポートを送信することを許可する - オン @@ -658,10 +642,6 @@ speech-to-text recognition. --> 音声検索 - - ユーザーエージェント文字列を切り替えます - 消去します @@ -778,4 +758,15 @@ パフォーマンスが低下しています このウェブページはパフォーマンスに影響を及ぼすため、読み込みが中断されています。ウィンドウのサイズを小さくすると、ウェブページのパフォーマンスを改善できる場合があります。 + + + 許可する + + + 許可しない + + + 既定 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index a0a0320d96..feb09b8be7 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -119,6 +119,11 @@ opens a dialog box that contains language-related settings. --> 언어 + + 언어 설정 + 음성 검색을 위한 귀하의 선호 언어를 선택하십시오 + + %1$s 표시 언어 + + + 메뉴와 메시지, 알림을 표시하는데 사용할 언어를 선택하십시오. + + + 음성 검색 언어 + + + 음성 검색을 위한 귀하의 선호 언어를 선택하십시오. + + + 선호하는 웹 사이트 언어 + + + 웹 페이지는 때때로 둘 이상의 언어로 제공됩니다. 표시하려는 언어를 선호도순으로 왼쪽에서 선택하십시오. + 영어(미국) @@ -194,6 +233,10 @@ a developer-build version of the app. --> 개발자 빌드 + + 도움말 + 재시작 필요 @@ -233,9 +276,32 @@ A dialog should appear with a field labeled with the text, 'Enable Multiprocess'. --> 멀티 프로세스 활성화 + + 성능 모니터 활성화 + + + 디버그 로깅 활성화 + 서보 활성화 + + 쿠키와 사이트 데이터 + + + 캐시된 웹 콘텐츠 + + + 데이터 삭제 + 디스플레이 밀도 @@ -332,6 +398,9 @@ 초기화 + + 편집 + 환경 @@ -374,6 +443,10 @@ for the home page. --> 홈페이지 + + DRM 제어 콘텐츠 재생 (<a href="http://somesite.com/">자세히 알아보기</a>) + 추적 방지 @@ -413,6 +486,10 @@ of this storage even though the storage may not reside on external removable media. --> 외부 저장소 읽기 + + %1$s 데이터 수집과 사용 + 켜기 @@ -585,6 +662,30 @@ 음성 검색 언어 설정 초기화 + + 표시 언어 초기화 + + + 선호하는 웹 사이트 언어 초기화 + + + 모든 언어 설정 초기화 + + +   %1$s]]> + + +   %1$s]]> + + +   %1$s]]> + + + 선호 언어 + + + 사용 가능한 언어 + 이름 @@ -615,6 +716,10 @@ 'Refresh' refers to reloading the current page. --> 새로 고침 + + 로딩 중지 + @@ -624,10 +729,26 @@ speech-to-text recognition. --> 음성 검색 + + 데스크톱 사이트 보기 + + + 지우기 + 사이트 정보 보기 + + Servo 엔진 사용 + + + 크기 조정 + 프라이빗 브라우징 시작 @@ -639,11 +760,32 @@ 새 창으로 열기 + + 이 페이지를 북마크 + 북마크 + + 북마크 열기 + + + 북마크 닫기 + + + 설정 + + + 새 창 열기 + @@ -695,9 +837,27 @@ that can be opened (initially 3) --> 한 번에 %1$s개 이상의 창을 열 수 없습니다. 다른 창을 열기 전에 이전 창을 하나 닫으세요. + + 확인 + 실행할 수 없음 이 기기에서 %1$s 어플리케이션을 실행을 권한이 없으므로 종료합니다. + + + 페이지 차단 해제 + + + 허용 + + + 허용하지 않음 + + + 기본 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9d7eb00132..9519dcb82e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -292,6 +292,17 @@ Включить Servo + + Куки и данные сайтов + + + Кэшированное веб-содержимое + + + Удалить данные + Плотность пикселей экрана: diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e6438fb9b1..773824ccfa 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -297,7 +297,7 @@ - 网络内容缓存 + 已缓存网络内容 清除数据 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 8cc0bee6e2..1c159db98f 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -18,6 +18,7 @@ + diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 3e800275b2..940fc1034d 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -83,7 +83,7 @@ 330dp - 7dp + 15dp 150dp @@ -127,7 +127,7 @@ 1.2 206dp 40dp - 4.0 + 4.5 585dp @@ -197,15 +197,15 @@ 2dp 0dp - - 64dp - - - 38dp - 18sp - 20sp - 16sp - 16sp + + 55dp + 18sp + 20sp + 16sp + 16sp + 2dp + 0dp + 13sp 22dp diff --git a/app/src/main/res/values/non_L10n.xml b/app/src/main/res/values/non_L10n.xml index 446b8d45d9..038d2984d7 100644 --- a/app/src/main/res/values/non_L10n.xml +++ b/app/src/main/res/values/non_L10n.xml @@ -20,11 +20,7 @@ settings_user_agent_version settings_touch_mode settings_display_density - settings_window_width - settings_window_height - settings_display_dpi - settings_max_window_width - settings_max_window_height + settings_display_dpi> settings_env settings_pointer_color settings_scroll_direction @@ -45,6 +41,8 @@ settings_browser_world_height settings_key_notifications settings_key_debug_logging + settings_key_autoplay + settings_key_pid 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 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f34dedcbc3..cddfa3dd6a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,4 @@ - - ENTER @@ -305,10 +302,6 @@ use for the display density. --> Display Density: - - Window Size: - @@ -327,10 +320,6 @@ for the virtual displays Dots Per Inch (DPI). --> Display DPI: - - Max. Window Size: - MSAA @@ -636,6 +625,12 @@ Bookmarks + + Your Bookmarks List is Empty + + + Click the @ icon when you’re on a page to add it to your bookmarks. + Loading Bookmarks @@ -644,6 +639,10 @@ item listed in the Bookmarks List. --> Remove Bookmark + + Saved to Bookmarks! + @@ -666,6 +665,9 @@ History + + Your History is Empty + You can access your browsing history here @@ -953,7 +955,7 @@ - Your data is always secure and never shared with third parties. We collect voice samples to improve the experience and performance of our products. You’ll always be able to use voice search, even if you don’t allow collection.<a href="privacy">Learn More</a> + In order to improve our voice recognition and other services, we need to collect voice samples for research. We store your data securely and without identifying information. You’ll always be able to use voice search, even if you don’t allow collection.<a href="privacy">Learn More</a> @@ -966,7 +968,8 @@ Default - + %1$s Home diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 761c2e2ed7..d4a57879b1 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -65,6 +65,7 @@ @drawable/main_button_icon_color @drawable/main_button_icon_color_private @drawable/main_button_icon_color_active + @drawable/main_button_icon_color_notification