1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file. 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)package org.chromium.chrome.browser.tabmodel; 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import org.chromium.chrome.browser.Tab; 8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * This class acts as a controller for determining where tabs should be inserted 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * into a tab strip model. See tab_strip_model_order_controller.cc and 13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * tab_strip_model.cc 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)public class TabModelOrderController { 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) private static final int NO_TAB = -1; 18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) private final TabModelSelector mTabModelSelector; 19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public TabModelOrderController(TabModelSelector modelSelector) { 21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) mTabModelSelector = modelSelector; 22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Determine the insertion index of the next tab. If it's not the result of 26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * a link being pressed, the provided index will be returned. 27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param type The launch type of the new tab. 29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param position The provided position. 30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return Where to insert the tab. 31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public int determineInsertionIndex(TabLaunchType type, int position, Tab newTab) { 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (linkClicked(type)) { 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) position = determineInsertionIndex(type, newTab); 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (willOpenInForeground(type, newTab.isIncognito())) { 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Forget any existing relationships, we don't want to make things 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // too confusing by having multiple groups active at the same time. 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) forgetAllOpeners(); 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return position; 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Determine the insertion index of the next tab. 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param type The launch type of the new tab. 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return Where to insert the tab. 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public int determineInsertionIndex(TabLaunchType type, Tab newTab) { 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) TabModel currentModel = mTabModelSelector.getCurrentModel(); 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Tab currentTab = TabModelUtils.getCurrentTab(currentModel); 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (currentTab == null) { 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) assert (currentModel.getCount() == 0); 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return 0; 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int currentId = currentTab.getId(); 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int currentIndex = TabModelUtils.getTabIndexById(currentModel, currentId); 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (sameModelType(currentModel, newTab)) { 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (willOpenInForeground(type, newTab.isIncognito())) { 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If the tab was opened in the foreground, insert it adjacent to 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // the tab that opened that link. 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return currentIndex + 1; 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If the tab was opened in the background, position at the end of 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // it's 'group'. 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int index = getIndexOfLastTabOpenedBy(currentId, currentIndex); 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (index != NO_TAB) { 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return index + 1; 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return currentIndex + 1; 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else { 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // If the tab is opening in the other model type, just put it at the end. 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return mTabModelSelector.getModel(newTab.isIncognito()).getCount(); 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Returns the index of the last tab in the model opened by the specified 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * opener, starting at startIndex. To clarify, the tabs are traversed in the 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * descending order of their position in the model. This means that the tab 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * furthest in the stack with the given opener id will be returned. 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param openerId The opener of interest. 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param startIndex The start point of the search. 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return The last tab if found, NO_TAB otherwise. 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) private int getIndexOfLastTabOpenedBy(int openerId, int startIndex) { 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) TabModel currentModel = mTabModelSelector.getCurrentModel(); 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int count = currentModel.getCount(); 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (int i = count - 1; i >= startIndex; i--) { 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Tab tab = currentModel.getTabAt(i); 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (tab.getParentId() == openerId && tab.isGroupedWithParent()) { 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return i; 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return NO_TAB; 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Clear the opener attribute on all tabs in the model. 107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) void forgetAllOpeners() { 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) TabModel currentModel = mTabModelSelector.getCurrentModel(); 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) int count = currentModel.getCount(); 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (int i = 0; i < count; i++) { 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) currentModel.getTabAt(i).setGroupedWithParent(false); 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Determine if a launch type is the result of linked being clicked. 118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) static boolean linkClicked(TabLaunchType type) { 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return type == TabLaunchType.FROM_LINK || 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) type == TabLaunchType.FROM_LONGPRESS_FOREGROUND || 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) type == TabLaunchType.FROM_LONGPRESS_BACKGROUND; 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Determine if a launch type will result in the tab being opened in the 127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * foreground. 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param type The type of opening event. 129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param isNewTabIncognito True if the new opened tab is incognito. 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return True if the tab will be in the foreground 131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public boolean willOpenInForeground(TabLaunchType type, boolean isNewTabIncognito) { 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Restore is handling the active index by itself. 134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (type == TabLaunchType.FROM_RESTORE) return false; 135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return type != TabLaunchType.FROM_LONGPRESS_BACKGROUND 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) || (!mTabModelSelector.isIncognitoSelected() && isNewTabIncognito); 137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) /** 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {@code true} If both tabs have the same model type, {@code false} otherwise. 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) static boolean sameModelType(TabModel model, Tab tab) { 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return model.isIncognito() == tab.isIncognito(); 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 147