Skip to content

Commit

Permalink
Initial implementation of browser tabs
Browse files Browse the repository at this point in the history
    The Tabs Bar is a small window that sits adyacent to the current window and which lists the open tabs.

    This is a very simple implementation for now, as we test and iterate.

    Other minor changes:
     - TabsBar is managed by VRBrowserActivity
     - Add session change listeners to SessionStore
     - Move TabDelegate to a separate interface
  • Loading branch information
felipeerias committed Sep 19, 2024
1 parent 22da6fa commit 3756d48
Show file tree
Hide file tree
Showing 17 changed files with 930 additions and 8 deletions.
12 changes: 11 additions & 1 deletion app/src/common/shared/com/igalia/wolvic/VRBrowserActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@
import com.igalia.wolvic.telemetry.TelemetryService;
import com.igalia.wolvic.ui.OffscreenDisplay;
import com.igalia.wolvic.ui.adapters.Language;
import com.igalia.wolvic.ui.widgets.AbstractTabsBar;
import com.igalia.wolvic.ui.widgets.AppServicesProvider;
import com.igalia.wolvic.ui.widgets.HorizontalTabsBar;
import com.igalia.wolvic.ui.widgets.KeyboardWidget;
import com.igalia.wolvic.ui.widgets.NavigationBarWidget;
import com.igalia.wolvic.ui.widgets.RootWidget;
Expand Down Expand Up @@ -213,6 +215,7 @@ public void run() {
RootWidget mRootWidget;
KeyboardWidget mKeyboard;
NavigationBarWidget mNavigationBar;
AbstractTabsBar mTabsBar;
CrashDialogWidget mCrashDialog;
TrayWidget mTray;
WhatsNewWidget mWhatsNewWidget = null;
Expand Down Expand Up @@ -461,13 +464,18 @@ public void onWindowVideoAvailabilityChanged(@NonNull WindowWidget aWindow) {
}
});

// Create Tabs bar widget
// TODO support horizontal tabs bar depending on setting
mTabsBar = new HorizontalTabsBar(this, mWindows);
// mTabsBar = new VerticalTabsBar(this, mWindows);

// Create the tray
mTray = new TrayWidget(this);
mTray.addListeners(mWindows);
mTray.setAddWindowVisible(mWindows.canOpenNewWindow());
attachToWindow(mWindows.getFocusedWindow(), null);

addWidgets(Arrays.asList(mRootWidget, mNavigationBar, mKeyboard, mTray, mWebXRInterstitial));
addWidgets(Arrays.asList(mRootWidget, mNavigationBar, mTabsBar, mKeyboard, mTray, mWebXRInterstitial));

// Create the platform plugin after widgets are created to be extra safe.
mPlatformPlugin = createPlatformPlugin(this);
Expand All @@ -480,11 +488,13 @@ public void onWindowVideoAvailabilityChanged(@NonNull WindowWidget aWindow) {
private void attachToWindow(@NonNull WindowWidget aWindow, @Nullable WindowWidget aPrevWindow) {
mPermissionDelegate.setParentWidgetHandle(aWindow.getHandle());
mNavigationBar.attachToWindow(aWindow);
mTabsBar.attachToWindow(aWindow);
mKeyboard.attachToWindow(aWindow);
mTray.attachToWindow(aWindow);

if (aPrevWindow != null) {
updateWidget(mNavigationBar);
updateWidget(mTabsBar);
updateWidget(mKeyboard);
updateWidget(mTray);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
Expand Down Expand Up @@ -84,9 +85,11 @@ public static SessionStore get() {
private FxaWebChannelFeature mWebChannelsFeature;
private Store.Subscription mStoreSubscription;
private BrowserIconsHelper mBrowserIconsHelper;
private final LinkedHashSet<SessionChangeListener> mSessionChangeListeners;

private SessionStore() {
mSessions = new ArrayList<>();
mSessionChangeListeners = new LinkedHashSet<>();
}

public void initialize(Context context) {
Expand Down Expand Up @@ -358,6 +361,10 @@ public Session getActiveSession() {
return mActiveSession;
}

public List<Session> getSessions(boolean aPrivateMode) {
return mSessions.stream().filter(session -> session.isPrivateMode() == aPrivateMode).collect(Collectors.toList());
}

public ArrayList<Session> getSortedSessions(boolean aPrivateMode) {
ArrayList<Session> result = new ArrayList<>(mSessions);
result.removeIf(session -> session.isPrivateMode() != aPrivateMode);
Expand All @@ -374,6 +381,14 @@ public void setPermissionDelegate(PermissionDelegate delegate) {
mPermissionDelegate = delegate;
}

public void addSessionChangeListener(SessionChangeListener listener) {
mSessionChangeListeners.add(listener);
}

public void removeSessionChangeListener(SessionChangeListener listener) {
mSessionChangeListeners.remove(listener);
}

public BookmarksStore getBookmarkStore() {
return mBookmarksStore;
}
Expand Down Expand Up @@ -513,33 +528,54 @@ public void removePermissionException(@NonNull String uri, @SitePermission.Categ

@Override
public void onSessionAdded(Session aSession) {
Log.e(LOGTAG, "onSessionAdded " + aSession);
ComponentsAdapter.get().addSession(aSession);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionAdded(aSession);
}
}

@Override
public void onSessionOpened(Session aSession) {
Log.e(LOGTAG, "onSessionOpened " + aSession);
ComponentsAdapter.get().link(aSession);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionOpened(aSession);
}
}

@Override
public void onSessionClosed(Session aSession) {
Log.e(LOGTAG, "onSessionClosed " + aSession);
ComponentsAdapter.get().unlink(aSession);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionClosed(aSession);
}
}

@Override
public void onSessionRemoved(String aId) {
Log.e(LOGTAG, "onSessionRemoved " + aId);
ComponentsAdapter.get().removeSession(aId);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionRemoved(aId);
}
}

@Override
public void onSessionStateChanged(Session aSession, boolean aActive) {
Log.e(LOGTAG, "onSessionStateChanged " + aSession + " active=" + aActive);
if (aActive) {
ComponentsAdapter.get().selectSession(aSession);
}
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionStateChanged(aSession, aActive);
}
}

@Override
public void onCurrentSessionChange(WSession aOldSession, WSession aSession) {
Log.e(LOGTAG, "onCurrentSessionChange old: " + aOldSession + " new: " + aSession);
Session oldSession = getSession(aOldSession);
Session newSession = getSession(aSession);
if (oldSession != null) {
Expand All @@ -549,6 +585,9 @@ public void onCurrentSessionChange(WSession aOldSession, WSession aSession) {
ComponentsAdapter.get().link(newSession);
}

for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onCurrentSessionChange(aOldSession, aSession);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.igalia.wolvic.ui.adapters;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;

import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.igalia.wolvic.R;
import com.igalia.wolvic.browser.engine.Session;
import com.igalia.wolvic.ui.views.TabsBarItem;
import com.igalia.wolvic.ui.widgets.TabDelegate;
import com.igalia.wolvic.utils.SystemUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TabsBarAdapter extends RecyclerView.Adapter<TabsBarAdapter.ViewHolder> {

private static final String LOGTAG = SystemUtils.createLogtag(TabsBarAdapter.class);

public enum Orientation {HORIZONTAL, VERTICAL}

private final TabDelegate mTabDelegate;
private final Orientation mOrientation;
private final List<Session> mTabs = new ArrayList<>();

static class ViewHolder extends RecyclerView.ViewHolder {
TabsBarItem mTabBarItem;

ViewHolder(TabsBarItem v) {
super(v);
mTabBarItem = v;
}
}

public TabsBarAdapter(@NonNull TabDelegate tabDelegate, Orientation orientation) {
mTabDelegate = tabDelegate;
mOrientation = orientation;
}

@Override
public long getItemId(int position) {
if (position == 0) {
return 0;
} else {
return mTabs.get(position - 1).getId().hashCode();
}
}

public void updateTabs(List<Session> aTabs) {
mTabs.clear();
mTabs.addAll(aTabs);

Log.e(LOGTAG, "updateTabs: " + aTabs.size());
for (Session session : aTabs) {
Log.e(LOGTAG, " " + session.getCurrentUri() + " " + session.getLastUse() + (session.isActive() ? " ACTIVE" : ""));
}

notifyDataSetChanged();
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@LayoutRes int layout;
if (mOrientation == Orientation.HORIZONTAL) {
layout = R.layout.tabs_bar_item_horizontal;
} else {
layout = R.layout.tabs_bar_item_vertical;
}
TabsBarItem view = (TabsBarItem) LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
if (position > 0) {
Session session = mTabs.get(position - 1);
holder.mTabBarItem.attachToSession(session);
holder.mTabBarItem.setMode(TabsBarItem.Mode.TAB_DETAILS);
holder.mTabBarItem.setDelegate(mItemDelegate);
} else {
holder.mTabBarItem.attachToSession(null);
holder.mTabBarItem.setMode(TabsBarItem.Mode.ADD_TAB);
}
}

@Override
public int getItemCount() {
return mTabs.size() + 1;
}

private final TabsBarItem.Delegate mItemDelegate = new TabsBarItem.Delegate() {
@Override
public void onAdd(TabsBarItem item) {

Log.e(LOGTAG, "TabsBarItem.Delegate onAdd");

mTabDelegate.onTabAdd();
}

@Override
public void onClick(TabsBarItem item) {
Log.e(LOGTAG, "onClick");

mTabDelegate.onTabSelect(item.getSession());
}

@Override
public void onClose(TabsBarItem item) {
Log.e(LOGTAG, "onClose");

mTabDelegate.onTabsClose(Collections.singletonList(item.getSession()));

// this should eventually trigger a refresh of the tabs
}
};
}
Loading

0 comments on commit 3756d48

Please sign in to comment.