mobile/android/base/TabsPanel.java

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:592e60c3cb61
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 package org.mozilla.gecko;
7
8 import org.mozilla.gecko.Telemetry;
9 import org.mozilla.gecko.TelemetryContract;
10 import org.mozilla.gecko.animation.PropertyAnimator;
11 import org.mozilla.gecko.animation.ViewHelper;
12 import org.mozilla.gecko.widget.IconTabWidget;
13
14 import android.content.Context;
15 import android.content.res.Resources;
16 import android.graphics.Rect;
17 import android.os.Build;
18 import android.util.AttributeSet;
19 import android.view.LayoutInflater;
20 import android.view.View;
21 import android.view.ViewGroup;
22 import android.widget.Button;
23 import android.widget.FrameLayout;
24 import android.widget.ImageButton;
25 import android.widget.LinearLayout;
26 import android.widget.RelativeLayout;
27
28 public class TabsPanel extends LinearLayout
29 implements LightweightTheme.OnChangeListener,
30 IconTabWidget.OnTabChangedListener {
31 private static final String LOGTAG = "GeckoTabsPanel";
32
33 public static enum Panel {
34 NORMAL_TABS,
35 PRIVATE_TABS,
36 REMOTE_TABS
37 }
38
39 public static interface PanelView {
40 public ViewGroup getLayout();
41 public void setTabsPanel(TabsPanel panel);
42 public void show();
43 public void hide();
44 public boolean shouldExpand();
45 }
46
47 public static interface TabsLayoutChangeListener {
48 public void onTabsLayoutChange(int width, int height);
49 }
50
51 private Context mContext;
52 private final GeckoApp mActivity;
53 private final LightweightTheme mTheme;
54 private RelativeLayout mHeader;
55 private TabsListContainer mTabsContainer;
56 private PanelView mPanel;
57 private PanelView mPanelNormal;
58 private PanelView mPanelPrivate;
59 private PanelView mPanelRemote;
60 private RelativeLayout mFooter;
61 private TabsLayoutChangeListener mLayoutChangeListener;
62
63 private IconTabWidget mTabWidget;
64 private static ImageButton mAddTab;
65
66 private Panel mCurrentPanel;
67 private boolean mIsSideBar;
68 private boolean mVisible;
69
70 public TabsPanel(Context context, AttributeSet attrs) {
71 super(context, attrs);
72 mContext = context;
73 mActivity = (GeckoApp) context;
74 mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
75
76 setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
77 LinearLayout.LayoutParams.FILL_PARENT));
78 setOrientation(LinearLayout.VERTICAL);
79
80 mCurrentPanel = Panel.NORMAL_TABS;
81 mVisible = false;
82
83 mIsSideBar = false;
84
85 LayoutInflater.from(context).inflate(R.layout.tabs_panel, this);
86 initialize();
87 }
88
89 private void initialize() {
90 mHeader = (RelativeLayout) findViewById(R.id.tabs_panel_header);
91 mTabsContainer = (TabsListContainer) findViewById(R.id.tabs_container);
92
93 mPanelNormal = (PanelView) findViewById(R.id.normal_tabs);
94 mPanelNormal.setTabsPanel(this);
95
96 mPanelPrivate = (PanelView) findViewById(R.id.private_tabs);
97 mPanelPrivate.setTabsPanel(this);
98
99 mPanelRemote = (PanelView) findViewById(R.id.synced_tabs);
100 mPanelRemote.setTabsPanel(this);
101
102 mFooter = (RelativeLayout) findViewById(R.id.tabs_panel_footer);
103
104 mAddTab = (ImageButton) findViewById(R.id.add_tab);
105 mAddTab.setOnClickListener(new Button.OnClickListener() {
106 @Override
107 public void onClick(View v) {
108 TabsPanel.this.addTab();
109 }
110 });
111
112 mTabWidget = (IconTabWidget) findViewById(R.id.tab_widget);
113
114 mTabWidget.addTab(R.drawable.tabs_normal, R.string.tabs_normal);
115 mTabWidget.addTab(R.drawable.tabs_private, R.string.tabs_private);
116
117 if (!GeckoProfile.get(mContext).inGuestMode()) {
118 mTabWidget.addTab(R.drawable.tabs_synced, R.string.tabs_synced);
119 }
120
121 mTabWidget.setTabSelectionListener(this);
122 }
123
124 private void addTab() {
125 if (mCurrentPanel == Panel.NORMAL_TABS) {
126 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "new_tab");
127 mActivity.addTab();
128 } else {
129 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "new_private_tab");
130 mActivity.addPrivateTab();
131 }
132
133 mActivity.autoHideTabs();
134 }
135
136 @Override
137 public void onTabChanged(int index) {
138 if (index == 0)
139 show(Panel.NORMAL_TABS, false);
140 else if (index == 1)
141 show(Panel.PRIVATE_TABS, false);
142 else
143 show(Panel.REMOTE_TABS, false);
144 }
145
146 private static int getTabContainerHeight(TabsListContainer listContainer) {
147 Resources resources = listContainer.getContext().getResources();
148
149 PanelView panelView = listContainer.getCurrentPanelView();
150 if (panelView != null && !panelView.shouldExpand()) {
151 return resources.getDimensionPixelSize(R.dimen.tabs_tray_horizontal_height);
152 }
153
154 int actionBarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height);
155 int screenHeight = resources.getDisplayMetrics().heightPixels;
156
157 Rect windowRect = new Rect();
158 listContainer.getWindowVisibleDisplayFrame(windowRect);
159 int windowHeight = windowRect.bottom - windowRect.top;
160
161 // The web content area should have at least 1.5x the height of the action bar.
162 // The tabs panel shouldn't take less than 50% of the screen height and can take
163 // up to 80% of the window height.
164 return (int) Math.max(screenHeight * 0.5f,
165 Math.min(windowHeight - 2.5f * actionBarHeight, windowHeight * 0.8f) - actionBarHeight);
166 }
167
168 @Override
169 public void onAttachedToWindow() {
170 super.onAttachedToWindow();
171 mTheme.addListener(this);
172 }
173
174 @Override
175 public void onDetachedFromWindow() {
176 super.onDetachedFromWindow();
177 mTheme.removeListener(this);
178 }
179
180 @Override
181 public void onLightweightThemeChanged() {
182 final int background = getResources().getColor(R.color.background_tabs);
183 final LightweightThemeDrawable drawable = mTheme.getColorDrawable(this, background, true);
184 if (drawable == null)
185 return;
186
187 drawable.setAlpha(34, 0);
188 setBackgroundDrawable(drawable);
189 }
190
191 @Override
192 public void onLightweightThemeReset() {
193 setBackgroundColor(getContext().getResources().getColor(R.color.background_tabs));
194 }
195
196 @Override
197 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
198 super.onLayout(changed, left, top, right, bottom);
199 onLightweightThemeChanged();
200 }
201
202 // Tabs List Container holds the ListView
203 public static class TabsListContainer extends FrameLayout {
204 public TabsListContainer(Context context, AttributeSet attrs) {
205 super(context, attrs);
206 }
207
208 public PanelView getCurrentPanelView() {
209 final int childCount = getChildCount();
210 for (int i = 0; i < childCount; i++) {
211 View child = getChildAt(i);
212 if (!(child instanceof PanelView))
213 continue;
214
215 if (child.getVisibility() == View.VISIBLE)
216 return (PanelView) child;
217 }
218
219 return null;
220 }
221
222 @Override
223 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
224 if (!GeckoAppShell.getGeckoInterface().hasTabsSideBar()) {
225 int heightSpec = MeasureSpec.makeMeasureSpec(getTabContainerHeight(TabsListContainer.this), MeasureSpec.EXACTLY);
226 super.onMeasure(widthMeasureSpec, heightSpec);
227 } else {
228 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
229 }
230 }
231 }
232
233 // Tabs Panel Toolbar contains the Buttons
234 public static class TabsPanelToolbar extends LinearLayout
235 implements LightweightTheme.OnChangeListener {
236 private final LightweightTheme mTheme;
237
238 public TabsPanelToolbar(Context context, AttributeSet attrs) {
239 super(context, attrs);
240 mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
241
242 setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,
243 (int) context.getResources().getDimension(R.dimen.browser_toolbar_height)));
244
245 setOrientation(LinearLayout.HORIZONTAL);
246 }
247
248 @Override
249 public void onAttachedToWindow() {
250 super.onAttachedToWindow();
251 mTheme.addListener(this);
252 }
253
254 @Override
255 public void onDetachedFromWindow() {
256 super.onDetachedFromWindow();
257 mTheme.removeListener(this);
258 }
259
260 @Override
261 public void onLightweightThemeChanged() {
262 final int background = getResources().getColor(R.color.background_tabs);
263 final LightweightThemeDrawable drawable = mTheme.getColorDrawable(this, background);
264 if (drawable == null)
265 return;
266
267 drawable.setAlpha(34, 34);
268 setBackgroundDrawable(drawable);
269 }
270
271 @Override
272 public void onLightweightThemeReset() {
273 setBackgroundColor(getContext().getResources().getColor(R.color.background_tabs));
274 }
275
276 @Override
277 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
278 super.onLayout(changed, left, top, right, bottom);
279 onLightweightThemeChanged();
280 }
281 }
282
283 public void show(Panel panel) {
284 show(panel, true);
285 }
286
287 public void show(Panel panel, boolean shouldResize) {
288 if (!isShown())
289 setVisibility(View.VISIBLE);
290
291 if (mPanel != null) {
292 // Hide the old panel.
293 mPanel.hide();
294 }
295
296 final boolean showAnimation = !mVisible;
297 mVisible = true;
298 mCurrentPanel = panel;
299
300 int index = panel.ordinal();
301 mTabWidget.setCurrentTab(index);
302
303 if (index == 0) {
304 mPanel = mPanelNormal;
305 } else if (index == 1) {
306 mPanel = mPanelPrivate;
307 } else {
308 mPanel = mPanelRemote;
309 }
310
311 mPanel.show();
312
313 if (mCurrentPanel == Panel.REMOTE_TABS) {
314 if (mFooter != null)
315 mFooter.setVisibility(View.GONE);
316
317 mAddTab.setVisibility(View.INVISIBLE);
318 } else {
319 if (mFooter != null)
320 mFooter.setVisibility(View.VISIBLE);
321
322 mAddTab.setVisibility(View.VISIBLE);
323 mAddTab.setImageLevel(index);
324 }
325
326 if (shouldResize) {
327 if (isSideBar()) {
328 if (showAnimation)
329 dispatchLayoutChange(getWidth(), getHeight());
330 } else {
331 int actionBarHeight = mContext.getResources().getDimensionPixelSize(R.dimen.browser_toolbar_height);
332 int height = actionBarHeight + getTabContainerHeight(mTabsContainer);
333 dispatchLayoutChange(getWidth(), height);
334 }
335 }
336 }
337
338 public void hide() {
339 if (mVisible) {
340 mVisible = false;
341 dispatchLayoutChange(0, 0);
342 }
343 }
344
345 public void refresh() {
346 removeAllViews();
347
348 LayoutInflater.from(mContext).inflate(R.layout.tabs_panel, this);
349 initialize();
350
351 if (mVisible)
352 show(mCurrentPanel);
353 }
354
355 public void autoHidePanel() {
356 mActivity.autoHideTabs();
357 }
358
359 @Override
360 public boolean isShown() {
361 return mVisible;
362 }
363
364 public boolean isSideBar() {
365 return mIsSideBar;
366 }
367
368 public void setIsSideBar(boolean isSideBar) {
369 mIsSideBar = isSideBar;
370 }
371
372 public Panel getCurrentPanel() {
373 return mCurrentPanel;
374 }
375
376 public void prepareTabsAnimation(PropertyAnimator animator) {
377 // Not worth doing this on pre-Honeycomb without proper
378 // hardware accelerated animations.
379 if (Build.VERSION.SDK_INT < 11) {
380 return;
381 }
382
383 final Resources resources = getContext().getResources();
384 final int toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height);
385 final int tabsPanelWidth = getWidth();
386
387 if (mVisible) {
388 if (mIsSideBar) {
389 ViewHelper.setTranslationX(mHeader, -tabsPanelWidth);
390 } else {
391 ViewHelper.setTranslationY(mHeader, -toolbarHeight);
392 }
393
394 if (mIsSideBar) {
395 ViewHelper.setTranslationX(mTabsContainer, -tabsPanelWidth);
396 } else {
397 ViewHelper.setTranslationY(mTabsContainer, -toolbarHeight);
398 ViewHelper.setAlpha(mTabsContainer, 0);
399 }
400
401 // The footer view is only present on the sidebar
402 if (mIsSideBar) {
403 ViewHelper.setTranslationX(mFooter, -tabsPanelWidth);
404 }
405 }
406
407 if (mIsSideBar) {
408 final int translationX = (mVisible ? 0 : -tabsPanelWidth);
409
410 animator.attach(mTabsContainer,
411 PropertyAnimator.Property.TRANSLATION_X,
412 translationX);
413 animator.attach(mHeader,
414 PropertyAnimator.Property.TRANSLATION_X,
415 translationX);
416 animator.attach(mFooter,
417 PropertyAnimator.Property.TRANSLATION_X,
418 translationX);
419 } else {
420 final int translationY = (mVisible ? 0 : -toolbarHeight);
421
422 animator.attach(mTabsContainer,
423 PropertyAnimator.Property.ALPHA,
424 mVisible ? 1.0f : 0.0f);
425 animator.attach(mTabsContainer,
426 PropertyAnimator.Property.TRANSLATION_Y,
427 translationY);
428 animator.attach(mHeader,
429 PropertyAnimator.Property.TRANSLATION_Y,
430 translationY);
431 }
432
433 mHeader.setLayerType(View.LAYER_TYPE_HARDWARE, null);
434 mTabsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
435 }
436
437 public void finishTabsAnimation() {
438 if (Build.VERSION.SDK_INT < 11) {
439 return;
440 }
441
442 mHeader.setLayerType(View.LAYER_TYPE_NONE, null);
443 mTabsContainer.setLayerType(View.LAYER_TYPE_NONE, null);
444
445 // If the tray is now hidden, call hide() on current panel and unset it as the current panel
446 // to avoid hide() being called again when the tray is opened next.
447 if (!mVisible && mPanel != null) {
448 mPanel.hide();
449 mPanel = null;
450 }
451 }
452
453 public void setTabsLayoutChangeListener(TabsLayoutChangeListener listener) {
454 mLayoutChangeListener = listener;
455 }
456
457 private void dispatchLayoutChange(int width, int height) {
458 if (mLayoutChangeListener != null)
459 mLayoutChangeListener.onTabsLayoutChange(width, height);
460 }
461 }

mercurial