diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index dbdb230ea..e9cad38ae 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -1400,7 +1400,6 @@ public void openNewWindow(String uri) { @Override public void openNewTab(@NonNull String uri) { mWindows.addBackgroundTab(mWindows.getFocusedWindow(), uri); - mTray.showTabAddedNotification(); } @Override 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 944edab29..cb63c8241 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/Services.kt +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/Services.kt @@ -14,10 +14,7 @@ import androidx.work.WorkManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import mozilla.components.concept.sync.DeviceCapability -import mozilla.components.concept.sync.DeviceEvent -import mozilla.components.concept.sync.DeviceEventsObserver -import mozilla.components.concept.sync.DeviceType +import mozilla.components.concept.sync.* import mozilla.components.lib.fetch.httpurlconnection.HttpURLConnectionClient import mozilla.components.service.fxa.* import mozilla.components.service.fxa.manager.FxaAccountManager @@ -38,6 +35,11 @@ class Services(context: Context, places: Places): GeckoSession.NavigationDelegat const val CLIENT_ID = "7ad9917f6c55fb77" const val REDIRECT_URL = "https://accounts.firefox.com/oauth/success/$CLIENT_ID" } + interface TabReceivedDelegate { + fun onTabsReceived(uri: List) + } + + var tabReceivedDelegate: TabReceivedDelegate? = null // This makes bookmarks storage accessible to background sync workers. init { @@ -73,10 +75,10 @@ class Services(context: Context, places: Places): GeckoSession.NavigationDelegat override fun onEvents(events: List) { CoroutineScope(Dispatchers.Main).launch { Logger(logTag).info("Received ${events.size} device event(s)") - events.filterIsInstance(DeviceEvent.TabReceived::class.java).forEach { - // Just load the first tab that was sent. - // TODO Update when there is a push notifications API available - SessionStore.get().activeSession.loadUri(it.entries[0].url) + val events = events.filterIsInstance(DeviceEvent.TabReceived::class.java) + if (!events.isEmpty()) { + val tabs = events.map { event -> event.entries }.flatten() + tabReceivedDelegate?.onTabsReceived(tabs) } } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/Session.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/Session.java index 4c3f819ce..c1d603ca0 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/Session.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/engine/Session.java @@ -421,6 +421,10 @@ public void captureBitmap(@NonNull GeckoDisplay aDisplay) { }); } + public boolean hasCapturedBitmap() { + return BitmapCache.getInstance(mContext).hasBitmap(mState.mId); + } + public void purgeHistory() { if (mState.mSession != null) { mState.mSession.purgeHistory(); 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 8f4f71f45..991f28aa8 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 @@ -153,7 +153,7 @@ public void releaseWidget() { @Override public void show(int aShowFlags) { super.show(aShowFlags); - mAdapter.updateTabs(SessionStore.get().getSortedSessions(mPrivateMode)); + refreshTabs(); mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); mTabsList.requestFocusFromTouch(); } @@ -169,6 +169,10 @@ public void setTabDelegate(TabDelegate aDelegate) { mTabDelegate = aDelegate; } + public void refreshTabs() { + mAdapter.updateTabs(SessionStore.get().getSortedSessions(mPrivateMode)); + } + public class TabAdapter extends RecyclerView.Adapter { private ArrayList mTabs = new ArrayList<>(); 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 8b6b52d42..15fe9be56 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 @@ -52,7 +52,6 @@ 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.MaxWindowsWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.MessageDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.SelectionActionWidget; import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget; @@ -1459,7 +1458,7 @@ public void onPageStart(@NonNull GeckoSession geckoSession, @NonNull String s) { @Override public void onPageStop(@NonNull GeckoSession aSession, boolean b) { - if (mCaptureOnPageStop) { + if (mCaptureOnPageStop || !mSession.hasCapturedBitmap()) { mCaptureOnPageStop = false; captureImage(); } 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 5773da9d3..be0f5d9ae 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 @@ -17,6 +17,7 @@ import org.mozilla.vrbrowser.browser.Accounts; import org.mozilla.vrbrowser.browser.Media; import org.mozilla.vrbrowser.browser.PromptDelegate; +import org.mozilla.vrbrowser.browser.Services; import org.mozilla.vrbrowser.browser.SettingsStore; import org.mozilla.vrbrowser.browser.engine.Session; import org.mozilla.vrbrowser.browser.engine.SessionState; @@ -34,15 +35,18 @@ import java.io.Writer; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.List; import java.util.stream.Collectors; 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 mozilla.components.concept.sync.TabData; public class Windows implements TrayListener, TopBarWidget.Delegate, TitleBarWidget.Delegate, - GeckoSession.ContentDelegate, WindowWidget.WindowListener, TabsWidget.TabDelegate { + GeckoSession.ContentDelegate, WindowWidget.WindowListener, TabsWidget.TabDelegate, + Services.TabReceivedDelegate { private static final String LOGTAG = SystemUtils.createLogtag(Windows.class); @@ -100,6 +104,7 @@ class WindowsState { private PromptDelegate mPromptDelegate; private TabsWidget mTabsWidget; private Accounts mAccounts; + private Services mServices; public enum WindowPlacement{ FRONT(0), @@ -137,6 +142,8 @@ public Windows(Context aContext) { mAccounts = ((VRBrowserApplication)mContext.getApplicationContext()).getAccounts(); mAccounts.addAccountListener(mAccountObserver); + mServices = ((VRBrowserApplication)mContext.getApplicationContext()).getServices(); + mServices.setTabReceivedDelegate(this); restoreWindows(); } @@ -435,6 +442,7 @@ public void onDestroy() { window.close(); } mAccounts.removeAccountListener(mAccountObserver); + mServices.setTabReceivedDelegate(null); } public boolean isInPrivateMode() { @@ -943,6 +951,10 @@ public void onTabsClicked() { mTabsWidget.getPlacement().parentHandle = mFocusedWindow.getHandle(); mTabsWidget.attachToWindow(mFocusedWindow); mTabsWidget.show(UIWidget.KEEP_FOCUS); + // If we're signed-in, poll for any new device events (e.g. received tabs) + // There's no push support right now, so this helps with the perception of speedy tab delivery. + ((VRBrowserApplication)mContext.getApplicationContext()).getAccounts().refreshDevicesAsync(); + ((VRBrowserApplication)mContext.getApplicationContext()).getAccounts().pollForEventsAsync(); } } @@ -1105,7 +1117,7 @@ public void onTabSelect(Session aTab) { } } - public void addTab(WindowWidget targetWindow) { + public void addTab(WindowWidget targetWindow, @Nullable String aUri) { Session session = SessionStore.get().createSession(targetWindow.getSession().isPrivateMode()); targetWindow.setFirstPaintReady(false); targetWindow.setFirstDrawCallback(() -> { @@ -1117,7 +1129,11 @@ public void addTab(WindowWidget targetWindow) { mWidgetManager.updateWidget(targetWindow); targetWindow.getSession().setActive(false); targetWindow.setSession(session); - session.loadHomePage(); + if (aUri != null) { + session.loadUri(aUri); + } else { + session.loadHomePage(); + } SessionStore.get().setActiveSession(session); } @@ -1126,11 +1142,12 @@ public void addBackgroundTab(WindowWidget targetWindow, String aUri) { session.loadUri(aUri); session.updateLastUse(); mFocusedWindow.getSession().updateLastUse(); + mWidgetManager.getTray().showTabAddedNotification(); } @Override public void onTabAdd() { - addTab(mFocusedWindow); + addTab(mFocusedWindow, null); } @Override @@ -1175,7 +1192,7 @@ public void onTabsClose(ArrayList aTabs) { available.remove(0); } else { // We don't have more tabs available for the front window, load home. - addTab(window); + addTab(window, null); } } @@ -1187,4 +1204,30 @@ public void onTabsClose(ArrayList aTabs) { SessionStore.get().setActiveSession(targetWindow.getSession()); } + + @Override + public void onTabsReceived(@NotNull List aTabs) { + WindowWidget targetWindow = mFocusedWindow; + for (int i = aTabs.size() - 1; i >= 0; --i) { + Session session = SessionStore.get().createSession(targetWindow.getSession().isPrivateMode()); + // Cache the provided data to avoid delays if the tabs are loaded at the same time the + // tabs panel is shown. + session.getSessionState().mTitle = aTabs.get(i).getTitle(); + session.getSessionState().mUri = aTabs.get(i).getUrl(); + session.loadUri(aTabs.get(i).getUrl()); + session.updateLastUse(); + if (i == 0) { + // Set the first received tab of the list the current one. + SessionStore.get().setActiveSession(session); + targetWindow.setSession(session); + } + } + + mWidgetManager.getTray().showTabAddedNotification(); + + if (mTabsWidget != null && mTabsWidget.isVisible()) { + mTabsWidget.refreshTabs(); + } + } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/BitmapCache.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/BitmapCache.java index 7288613e8..2a7ebddd0 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/BitmapCache.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/BitmapCache.java @@ -135,6 +135,10 @@ public void removeBitmap(@NonNull String aKey) { }); } + public boolean hasBitmap(@NonNull String aKey) { + return mMemoryCache.get(aKey) != null; + } + private void runIO(Runnable aRunnable) { mIOExecutor.execute(() -> { if (mDiskCache != null) {