Skip to content

Commit

Permalink
Initial implementation of tabs bar
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 Jul 8, 2024
1 parent d6ec7a0 commit 641b268
Show file tree
Hide file tree
Showing 12 changed files with 661 additions and 8 deletions.
10 changes: 9 additions & 1 deletion app/src/common/shared/com/igalia/wolvic/VRBrowserActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import com.igalia.wolvic.ui.widgets.Widget;
import com.igalia.wolvic.ui.widgets.WidgetManagerDelegate;
import com.igalia.wolvic.ui.widgets.WidgetPlacement;
import com.igalia.wolvic.ui.widgets.TabsBar;
import com.igalia.wolvic.ui.widgets.WindowWidget;
import com.igalia.wolvic.ui.widgets.Windows;
import com.igalia.wolvic.ui.widgets.dialogs.CrashDialogWidget;
Expand Down Expand Up @@ -213,6 +214,7 @@ public void run() {
RootWidget mRootWidget;
KeyboardWidget mKeyboard;
NavigationBarWidget mNavigationBar;
TabsBar mTabsBar;
CrashDialogWidget mCrashDialog;
TrayWidget mTray;
WhatsNewWidget mWhatsNewWidget = null;
Expand Down Expand Up @@ -455,13 +457,17 @@ public void onWindowVideoAvailabilityChanged(@NonNull WindowWidget aWindow) {
}
});

// Create Tabs bar widget
mTabsBar = new TabsBar(this);
mTabsBar.setTabDelegate(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 @@ -474,11 +480,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 @@ -514,28 +529,43 @@ public void removePermissionException(@NonNull String uri, @SitePermission.Categ
@Override
public void onSessionAdded(Session aSession) {
ComponentsAdapter.get().addSession(aSession);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionAdded(aSession);
}
}

@Override
public void onSessionOpened(Session aSession) {
ComponentsAdapter.get().link(aSession);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionOpened(aSession);
}
}

@Override
public void onSessionClosed(Session aSession) {
ComponentsAdapter.get().unlink(aSession);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionClosed(aSession);
}
}

@Override
public void onSessionRemoved(String aId) {
ComponentsAdapter.get().removeSession(aId);
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionRemoved(aId);
}
}

@Override
public void onSessionStateChanged(Session aSession, boolean aActive) {
if (aActive) {
ComponentsAdapter.get().selectSession(aSession);
}
for (SessionChangeListener listener : mSessionChangeListeners) {
listener.onSessionStateChanged(aSession, aActive);
}
}

@Override
Expand All @@ -549,6 +579,9 @@ public void onCurrentSessionChange(WSession aOldSession, WSession aSession) {
ComponentsAdapter.get().link(newSession);
}

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

@Override
Expand Down
178 changes: 178 additions & 0 deletions app/src/common/shared/com/igalia/wolvic/ui/views/TabsBarItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package com.igalia.wolvic.ui.views;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModel;

import com.igalia.wolvic.R;
import com.igalia.wolvic.browser.api.WSession;
import com.igalia.wolvic.browser.engine.Session;
import com.igalia.wolvic.browser.engine.SessionStore;
import com.igalia.wolvic.utils.SystemUtils;
import com.igalia.wolvic.utils.UrlUtils;

import mozilla.components.browser.icons.IconRequest;

public class TabsBarItem extends RelativeLayout implements WSession.ContentDelegate, WSession.NavigationDelegate {

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

public enum Mode {ADD_TAB, TAB_DETAILS}

protected Mode mMode = Mode.TAB_DETAILS;
protected ViewGroup mTabDetailsView;
protected ViewGroup mAddTabView;
protected ImageView mFavicon;
protected TextView mSubtitle;
protected TextView mTitle;
protected UIButton mCloseButton;
protected Delegate mDelegate;
protected Session mSession;
protected ViewModel mViewModel;

public interface Delegate {
void onAdd(TabsBarItem aSender);

void onClick(TabsBarItem aSender);

void onClose(TabsBarItem aSender);
}

public TabsBarItem(Context context) {
super(context);
}

public TabsBarItem(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public TabsBarItem(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();

mTabDetailsView = findViewById(R.id.tab_details);
mAddTabView = findViewById(R.id.add_tab);

mCloseButton = findViewById(R.id.tab_close_button);
mCloseButton.setOnClickListener(v -> {
v.requestFocusFromTouch();
if (mDelegate != null) {
mDelegate.onClose(this);
}
});

mFavicon = findViewById(R.id.tab_favicon);
mTitle = findViewById(R.id.tab_title);
mSubtitle = findViewById(R.id.tab_subtitle);

this.setOnClickListener(mClickListener);

setMode(mMode);
}

private final OnClickListener mClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mDelegate != null) {
if (mMode == Mode.ADD_TAB) {
mDelegate.onAdd(TabsBarItem.this);
} else {
mDelegate.onClick(TabsBarItem.this);
}
}
}
};

public void attachToSession(@Nullable Session aSession) {
if (mSession != null) {
mSession.removeContentListener(this);
}

mSession = aSession;
if (mSession != null) {
mSession.addContentListener(this);

Log.e(LOGTAG, "attachToSession: " + mSession.getCurrentTitle() + " " + mSession.getCurrentUri());

mTitle.setText(mSession.getCurrentTitle());
mSubtitle.setText(UrlUtils.stripProtocol(mSession.getCurrentUri()));
SessionStore.get().getBrowserIcons().loadIntoView(
mFavicon, mSession.getCurrentUri(), IconRequest.Size.DEFAULT);

setActive(mSession.isActive());
} else {
// Null session
mTitle.setText(null);
mSubtitle.setText(null);
mFavicon.setImageDrawable(null);
}
}

public Session getSession() {
return mSession;
}

public void setDelegate(Delegate aDelegate) {
mDelegate = aDelegate;
}

public void reset() {
mCloseButton.setHovered(false);
setMode(Mode.TAB_DETAILS);
}

public void setMode(Mode mode) {
mMode = mode;
mAddTabView.setVisibility((mMode == Mode.ADD_TAB) ? VISIBLE : GONE);
mTabDetailsView.setVisibility((mMode == Mode.TAB_DETAILS) ? VISIBLE : GONE);
}

@Override
public void onTitleChange(@NonNull WSession session, @Nullable String title) {
if (mSession == null || mSession.getWSession() != session) {
return;
}

mTitle.setText(title);
}

@Override
public void onLocationChange(@NonNull WSession session, @Nullable String url) {
if (mSession == null || mSession.getWSession() != session) {
return;
}

if (url == null) {
mSubtitle.setText(null);
mFavicon.setImageDrawable(null);
} else {
mSubtitle.setText(UrlUtils.stripProtocol(mSession.getCurrentUri()));
SessionStore.get().getBrowserIcons().loadIntoView(
mFavicon, mSession.getCurrentUri(), IconRequest.Size.DEFAULT);
}
}

@Override
public void onCloseRequest(@NonNull WSession aSession) {
if (mSession.getWSession() == aSession) {
mDelegate.onClose(this);
}
}

public void setActive(boolean isActive) {
setSelected(isActive);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.igalia.wolvic.ui.widgets;

import com.igalia.wolvic.browser.engine.Session;

import java.util.List;

public interface TabDelegate {
void onTabSelect(Session aTab);
void onTabAdd();
void onTabsClose(List<Session> aTabs);
}
Loading

0 comments on commit 641b268

Please sign in to comment.