|
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 } |