diff --git a/.gitignore b/.gitignore index 7c3666936..287c93cf8 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ infer-out/ fastlane/ app/.externalNativeBuild +app/.cxx openwnn/.externalNativeBuild *.swp diff --git a/app/build.gradle b/app/build.gradle index 72c510fdc..f907177c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -464,6 +464,9 @@ dependencies { // TODO this should not be necessary at all, see Services.kt implementation deps.work.runtime + // TODO this should not be necessary at all, see Services.kt + implementation deps.work.runtime + // Kotlin dependency implementation deps.kotlin.stdlib implementation deps.kotlin.coroutines diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index e9cad38ae..6fd11ee7d 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -58,7 +58,7 @@ import org.mozilla.vrbrowser.ui.widgets.RootWidget; import org.mozilla.vrbrowser.ui.widgets.TrayWidget; import org.mozilla.vrbrowser.ui.widgets.UIWidget; -import org.mozilla.vrbrowser.ui.widgets.VideoProjectionMenuWidget; +import org.mozilla.vrbrowser.ui.widgets.menus.VideoProjectionMenuWidget; import org.mozilla.vrbrowser.ui.widgets.Widget; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; @@ -1402,6 +1402,11 @@ public void openNewTab(@NonNull String uri) { mWindows.addBackgroundTab(mWindows.getFocusedWindow(), uri); } + @Override + public void openNewTabForeground(@NonNull String uri) { + mWindows.addTab(mWindows.getFocusedWindow(), uri); + } + @Override public WindowWidget getFocusedWindow() { return mWindows.getFocusedWindow(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/Accounts.kt b/app/src/common/shared/org/mozilla/vrbrowser/browser/Accounts.kt index 9a8af24e0..dff31c3c7 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/Accounts.kt +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/Accounts.kt @@ -12,10 +12,8 @@ import androidx.lifecycle.ProcessLifecycleOwner import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.future.future -import mozilla.components.concept.sync.AccountObserver -import mozilla.components.concept.sync.AuthType -import mozilla.components.concept.sync.OAuthAccount -import mozilla.components.concept.sync.Profile +import kotlinx.coroutines.launch +import mozilla.components.concept.sync.* import mozilla.components.service.fxa.SyncEngine import mozilla.components.service.fxa.manager.SyncEnginesStorage import mozilla.components.service.fxa.sync.SyncReason @@ -49,7 +47,9 @@ class Accounts constructor(val context: Context) { var accountStatus = AccountStatus.SIGNED_OUT private val accountListeners = ArrayList() private val syncListeners = ArrayList() + private val deviceConstellationListeners = ArrayList() private val services = (context.applicationContext as VRBrowserApplication).services + private var otherDevices = emptyList() private val syncStorage = SyncEnginesStorage(context) var isSyncing = false @@ -82,6 +82,17 @@ class Accounts constructor(val context: Context) { } } + private val deviceConstellationObserver = object : DeviceConstellationObserver { + override fun onDevicesUpdate(constellation: ConstellationState) { + otherDevices = constellation.otherDevices + deviceConstellationListeners.toMutableList().forEach { + Handler(Looper.getMainLooper()).post { + it.onDevicesUpdate(constellation) + } + } + } + } + private val accountObserver = object : AccountObserver { override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { accountStatus = AccountStatus.SIGNED_IN @@ -91,6 +102,13 @@ class Accounts constructor(val context: Context) { syncStorage.setStatus(SyncEngine.History, SettingsStore.getInstance(context).isHistorySyncEnabled) services.accountManager.syncNowAsync(SyncReason.EngineChange, false) + // Update device list + account.deviceConstellation().registerDeviceObserver( + deviceConstellationObserver, + ProcessLifecycleOwner.get(), + true + ) + account.deviceConstellation().refreshDevicesAsync() accountListeners.toMutableList().forEach { Handler(Looper.getMainLooper()).post { @@ -173,6 +191,20 @@ class Accounts constructor(val context: Context) { syncListeners.clear() } + fun addDeviceConstellationListener(aListener: DeviceConstellationObserver) { + if (!deviceConstellationListeners.contains(aListener)) { + deviceConstellationListeners.add(aListener) + } + } + + fun removeDeviceConstellationListener(aListener: DeviceConstellationObserver) { + deviceConstellationListeners.remove(aListener) + } + + fun removeAllDeviceConstellationListeners() { + deviceConstellationListeners.clear() + } + fun authUrlAsync(): CompletableFuture? { return CoroutineScope(Dispatchers.Main).future { services.accountManager.beginAuthenticationAsync().await() @@ -219,6 +251,7 @@ class Accounts constructor(val context: Context) { } fun logoutAsync(): CompletableFuture? { + otherDevices = emptyList() return CoroutineScope(Dispatchers.Main).future { services.accountManager.logoutAsync().await() } @@ -283,4 +316,26 @@ class Accounts constructor(val context: Context) { return getLastSynced(context) } + fun devicesByCapability(capabilities: List): List { + return otherDevices.filter { it.capabilities.containsAll(capabilities) } + } + + fun sendTabs(targetDevices: List, url: String, title: String) { + CoroutineScope(Dispatchers.Main).launch { + services.accountManager.authenticatedAccount()?.deviceConstellation()?.let { constellation -> + // Ignore devices that can't receive tabs or are not in the received list + val targets = constellation.state()?.otherDevices?.filter { + it.capabilities.contains(DeviceCapability.SEND_TAB) + targetDevices.contains(it) + } + + targets?.forEach { + constellation.sendEventToDeviceAsync( + it.id, DeviceEventOutgoing.SendTab(title, url) + ).await() + } + } + } + } + } \ No newline at end of file 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 172956137..4fc9eaed2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt @@ -162,6 +162,10 @@ class BookmarksStore constructor(val context: Context) { } } + fun searchBookmarks(query: String, limit: Int): CompletableFuture> = GlobalScope.future { + storage.searchBookmarks(query, limit) + } + private suspend fun getBookmarkByUrl(aURL: String): BookmarkNode? { val bookmarks: List? = storage.getBookmarksWithUrl(aURL) if (bookmarks == null || bookmarks.isEmpty()) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/HistoryStore.kt b/app/src/common/shared/org/mozilla/vrbrowser/browser/HistoryStore.kt index 4c05121a8..c2f3584a4 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/HistoryStore.kt +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/HistoryStore.kt @@ -11,10 +11,7 @@ import android.os.Looper import androidx.lifecycle.ProcessLifecycleOwner import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.future.future -import mozilla.components.concept.storage.PageObservation -import mozilla.components.concept.storage.PageVisit -import mozilla.components.concept.storage.VisitInfo -import mozilla.components.concept.storage.VisitType +import mozilla.components.concept.storage.* import mozilla.components.service.fxa.sync.SyncStatusObserver import mozilla.components.support.base.log.logger.Logger import org.mozilla.vrbrowser.VRBrowserApplication @@ -126,6 +123,10 @@ class HistoryStore constructor(val context: Context) { result.isNotEmpty() && result[0] } + fun getSuggestions(query: String, limit: Int): CompletableFuture> = GlobalScope.future { + storage.getSuggestions(query, limit) + } + private fun notifyListeners() { if (listeners.size > 0) { val listenersCopy = ArrayList(listeners) diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/PromptDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/PromptDelegate.java index 132b5a855..c87e9fc59 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/PromptDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/PromptDelegate.java @@ -262,7 +262,7 @@ private void handlePopUpRequest(@NonNull PopUpRequest request) { mPopUpPrompt.setButtonsDelegate(new BaseAppDialogWidget.Delegate() { @Override public void onButtonClicked(int index) { - boolean allowed = index != PopUpBlockDialogWidget.LEFT; + boolean allowed = index != PopUpBlockDialogWidget.NEGATIVE; boolean askAgain = mPopUpPrompt.askAgain(); if (!askAgain) { mAllowedPopUpSites.add(new PopUpSite(request.uri, allowed)); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/Services.kt b/app/src/common/shared/org/mozilla/vrbrowser/browser/Services.kt index cb63c8241..2d459de5b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/Services.kt +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/Services.kt @@ -27,8 +27,8 @@ import mozilla.components.support.rustlog.RustLog import org.mozilla.geckoview.AllowOrDeny import org.mozilla.geckoview.GeckoResult import org.mozilla.geckoview.GeckoSession -import org.mozilla.vrbrowser.browser.engine.SessionStore import org.mozilla.vrbrowser.R +import org.mozilla.vrbrowser.browser.engine.SessionStore class Services(context: Context, places: Places): GeckoSession.NavigationDelegate { companion object { @@ -83,7 +83,6 @@ class Services(context: Context, places: Places): GeckoSession.NavigationDelegat } } } - val accountManager = FxaAccountManager( context = context, serverConfig = ServerConfig.release(CLIENT_ID, REDIRECT_URL), @@ -91,7 +90,6 @@ class Services(context: Context, places: Places): GeckoSession.NavigationDelegat // This is a default name, and can be changed once user is logged in. // E.g. accountManager.authenticatedAccount()?.deviceConstellation()?.setDeviceNameAsync("new name") name = "${context.getString(R.string.app_name)} on ${Build.MANUFACTURER} ${Build.MODEL}", - // TODO need a new device type! "VR" type = DeviceType.VR, capabilities = setOf(DeviceCapability.SEND_TAB) ), diff --git a/app/src/common/shared/org/mozilla/vrbrowser/search/suggestions/SuggestionsProvider.java b/app/src/common/shared/org/mozilla/vrbrowser/search/suggestions/SuggestionsProvider.java index 05e7fbb26..295a89167 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/search/suggestions/SuggestionsProvider.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/search/suggestions/SuggestionsProvider.java @@ -16,8 +16,6 @@ import java.util.List; import java.util.concurrent.CompletableFuture; -import mozilla.appservices.places.BookmarkRoot; - public class SuggestionsProvider { private static final String LOGTAG = SuggestionsProvider.class.getSimpleName(); @@ -31,31 +29,27 @@ public int compare(Object obj1, Object obj2) { return 0; } else if (suggestion1.type == suggestion2.type) { - if (mFilterText != null) { - if (suggestion1.title != null && suggestion2.title != null) { - return suggestion1.title.toLowerCase().indexOf(mFilterText) - suggestion2.title.toLowerCase().indexOf(mFilterText); + if (suggestion1.type == Type.HISTORY) { + if (suggestion1.score != suggestion2.score) { + return suggestion1.score - suggestion2.score; } - return suggestion1.url.toLowerCase().indexOf(mFilterText) - suggestion2.url.indexOf(mFilterText); - - } else { - return suggestion1.url.compareTo(suggestion2.url); } + return suggestion1.url.compareTo(suggestion2.url); + } else { return suggestion1.type.ordinal() - suggestion2.type.ordinal(); } } } - private Context mContext; private SearchEngineWrapper mSearchEngineWrapper; private String mText; private String mFilterText; private Comparator mComparator; public SuggestionsProvider(Context context) { - mContext = context; - mSearchEngineWrapper = SearchEngineWrapper.get(mContext); + mSearchEngineWrapper = SearchEngineWrapper.get(context); mFilterText = ""; mComparator = new DefaultSuggestionsComparator(); } @@ -81,16 +75,16 @@ public void setComparator(Comparator comparator) { public CompletableFuture> getBookmarkSuggestions(@NonNull List items) { CompletableFuture future = new CompletableFuture(); - // Explicitly passing Root will look in all the bookmarks, default is just to look in the mobile bookmarks. - SessionStore.get().getBookmarkStore().getBookmarks(BookmarkRoot.Root.getId()).thenAcceptAsync((bookmarks) -> { - bookmarks.stream(). - filter(b -> b.getUrl().toLowerCase().contains(mFilterText) || - b.getTitle().toLowerCase().contains(mFilterText)) + SessionStore.get().getBookmarkStore().searchBookmarks(mFilterText, 100).thenAcceptAsync((bookmarks) -> { + bookmarks.stream() + .filter((b) -> !b.getUrl().startsWith("place:") && + !b.getUrl().startsWith("about:reader")) .forEach(b -> items.add(SuggestionItem.create( b.getTitle(), b.getUrl(), null, - Type.BOOKMARK + Type.BOOKMARK, + 0 ))); if (mComparator != null) { items.sort(mComparator); @@ -108,15 +102,13 @@ public CompletableFuture> getBookmarkSuggestions(@NonNull L public CompletableFuture> getHistorySuggestions(@NonNull final List items) { CompletableFuture future = new CompletableFuture(); - SessionStore.get().getHistoryStore().getHistory().thenAcceptAsync((history) -> { - history.stream() - .filter(h -> - h.toLowerCase().contains(mFilterText)) - .forEach(h -> items.add(SuggestionItem.create( - h, - h, + SessionStore.get().getHistoryStore().getSuggestions(mFilterText, 100).thenAcceptAsync((history) -> { + history.forEach(h -> items.add(SuggestionItem.create( + h.getTitle(), + h.getUrl(), null, - Type.HISTORY + Type.HISTORY, + h.getScore() ))); if (mComparator != null) { items.sort(mComparator); @@ -141,7 +133,8 @@ public CompletableFuture> getSearchEngineSuggestions(@NonNu mText, getSearchURLOrDomain(mText), null, - Type.COMPLETION + Type.COMPLETION, + 0 )); } @@ -150,7 +143,8 @@ public CompletableFuture> getSearchEngineSuggestions(@NonNu mFilterText, getSearchURLOrDomain(mFilterText), null, - Type.SUGGESTION + Type.SUGGESTION, + 0 )); // Suggestions @@ -161,7 +155,8 @@ public CompletableFuture> getSearchEngineSuggestions(@NonNu s, url, null, - Type.SUGGESTION + Type.SUGGESTION, + 0 )); }); if (mComparator != null) { 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 6ef877cce..7f9a776f9 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 @@ -9,6 +9,7 @@ import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.DimenRes; import androidx.annotation.Dimension; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -16,6 +17,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.ui.views.HoneycombButton; +import org.mozilla.vrbrowser.ui.views.UIButton; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -111,4 +113,9 @@ public static void setFxALastSync(@NonNull TextView view, long lastSync) { } + + @BindingAdapter("android:layout_width") + public static void setLayoutWidth(@NonNull UIButton button, @NonNull @Dimension float dimen) { + button.setLayoutWidth(dimen); + } } \ No newline at end of file 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 2a20daee7..c1ae0c65c 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 @@ -19,7 +19,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.VRBrowserActivity; import org.mozilla.vrbrowser.VRBrowserApplication; import org.mozilla.vrbrowser.browser.Accounts; import org.mozilla.vrbrowser.browser.BookmarksStore; @@ -32,6 +34,7 @@ import org.mozilla.vrbrowser.ui.adapters.CustomLinearLayoutManager; import org.mozilla.vrbrowser.ui.callbacks.BookmarkItemCallback; import org.mozilla.vrbrowser.ui.callbacks.BookmarksCallback; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.utils.UIThreadExecutor; import java.util.ArrayList; @@ -107,6 +110,12 @@ private void initialize(Context aContext) { updateBookmarks(); SessionStore.get().getBookmarkStore().addListener(this); + mBinding.setIsSignedIn(mAccounts.isSignedIn()); + mBinding.setIsSyncEnabled(mAccounts.isEngineEnabled(SyncEngine.Bookmarks.INSTANCE)); + + updateBookmarks(); + SessionStore.get().getBookmarkStore().addListener(this); + setVisibility(GONE); setOnTouchListener((v, event) -> { @@ -192,9 +201,11 @@ public void onFxALogin(@NonNull View view) { mAccounts.getAuthenticationUrlAsync().thenAcceptAsync((url) -> { if (url != null) { mAccounts.setLoginOrigin(Accounts.LoginOrigin.BOOKMARKS); - SessionStore.get().getActiveSession().loadUri(url); + WidgetManagerDelegate widgetManager = ((VRBrowserActivity)getContext()); + widgetManager.openNewTabForeground(url); + widgetManager.getFocusedWindow().getSession().setUaMode(GeckoSessionSettings.USER_AGENT_MODE_MOBILE); } - }); + }, new UIThreadExecutor()); } @Override @@ -299,6 +310,9 @@ private void updateLayout() { mBinding.setIsNarrow(isNarrow); mBinding.executePendingBindings(); + mBinding.setIsNarrow(isNarrow); + mBinding.executePendingBindings(); + requestLayout(); } }); 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 9f363423a..deb6a95dd 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 @@ -20,7 +20,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.VRBrowserActivity; import org.mozilla.vrbrowser.VRBrowserApplication; import org.mozilla.vrbrowser.browser.Accounts; import org.mozilla.vrbrowser.browser.HistoryStore; @@ -31,6 +33,7 @@ import org.mozilla.vrbrowser.ui.adapters.HistoryAdapter; import org.mozilla.vrbrowser.ui.callbacks.HistoryCallback; import org.mozilla.vrbrowser.ui.callbacks.HistoryItemCallback; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.utils.SystemUtils; import org.mozilla.vrbrowser.utils.UIThreadExecutor; @@ -110,6 +113,12 @@ private void initialize(Context aContext) { updateHistory(); SessionStore.get().getHistoryStore().addListener(this); + mBinding.setIsSignedIn(mAccounts.isSignedIn()); + mBinding.setIsSyncEnabled(mAccounts.isEngineEnabled(SyncEngine.History.INSTANCE)); + + updateHistory(); + SessionStore.get().getHistoryStore().addListener(this); + setVisibility(GONE); setOnTouchListener((v, event) -> { @@ -189,9 +198,11 @@ public void onFxALogin(@NonNull View view) { mAccounts.getAuthenticationUrlAsync().thenAcceptAsync((url) -> { if (url != null) { mAccounts.setLoginOrigin(Accounts.LoginOrigin.HISTORY); - SessionStore.get().getActiveSession().loadUri(url); + WidgetManagerDelegate widgetManager = ((VRBrowserActivity)getContext()); + widgetManager.openNewTabForeground(url); + widgetManager.getFocusedWindow().getSession().setUaMode(GeckoSessionSettings.USER_AGENT_MODE_MOBILE); } - }); + }, new UIThreadExecutor()); } @Override @@ -342,6 +353,9 @@ private void updateLayout() { mBinding.setIsNarrow(isNarrow); mBinding.executePendingBindings(); + mBinding.setIsNarrow(isNarrow); + mBinding.executePendingBindings(); + requestLayout(); } }); 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 66e2648ef..ef9ae55d0 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 @@ -18,32 +18,28 @@ import android.util.Log; import android.util.TypedValue; import android.view.GestureDetector; +import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; import androidx.annotation.NonNull; import androidx.annotation.StringRes; +import androidx.databinding.DataBindingUtil; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.GeckoSession; -import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.audio.AudioEngine; import org.mozilla.vrbrowser.browser.BookmarksStore; import org.mozilla.vrbrowser.browser.engine.Session; import org.mozilla.vrbrowser.browser.engine.SessionStore; +import org.mozilla.vrbrowser.databinding.NavigationUrlBinding; import org.mozilla.vrbrowser.search.SearchEngineWrapper; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.ui.widgets.UIWidget; -import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.ui.widgets.dialogs.SelectionActionWidget; import org.mozilla.vrbrowser.utils.StringUtils; import org.mozilla.vrbrowser.utils.SystemUtils; @@ -66,27 +62,13 @@ public class NavigationURLBar extends FrameLayout { private static final String LOGTAG = SystemUtils.createLogtag(NavigationURLBar.class); - private CustomInlineAutocompleteEditText mURL; - private UIButton mMicrophoneButton; - private UIButton mUAModeButton; - private ImageView mInsecureIcon; - private ImageView mLoadingView; + private NavigationUrlBinding mBinding; private Animation mLoadingAnimation; - private RelativeLayout mURLLeftContainer; - private View mHintFading; - private boolean mIsLoading = false; - private boolean mIsInsecure = false; - private boolean mIsPrivateMode = false; - private int mDefaultURLLeftPadding = 0; private int mURLProtocolColor; private int mURLWebsiteColor; private NavigationURLBarDelegate mDelegate; private ShippedDomainsProvider mAutocompleteProvider; - private UIButton mBookmarkButton; private AudioEngine mAudio; - private boolean mIsContentMode; - private boolean mBookmarkEnabled = true; - private boolean mIsContextButtonsEnabled = true; private UIThreadExecutor mUIThreadExecutor = new UIThreadExecutor(); private Session mSession; private SelectionActionWidget mSelectionMenu; @@ -95,16 +77,16 @@ public class NavigationURLBar extends FrameLayout { private int lastTouchDownOffset = 0; private Unit domainAutocompleteFilter(String text) { - if (mURL != null) { + if (mBinding.urlEditText != null) { DomainAutocompleteResult result = mAutocompleteProvider.getAutocompleteSuggestion(text); if (result != null) { - mURL.applyAutocompleteResult(new InlineAutocompleteEditText.AutocompleteResult( + mBinding.urlEditText.applyAutocompleteResult(new InlineAutocompleteEditText.AutocompleteResult( result.getText(), result.getSource(), result.getTotalItems(), null)); } else { - mURL.noAutocompleteResult(); + mBinding.urlEditText.noAutocompleteResult(); } } return Unit.INSTANCE; @@ -128,16 +110,18 @@ private void initialize(Context aContext) { mSession = SessionStore.get().getActiveSession(); + LayoutInflater inflater = LayoutInflater.from(aContext); + // Inflate this data binding layout - inflate(aContext, R.layout.navigation_url, this); + mBinding = DataBindingUtil.inflate(inflater, R.layout.navigation_url, this, true); // Use Domain autocomplete provider from components mAutocompleteProvider = new ShippedDomainsProvider(); mAutocompleteProvider.initialize(aContext); - mURL = findViewById(R.id.urlEditText); - mURL.setShowSoftInputOnFocus(false); - mURL.setOnEditorActionListener((aTextView, actionId, event) -> { + mBinding.urlEditText.clearFocus(); + mBinding.urlEditText.setShowSoftInputOnFocus(false); + mBinding.urlEditText.setOnEditorActionListener((aTextView, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO || actionId == EditorInfo.IME_ACTION_SEND) { handleURLEdit(aTextView.getText().toString()); @@ -146,69 +130,75 @@ private void initialize(Context aContext) { return false; }); - mURL.setOnFocusChangeListener((view, focused) -> { - showVoiceSearch(!focused || (mURL.getText().length() == 0)); - showContextButtons(!focused && mIsContextButtonsEnabled); - updateHintFading(); - - mURL.setSelection(mURL.getText().length(), 0); - if (focused) { - mURL.selectAll(); - } else { + mBinding.urlEditText.setOnFocusChangeListener((view, focused) -> { + boolean isUrlEmpty = mBinding.urlEditText.getText().length() == 0; + setMicrophoneEnabled(!focused || isUrlEmpty); + mBinding.setIsFocused(focused); + mBinding.setIsUrlEmpty(isUrlEmpty); + if (!focused) { hideSelectionMenu(); } - }); final GestureDetector gd = new GestureDetector(getContext(), new UrlGestureListener()); gd.setOnDoubleTapListener(mUrlDoubleTapListener); - mURL.setOnTouchListener((view, motionEvent) -> { + mBinding.urlEditText.setOnTouchListener((view, motionEvent) -> { if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mWasFocusedWhenTouchBegan = view.isFocused(); - lastTouchDownOffset = ViewUtils.getCursorOffset(mURL, motionEvent.getX()); + lastTouchDownOffset = ViewUtils.getCursorOffset(mBinding.urlEditText, motionEvent.getX()); + } else if (mLongPressed && motionEvent.getAction() == MotionEvent.ACTION_MOVE) { - // Selection gesture while longpressing - ViewUtils.placeSelection(mURL, lastTouchDownOffset, ViewUtils.getCursorOffset(mURL, motionEvent.getX())); + // Selection gesture while long pressing + ViewUtils.placeSelection(mBinding.urlEditText, lastTouchDownOffset, ViewUtils.getCursorOffset(mBinding.urlEditText, motionEvent.getX())); + } else if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { mLongPressed = false; } + if (gd.onTouchEvent(motionEvent)) { return true; } + if (mLongPressed) { // Do not scroll editable when selecting text after a long press. return true; } return view.onTouchEvent(motionEvent); }); - mURL.setOnClickListener(v -> { + + mBinding.urlEditText.setOnClickListener(v -> { if (mWasFocusedWhenTouchBegan) { hideSelectionMenu(); } }); - mURL.setOnLongClickListener(v -> { + + mBinding.urlEditText.setOnLongClickListener(v -> { if (!v.isFocused()) { - mURL.requestFocus(); - mURL.selectAll(); - } else if (!mURL.hasSelection()) { - // Place the cursor in the longpressed position. + mBinding.urlEditText.requestFocus(); + mBinding.urlEditText.selectAll(); + + } else if (!mBinding.urlEditText.hasSelection()) { + // Place the cursor in the long pressed position. if (lastTouchDownOffset >= 0) { - mURL.setSelection(lastTouchDownOffset); + mBinding.urlEditText.setSelection(lastTouchDownOffset); } mLongPressed = true; } + // Add some delay so selection ranges are ready ThreadUtils.postDelayedToUiThread(this::handleLongPress, 10); return true; }); - mURL.addTextChangedListener(mURLTextWatcher); - mURL.setOnSelectionChangedCallback((start, end) -> { + mBinding.urlEditText.addTextChangedListener(mURLTextWatcher); + + mBinding.urlEditText.setOnSelectionChangedCallback((start, end) -> { if (mSelectionMenu != null) { boolean hasCopy = mSelectionMenu.hasAction(GeckoSession.SelectionActionDelegate.ACTION_COPY); boolean showCopy = end > start; if (hasCopy != showCopy) { handleLongPress(); + } else { mDelegate.onLongPress(getSelectionCenterX(), mSelectionMenu); mSelectionMenu.updateWidget(); @@ -216,33 +206,22 @@ private void initialize(Context aContext) { } }); - mURL.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { + mBinding.urlEditText.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { if (mLongPressed) { hideSelectionMenu(); } }); // Set a filter to provide domain autocomplete results - mURL.setOnFilterListener(this::domainAutocompleteFilter); - - mURL.setFocusable(true); - mURL.setFocusableInTouchMode(true); + mBinding.urlEditText.setOnFilterListener(this::domainAutocompleteFilter); - mMicrophoneButton = findViewById(R.id.microphoneButton); - mMicrophoneButton.setTag(R.string.view_id_tag, R.id.microphoneButton); - mMicrophoneButton.setOnClickListener(mMicrophoneListener); + mBinding.microphoneButton.setTag(R.string.view_id_tag, R.id.microphoneButton); + mBinding.microphoneButton.setOnClickListener(mMicrophoneListener); - mUAModeButton = findViewById(R.id.uaModeButton); - mUAModeButton.setTag(R.string.view_id_tag, R.id.uaModeButton); - mUAModeButton.setOnClickListener(mUAModeListener); - setUAMode(mSession.getUaMode()); + mBinding.clearButton.setTag(R.string.view_id_tag, R.id.clearButton); + mBinding.clearButton.setOnClickListener(mClearListener); - mHintFading = findViewById(R.id.urlBarHintFadingEdge); - mURLLeftContainer = findViewById(R.id.urlLeftContainer); - mInsecureIcon = findViewById(R.id.insecureIcon); - mLoadingView = findViewById(R.id.loadingView); mLoadingAnimation = AnimationUtils.loadAnimation(aContext, R.anim.loading); - mDefaultURLLeftPadding = mURL.getPaddingLeft(); TypedValue typedValue = new TypedValue(); Resources.Theme theme = aContext.getTheme(); @@ -252,31 +231,34 @@ private void initialize(Context aContext) { mURLWebsiteColor = typedValue.data; // Bookmarks - mBookmarkButton = findViewById(R.id.bookmarkButton); - mBookmarkButton.setOnClickListener(v -> handleBookmarkClick()); - mIsContentMode = false; - - // Prevent the URL TextEdit to get focus when user touches something outside of it - setFocusable(true); - setClickable(true); - syncViews(); - updateHintFading(); + mBinding.bookmarkButton.setOnClickListener(v -> handleBookmarkClick()); + + // Initialize bindings + mBinding.setIsLibraryVisible(false); + mBinding.setIsLoading(false); + mBinding.setIsInsecure(false); + mBinding.setIsMicrophoneEnabled(true); + mBinding.setIsFocused(false); + mBinding.setIsSpecialUrl(false); + mBinding.setIsUrlEmpty(true); + mBinding.executePendingBindings(); + + clearFocus(); } public void setSession(Session session) { mSession = session; - setUAMode(mSession.getUaMode()); } public void onPause() { - if (mIsLoading) { - mLoadingView.clearAnimation(); + if (mBinding.getIsLoading()) { + mBinding.loadingView.clearAnimation(); } } public void onResume() { - if (mIsLoading) { - mLoadingView.startAnimation(mLoadingAnimation); + if (mBinding.getIsLoading()) { + mBinding.loadingView.startAnimation(mLoadingAnimation); } } @@ -284,46 +266,6 @@ public void setDelegate(NavigationURLBarDelegate delegate) { mDelegate = delegate; } - public void setIsContentMode(boolean isContentMode) { - if (mIsContentMode == isContentMode) { - return; - } - mIsContentMode = isContentMode; - if (isContentMode) { - mMicrophoneButton.setVisibility(GONE); - mUAModeButton.setVisibility(GONE); - mBookmarkButton.setVisibility(GONE); - - } else { - mMicrophoneButton.setVisibility(VISIBLE); - mUAModeButton.setVisibility(VISIBLE); - if (mBookmarkEnabled) { - mBookmarkButton.setVisibility(VISIBLE); - } - } - syncViews(); - updateRightPadding(); - } - - public boolean isInBookmarkMode() { - return mIsContentMode; - } - - private void setBookmarkEnabled(boolean aEnabled) { - if (mBookmarkEnabled != aEnabled) { - mBookmarkEnabled = aEnabled; - mBookmarkButton.setVisibility(aEnabled ? View.VISIBLE : View.GONE); - ViewGroup.LayoutParams params = mMicrophoneButton.getLayoutParams(); - params.width = (int) getResources().getDimension(aEnabled ? R.dimen.url_bar_item_width : R.dimen.url_bar_last_item_width); - mMicrophoneButton.setLayoutParams(params); - if (mIsPrivateMode) { - mMicrophoneButton.setBackgroundResource(aEnabled ? R.drawable.url_button_private : R.drawable.url_button_end_private); - } else { - mMicrophoneButton.setBackgroundResource(aEnabled ? R.drawable.url_button : R.drawable.url_button_end); - } - } - } - private void handleBookmarkClick() { if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -337,11 +279,11 @@ private void handleBookmarkClick() { bookmarkStore.isBookmarked(url).thenAcceptAsync(bookmarked -> { if (!bookmarked) { bookmarkStore.addBookmark(url, mSession.getCurrentTitle()); - setBookmarked(true); + setIsBookmarked(true); } else { // Delete bookmarkStore.deleteBookmarkByURL(url); - setBookmarked(false); + setIsBookmarked(false); } }, mUIThreadExecutor).exceptionally(th -> { Log.d(LOGTAG, "Error getting bookmarks: " + th.getLocalizedMessage()); @@ -350,31 +292,19 @@ private void handleBookmarkClick() { } - private void setBookmarked(boolean aValue) { - if (aValue) { - mBookmarkButton.setImageDrawable(getContext().getDrawable(R.drawable.ic_icon_bookmarked_active)); - } else { - mBookmarkButton.setImageDrawable(getContext().getDrawable(R.drawable.ic_icon_bookmarked)); - } - } - public void setHint(@StringRes int aHint) { - mURL.setHint(aHint); - } - - public void setInsecureVisibility(int visibility) { - mInsecureIcon.setVisibility(visibility); + mBinding.urlEditText.setHint(aHint); } public void setURL(String aURL) { - if (mIsContentMode) { + if (mBinding.getIsLibraryVisible()) { return; } - mURL.removeTextChangedListener(mURLTextWatcher); + mBinding.urlEditText.removeTextChangedListener(mURLTextWatcher); if (StringUtils.isEmpty(aURL)) { - setBookmarked(false); + setIsBookmarked(false); } else { - SessionStore.get().getBookmarkStore().isBookmarked(aURL).thenAcceptAsync(this::setBookmarked, mUIThreadExecutor); + SessionStore.get().getBookmarkStore().isBookmarked(aURL).thenAcceptAsync(this::setIsBookmarked, mUIThreadExecutor); } int index = -1; @@ -404,29 +334,25 @@ public void setURL(String aURL) { // Update the URL bar only if the URL is different than the current one and // the URL bar is not focused to avoid override user input - if (!mURL.getText().toString().equalsIgnoreCase(aURL) && !mURL.isFocused()) { - mURL.setText(aURL); + if (!mBinding.urlEditText.getText().toString().equalsIgnoreCase(aURL) && !mBinding.urlEditText.isFocused()) { + mBinding.urlEditText.setText(aURL); if (index > 0) { SpannableString spannable = new SpannableString(aURL); ForegroundColorSpan color1 = new ForegroundColorSpan(mURLProtocolColor); ForegroundColorSpan color2 = new ForegroundColorSpan(mURLWebsiteColor); spannable.setSpan(color1, 0, index + 3, 0); spannable.setSpan(color2, index + 3, aURL.length(), 0); - mURL.setText(spannable); + mBinding.urlEditText.setText(spannable); } else { - mURL.setText(aURL); + mBinding.urlEditText.setText(aURL); } } - mIsContextButtonsEnabled = aURL.length() > 0 && !aURL.startsWith("about://"); - if (!aURL.equals(getResources().getString(R.string.url_bookmarks_title)) && - !aURL.equals(getResources().getString(R.string.url_history_title))) { - showContextButtons(mIsContextButtonsEnabled); - } + mBinding.setIsSpecialUrl(aURL.isEmpty()); } - mURL.addTextChangedListener(mURLTextWatcher); + mBinding.urlEditText.addTextChangedListener(mURLTextWatcher); } private boolean isEmptyUrl(@NonNull String aURL) { @@ -434,137 +360,45 @@ private boolean isEmptyUrl(@NonNull String aURL) { } public String getText() { - return mURL.getText().toString(); + return mBinding.urlEditText.getText().toString(); } public String getOriginalText() { try { - return mURL.getOriginalText(); + return mBinding.urlEditText.getOriginalText(); } catch (IndexOutOfBoundsException e) { - return mURL.getNonAutocompleteText(); + return mBinding.urlEditText.getNonAutocompleteText(); } } - public void setIsInsecure(boolean aIsInsecure) { - if (mIsInsecure != aIsInsecure) { - mIsInsecure = aIsInsecure; - syncViews(); - } - } - - public void setIsLoading(boolean aIsLoading) { - if (mIsLoading != aIsLoading) { - mIsLoading = aIsLoading; - if (mIsLoading) { - mLoadingView.startAnimation(mLoadingAnimation); - } else { - mLoadingView.clearAnimation(); - } - syncViews(); - } + public void setIsLibraryVisible(boolean isLibraryVisible) { + mBinding.setIsLibraryVisible(isLibraryVisible); } - public void setUAMode(int uaMode) { - if (uaMode == GeckoSessionSettings.USER_AGENT_MODE_DESKTOP) { - mUAModeButton.setImageResource(R.drawable.ic_icon_ua_desktop); - - } else { - mUAModeButton.setImageResource(R.drawable.ic_icon_ua_default); - } + public void setIsInsecure(boolean aIsInsecure) { + mBinding.setIsInsecure(aIsInsecure); } - private void showContextButtons(boolean aEnabled) { - if (mBookmarkEnabled != aEnabled) { - mBookmarkEnabled = aEnabled; - } - - if (aEnabled) { - mMicrophoneButton.setBackgroundResource(mIsPrivateMode ? R.drawable.url_button_private : R.drawable.url_button); - mMicrophoneButton.getLayoutParams().width = (int)getContext().getResources().getDimension(R.dimen.url_bar_item_width); - mBookmarkButton.setVisibility(VISIBLE); - mUAModeButton.setVisibility(VISIBLE); - + public void setIsLoading(boolean aIsLoading) { + mBinding.setIsLoading(aIsLoading); + if (aIsLoading) { + mBinding.loadingView.startAnimation(mLoadingAnimation); } else { - mMicrophoneButton.setBackgroundResource(mIsPrivateMode ? R.drawable.url_button_end_private : R.drawable.url_button_end); - mMicrophoneButton.getLayoutParams().width = (int)getContext().getResources().getDimension(R.dimen.url_bar_last_item_width); - mBookmarkButton.setVisibility(GONE); - mUAModeButton.setVisibility(GONE); + mBinding.loadingView.clearAnimation(); } - updateRightPadding(); } - public void showVoiceSearch(boolean enabled) { - if (enabled) { - mMicrophoneButton.setImageResource(R.drawable.ic_icon_microphone); - mMicrophoneButton.setTooltip(getResources().getString(R.string.voice_search_tooltip)); - mMicrophoneButton.setOnClickListener(mMicrophoneListener); - - } else if (mURL.hasFocus()){ - mMicrophoneButton.setImageResource(R.drawable.ic_icon_clear); - mMicrophoneButton.setTooltip(getResources().getString(R.string.clear_tooltip)); - mMicrophoneButton.setOnClickListener(mClearListener); - } - updateRightPadding(); + public void setMicrophoneEnabled(boolean enabled) { + mBinding.setIsMicrophoneEnabled(enabled); } - public void updateHintFading() { - mHintFading.setVisibility(StringUtils.isEmpty(mURL.getText()) ? View.VISIBLE : View.GONE); - mHintFading.setEnabled(mURL.isFocused()); + private void setIsBookmarked(boolean aValue) { + mBinding.setIsBookmarked(aValue); } - private void updateRightPadding() { - int padding = WidgetPlacement.convertDpToPixel(getContext(), 5); - boolean anyButtonVisible = false; - if (mMicrophoneButton.getVisibility() == View.VISIBLE) { - padding += mMicrophoneButton.getLayoutParams().width; - anyButtonVisible = true; - } - if (mUAModeButton.getVisibility() == View.VISIBLE) { - padding += mUAModeButton.getLayoutParams().width; - anyButtonVisible = true; - } - if (mBookmarkButton.getVisibility() == View.VISIBLE) { - padding += mBookmarkButton.getLayoutParams().width; - anyButtonVisible = true; - } - // Min padding of 20 if no icons are visible - padding = Math.max(padding, WidgetPlacement.convertDpToPixel(getContext(), 20)); - mURL.setPadding(mURL.getPaddingLeft(), mURL.getPaddingTop(), padding, mURL.getPaddingBottom()); - - // Update hint fading - int margin = 0; - if (anyButtonVisible) { - mHintFading.setBackgroundResource(mIsPrivateMode ? R.drawable.url_bar_hint_fading_edge_private : R.drawable.url_bar_hint_fading_edge); - } else { - mHintFading.setBackgroundResource(mIsPrivateMode ? R.drawable.url_bar_hint_fading_edge_end_private : R.drawable.url_bar_hint_fading_edge_end); - margin = WidgetPlacement.convertDpToPixel(getContext(), 5); - } - - LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)mHintFading.getLayoutParams(); - params.rightMargin = margin; - mHintFading.setLayoutParams(params); - } - - private void syncViews() { - boolean showContainer = (mIsInsecure || mIsLoading) && !mIsContentMode; - int leftPadding = mDefaultURLLeftPadding; - if (showContainer) { - mURLLeftContainer.setVisibility(View.VISIBLE); - mURLLeftContainer.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - mLoadingView.setVisibility(mIsLoading ? View.VISIBLE : View.GONE); - if (!mIsContentMode) { - mInsecureIcon.setVisibility(!mIsLoading && mIsInsecure ? View.VISIBLE : View.GONE); - } - leftPadding = mURLLeftContainer.getMeasuredWidth(); - - } else { - mURLLeftContainer.setVisibility(View.GONE); - mLoadingView.setVisibility(View.GONE); - mInsecureIcon.setVisibility(View.GONE); - } - - mURL.setPadding(leftPadding, mURL.getPaddingTop(), mURL.getPaddingRight(), mURL.getPaddingBottom()); + public void setPrivateMode(boolean isEnabled) { + mBinding.setIsPrivateMode(isEnabled); } public void handleURLEdit(String text) { @@ -608,36 +442,14 @@ public void handleURLEdit(String text) { } } - showVoiceSearch(text.isEmpty()); - } - - public void setPrivateMode(boolean isEnabled) { - mIsPrivateMode = isEnabled; - if (isEnabled) { - mURL.setBackground(getContext().getDrawable(R.drawable.url_background_private)); - - } else { - mURL.setBackground(getContext().getDrawable(R.drawable.url_background)); - } - - - int background = isEnabled ? R.drawable.url_button_private : R.drawable.url_button; - int backgroundEnd = isEnabled ? R.drawable.url_button_end_private : R.drawable.url_button_end; - - mBookmarkButton.setBackgroundResource(backgroundEnd); - mUAModeButton.setBackgroundResource(background); - if (mBookmarkButton.getVisibility() == View.VISIBLE) { - mMicrophoneButton.setBackgroundResource(background); - } else { - mMicrophoneButton.setBackgroundResource(backgroundEnd); - } - updateRightPadding(); + setMicrophoneEnabled(!text.isEmpty()); + clearFocus(); } @Override public void setClickable(boolean clickable) { super.setClickable(clickable); - mURL.setEnabled(clickable); + mBinding.urlEditText.setEnabled(clickable); } private OnClickListener mMicrophoneListener = view -> { @@ -645,7 +457,6 @@ public void setClickable(boolean clickable) { mAudio.playSound(AudioEngine.Sound.CLICK); } - view.requestFocusFromTouch(); if (mDelegate != null) { mDelegate.onVoiceSearchClicked(); } @@ -653,31 +464,12 @@ public void setClickable(boolean clickable) { TelemetryWrapper.voiceInputEvent(); }; - private OnClickListener mUAModeListener = view -> { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } - view.requestFocusFromTouch(); - - int uaMode = mSession.getUaMode(); - if (uaMode == GeckoSessionSettings.USER_AGENT_MODE_DESKTOP) { - setUAMode(GeckoSessionSettings.USER_AGENT_MODE_VR); - mSession.setUaMode(GeckoSessionSettings.USER_AGENT_MODE_VR); - - }else { - setUAMode(GeckoSessionSettings.USER_AGENT_MODE_DESKTOP); - mSession.setUaMode(GeckoSessionSettings.USER_AGENT_MODE_DESKTOP); - } - - TelemetryWrapper.voiceInputEvent(); - }; - private OnClickListener mClearListener = view -> { if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); } - mURL.getText().clear(); + mBinding.urlEditText.getText().clear(); }; private TextWatcher mURLTextWatcher = new TextWatcher() { @@ -688,15 +480,15 @@ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - String aURL = mURL.getText().toString(); - showVoiceSearch(isEmptyUrl(aURL)); - showContextButtons(isEmptyUrl(aURL) && mIsContextButtonsEnabled); - updateHintFading(); + String aURL = mBinding.urlEditText.getText().toString(); + boolean empty = isEmptyUrl(aURL); + mBinding.setIsUrlEmpty(empty); + setMicrophoneEnabled(empty); } @Override public void afterTextChanged(Editable editable) { - if (mDelegate != null) { + if (mDelegate != null && mBinding.urlEditText.isFocused()) { mDelegate.onShowSearchPopup(); } hideSelectionMenu(); @@ -724,14 +516,14 @@ public boolean onDoubleTap(MotionEvent motionEvent) { @Override public boolean onDoubleTapEvent(MotionEvent motionEvent) { - mURL.selectAll(); + mBinding.urlEditText.selectAll(); return true; } }; private void handleLongPress() { ArrayList actions = new ArrayList<>(); - if (mURL.getSelectionEnd() > mURL.getSelectionStart()) { + if (mBinding.urlEditText.getSelectionEnd() > mBinding.urlEditText.getSelectionStart()) { actions.add(GeckoSession.SelectionActionDelegate.ACTION_CUT); actions.add(GeckoSession.SelectionActionDelegate.ACTION_COPY); } @@ -739,8 +531,8 @@ private void handleLongPress() { if (clipboard.hasPrimaryClip()) { actions.add(GeckoSession.SelectionActionDelegate.ACTION_PASTE); } - if (!StringUtils.isEmpty(mURL.getText().toString()) && - (mURL.getSelectionStart() != 0 || mURL.getSelectionEnd() != mURL.getText().toString().length())) { + if (!StringUtils.isEmpty(mBinding.urlEditText.getText().toString()) && + (mBinding.urlEditText.getSelectionStart() != 0 || mBinding.urlEditText.getSelectionEnd() != mBinding.urlEditText.getText().toString().length())) { actions.add(GeckoSession.SelectionActionDelegate.ACTION_SELECT_ALL); } @@ -761,29 +553,29 @@ private void handleLongPress() { mSelectionMenu.setDelegate(new SelectionActionWidget.Delegate() { @Override public void onAction(String action) { - int startSelection = mURL.getSelectionStart(); - int endSelection = mURL.getSelectionEnd(); + int startSelection = mBinding.urlEditText.getSelectionStart(); + int endSelection = mBinding.urlEditText.getSelectionEnd(); boolean selectionValid = endSelection > startSelection; if (action.equals(GeckoSession.SelectionActionDelegate.ACTION_CUT) && selectionValid) { - String selectedText = mURL.getText().toString().substring(startSelection, endSelection); + String selectedText = mBinding.urlEditText.getText().toString().substring(startSelection, endSelection); clipboard.setPrimaryClip(ClipData.newPlainText("text", selectedText)); - mURL.setText(StringUtils.removeRange(mURL.getText().toString(), startSelection, endSelection)); + mBinding.urlEditText.setText(StringUtils.removeRange(mBinding.urlEditText.getText().toString(), startSelection, endSelection)); } else if (action.equals(GeckoSession.SelectionActionDelegate.ACTION_COPY) && selectionValid) { - String selectedText = mURL.getText().toString().substring(startSelection, endSelection); + String selectedText = mBinding.urlEditText.getText().toString().substring(startSelection, endSelection); clipboard.setPrimaryClip(ClipData.newPlainText("text", selectedText)); } else if (action.equals(GeckoSession.SelectionActionDelegate.ACTION_PASTE) && clipboard.hasPrimaryClip()) { ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); if (selectionValid) { - mURL.setText(StringUtils.removeRange(mURL.getText().toString(), startSelection, endSelection)); + mBinding.urlEditText.setText(StringUtils.removeRange(mBinding.urlEditText.getText().toString(), startSelection, endSelection)); } if (item != null && item.getText() != null) { - mURL.getText().insert(mURL.getSelectionStart(), item.getText()); + mBinding.urlEditText.getText().insert(mBinding.urlEditText.getSelectionStart(), item.getText()); } else if (item != null && item.getUri() != null) { - mURL.getText().insert(mURL.getSelectionStart(), item.getUri().toString()); + mBinding.urlEditText.getText().insert(mBinding.urlEditText.getSelectionStart(), item.getUri().toString()); } } else if (action.equals(GeckoSession.SelectionActionDelegate.ACTION_SELECT_ALL)) { - mURL.selectAll(); + mBinding.urlEditText.selectAll(); handleLongPress(); return; @@ -808,12 +600,12 @@ public void onDismiss() { private float getSelectionCenterX() { float start = 0; - if (mURL.getSelectionStart() >= 0) { - start = ViewUtils.GetLetterPositionX(mURL, mURL.getSelectionStart(), true); + if (mBinding.urlEditText.getSelectionStart() >= 0) { + start = ViewUtils.GetLetterPositionX(mBinding.urlEditText, mBinding.urlEditText.getSelectionStart(), true); } float end = start; - if (mURL.getSelectionEnd() > mURL.getSelectionStart()) { - end = ViewUtils.GetLetterPositionX(mURL, mURL.getSelectionEnd(), true); + if (mBinding.urlEditText.getSelectionEnd() > mBinding.urlEditText.getSelectionStart()) { + end = ViewUtils.GetLetterPositionX(mBinding.urlEditText, mBinding.urlEditText.getSelectionEnd(), true); } if (end < start) { end = start; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/TabView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/TabView.java index 992a564af..a033f1793 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/TabView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/TabView.java @@ -30,6 +30,7 @@ public class TabView extends RelativeLayout implements GeckoSession.ContentDeleg protected TextView mURL; protected TextView mTitle; protected UIButton mCloseButton; + protected UIButton mSendTabButton; protected ImageView mSelectionImage; protected ImageView mUnselectImage; protected Delegate mDelegate; @@ -50,6 +51,7 @@ public interface Delegate { void onClose(TabView aSender); void onClick(TabView aSender); void onAdd(TabView aSender); + void onSend(TabView aSender); } public TabView(Context context) { @@ -77,6 +79,16 @@ protected void onFinishInflate() { mUnselectImage = findViewById(R.id.tabViewUnselect); mUnselectImage.setVisibility(View.GONE); + mSendTabButton = findViewById(R.id.tabViewSendButton); + mSendTabButton.setVisibility(View.GONE); + mSendTabButton.setOnClickListener(v -> { + v.requestFocusFromTouch(); + if (mDelegate != null) { + mDelegate.onSend(this); + } + }); + mSendTabButton.setOnHoverListener(mIconHoverListener); + mCloseButton = findViewById(R.id.tabViewCloseButton); mCloseButton.setVisibility(View.GONE); mCloseButton.setOnClickListener(v -> { @@ -222,11 +234,12 @@ public boolean onTouchEvent(MotionEvent event) { } private void updateState() { - boolean hovered = isHovered() || mCloseButton.isHovered(); + boolean hovered = isHovered() || mCloseButton.isHovered() || mSendTabButton.isHovered(); boolean interacted = hovered || mPressed; boolean selected = isSelected(); mCloseButton.setVisibility(interacted && !selected && !mSelecting ? View.VISIBLE : View.GONE); + mSendTabButton.setVisibility(interacted && !selected && !mSelecting ? View.VISIBLE : View.GONE); mTitle.setVisibility(interacted && !selected ? View.VISIBLE : View.GONE); mTabOverlay.setHovered(hovered || selected); mTabOverlay.setPressed(mPressed); 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 03d3a8c80..10f58c9eb 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 @@ -15,7 +15,9 @@ import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; +import android.view.ViewGroup; +import androidx.annotation.Dimension; import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; @@ -277,4 +279,10 @@ public void run() { } }; + public void setLayoutWidth(@NonNull @Dimension float dimen) { + ViewGroup.LayoutParams params = getLayoutParams(); + params.width = (int)dimen; + setLayoutParams(params); + } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ButtonSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ButtonSetting.java index d8cd3a5f8..0d860fd24 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ButtonSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/ButtonSetting.java @@ -104,4 +104,8 @@ public String getDescription() { return mDescription; } + public void setFooterButtonVisibility(int visibility) { + mButton.setVisibility(visibility); + } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java index 885c1039d..7f4d65be8 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/RadioGroupSetting.java @@ -1,6 +1,7 @@ package org.mozilla.vrbrowser.ui.views.settings; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; import android.text.InputType; import android.util.AttributeSet; @@ -16,6 +17,7 @@ import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; public class RadioGroupSetting extends LinearLayout { @@ -24,11 +26,11 @@ public interface OnCheckedChangeListener { } private AudioEngine mAudio; - private String mDescription; + protected String mDescription; private CharSequence[] mOptions; private Object[] mValues; protected RadioGroup mRadioGroup; - private TextView mRadioDescription; + protected TextView mRadioDescription; private OnCheckedChangeListener mRadioGroupListener; private @LayoutRes int mLayout; @@ -40,21 +42,27 @@ public RadioGroupSetting(Context context, AttributeSet attrs, int defStyleAttr) super(context, attrs, defStyleAttr); TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.RadioGroupSetting, defStyleAttr, 0); + mLayout = attributes.getResourceId(R.styleable.RadioGroupSetting_layout, R.layout.setting_radio_group); mDescription = attributes.getString(R.styleable.RadioGroupSetting_description); mOptions = attributes.getTextArray(R.styleable.RadioGroupSetting_options); - mLayout = attributes.getResourceId(R.styleable.RadioGroupSetting_layout, R.layout.setting_radio_group); int id = attributes.getResourceId(R.styleable.RadioGroupSetting_values, 0); - TypedArray array = context.getResources().obtainTypedArray(id); - if (array.getType(0) == TypedValue.TYPE_STRING) { - mValues = getResources().getStringArray(id); - - } else if (array.getType(0) == TypedValue.TYPE_INT_HEX || - array.getType(0) == TypedValue.TYPE_INT_DEC) { - int [] values = getResources().getIntArray(id); - mValues = new Integer[values.length]; - for (int i=0; i { + mMenuButton.setOnClickListener(view -> { view.requestFocusFromTouch(); - enterResizeMode(); + + showMenu(); + if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); } @@ -204,6 +213,7 @@ private void initialize(@NonNull Context aContext) { mResizeExitButton.setOnClickListener(view -> { view.requestFocusFromTouch(); exitResizeMode(ResizeAction.KEEP_SIZE); + if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); } @@ -300,7 +310,7 @@ private void initialize(@NonNull Context aContext) { mButtons = new ArrayList<>(); mButtons.addAll(Arrays.asList( - mBackButton, mForwardButton, mReloadButton, mHomeButton, mResizeEnterButton, mResizeExitButton, + mBackButton, mForwardButton, mReloadButton, mHomeButton, mMenuButton, mServoButton, mPreset0, mPreset1, mPreset15, mPreset2, mPreset3)); mURLBar.setDelegate(this); @@ -309,6 +319,8 @@ private void initialize(@NonNull Context aContext) { mWidgetManager.addWorldClickListener(this); mVoiceSearchWidget = createChild(VoiceSearchWidget.class, false); + mVoiceSearchWidget.getPlacement().parentAnchorY = 0.0f; + mVoiceSearchWidget.getPlacement().translationY = WidgetPlacement.unitFromMeters(getContext(), R.dimen.base_app_dialog_y_distance); mVoiceSearchWidget.setDelegate(this); mSuggestionsProvider = new SuggestionsProvider(getContext()); @@ -405,20 +417,20 @@ public void attachToWindow(@NonNull WindowWidget aWindow) { mAttachedWindow.addWindowListener(this); if (mAttachedWindow != null) { - mURLBar.setIsContentMode(mAttachedWindow.isBookmarksVisible() || mAttachedWindow.isHistoryVisible()); + mURLBar.setIsLibraryVisible(mAttachedWindow.isBookmarksVisible() || mAttachedWindow.isHistoryVisible()); mURLBar.setURL(""); if (mAttachedWindow.isBookmarksVisible()) { mURLBar.setHint(R.string.url_bookmarks_title); - mURLBar.setInsecureVisibility(View.GONE); + mURLBar.setIsLibraryVisible(true); } else if (mAttachedWindow.isHistoryVisible()) { mURLBar.setHint(R.string.url_history_title); - mURLBar.setInsecureVisibility(View.GONE); + mURLBar.setIsLibraryVisible(true); } else { mURLBar.setURL(mAttachedWindow.getSession().getCurrentUri()); mURLBar.setHint(R.string.search_placeholder); - mURLBar.setInsecureVisibility(View.VISIBLE); + mURLBar.setIsLibraryVisible(false); } } @@ -684,7 +696,7 @@ private void setResizePreset(float aMultiplier) { } public void showVoiceSearch() { - mURLBar.showVoiceSearch(true); + mURLBar.setMicrophoneEnabled(true); } public void updateServoButton() { @@ -904,15 +916,15 @@ public void onVoiceSearchClicked() { @Override public void onShowSearchPopup() { - if (mPopup == null) { - mPopup = createChild(SuggestionsWidget.class); - mPopup.setURLBarPopupDelegate(this); + if (mAwesomeBar == null) { + mAwesomeBar = createChild(SuggestionsWidget.class); + mAwesomeBar.setURLBarPopupDelegate(this); } final String text = mURLBar.getText().trim(); final String originalText = mURLBar.getOriginalText().trim(); if (originalText.length() <= 0) { - mPopup.hide(UIWidget.KEEP_WIDGET); + mAwesomeBar.hide(UIWidget.KEEP_WIDGET); return; } @@ -920,12 +932,14 @@ public void onShowSearchPopup() { mSuggestionsProvider.setFilterText(originalText); mSuggestionsProvider.getSuggestions() .whenCompleteAsync((items, ex) -> { - mPopup.updateItems(items); - mPopup.setHighlightedText(originalText); + if (mURLBar.hasFocus()) { + mAwesomeBar.updateItems(items); + mAwesomeBar.setHighlightedText(originalText); - if (!mPopup.isVisible()) { - mPopup.updatePlacement((int)WidgetPlacement.convertPixelsToDp(getContext(), mURLBar.getWidth())); - mPopup.show(CLEAR_FOCUS); + if (!mAwesomeBar.isVisible()) { + mAwesomeBar.updatePlacement((int) WidgetPlacement.convertPixelsToDp(getContext(), mURLBar.getWidth())); + mAwesomeBar.show(CLEAR_FOCUS); + } } }, new UIThreadExecutor()).exceptionally(th -> { @@ -936,8 +950,8 @@ public void onShowSearchPopup() { @Override public void onHideSearchPopup() { - if (mPopup != null) { - mPopup.hide(UIWidget.KEEP_WIDGET); + if (mAwesomeBar != null) { + mAwesomeBar.hide(UIWidget.KEEP_WIDGET); } } @@ -961,12 +975,14 @@ public void OnVoiceSearchResult(String transcription, float confidance) { } @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key == mAppContext.getString(R.string.settings_key_servo)) { + public void onSharedPreferenceChanged(@NonNull SharedPreferences sharedPreferences, String key) { + if (key.equals(mAppContext.getString(R.string.settings_key_servo))) { updateServoButton(); - } else if (key == mAppContext.getString(R.string.settings_key_user_agent_version)) { - mURLBar.setUAMode(SettingsStore.getInstance(getContext()).getUaMode()); + } else if (key.equals(mAppContext.getString(R.string.settings_key_user_agent_version))) { + if (mHamburgerMenu != null) { + mHamburgerMenu.setUAMode(SettingsStore.getInstance(getContext()).getUaMode()); + } } } @@ -1006,14 +1022,14 @@ public void onBookmarksShown(WindowWidget aWindow) { if (mAttachedWindow == aWindow) { mURLBar.setURL(""); mURLBar.setHint(R.string.url_bookmarks_title); - mURLBar.setIsContentMode(true); + mURLBar.setIsLibraryVisible(true); } } @Override public void onBookmarksHidden(WindowWidget aWindow) { if (mAttachedWindow == aWindow) { - mURLBar.setIsContentMode(false); + mURLBar.setIsLibraryVisible(false); mURLBar.setURL(getSession().getCurrentUri()); mURLBar.setHint(R.string.search_placeholder); } @@ -1026,14 +1042,14 @@ public void onHistoryViewShown(WindowWidget aWindow) { if (mAttachedWindow == aWindow) { mURLBar.setURL(""); mURLBar.setHint(R.string.url_history_title); - mURLBar.setIsContentMode(true); + mURLBar.setIsLibraryVisible(true); } } @Override public void onHistoryViewHidden(WindowWidget aWindow) { if (mAttachedWindow == aWindow) { - mURLBar.setIsContentMode(false); + mURLBar.setIsLibraryVisible(false); mURLBar.setURL(getSession().getCurrentUri()); mURLBar.setHint(R.string.search_placeholder); } @@ -1083,4 +1099,66 @@ private void startWidgetResize() { mWidgetManager.startWidgetResize(mAttachedWindow, maxSize.first, 4.5f, minSize.first, minSize.second); } } + + private void showMenu() { + if (mHamburgerMenu != null && mHamburgerMenu.isVisible()) { + // Release current selection menu to recreate it with different actions. + hideMenu(); + return; + } + + if (mHamburgerMenu == null) { + mHamburgerMenu = new HamburgerMenuWidget(getContext()); + mHamburgerMenu.getPlacement().parentHandle = getHandle(); + mHamburgerMenu.setMenuDelegate(new HamburgerMenuWidget.MenuDelegate() { + @Override + public void onSendTab() { + hideMenu(); + + showSendTabDialog(); + } + + @Override + public void onResize() { + hideMenu(); + + enterResizeMode(); + } + + @Override + public void onSwitchMode() { + int uaMode = mAttachedWindow.getSession().getUaMode(); + if (uaMode == GeckoSessionSettings.USER_AGENT_MODE_DESKTOP) { + mHamburgerMenu.setUAMode(GeckoSessionSettings.USER_AGENT_MODE_MOBILE); + mAttachedWindow.getSession().setUaMode(GeckoSessionSettings.USER_AGENT_MODE_MOBILE); + + } else { + mHamburgerMenu.setUAMode(GeckoSessionSettings.USER_AGENT_MODE_DESKTOP); + mAttachedWindow.getSession().setUaMode(GeckoSessionSettings.USER_AGENT_MODE_DESKTOP); + } + + hideMenu(); + } + }); + } + + mHamburgerMenu.setUAMode(mAttachedWindow.getSession().getUaMode()); + mHamburgerMenu.show(UIWidget.KEEP_FOCUS); + } + + private void hideMenu() { + if (mHamburgerMenu != null) { + mHamburgerMenu.hide(UIWidget.REMOVE_WIDGET); + } + } + + public void showSendTabDialog() { + mSendTabDialog = new SendTabDialogWidget(getContext()); + mSendTabDialog.mWidgetPlacement.parentHandle = mAttachedWindow.getHandle(); + mSendTabDialog.setDelegate(() -> { + mSendTabDialog.releaseWidget(); + mSendTabDialog = null; + }); + mSendTabDialog.show(REQUEST_FOCUS); + } } 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 43c2d6210..d28f48d36 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 @@ -173,13 +173,15 @@ public enum Type { public String title; public String url; public Type type = Type.SUGGESTION; + public int score; - public static SuggestionItem create(@NonNull String title, String url, String faviconURL, Type type) { + public static SuggestionItem create(@NonNull String title, String url, String faviconURL, Type type, int score) { SuggestionItem item = new SuggestionItem(); item.title = title; item.url = url; item.faviconURL = faviconURL; item.type = type; + item.score = score; return item; } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TabsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TabsWidget.java index 991f28aa8..3b2961391 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TabsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TabsWidget.java @@ -17,6 +17,7 @@ import org.mozilla.vrbrowser.ui.views.TabView; import org.mozilla.vrbrowser.ui.views.UIButton; import org.mozilla.vrbrowser.ui.views.UITextButton; +import org.mozilla.vrbrowser.ui.widgets.dialogs.SendTabDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.UIDialog; import org.mozilla.vrbrowser.utils.BitmapCache; import org.mozilla.vrbrowser.utils.ViewUtils; @@ -39,6 +40,7 @@ public class TabsWidget extends UIDialog implements WidgetManagerDelegate.WorldC protected UITextButton mSelectAllButton; protected UITextButton mUnselectTabs; protected LinearLayout mTabsSelectModeView; + protected SendTabDialogWidget mSendTabDialog; protected boolean mSelecting; protected ArrayList mSelectedTabs = new ArrayList<>(); @@ -270,6 +272,19 @@ public void onAdd(TabView aSender) { } onDismiss(); } + + @Override + public void onSend(TabView aSender) { + hide(KEEP_WIDGET); + mSendTabDialog = new SendTabDialogWidget(getContext()); + mSendTabDialog.mWidgetPlacement.parentHandle = mWidgetManager.getFocusedWindow().getHandle(); + mSendTabDialog.setDelegate(() -> { + mSendTabDialog.releaseWidget(); + mSendTabDialog = null; + show(REQUEST_FOCUS); + }); + mSendTabDialog.show(UIWidget.REQUEST_FOCUS); + } }); } 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 491a8fb36..659c37f1e 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 @@ -494,6 +494,11 @@ public void showTabAddedNotification() { ThreadUtils.postToUiThread(() -> showNotification(mTabsButton, R.string.tab_added_notification)); } + public void showTabSentNotification() { + mTabsButton.setNotificationMode(true); + ThreadUtils.postToUiThread(() -> showNotification(mTabsButton, R.string.tab_sent_notification)); + } + private BookmarksStore.BookmarkListener mBookmarksListener = new BookmarksStore.BookmarkListener() { @Override public void onBookmarksUpdated() { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java index 058682179..02a2282be 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java @@ -2,12 +2,13 @@ import android.view.View; -import org.mozilla.geckoview.GeckoSession; -import org.mozilla.vrbrowser.VRBrowserActivity; - import androidx.annotation.IntDef; import androidx.annotation.NonNull; +import org.mozilla.geckoview.GeckoSession; +import org.mozilla.vrbrowser.VRBrowserActivity; +import org.mozilla.vrbrowser.ui.widgets.menus.VideoProjectionMenuWidget; + public interface WidgetManagerDelegate { interface UpdateListener { @@ -82,6 +83,7 @@ interface WorldClickListener { boolean canOpenNewWindow(); void openNewWindow(@NonNull String uri); void openNewTab(@NonNull String uri); + void openNewTabForeground(@NonNull String uri); WindowWidget getFocusedWindow(); TrayWidget getTray(); } 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 15fe9be56..81d9c7179 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 @@ -50,10 +50,10 @@ 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.LibraryItemContextMenuWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.MessageDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.SelectionActionWidget; +import org.mozilla.vrbrowser.ui.widgets.menus.ContextMenuWidget; import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget; import org.mozilla.vrbrowser.ui.widgets.prompts.ConfirmPromptWidget; import org.mozilla.vrbrowser.ui.widgets.prompts.PromptWidget; @@ -134,6 +134,7 @@ default void onBookmarksHidden(WindowWidget aWindow) {} private boolean mIsFullScreen; private boolean mAfterFirstPaint; private boolean mCaptureOnPageStop; + private View mSendTabCheckLayout; public interface WindowListener { default void onFocusRequest(@NonNull WindowWidget aWindow) {} @@ -188,6 +189,9 @@ private void initialize(Context aContext) { mTitleBar = new TitleBarWidget(aContext); mTitleBar.attachToWindow(this); + // Load the send tab check layout that is displayed after a tab is sent + mSendTabCheckLayout = inflate(getContext(), R.layout.window_check, null); + setFocusable(true); TelemetryWrapper.openWindowEvent(mWindowId); @@ -1203,8 +1207,8 @@ public void showClearCacheDialog() { R.string.history_clear_cancel, R.string.history_clear_now }); - mClearCacheDialog.setButtonsDelegate(index -> { - if (index == BaseAppDialogWidget.LEFT) { + mClearCacheDialog.setButtonsDelegate((index) -> { + if (index == BaseAppDialogWidget.NEGATIVE) { mClearCacheDialog.hide(REMOVE_WIDGET); } else { @@ -1520,6 +1524,7 @@ public GeckoResult onVisited(@NonNull GeckoSession geckoSession, @NonNu SessionStore.get().getHistoryStore().deleteVisitsFor(url).thenAcceptAsync(result -> { SessionStore.get().getHistoryStore().recordVisit(url, pageVisit); + SessionStore.get().getHistoryStore().recordObservation(url, new PageObservation(url)); }); return GeckoResult.fromValue(true); } 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 be0f5d9ae..565419391 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 @@ -1117,6 +1117,10 @@ public void onTabSelect(Session aTab) { } } + public void addTab(WindowWidget targetWindow) { + addTab(targetWindow, null); + } + public void addTab(WindowWidget targetWindow, @Nullable String aUri) { Session session = SessionStore.get().createSession(targetWindow.getSession().isPrivateMode()); targetWindow.setFirstPaintReady(false); @@ -1129,10 +1133,10 @@ public void addTab(WindowWidget targetWindow, @Nullable String aUri) { mWidgetManager.updateWidget(targetWindow); targetWindow.getSession().setActive(false); targetWindow.setSession(session); - if (aUri != null) { - session.loadUri(aUri); - } else { + if (aUri == null || aUri.isEmpty()) { session.loadHomePage(); + } else { + session.loadUri(aUri); } SessionStore.get().setActiveSession(session); } @@ -1229,5 +1233,4 @@ public void onTabsReceived(@NotNull List aTabs) { mTabsWidget.refreshTabs(); } } - } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/BaseAppDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/BaseAppDialogWidget.java index c2c9c43b2..c65f6c407 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/BaseAppDialogWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/BaseAppDialogWidget.java @@ -6,7 +6,6 @@ package org.mozilla.vrbrowser.ui.widgets.dialogs; import android.content.Context; -import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewTreeObserver; @@ -16,6 +15,7 @@ import androidx.databinding.DataBindingUtil; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.browser.engine.SessionStore; import org.mozilla.vrbrowser.databinding.BaseAppDialogBinding; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; @@ -27,28 +27,19 @@ public interface Delegate { default void onDismiss() {} } - public static final int LEFT = 0; - public static final int RIGHT = 1; + public static final int NEGATIVE = 0; + public static final int POSITIVE = 1; protected BaseAppDialogBinding mBinding; private Delegate mAppDialogDelegate; + private String mHelpLink; public BaseAppDialogWidget(Context aContext) { super(aContext); initialize(aContext); } - public BaseAppDialogWidget(Context aContext, AttributeSet aAttrs) { - super(aContext, aAttrs); - initialize(aContext); - } - - public BaseAppDialogWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { - super(aContext, aAttrs, aDefStyle); - initialize(aContext); - } - - private void initialize(Context aContext) { + protected void initialize(Context aContext) { LayoutInflater inflater = LayoutInflater.from(aContext); // Inflate this data binding layout @@ -56,18 +47,19 @@ private void initialize(Context aContext) { mBinding.leftButton.setOnClickListener(v -> { if (mAppDialogDelegate != null) { - mAppDialogDelegate.onButtonClicked(LEFT); + mAppDialogDelegate.onButtonClicked(NEGATIVE); } BaseAppDialogWidget.this.onDismiss(); }); mBinding.rightButton.setOnClickListener(v -> { if (mAppDialogDelegate != null) { - mAppDialogDelegate.onButtonClicked(RIGHT); + mAppDialogDelegate.onButtonClicked(POSITIVE); } BaseAppDialogWidget.this.onDismiss(); }); + mBinding.helpButton.setOnClickListener(v -> SessionStore.get().getActiveSession().loadUri(mHelpLink)); } @Override @@ -82,7 +74,6 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.base_app_dialog_z_distance); } - @Override public void show(@ShowFlags int aShowFlags) { measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), @@ -130,21 +121,41 @@ public void setTitle(String title) { mBinding.title.setText(title); } - public void setButtons(@StringRes int[] buttons) { + public void setDescription(String title) { + mBinding.description.setVisibility(VISIBLE); + mBinding.description.setText(title); + } + + public void setDescription(@StringRes int title) { + mBinding.description.setVisibility(VISIBLE); + mBinding.description.setText(title); + } + + public void setHelpLink(@NonNull String text) { + mBinding.helpButton.setVisibility(VISIBLE); + mHelpLink = text; + } + + public void setHelpLink(@StringRes int textRes) { + mBinding.helpButton.setVisibility(VISIBLE); + mHelpLink = getResources().getString(textRes); + } + + public void setButtons(@NonNull @StringRes int[] buttons) { if (buttons.length > 0) { - mBinding.leftButton.setText(buttons[LEFT]); + mBinding.leftButton.setText(buttons[NEGATIVE]); } if (buttons.length > 1) { - mBinding.rightButton.setText(buttons[RIGHT]); + mBinding.rightButton.setText(buttons[POSITIVE]); } } public void setButtons(@NonNull String[] buttons) { if (buttons.length > 0) { - mBinding.leftButton.setText(buttons[LEFT]); + mBinding.leftButton.setText(buttons[NEGATIVE]); } if (buttons.length > 1) { - mBinding.rightButton.setText(buttons[RIGHT]); + mBinding.rightButton.setText(buttons[POSITIVE]); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/ClearCacheDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/ClearCacheDialogWidget.java index 201811fcc..8f9e06f95 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/ClearCacheDialogWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/ClearCacheDialogWidget.java @@ -6,7 +6,6 @@ package org.mozilla.vrbrowser.ui.widgets.dialogs; import android.content.Context; -import android.util.AttributeSet; import android.view.LayoutInflater; import androidx.annotation.IntDef; @@ -29,20 +28,12 @@ public class ClearCacheDialogWidget extends BaseAppDialogWidget { public ClearCacheDialogWidget(Context aContext) { super(aContext); - initialize(aContext); } - public ClearCacheDialogWidget(Context aContext, AttributeSet aAttrs) { - super(aContext, aAttrs); - initialize(aContext); - } - - public ClearCacheDialogWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { - super(aContext, aAttrs, aDefStyle); - initialize(aContext); - } + @Override + protected void initialize(Context aContext) { + super.initialize(aContext); - private void initialize(Context aContext) { LayoutInflater inflater = LayoutInflater.from(aContext); // Inflate this data binding layout diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/MessageDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/MessageDialogWidget.java index 038894251..8ab5c56ac 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/MessageDialogWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/MessageDialogWidget.java @@ -6,7 +6,6 @@ package org.mozilla.vrbrowser.ui.widgets.dialogs; import android.content.Context; -import android.util.AttributeSet; import android.view.LayoutInflater; import androidx.annotation.NonNull; @@ -28,20 +27,12 @@ public interface Delegate { public MessageDialogWidget(Context aContext) { super(aContext); - initialize(aContext); } - public MessageDialogWidget(Context aContext, AttributeSet aAttrs) { - super(aContext, aAttrs); - initialize(aContext); - } - - public MessageDialogWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { - super(aContext, aAttrs, aDefStyle); - initialize(aContext); - } + @Override + protected void initialize(Context aContext) { + super.initialize(aContext); - private void initialize(Context aContext) { LayoutInflater inflater = LayoutInflater.from(aContext); // Inflate this data binding layout diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PopUpBlockDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PopUpBlockDialogWidget.java index 478d22344..58b0be6ee 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PopUpBlockDialogWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/PopUpBlockDialogWidget.java @@ -6,7 +6,6 @@ package org.mozilla.vrbrowser.ui.widgets.dialogs; import android.content.Context; -import android.util.AttributeSet; import android.view.LayoutInflater; import androidx.databinding.DataBindingUtil; @@ -22,20 +21,12 @@ public class PopUpBlockDialogWidget extends BaseAppDialogWidget { public PopUpBlockDialogWidget(Context aContext) { super(aContext); - initialize(aContext); } - public PopUpBlockDialogWidget(Context aContext, AttributeSet aAttrs) { - super(aContext, aAttrs); - initialize(aContext); - } - - public PopUpBlockDialogWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { - super(aContext, aAttrs, aDefStyle); - initialize(aContext); - } + @Override + protected void initialize(Context aContext) { + super.initialize(aContext); - private void initialize(Context aContext) { LayoutInflater inflater = LayoutInflater.from(aContext); // Inflate this data binding layout diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SendTabDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SendTabDialogWidget.java new file mode 100644 index 000000000..891cf6ebc --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SendTabDialogWidget.java @@ -0,0 +1,148 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.vrbrowser.ui.widgets.dialogs; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.databinding.DataBindingUtil; + +import org.jetbrains.annotations.NotNull; +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.VRBrowserApplication; +import org.mozilla.vrbrowser.browser.Accounts; +import org.mozilla.vrbrowser.browser.engine.SessionStore; +import org.mozilla.vrbrowser.databinding.SendTabsDisplayBinding; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import mozilla.components.concept.sync.ConstellationState; +import mozilla.components.concept.sync.Device; +import mozilla.components.concept.sync.DeviceCapability; +import mozilla.components.concept.sync.DeviceConstellationObserver; + +public class SendTabDialogWidget extends SettingDialogWidget implements + DeviceConstellationObserver, + WidgetManagerDelegate.WorldClickListener { + + private SendTabsDisplayBinding mSendTabsDialogBinding; + private Accounts mAccounts; + private List mDevicesList = new ArrayList<>(); + + public SendTabDialogWidget(@NonNull Context aContext) { + super(aContext); + } + + @Override + protected void initialize(@NonNull Context aContext) { + super.initialize(aContext); + + LayoutInflater inflater = LayoutInflater.from(aContext); + + // Inflate this data binding layout + mSendTabsDialogBinding = DataBindingUtil.inflate(inflater, R.layout.send_tabs_display, mBinding.content, true); + mSendTabsDialogBinding.setIsSyncing(false); + mSendTabsDialogBinding.setIsEmpty(false); + + mAccounts = ((VRBrowserApplication)getContext().getApplicationContext()).getAccounts(); + mAccounts.addDeviceConstellationListener(this); + + mBinding.headerLayout.setTitle(getResources().getString(R.string.send_tab_dialog_title)); + mBinding.headerLayout.setDescription(R.string.send_tab_dialog_description); + mBinding.footerLayout.setFooterButtonText(R.string.send_tab_dialog_button); + mBinding.footerLayout.setFooterButtonClickListener(v -> { + Device device = mDevicesList.get(mSendTabsDialogBinding.devicesList.getCheckedRadioButtonId()); + String uri = SessionStore.get().getActiveSession().getCurrentUri(); + String title = SessionStore.get().getActiveSession().getCurrentTitle(); + // At some point we will support sending to multiple devices or to all of them + mAccounts.sendTabs(Collections.singletonList(device), uri, title); + + // Show the tab sent notifications in the tray + mWidgetManager.getTray().showTabSentNotification(); + + onDismiss(); + }); + + mWidgetPlacement.width = WidgetPlacement.dpDimension(getContext(), R.dimen.cache_app_dialog_width); + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + super.initializeWidgetPlacement(aPlacement); + + mWidgetPlacement.parentAnchorY = 0.0f; + mWidgetPlacement.translationY = WidgetPlacement.unitFromMeters(getContext(), R.dimen.settings_world_y) - + WidgetPlacement.unitFromMeters(getContext(), R.dimen.window_world_y); + mWidgetPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.settings_world_z) - + WidgetPlacement.unitFromMeters(getContext(), R.dimen.window_world_z); + } + + @Override + public void releaseWidget() { + mAccounts.removeDeviceConstellationListener(this); + + super.releaseWidget(); + } + + @Override + public void show(int aShowFlags) { + if (mAccounts.isSignedIn()) { + mAccounts.refreshDevicesAsync(); + mSendTabsDialogBinding.setIsSyncing(true); + + } else { + mSendTabsDialogBinding.setIsEmpty(true); + mBinding.footerLayout.setFooterButtonVisibility(View.GONE); + } + + mWidgetManager.addWorldClickListener(this); + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); + + super.show(aShowFlags); + } + + @Override + public void hide(int aHideFlags) { + super.hide(aHideFlags); + + mWidgetManager.popWorldBrightness(this); + mWidgetManager.removeWorldClickListener(this); + } + + @Override + public void onDevicesUpdate(@NotNull ConstellationState constellationState) { + post(() -> { + mSendTabsDialogBinding.setIsSyncing(false); + + List list = constellationState.getOtherDevices().stream() + .filter(device -> device.getCapabilities().contains(DeviceCapability.SEND_TAB)).collect(Collectors.toList()); + if (!mDevicesList.equals(list)) { + mDevicesList = list; + + List devicesNamesList = new ArrayList<>(); + mDevicesList.forEach((device) -> devicesNamesList.add(device.getDisplayName())); + mSendTabsDialogBinding.devicesList.setOptions(devicesNamesList.toArray(new String[]{})); + } + + mSendTabsDialogBinding.setIsEmpty(mDevicesList.isEmpty()); + mBinding.footerLayout.setFooterButtonVisibility(mDevicesList.isEmpty() ? View.GONE : View.VISIBLE); + }); + } + + // WidgetManagerDelegate.WorldClickListener + + @Override + public void onWorldClick() { + onDismiss(); + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SettingDialogWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SettingDialogWidget.java new file mode 100644 index 000000000..57de2bebe --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/SettingDialogWidget.java @@ -0,0 +1,50 @@ +package org.mozilla.vrbrowser.ui.widgets.dialogs; + +import android.content.Context; +import android.view.LayoutInflater; + +import androidx.databinding.DataBindingUtil; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.databinding.SettingDialogBinding; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; + +public abstract class SettingDialogWidget extends UIDialog { + + protected SettingDialogBinding mBinding; + + public SettingDialogWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + protected void initialize(Context aContext) { + LayoutInflater inflater = LayoutInflater.from(aContext); + + // Inflate this data binding layout + mBinding = DataBindingUtil.inflate(inflater, R.layout.setting_dialog, this, true); + + // Header + mBinding.headerLayout.setBackClickListener(view -> onDismiss()); + + // Footer + mBinding.footerLayout.setFooterButtonClickListener(v -> onFooterButton()); + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + aPlacement.visible = false; + aPlacement.width = WidgetPlacement.dpDimension(getContext(), R.dimen.base_app_dialog_width); + aPlacement.height = WidgetPlacement.pixelDimension(getContext(), R.dimen.browser_width_pixels)/2; + aPlacement.parentAnchorX = 0.5f; + aPlacement.parentAnchorY = 0.5f; + aPlacement.anchorX = 0.5f; + aPlacement.anchorY = 0.5f; + aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.base_app_dialog_z_distance); + } + + protected void onFooterButton() { + + } + +} 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 5ab39ae6c..c6937b9e0 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 @@ -292,7 +292,7 @@ public void show(@ShowFlags int aShowFlags) { R.string.voice_samples_collect_dialog_allow}, index -> { SettingsStore.getInstance(getContext()).setSpeechDataCollectionReviewed(true); - if (index == MessageDialogWidget.RIGHT) { + if (index == MessageDialogWidget.POSITIVE) { SettingsStore.getInstance(getContext()).setSpeechDataCollectionEnabled(true); } ThreadUtils.postToUiThread(() -> show(aShowFlags)); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrightnessMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/BrightnessMenuWidget.java similarity index 88% rename from app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrightnessMenuWidget.java rename to app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/BrightnessMenuWidget.java index 1bef27503..9338e86c6 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/BrightnessMenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/BrightnessMenuWidget.java @@ -1,8 +1,10 @@ -package org.mozilla.vrbrowser.ui.widgets; +package org.mozilla.vrbrowser.ui.widgets.menus; import android.content.Context; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.ui.widgets.UIWidget; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import java.util.ArrayList; @@ -10,7 +12,7 @@ public class BrightnessMenuWidget extends MenuWidget { ArrayList mItems; public BrightnessMenuWidget(Context aContext) { - super(aContext); + super(aContext, R.layout.menu); createMenuItems(); } @@ -33,12 +35,7 @@ public void setParentWidget(UIWidget aParent) { private void createMenuItems() { mItems = new ArrayList<>(); - final Runnable action = new Runnable() { - @Override - public void run() { - handleClick(); - } - }; + final Runnable action = () -> handleClick(); mItems.add(new MenuItem(getContext().getString(R.string.brightness_mode_normal), 0, action)); mItems.add(new MenuItem(getContext().getString(R.string.brightness_mode_dark), 0, action)); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/ContextMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/ContextMenuWidget.java similarity index 95% rename from app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/ContextMenuWidget.java rename to app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/ContextMenuWidget.java index 444e8eaf0..1ac88cf41 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/ContextMenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/ContextMenuWidget.java @@ -3,18 +3,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mozilla.vrbrowser.ui.widgets.dialogs; +package org.mozilla.vrbrowser.ui.widgets.menus; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.net.Uri; -import android.util.Log; import android.view.View; import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.ui.widgets.MenuWidget; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.StringUtils; @@ -27,7 +25,7 @@ public class ContextMenuWidget extends MenuWidget implements WidgetManagerDelega private Runnable mDismissCallback; public ContextMenuWidget(Context aContext) { - super(aContext); + super(aContext, R.layout.menu); initialize(); } @@ -35,7 +33,7 @@ private void initialize() { mAdapter.updateBackgrounds(R.drawable.context_menu_item_background_first, R.drawable.context_menu_item_background_last, R.drawable.context_menu_item_background); - mAdapter.updateLayourId(R.layout.context_menu_item); + mAdapter.updateLayoutId(R.layout.context_menu_item); menuContainer.setBackground(getContext().getDrawable(R.drawable.context_menu_background)); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/HamburgerMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/HamburgerMenuWidget.java new file mode 100644 index 000000000..804ff47c7 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/HamburgerMenuWidget.java @@ -0,0 +1,129 @@ +package org.mozilla.vrbrowser.ui.widgets.menus; + +import android.content.Context; +import android.view.View; + +import androidx.annotation.IntDef; + +import org.mozilla.geckoview.GeckoSessionSettings; +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; +import org.mozilla.vrbrowser.utils.AnimationHelper; +import org.mozilla.vrbrowser.utils.ViewUtils; + +import java.util.ArrayList; +import java.util.Optional; + +public class HamburgerMenuWidget extends MenuWidget implements WidgetManagerDelegate.FocusChangeListener { + + public interface MenuDelegate { + void onSendTab(); + void onResize(); + void onSwitchMode(); + } + + @IntDef(value = { SWITCH_MODE, WINDOW_RESIZE, SEND_TAB}) + public @interface Action {} + public static final int SEND_TAB = 0; + public static final int WINDOW_RESIZE = 1; + public static final int SWITCH_MODE = 2; + + ArrayList mItems; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + Optional mMenuDelegate; + + public HamburgerMenuWidget(Context aContext) { + super(aContext, R.layout.hamburger_menu); + initialize(); + createMenuItems(); + } + + private void initialize() { + mAdapter.updateBackgrounds(R.drawable.context_menu_item_background_first, + R.drawable.context_menu_item_background_last, + R.drawable.context_menu_item_background); + mAdapter.updateLayoutId(R.layout.hamburger_menu_item); + } + + @Override + public void show(int aShowFlags) { + super.show(aShowFlags); + + AnimationHelper.scaleIn(findViewById(R.id.menuContainer), 100, 0, null); + mWidgetManager.addFocusChangeListener(this); + } + + @Override + public void hide(int aHideFlags) { + AnimationHelper.scaleOut(findViewById(R.id.menuContainer), 100, 0, () -> HamburgerMenuWidget.super.hide(aHideFlags)); + mWidgetManager.removeFocusChangeListener(this); + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + aPlacement.visible = false; + aPlacement.width = WidgetPlacement.dpDimension(getContext(), R.dimen.hamburger_menu_width); + aPlacement.parentAnchorX = 1.0f; + aPlacement.parentAnchorY = 1.0f; + aPlacement.anchorX = 1.0f; + aPlacement.anchorY = 0.0f; + aPlacement.translationX = 20; + aPlacement.translationY = 10; + aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.context_menu_z_distance); + } + + public void setUAMode(int uaMode) { + switch (uaMode) { + case GeckoSessionSettings.USER_AGENT_MODE_DESKTOP: + mItems.get(SWITCH_MODE).mImageId = R.drawable.ic_icon_ua_desktop; + break; + + case GeckoSessionSettings.USER_AGENT_MODE_MOBILE: + case GeckoSessionSettings.USER_AGENT_MODE_VR: + mItems.get(SWITCH_MODE).mImageId = R.drawable.ic_icon_ua_default; + break; + + } + + mListView.invalidateViews(); + } + + public void setMenuDelegate(MenuDelegate delegate) { + mMenuDelegate = Optional.ofNullable(delegate); + } + + private void createMenuItems() { + mItems = new ArrayList<>(); + + mItems.add(SEND_TAB, + new MenuItem(getContext().getString(R.string.hamburger_menu_send_tab), + R.drawable.ic_icon_tabs_sendtodevice, + () -> mMenuDelegate.ifPresent(MenuDelegate::onSendTab))); + + mItems.add(WINDOW_RESIZE, + new MenuItem(getContext().getString(R.string.hamburger_menu_resize), + R.drawable.ic_icon_resize, + () -> mMenuDelegate.ifPresent(MenuDelegate::onResize))); + + mItems.add(SWITCH_MODE, + new MenuItem(getContext().getString(R.string.hamburger_menu_switch_to_desktop), + R.drawable.ic_icon_ua_default, + () -> mMenuDelegate.ifPresent(MenuDelegate::onSwitchMode))); + + super.updateMenuItems(mItems); + + mWidgetPlacement.height = mItems.size() * WidgetPlacement.dpDimension(getContext(), R.dimen.hamburger_menu_item_height); + mWidgetPlacement.height += mBorderWidth * 2; + mWidgetPlacement.height += WidgetPlacement.dpDimension(getContext(), R.dimen.hamburger_menu_triangle_height); + } + + // FocusChangeListener + + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (!ViewUtils.isChildrenOf(menuContainer, newFocus)) { + hide(KEEP_WIDGET); + } + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/MenuWidget.java similarity index 80% rename from app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MenuWidget.java rename to app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/MenuWidget.java index d396f7947..396218105 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/MenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/MenuWidget.java @@ -4,53 +4,43 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mozilla.vrbrowser.ui.widgets; +package org.mozilla.vrbrowser.ui.widgets.menus; import android.content.Context; -import android.graphics.Point; -import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import androidx.annotation.LayoutRes; -import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.ui.views.UITextButton; -import org.mozilla.vrbrowser.utils.StringUtils; -import org.mozilla.vrbrowser.utils.ViewUtils; +import org.mozilla.vrbrowser.ui.widgets.UIWidget; import java.util.ArrayList; -import static android.view.Gravity.CENTER_VERTICAL; - public abstract class MenuWidget extends UIWidget { protected MenuAdapter mAdapter; protected ListView mListView; protected View menuContainer; - public MenuWidget(Context aContext) { + public MenuWidget(Context aContext, @LayoutRes int layout) { super(aContext); - initialize(aContext, null); + initialize(aContext, layout, null); } - public MenuWidget(Context aContext, ArrayList aItems) { + public MenuWidget(Context aContext, @LayoutRes int layout, ArrayList aItems) { super(aContext); - initialize(aContext, aItems); + initialize(aContext, layout, aItems); } - private void initialize(Context aContext, ArrayList aItems) { - inflate(aContext, R.layout.menu, this); + private void initialize(Context aContext, @LayoutRes int layout, ArrayList aItems) { + inflate(aContext, layout, this); mListView = findViewById(R.id.menuListView); menuContainer = findViewById(R.id.menuContainer); @@ -59,18 +49,6 @@ private void initialize(Context aContext, ArrayList aItems) { mListView.setAdapter(mAdapter); mListView.setVerticalScrollBarEnabled(false); mListView.setFastScrollAlwaysVisible(false); - - mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - setSelectedItem(position); - MenuItem item = mAdapter.mItems.get(position); - if (item.mCallback != null) { - item.mCallback.run(); - } - } - }); } public void updateMenuItems(ArrayList aItems) { @@ -98,7 +76,7 @@ public MenuItem(String aString, int aImage, Runnable aCallback) { } } - public static class MenuAdapter extends BaseAdapter implements OnHoverListener { + public class MenuAdapter extends BaseAdapter implements OnHoverListener { private Context mContext; private ArrayList mItems; private LayoutInflater mInflater; @@ -109,7 +87,7 @@ public static class MenuAdapter extends BaseAdapter implements OnHoverListener { MenuAdapter(Context aContext, ArrayList aItems) { mContext = aContext; - mItems = aItems != null ? aItems : new ArrayList(); + mItems = aItems != null ? aItems : new ArrayList<>(); mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); firstItemDrawable = R.drawable.menu_item_background_first; lastItemDrawable = R.drawable.menu_item_background_last; @@ -123,7 +101,7 @@ public void updateBackgrounds(int first, int last, int regular) { regularItemDrawable = regular; } - public void updateLayourId(int aLayoutId) { + public void updateLayoutId(int aLayoutId) { layoutId = aLayoutId; } @@ -149,6 +127,13 @@ public View getView(int position, View convertView, ViewGroup parent) { if (view == null) { view = mInflater.inflate(layoutId, parent, false); view.setOnHoverListener(this); + view.setOnClickListener(v -> { + setSelectedItem(position); + MenuItem item = mItems.get(position); + if (item.mCallback != null) { + item.mCallback.run(); + } + }); } view.setTag(R.string.position_tag, position); if (position == 0) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VideoProjectionMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/VideoProjectionMenuWidget.java similarity index 96% rename from app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VideoProjectionMenuWidget.java rename to app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/VideoProjectionMenuWidget.java index f504a0447..d2ca8cedd 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/VideoProjectionMenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/menus/VideoProjectionMenuWidget.java @@ -1,15 +1,18 @@ -package org.mozilla.vrbrowser.ui.widgets; +package org.mozilla.vrbrowser.ui.widgets.menus; import android.content.Context; import android.net.Uri; + +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; + import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.ui.widgets.UIWidget; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; -import androidx.annotation.IntDef; -import androidx.annotation.Nullable; - public class VideoProjectionMenuWidget extends MenuWidget { @IntDef(value = { VIDEO_PROJECTION_3D_SIDE_BY_SIDE, VIDEO_PROJECTION_360, @@ -33,7 +36,7 @@ public interface Delegate { @VideoProjectionFlags int mSelectedProjection = VIDEO_PROJECTION_3D_SIDE_BY_SIDE; public VideoProjectionMenuWidget(Context aContext) { - super(aContext); + super(aContext, R.layout.menu); createMenuItems(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/AllowedPopUpsOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/AllowedPopUpsOptionsView.java index 271a5c9af..f112298da 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/AllowedPopUpsOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/AllowedPopUpsOptionsView.java @@ -57,7 +57,7 @@ private void initialize(Context aContext) { mBinding.siteList.setAdapter(mAdapter); // Footer - mBinding.footerLayout.setResetClickListener(mClearAllListener); + mBinding.footerLayout.setFooterButtonClickListener(mClearAllListener); mBinding.executePendingBindings(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java index 4bee6e9e0..e68c56e7c 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ContentLanguageOptionsView.java @@ -65,7 +65,7 @@ private void initialize(Context aContext) { mBinding.availableList.setAdapter(mAvailableAdapter); // Footer - mBinding.footerLayout.setResetClickListener(mResetListener); + mBinding.footerLayout.setFooterButtonClickListener(mResetListener); mBinding.executePendingBindings(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ControllerOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ControllerOptionsView.java index 8b0c19f09..1fdf0ea25 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ControllerOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/ControllerOptionsView.java @@ -37,7 +37,7 @@ private void initialize(Context aContext) { mBinding.headerLayout.setBackClickListener(view -> onDismiss()); // Footer - mBinding.footerLayout.setResetClickListener(v -> resetOptions()); + mBinding.footerLayout.setFooterButtonClickListener(v -> resetOptions()); int color = SettingsStore.getInstance(getContext()).getPointerColor(); mBinding.pointerColorRadio.setOnCheckedChangeListener(mPointerColorListener); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java index b3dd45a84..43df28e03 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java @@ -42,7 +42,7 @@ private void initialize(Context aContext) { mBinding.headerLayout.setBackClickListener(view -> onDismiss()); // Footer - mBinding.footerLayout.setResetClickListener(mResetListener); + mBinding.footerLayout.setFooterButtonClickListener(mResetListener); // Switches mBinding.remoteDebuggingSwitch.setOnCheckedChangeListener(mRemoteDebuggingListener); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java index 72cf515a8..06032b9c7 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DisplayLanguageOptionsView.java @@ -46,7 +46,7 @@ private void initialize(Context aContext) { }); // Footer - mBinding.footerLayout.setResetClickListener(mResetListener); + mBinding.footerLayout.setFooterButtonClickListener(mResetListener); String language = LocaleUtils.getDisplayLocale(getContext()); mBinding.languageRadio.setOnCheckedChangeListener(mLanguageListener); 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 092ad8080..08a02b8e3 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 @@ -44,7 +44,7 @@ private void initialize(Context aContext) { mBinding.headerLayout.setBackClickListener(view -> onDismiss()); // Footer - mBinding.footerLayout.setResetClickListener(mResetListener); + mBinding.footerLayout.setFooterButtonClickListener(mResetListener); // Options mBinding.curvedDisplaySwitch.setOnCheckedChangeListener(mCurvedDisplayListener); @@ -264,7 +264,7 @@ private void setMSAAMode(int checkedId, boolean doApply) { } private void setFoveatedLevel(RadioGroupSetting aSetting, int checkedId, boolean doApply) { - RadioGroupSetting.OnCheckedChangeListener listener = aSetting.getOnCheckdChangeListener(); + RadioGroupSetting.OnCheckedChangeListener listener = aSetting.getOnCheckedChangeListener(); aSetting.setOnCheckedChangeListener(null); aSetting.setChecked(checkedId, doApply); aSetting.setOnCheckedChangeListener(listener); 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 d47833efe..9f0a4cf54 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 @@ -40,7 +40,7 @@ private void initialize(Context aContext) { mBinding.headerLayout.setBackClickListener(view -> onDismiss()); // Footer - mBinding.footerLayout.setResetClickListener(mResetListener); + mBinding.footerLayout.setFooterButtonClickListener(mResetListener); String env = SettingsStore.getInstance(getContext()).getEnvironment(); mEnvironmentsRadio = findViewById(R.id.environmentRadio); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/FxAAccountOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/FxAAccountOptionsView.java index 70361c172..d05589ff9 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/FxAAccountOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/FxAAccountOptionsView.java @@ -64,7 +64,7 @@ private void initialize(Context aContext) { updateCurrentAccountState(); // Footer - mBinding.footerLayout.setResetClickListener(v -> resetOptions()); + mBinding.footerLayout.setFooterButtonClickListener(v -> resetOptions()); } @Override 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 04498518f..6a9832f78 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 @@ -51,7 +51,7 @@ private void initialize(Context aContext) { mBinding.headerLayout.setBackClickListener(view -> onDismiss()); // Footer - mBinding.footerLayout.setResetClickListener(mResetListener); + mBinding.footerLayout.setFooterButtonClickListener(mResetListener); // Set listeners mBinding.setContentClickListener(mContentListener); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java index b83091816..5bff7bd23 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/PrivacyOptionsView.java @@ -54,7 +54,7 @@ private void initialize(Context aContext) { mBinding.headerLayout.setBackClickListener(view -> onDismiss()); // Footer - mBinding.footerLayout.setResetClickListener(v -> resetOptions()); + mBinding.footerLayout.setFooterButtonClickListener(v -> resetOptions()); ((Application)aContext.getApplicationContext()).registerActivityLifecycleCallbacks(mLifeCycleListener); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java index 2c3ff9135..98ecd2ed1 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsFooter.java @@ -9,6 +9,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.databinding.DataBindingUtil; import org.mozilla.vrbrowser.R; @@ -64,7 +65,19 @@ private void initialize(@NonNull Context aContext, @Nullable AttributeSet attrs, attributes.recycle(); } - public void setResetClickListener(@NonNull View.OnClickListener listener) { + public void setFooterButtonClickListener(@NonNull View.OnClickListener listener) { mBinding.setResetClickListener(listener); } + + public void setFooterButtonText(String text) { + mBinding.resetButton.setButtonText(text); + } + + public void setFooterButtonText(@StringRes int textRes) { + mBinding.resetButton.setButtonText(textRes); + } + + public void setFooterButtonVisibility(int visibility) { + mBinding.resetButton.setFooterButtonVisibility(visibility); + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java index 220dfcae0..9d443673b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/SettingsHeader.java @@ -9,6 +9,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.databinding.DataBindingUtil; import org.mozilla.vrbrowser.R; @@ -81,8 +82,20 @@ public void setHelpClickListener(@NonNull View.OnClickListener listener) { mBinding.setHelpClickListener(listener); } - public void setTitle(@NonNull String title) { - mBinding.setTitle(title); + public void setTitle(@NonNull String text) { + mBinding.setTitle(text); + } + + public void setTitle(@StringRes int textRes) { + mBinding.setTitle(getResources().getString(textRes)); + } + + public void setDescription(@NonNull String text) { + mBinding.setDescription(text); + } + + public void setDescription(@StringRes int textRes) { + mBinding.setDescription(getResources().getString(textRes)); } } 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 8b3400e71..695198a7b 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 @@ -26,8 +26,10 @@ import org.jetbrains.annotations.NotNull; import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.vrbrowser.BuildConfig; import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.VRBrowserActivity; import org.mozilla.vrbrowser.VRBrowserApplication; import org.mozilla.vrbrowser.audio.AudioEngine; import org.mozilla.vrbrowser.browser.Accounts; @@ -41,6 +43,7 @@ import org.mozilla.vrbrowser.ui.widgets.dialogs.UIDialog; import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget; import org.mozilla.vrbrowser.utils.StringUtils; +import org.mozilla.vrbrowser.utils.UIThreadExecutor; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -276,13 +279,13 @@ private void manageAccount() { case NEEDS_RECONNECT: mAccounts.getAuthenticationUrlAsync().thenAcceptAsync((url) -> { if (url != null) { - post(() -> { - mAccounts.setLoginOrigin(Accounts.LoginOrigin.SETTINGS); - SessionStore.get().getActiveSession().loadUri(url); - hide(REMOVE_WIDGET); - }); + mAccounts.setLoginOrigin(Accounts.LoginOrigin.SETTINGS); + WidgetManagerDelegate widgetManager = ((VRBrowserActivity)getContext()); + widgetManager.openNewTabForeground(url); + widgetManager.getFocusedWindow().getSession().setUaMode(GeckoSessionSettings.USER_AGENT_MODE_MOBILE); + hide(REMOVE_WIDGET); } - }); + }, new UIThreadExecutor()); break; case SIGNED_IN: @@ -347,7 +350,7 @@ private void updateProfile(Profile profile) { }); } else { - mBinding.fxaButton.setImageDrawable(getContext().getDrawable(R.drawable.ic_icon_settings_sign_in)); + mBinding.fxaButton.setImageDrawable(getContext().getDrawable(R.drawable.ic_icon_settings_account)); } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java index c63479df3..748e308c2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/VoiceSearchLanguageOptionsView.java @@ -46,7 +46,7 @@ private void initialize(Context aContext) { }); // Footer - mBinding.footerLayout.setResetClickListener(mResetListener); + mBinding.footerLayout.setFooterButtonClickListener(mResetListener); String language = LocaleUtils.getVoiceSearchLocale(getContext()); mBinding.languageRadio.setOnCheckedChangeListener(mLanguageListener); diff --git a/app/src/main/res/drawable/ic_icon_hamburger_menu.xml b/app/src/main/res/drawable/ic_icon_hamburger_menu.xml new file mode 100644 index 000000000..aac983681 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_hamburger_menu.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_icon_settings_account.xml b/app/src/main/res/drawable/ic_icon_settings_account.xml new file mode 100644 index 000000000..4be66304f --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_settings_account.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_icon_tabs_sendtodevice.xml b/app/src/main/res/drawable/ic_icon_tabs_sendtodevice.xml new file mode 100644 index 000000000..0764e1823 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_tabs_sendtodevice.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/triangle.xml b/app/src/main/res/drawable/triangle.xml new file mode 100644 index 000000000..52a1553b6 --- /dev/null +++ b/app/src/main/res/drawable/triangle.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/url_background.xml b/app/src/main/res/drawable/url_background.xml index 14df2cb36..f21280ec8 100644 --- a/app/src/main/res/drawable/url_background.xml +++ b/app/src/main/res/drawable/url_background.xml @@ -5,48 +5,24 @@ - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - + + + + + - diff --git a/app/src/main/res/drawable/url_background_outline.xml b/app/src/main/res/drawable/url_background_outline.xml new file mode 100644 index 000000000..c3ee73b8e --- /dev/null +++ b/app/src/main/res/drawable/url_background_outline.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/url_background_private.xml b/app/src/main/res/drawable/url_background_private.xml index d589f1620..33ed3953b 100644 --- a/app/src/main/res/drawable/url_background_private.xml +++ b/app/src/main/res/drawable/url_background_private.xml @@ -12,14 +12,12 @@ - - diff --git a/app/src/main/res/drawable/url_background_private_outline.xml b/app/src/main/res/drawable/url_background_private_outline.xml new file mode 100644 index 000000000..d1b461fb7 --- /dev/null +++ b/app/src/main/res/drawable/url_background_private_outline.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/url_bar_hint_fading_edge.xml b/app/src/main/res/drawable/url_bar_hint_fading_edge.xml index e8e90cce3..374489a3e 100644 --- a/app/src/main/res/drawable/url_bar_hint_fading_edge.xml +++ b/app/src/main/res/drawable/url_bar_hint_fading_edge.xml @@ -1,33 +1,28 @@ - - - - - - + - - - + + + + + + + + - - - - + + + + + + + + + diff --git a/app/src/main/res/layout/base_app_dialog.xml b/app/src/main/res/layout/base_app_dialog.xml index 274e5390c..5bc7165d2 100644 --- a/app/src/main/res/layout/base_app_dialog.xml +++ b/app/src/main/res/layout/base_app_dialog.xml @@ -2,10 +2,10 @@ - - + android:paddingBottom="10dp"> + + + + + + + - + + + + + android:layout_below="@+id/header_layout" + android:layout_above="@+id/footer_layout" + android:orientation="vertical"> + + + + + + +