|
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 |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.toolbar; |
|
7 |
|
8 import java.util.ArrayList; |
|
9 import java.util.Arrays; |
|
10 import java.util.EnumSet; |
|
11 import java.util.List; |
|
12 |
|
13 import org.json.JSONObject; |
|
14 import org.mozilla.gecko.BrowserApp; |
|
15 import org.mozilla.gecko.GeckoAppShell; |
|
16 import org.mozilla.gecko.GeckoApplication; |
|
17 import org.mozilla.gecko.GeckoProfile; |
|
18 import org.mozilla.gecko.LightweightTheme; |
|
19 import org.mozilla.gecko.R; |
|
20 import org.mozilla.gecko.Tab; |
|
21 import org.mozilla.gecko.Tabs; |
|
22 import org.mozilla.gecko.Telemetry; |
|
23 import org.mozilla.gecko.TelemetryContract; |
|
24 import org.mozilla.gecko.animation.PropertyAnimator; |
|
25 import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener; |
|
26 import org.mozilla.gecko.animation.ViewHelper; |
|
27 import org.mozilla.gecko.menu.GeckoMenu; |
|
28 import org.mozilla.gecko.menu.MenuPopup; |
|
29 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener; |
|
30 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener; |
|
31 import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags; |
|
32 import org.mozilla.gecko.util.Clipboard; |
|
33 import org.mozilla.gecko.util.GeckoEventListener; |
|
34 import org.mozilla.gecko.util.HardwareUtils; |
|
35 import org.mozilla.gecko.util.MenuUtils; |
|
36 import org.mozilla.gecko.widget.ThemedImageButton; |
|
37 import org.mozilla.gecko.widget.ThemedImageView; |
|
38 import org.mozilla.gecko.widget.ThemedRelativeLayout; |
|
39 |
|
40 import android.content.Context; |
|
41 import android.content.res.Resources; |
|
42 import android.graphics.Bitmap; |
|
43 import android.graphics.drawable.BitmapDrawable; |
|
44 import android.graphics.drawable.Drawable; |
|
45 import android.graphics.drawable.StateListDrawable; |
|
46 import android.os.Build; |
|
47 import android.text.TextUtils; |
|
48 import android.util.AttributeSet; |
|
49 import android.util.Log; |
|
50 import android.view.ContextMenu; |
|
51 import android.view.KeyEvent; |
|
52 import android.view.LayoutInflater; |
|
53 import android.view.MenuInflater; |
|
54 import android.view.MotionEvent; |
|
55 import android.view.View; |
|
56 import android.view.ViewGroup; |
|
57 import android.view.animation.AccelerateInterpolator; |
|
58 import android.view.animation.Interpolator; |
|
59 import android.view.inputmethod.InputMethodManager; |
|
60 import android.widget.Button; |
|
61 import android.widget.ImageButton; |
|
62 import android.widget.ImageView; |
|
63 import android.widget.LinearLayout; |
|
64 import android.widget.PopupWindow; |
|
65 import android.widget.RelativeLayout; |
|
66 |
|
67 /** |
|
68 * {@code BrowserToolbar} is single entry point for users of the toolbar |
|
69 * subsystem i.e. this should be the only import outside the 'toolbar' |
|
70 * package. |
|
71 * |
|
72 * {@code BrowserToolbar} serves at the single event bus for all |
|
73 * sub-components in the toolbar. It tracks tab events and gecko messages |
|
74 * and update the state of its inner components accordingly. |
|
75 * |
|
76 * It has two states, display and edit, which are controlled by |
|
77 * ToolbarEditLayout and ToolbarDisplayLayout. In display state, the toolbar |
|
78 * displays the current state for the selected tab. In edit state, it shows |
|
79 * a text entry for searching bookmarks/history. {@code BrowserToolbar} |
|
80 * provides public API to enter, cancel, and commit the edit state as well |
|
81 * as a set of listeners to allow {@code BrowserToolbar} users to react |
|
82 * to state changes accordingly. |
|
83 */ |
|
84 public class BrowserToolbar extends ThemedRelativeLayout |
|
85 implements Tabs.OnTabsChangedListener, |
|
86 GeckoMenu.ActionItemBarPresenter, |
|
87 GeckoEventListener { |
|
88 private static final String LOGTAG = "GeckoToolbar"; |
|
89 |
|
90 public interface OnActivateListener { |
|
91 public void onActivate(); |
|
92 } |
|
93 |
|
94 public interface OnCommitListener { |
|
95 public void onCommit(); |
|
96 } |
|
97 |
|
98 public interface OnDismissListener { |
|
99 public void onDismiss(); |
|
100 } |
|
101 |
|
102 public interface OnFilterListener { |
|
103 public void onFilter(String searchText, AutocompleteHandler handler); |
|
104 } |
|
105 |
|
106 public interface OnStartEditingListener { |
|
107 public void onStartEditing(); |
|
108 } |
|
109 |
|
110 public interface OnStopEditingListener { |
|
111 public void onStopEditing(); |
|
112 } |
|
113 |
|
114 private enum UIMode { |
|
115 EDIT, |
|
116 DISPLAY |
|
117 } |
|
118 |
|
119 enum ForwardButtonAnimation { |
|
120 SHOW, |
|
121 HIDE |
|
122 } |
|
123 |
|
124 private ToolbarDisplayLayout urlDisplayLayout; |
|
125 private ToolbarEditLayout urlEditLayout; |
|
126 private View urlBarEntry; |
|
127 private RelativeLayout.LayoutParams urlBarEntryDefaultLayoutParams; |
|
128 private RelativeLayout.LayoutParams urlBarEntryShrunkenLayoutParams; |
|
129 private ImageView urlBarTranslatingEdge; |
|
130 private boolean isSwitchingTabs; |
|
131 private ShapedButton tabsButton; |
|
132 private ImageButton backButton; |
|
133 private ImageButton forwardButton; |
|
134 |
|
135 private ToolbarProgressView progressBar; |
|
136 private TabCounter tabsCounter; |
|
137 private ThemedImageButton menuButton; |
|
138 private ThemedImageView menuIcon; |
|
139 private LinearLayout actionItemBar; |
|
140 private MenuPopup menuPopup; |
|
141 private List<View> focusOrder; |
|
142 |
|
143 private final ThemedImageView editCancel; |
|
144 |
|
145 private boolean shouldShrinkURLBar = false; |
|
146 |
|
147 private OnActivateListener activateListener; |
|
148 private OnFocusChangeListener focusChangeListener; |
|
149 private OnStartEditingListener startEditingListener; |
|
150 private OnStopEditingListener stopEditingListener; |
|
151 |
|
152 private final BrowserApp activity; |
|
153 private boolean hasSoftMenuButton; |
|
154 |
|
155 private UIMode uiMode; |
|
156 private boolean isAnimatingEntry; |
|
157 |
|
158 private int urlBarViewOffset; |
|
159 private int defaultForwardMargin; |
|
160 |
|
161 private static final Interpolator buttonsInterpolator = new AccelerateInterpolator(); |
|
162 |
|
163 private static final int FORWARD_ANIMATION_DURATION = 450; |
|
164 |
|
165 private final LightweightTheme theme; |
|
166 |
|
167 public BrowserToolbar(Context context) { |
|
168 this(context, null); |
|
169 } |
|
170 |
|
171 public BrowserToolbar(Context context, AttributeSet attrs) { |
|
172 super(context, attrs); |
|
173 theme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); |
|
174 |
|
175 // BrowserToolbar is attached to BrowserApp only. |
|
176 activity = (BrowserApp) context; |
|
177 |
|
178 // Inflate the content. |
|
179 LayoutInflater.from(context).inflate(R.layout.browser_toolbar, this); |
|
180 |
|
181 Tabs.registerOnTabsChangedListener(this); |
|
182 isSwitchingTabs = true; |
|
183 isAnimatingEntry = false; |
|
184 |
|
185 registerEventListener("Reader:Click"); |
|
186 registerEventListener("Reader:LongClick"); |
|
187 |
|
188 final Resources res = getResources(); |
|
189 urlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left); |
|
190 defaultForwardMargin = res.getDimensionPixelSize(R.dimen.forward_default_offset); |
|
191 urlDisplayLayout = (ToolbarDisplayLayout) findViewById(R.id.display_layout); |
|
192 urlBarEntry = findViewById(R.id.url_bar_entry); |
|
193 urlEditLayout = (ToolbarEditLayout) findViewById(R.id.edit_layout); |
|
194 |
|
195 urlBarEntryDefaultLayoutParams = (RelativeLayout.LayoutParams) urlBarEntry.getLayoutParams(); |
|
196 // API level 19 adds a RelativeLayout.LayoutParams copy constructor, so we explicitly cast |
|
197 // to ViewGroup.MarginLayoutParams to ensure consistency across platforms. |
|
198 urlBarEntryShrunkenLayoutParams = new RelativeLayout.LayoutParams( |
|
199 (ViewGroup.MarginLayoutParams) urlBarEntryDefaultLayoutParams); |
|
200 urlBarEntryShrunkenLayoutParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.edit_layout); |
|
201 urlBarEntryShrunkenLayoutParams.rightMargin = 0; |
|
202 |
|
203 // This will clip the translating edge's image at 60% of its width |
|
204 urlBarTranslatingEdge = (ImageView) findViewById(R.id.url_bar_translating_edge); |
|
205 if (urlBarTranslatingEdge != null) { |
|
206 urlBarTranslatingEdge.getDrawable().setLevel(6000); |
|
207 } |
|
208 |
|
209 tabsButton = (ShapedButton) findViewById(R.id.tabs); |
|
210 tabsCounter = (TabCounter) findViewById(R.id.tabs_counter); |
|
211 if (Build.VERSION.SDK_INT >= 11) { |
|
212 tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null); |
|
213 } |
|
214 |
|
215 backButton = (ImageButton) findViewById(R.id.back); |
|
216 setButtonEnabled(backButton, false); |
|
217 forwardButton = (ImageButton) findViewById(R.id.forward); |
|
218 setButtonEnabled(forwardButton, false); |
|
219 |
|
220 menuButton = (ThemedImageButton) findViewById(R.id.menu); |
|
221 menuIcon = (ThemedImageView) findViewById(R.id.menu_icon); |
|
222 actionItemBar = (LinearLayout) findViewById(R.id.menu_items); |
|
223 hasSoftMenuButton = !HardwareUtils.hasMenuButton(); |
|
224 |
|
225 editCancel = (ThemedImageView) findViewById(R.id.edit_cancel); |
|
226 |
|
227 // We use different layouts on phones and tablets, so adjust the focus |
|
228 // order appropriately. |
|
229 focusOrder = new ArrayList<View>(); |
|
230 if (HardwareUtils.isTablet()) { |
|
231 focusOrder.addAll(Arrays.asList(tabsButton, backButton, forwardButton, this)); |
|
232 focusOrder.addAll(urlDisplayLayout.getFocusOrder()); |
|
233 focusOrder.addAll(Arrays.asList(actionItemBar, menuButton)); |
|
234 } else { |
|
235 focusOrder.add(this); |
|
236 focusOrder.addAll(urlDisplayLayout.getFocusOrder()); |
|
237 focusOrder.addAll(Arrays.asList(tabsButton, menuButton)); |
|
238 } |
|
239 |
|
240 setUIMode(UIMode.DISPLAY); |
|
241 } |
|
242 |
|
243 @Override |
|
244 public void onAttachedToWindow() { |
|
245 super.onAttachedToWindow(); |
|
246 |
|
247 setOnClickListener(new Button.OnClickListener() { |
|
248 @Override |
|
249 public void onClick(View v) { |
|
250 if (activateListener != null) { |
|
251 activateListener.onActivate(); |
|
252 } |
|
253 } |
|
254 }); |
|
255 |
|
256 setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { |
|
257 @Override |
|
258 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { |
|
259 // We don't the context menu while editing |
|
260 if (isEditing()) { |
|
261 return; |
|
262 } |
|
263 |
|
264 // NOTE: Use MenuUtils.safeSetVisible because some actions might |
|
265 // be on the Page menu |
|
266 |
|
267 MenuInflater inflater = activity.getMenuInflater(); |
|
268 inflater.inflate(R.menu.titlebar_contextmenu, menu); |
|
269 |
|
270 String clipboard = Clipboard.getText(); |
|
271 if (TextUtils.isEmpty(clipboard)) { |
|
272 menu.findItem(R.id.pasteandgo).setVisible(false); |
|
273 menu.findItem(R.id.paste).setVisible(false); |
|
274 } |
|
275 |
|
276 Tab tab = Tabs.getInstance().getSelectedTab(); |
|
277 if (tab != null) { |
|
278 String url = tab.getURL(); |
|
279 if (url == null) { |
|
280 menu.findItem(R.id.copyurl).setVisible(false); |
|
281 menu.findItem(R.id.add_to_launcher).setVisible(false); |
|
282 MenuUtils.safeSetVisible(menu, R.id.share, false); |
|
283 } |
|
284 |
|
285 MenuUtils.safeSetVisible(menu, R.id.subscribe, tab.hasFeeds()); |
|
286 MenuUtils.safeSetVisible(menu, R.id.add_search_engine, tab.hasOpenSearch()); |
|
287 } else { |
|
288 // if there is no tab, remove anything tab dependent |
|
289 menu.findItem(R.id.copyurl).setVisible(false); |
|
290 menu.findItem(R.id.add_to_launcher).setVisible(false); |
|
291 MenuUtils.safeSetVisible(menu, R.id.share, false); |
|
292 MenuUtils.safeSetVisible(menu, R.id.subscribe, false); |
|
293 MenuUtils.safeSetVisible(menu, R.id.add_search_engine, false); |
|
294 } |
|
295 |
|
296 MenuUtils.safeSetVisible(menu, R.id.share, !GeckoProfile.get(getContext()).inGuestMode()); |
|
297 } |
|
298 }); |
|
299 |
|
300 urlDisplayLayout.setOnStopListener(new OnStopListener() { |
|
301 @Override |
|
302 public Tab onStop() { |
|
303 final Tab tab = Tabs.getInstance().getSelectedTab(); |
|
304 if (tab != null) { |
|
305 tab.doStop(); |
|
306 return tab; |
|
307 } |
|
308 |
|
309 return null; |
|
310 } |
|
311 }); |
|
312 |
|
313 urlDisplayLayout.setOnTitleChangeListener(new OnTitleChangeListener() { |
|
314 @Override |
|
315 public void onTitleChange(CharSequence title) { |
|
316 final String contentDescription; |
|
317 if (title != null) { |
|
318 contentDescription = title.toString(); |
|
319 } else { |
|
320 contentDescription = activity.getString(R.string.url_bar_default_text); |
|
321 } |
|
322 |
|
323 // The title and content description should |
|
324 // always be sync. |
|
325 setContentDescription(contentDescription); |
|
326 } |
|
327 }); |
|
328 |
|
329 urlEditLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() { |
|
330 @Override |
|
331 public void onFocusChange(View v, boolean hasFocus) { |
|
332 // This will select the url bar when entering editing mode. |
|
333 setSelected(hasFocus); |
|
334 if (focusChangeListener != null) { |
|
335 focusChangeListener.onFocusChange(v, hasFocus); |
|
336 } |
|
337 } |
|
338 }); |
|
339 |
|
340 tabsButton.setOnClickListener(new Button.OnClickListener() { |
|
341 @Override |
|
342 public void onClick(View v) { |
|
343 toggleTabs(); |
|
344 } |
|
345 }); |
|
346 tabsButton.setImageLevel(0); |
|
347 |
|
348 backButton.setOnClickListener(new Button.OnClickListener() { |
|
349 @Override |
|
350 public void onClick(View view) { |
|
351 Tabs.getInstance().getSelectedTab().doBack(); |
|
352 } |
|
353 }); |
|
354 backButton.setOnLongClickListener(new Button.OnLongClickListener() { |
|
355 @Override |
|
356 public boolean onLongClick(View view) { |
|
357 return Tabs.getInstance().getSelectedTab().showBackHistory(); |
|
358 } |
|
359 }); |
|
360 |
|
361 forwardButton.setOnClickListener(new Button.OnClickListener() { |
|
362 @Override |
|
363 public void onClick(View view) { |
|
364 Tabs.getInstance().getSelectedTab().doForward(); |
|
365 } |
|
366 }); |
|
367 forwardButton.setOnLongClickListener(new Button.OnLongClickListener() { |
|
368 @Override |
|
369 public boolean onLongClick(View view) { |
|
370 return Tabs.getInstance().getSelectedTab().showForwardHistory(); |
|
371 } |
|
372 }); |
|
373 |
|
374 if (editCancel != null) { |
|
375 editCancel.setOnClickListener(new OnClickListener() { |
|
376 @Override |
|
377 public void onClick(View v) { |
|
378 // If we exit editing mode during the animation, |
|
379 // we're put into an inconsistent state (bug 1017276). |
|
380 if (!isAnimatingEntry) { |
|
381 Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, |
|
382 TelemetryContract.Method.ACTIONBAR, |
|
383 getResources().getResourceEntryName(editCancel.getId())); |
|
384 cancelEdit(); |
|
385 } |
|
386 } |
|
387 }); |
|
388 } |
|
389 |
|
390 if (hasSoftMenuButton) { |
|
391 menuButton.setVisibility(View.VISIBLE); |
|
392 menuIcon.setVisibility(View.VISIBLE); |
|
393 |
|
394 menuButton.setOnClickListener(new Button.OnClickListener() { |
|
395 @Override |
|
396 public void onClick(View view) { |
|
397 activity.openOptionsMenu(); |
|
398 } |
|
399 }); |
|
400 } |
|
401 } |
|
402 |
|
403 public void setProgressBar(ToolbarProgressView progressBar) { |
|
404 this.progressBar = progressBar; |
|
405 } |
|
406 |
|
407 public void refresh() { |
|
408 urlDisplayLayout.dismissSiteIdentityPopup(); |
|
409 } |
|
410 |
|
411 public boolean onBackPressed() { |
|
412 // If we exit editing mode during the animation, |
|
413 // we're put into an inconsistent state (bug 1017276). |
|
414 if (isEditing() && !isAnimatingEntry) { |
|
415 Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, |
|
416 TelemetryContract.Method.BACK); |
|
417 cancelEdit(); |
|
418 return true; |
|
419 } |
|
420 |
|
421 return urlDisplayLayout.dismissSiteIdentityPopup(); |
|
422 } |
|
423 |
|
424 @Override |
|
425 public boolean onTouchEvent(MotionEvent event) { |
|
426 // If the motion event has occured below the toolbar (due to the scroll |
|
427 // offset), let it pass through to the page. |
|
428 if (event != null && event.getY() > getHeight() + ViewHelper.getTranslationY(this)) { |
|
429 return false; |
|
430 } |
|
431 |
|
432 return super.onTouchEvent(event); |
|
433 } |
|
434 |
|
435 @Override |
|
436 protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
|
437 super.onSizeChanged(w, h, oldw, oldh); |
|
438 |
|
439 if (h != oldh) { |
|
440 // Post this to happen outside of onSizeChanged, as this may cause |
|
441 // a layout change and relayouts within a layout change don't work. |
|
442 post(new Runnable() { |
|
443 @Override |
|
444 public void run() { |
|
445 activity.refreshToolbarHeight(); |
|
446 } |
|
447 }); |
|
448 } |
|
449 } |
|
450 |
|
451 @Override |
|
452 public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { |
|
453 Log.d(LOGTAG, "onTabChanged: " + msg); |
|
454 final Tabs tabs = Tabs.getInstance(); |
|
455 |
|
456 // These conditions are split into three phases: |
|
457 // * Always do first |
|
458 // * Handling specific to the selected tab |
|
459 // * Always do afterwards. |
|
460 |
|
461 switch (msg) { |
|
462 case ADDED: |
|
463 case CLOSED: |
|
464 updateTabCount(tabs.getDisplayCount()); |
|
465 break; |
|
466 case RESTORED: |
|
467 // TabCount fixup after OOM |
|
468 case SELECTED: |
|
469 urlDisplayLayout.dismissSiteIdentityPopup(); |
|
470 updateTabCount(tabs.getDisplayCount()); |
|
471 isSwitchingTabs = true; |
|
472 break; |
|
473 } |
|
474 |
|
475 if (tabs.isSelectedTab(tab)) { |
|
476 final EnumSet<UpdateFlags> flags = EnumSet.noneOf(UpdateFlags.class); |
|
477 |
|
478 // Progress-related handling |
|
479 switch (msg) { |
|
480 case START: |
|
481 updateProgressVisibility(tab, Tab.LOAD_PROGRESS_INIT); |
|
482 // Fall through. |
|
483 case ADDED: |
|
484 case LOCATION_CHANGE: |
|
485 case LOAD_ERROR: |
|
486 case LOADED: |
|
487 case STOP: |
|
488 flags.add(UpdateFlags.PROGRESS); |
|
489 if (progressBar.getVisibility() == View.VISIBLE) { |
|
490 progressBar.animateProgress(tab.getLoadProgress()); |
|
491 } |
|
492 break; |
|
493 |
|
494 case SELECTED: |
|
495 flags.add(UpdateFlags.PROGRESS); |
|
496 updateProgressVisibility(); |
|
497 break; |
|
498 } |
|
499 |
|
500 switch (msg) { |
|
501 case STOP: |
|
502 // Reset the title in case we haven't navigated |
|
503 // to a new page yet. |
|
504 flags.add(UpdateFlags.TITLE); |
|
505 // Fall through. |
|
506 case START: |
|
507 case CLOSED: |
|
508 case ADDED: |
|
509 updateBackButton(tab); |
|
510 updateForwardButton(tab); |
|
511 break; |
|
512 |
|
513 case SELECTED: |
|
514 flags.add(UpdateFlags.PRIVATE_MODE); |
|
515 setPrivateMode(tab.isPrivate()); |
|
516 // Fall through. |
|
517 case LOAD_ERROR: |
|
518 flags.add(UpdateFlags.TITLE); |
|
519 // Fall through. |
|
520 case LOCATION_CHANGE: |
|
521 // A successful location change will cause Tab to notify |
|
522 // us of a title change, so we don't update the title here. |
|
523 flags.add(UpdateFlags.FAVICON); |
|
524 flags.add(UpdateFlags.SITE_IDENTITY); |
|
525 |
|
526 updateBackButton(tab); |
|
527 updateForwardButton(tab); |
|
528 break; |
|
529 |
|
530 case TITLE: |
|
531 flags.add(UpdateFlags.TITLE); |
|
532 break; |
|
533 |
|
534 case FAVICON: |
|
535 flags.add(UpdateFlags.FAVICON); |
|
536 break; |
|
537 |
|
538 case SECURITY_CHANGE: |
|
539 flags.add(UpdateFlags.SITE_IDENTITY); |
|
540 break; |
|
541 } |
|
542 |
|
543 if (!flags.isEmpty()) { |
|
544 updateDisplayLayout(tab, flags); |
|
545 } |
|
546 } |
|
547 |
|
548 switch (msg) { |
|
549 case SELECTED: |
|
550 case LOAD_ERROR: |
|
551 case LOCATION_CHANGE: |
|
552 isSwitchingTabs = false; |
|
553 } |
|
554 } |
|
555 |
|
556 private void updateProgressVisibility() { |
|
557 final Tab selectedTab = Tabs.getInstance().getSelectedTab(); |
|
558 updateProgressVisibility(selectedTab, selectedTab.getLoadProgress()); |
|
559 } |
|
560 |
|
561 private void updateProgressVisibility(Tab selectedTab, int progress) { |
|
562 if (!isEditing() && selectedTab.getState() == Tab.STATE_LOADING) { |
|
563 progressBar.setProgress(progress); |
|
564 progressBar.setVisibility(View.VISIBLE); |
|
565 } else { |
|
566 progressBar.setVisibility(View.GONE); |
|
567 } |
|
568 } |
|
569 |
|
570 private boolean isVisible() { |
|
571 return ViewHelper.getTranslationY(this) == 0; |
|
572 } |
|
573 |
|
574 @Override |
|
575 public void setNextFocusDownId(int nextId) { |
|
576 super.setNextFocusDownId(nextId); |
|
577 tabsButton.setNextFocusDownId(nextId); |
|
578 backButton.setNextFocusDownId(nextId); |
|
579 forwardButton.setNextFocusDownId(nextId); |
|
580 urlDisplayLayout.setNextFocusDownId(nextId); |
|
581 menuButton.setNextFocusDownId(nextId); |
|
582 } |
|
583 |
|
584 private int getUrlBarEntryTranslation() { |
|
585 if (editCancel == null) { |
|
586 // We are on tablet, and there is no animation so return a translation of 0. |
|
587 return 0; |
|
588 } |
|
589 |
|
590 // Find the distance from the right-edge of the url bar (where we're translating from) to |
|
591 // the left-edge of the cancel button (where we're translating to; note that the cancel |
|
592 // button must be laid out, i.e. not View.GONE). |
|
593 final LayoutParams lp = (LayoutParams) urlEditLayout.getLayoutParams(); |
|
594 return editCancel.getLeft() - lp.leftMargin - urlBarEntry.getRight(); |
|
595 } |
|
596 |
|
597 private int getUrlBarCurveTranslation() { |
|
598 return getWidth() - tabsButton.getLeft(); |
|
599 } |
|
600 |
|
601 private boolean canDoBack(Tab tab) { |
|
602 return (tab.canDoBack() && !isEditing()); |
|
603 } |
|
604 |
|
605 private boolean canDoForward(Tab tab) { |
|
606 return (tab.canDoForward() && !isEditing()); |
|
607 } |
|
608 |
|
609 private void addTab() { |
|
610 activity.addTab(); |
|
611 } |
|
612 |
|
613 private void toggleTabs() { |
|
614 if (activity.areTabsShown()) { |
|
615 if (activity.hasTabsSideBar()) |
|
616 activity.hideTabs(); |
|
617 } else { |
|
618 // hide the virtual keyboard |
|
619 InputMethodManager imm = |
|
620 (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); |
|
621 imm.hideSoftInputFromWindow(tabsButton.getWindowToken(), 0); |
|
622 |
|
623 Tab tab = Tabs.getInstance().getSelectedTab(); |
|
624 if (tab != null) { |
|
625 if (!tab.isPrivate()) |
|
626 activity.showNormalTabs(); |
|
627 else |
|
628 activity.showPrivateTabs(); |
|
629 } |
|
630 } |
|
631 } |
|
632 |
|
633 private void updateTabCountAndAnimate(int count) { |
|
634 // Don't animate if the toolbar is hidden. |
|
635 if (!isVisible()) { |
|
636 updateTabCount(count); |
|
637 return; |
|
638 } |
|
639 |
|
640 // If toolbar is in edit mode on a phone, this means the entry is expanded |
|
641 // and the tabs button is translated offscreen. Don't trigger tabs counter |
|
642 // updates until the tabs button is back on screen. |
|
643 // See stopEditing() |
|
644 if (!isEditing() || HardwareUtils.isTablet()) { |
|
645 tabsCounter.setCount(count); |
|
646 |
|
647 tabsButton.setContentDescription((count > 1) ? |
|
648 activity.getString(R.string.num_tabs, count) : |
|
649 activity.getString(R.string.one_tab)); |
|
650 } |
|
651 } |
|
652 |
|
653 private void updateTabCount(int count) { |
|
654 // If toolbar is in edit mode on a phone, this means the entry is expanded |
|
655 // and the tabs button is translated offscreen. Don't trigger tabs counter |
|
656 // updates until the tabs button is back on screen. |
|
657 // See stopEditing() |
|
658 if (isEditing() && !HardwareUtils.isTablet()) { |
|
659 return; |
|
660 } |
|
661 |
|
662 // Set TabCounter based on visibility |
|
663 if (isVisible() && ViewHelper.getAlpha(tabsCounter) != 0 && !isEditing()) { |
|
664 tabsCounter.setCountWithAnimation(count); |
|
665 } else { |
|
666 tabsCounter.setCount(count); |
|
667 } |
|
668 |
|
669 // Update A11y information |
|
670 tabsButton.setContentDescription((count > 1) ? |
|
671 activity.getString(R.string.num_tabs, count) : |
|
672 activity.getString(R.string.one_tab)); |
|
673 } |
|
674 |
|
675 private void updateDisplayLayout(Tab tab, EnumSet<UpdateFlags> flags) { |
|
676 if (isSwitchingTabs) { |
|
677 flags.add(UpdateFlags.DISABLE_ANIMATIONS); |
|
678 } |
|
679 |
|
680 urlDisplayLayout.updateFromTab(tab, flags); |
|
681 |
|
682 if (flags.contains(UpdateFlags.TITLE)) { |
|
683 if (!isEditing()) { |
|
684 urlEditLayout.setText(tab.getURL()); |
|
685 } |
|
686 } |
|
687 |
|
688 if (flags.contains(UpdateFlags.PROGRESS)) { |
|
689 updateFocusOrder(); |
|
690 } |
|
691 } |
|
692 |
|
693 private void updateFocusOrder() { |
|
694 View prevView = null; |
|
695 |
|
696 // If the element that has focus becomes disabled or invisible, focus |
|
697 // is given to the URL bar. |
|
698 boolean needsNewFocus = false; |
|
699 |
|
700 for (View view : focusOrder) { |
|
701 if (view.getVisibility() != View.VISIBLE || !view.isEnabled()) { |
|
702 if (view.hasFocus()) { |
|
703 needsNewFocus = true; |
|
704 } |
|
705 continue; |
|
706 } |
|
707 |
|
708 if (view == actionItemBar) { |
|
709 final int childCount = actionItemBar.getChildCount(); |
|
710 for (int child = 0; child < childCount; child++) { |
|
711 View childView = actionItemBar.getChildAt(child); |
|
712 if (prevView != null) { |
|
713 childView.setNextFocusLeftId(prevView.getId()); |
|
714 prevView.setNextFocusRightId(childView.getId()); |
|
715 } |
|
716 prevView = childView; |
|
717 } |
|
718 } else { |
|
719 if (prevView != null) { |
|
720 view.setNextFocusLeftId(prevView.getId()); |
|
721 prevView.setNextFocusRightId(view.getId()); |
|
722 } |
|
723 prevView = view; |
|
724 } |
|
725 } |
|
726 |
|
727 if (needsNewFocus) { |
|
728 requestFocus(); |
|
729 } |
|
730 } |
|
731 |
|
732 public void onEditSuggestion(String suggestion) { |
|
733 if (!isEditing()) { |
|
734 return; |
|
735 } |
|
736 |
|
737 urlEditLayout.onEditSuggestion(suggestion); |
|
738 } |
|
739 |
|
740 public void setTitle(CharSequence title) { |
|
741 urlDisplayLayout.setTitle(title); |
|
742 } |
|
743 |
|
744 public void prepareTabsAnimation(PropertyAnimator animator, boolean tabsAreShown) { |
|
745 if (!tabsAreShown) { |
|
746 PropertyAnimator buttonsAnimator = |
|
747 new PropertyAnimator(animator.getDuration(), buttonsInterpolator); |
|
748 |
|
749 buttonsAnimator.attach(tabsCounter, |
|
750 PropertyAnimator.Property.ALPHA, |
|
751 1.0f); |
|
752 |
|
753 if (hasSoftMenuButton && !HardwareUtils.isTablet()) { |
|
754 buttonsAnimator.attach(menuIcon, |
|
755 PropertyAnimator.Property.ALPHA, |
|
756 1.0f); |
|
757 } |
|
758 |
|
759 buttonsAnimator.start(); |
|
760 |
|
761 return; |
|
762 } |
|
763 |
|
764 ViewHelper.setAlpha(tabsCounter, 0.0f); |
|
765 |
|
766 if (hasSoftMenuButton && !HardwareUtils.isTablet()) { |
|
767 ViewHelper.setAlpha(menuIcon, 0.0f); |
|
768 } |
|
769 } |
|
770 |
|
771 public void finishTabsAnimation(boolean tabsAreShown) { |
|
772 if (tabsAreShown) { |
|
773 return; |
|
774 } |
|
775 |
|
776 PropertyAnimator animator = new PropertyAnimator(150); |
|
777 |
|
778 animator.attach(tabsCounter, |
|
779 PropertyAnimator.Property.ALPHA, |
|
780 1.0f); |
|
781 |
|
782 if (hasSoftMenuButton && !HardwareUtils.isTablet()) { |
|
783 animator.attach(menuIcon, |
|
784 PropertyAnimator.Property.ALPHA, |
|
785 1.0f); |
|
786 } |
|
787 |
|
788 animator.start(); |
|
789 } |
|
790 |
|
791 public void setOnActivateListener(OnActivateListener listener) { |
|
792 activateListener = listener; |
|
793 } |
|
794 |
|
795 public void setOnCommitListener(OnCommitListener listener) { |
|
796 urlEditLayout.setOnCommitListener(listener); |
|
797 } |
|
798 |
|
799 public void setOnDismissListener(OnDismissListener listener) { |
|
800 urlEditLayout.setOnDismissListener(listener); |
|
801 } |
|
802 |
|
803 public void setOnFilterListener(OnFilterListener listener) { |
|
804 urlEditLayout.setOnFilterListener(listener); |
|
805 } |
|
806 |
|
807 public void setOnFocusChangeListener(OnFocusChangeListener listener) { |
|
808 focusChangeListener = listener; |
|
809 } |
|
810 |
|
811 public void setOnStartEditingListener(OnStartEditingListener listener) { |
|
812 startEditingListener = listener; |
|
813 } |
|
814 |
|
815 public void setOnStopEditingListener(OnStopEditingListener listener) { |
|
816 stopEditingListener = listener; |
|
817 } |
|
818 |
|
819 private void showUrlEditLayout() { |
|
820 setUrlEditLayoutVisibility(true, null); |
|
821 } |
|
822 |
|
823 private void showUrlEditLayout(PropertyAnimator animator) { |
|
824 setUrlEditLayoutVisibility(true, animator); |
|
825 } |
|
826 |
|
827 private void hideUrlEditLayout() { |
|
828 setUrlEditLayoutVisibility(false, null); |
|
829 } |
|
830 |
|
831 private void hideUrlEditLayout(PropertyAnimator animator) { |
|
832 setUrlEditLayoutVisibility(false, animator); |
|
833 } |
|
834 |
|
835 private void setUrlEditLayoutVisibility(final boolean showEditLayout, PropertyAnimator animator) { |
|
836 if (showEditLayout) { |
|
837 urlEditLayout.prepareShowAnimation(animator); |
|
838 } |
|
839 |
|
840 if (animator == null) { |
|
841 final View viewToShow = (showEditLayout ? urlEditLayout : urlDisplayLayout); |
|
842 final View viewToHide = (showEditLayout ? urlDisplayLayout : urlEditLayout); |
|
843 |
|
844 viewToHide.setVisibility(View.GONE); |
|
845 viewToShow.setVisibility(View.VISIBLE); |
|
846 |
|
847 final int cancelVisibility = (showEditLayout ? View.VISIBLE : View.INVISIBLE); |
|
848 setCancelVisibility(cancelVisibility); |
|
849 return; |
|
850 } |
|
851 |
|
852 animator.addPropertyAnimationListener(new PropertyAnimationListener() { |
|
853 @Override |
|
854 public void onPropertyAnimationStart() { |
|
855 if (!showEditLayout) { |
|
856 urlEditLayout.setVisibility(View.GONE); |
|
857 urlDisplayLayout.setVisibility(View.VISIBLE); |
|
858 |
|
859 setCancelVisibility(View.INVISIBLE); |
|
860 } |
|
861 } |
|
862 |
|
863 @Override |
|
864 public void onPropertyAnimationEnd() { |
|
865 if (showEditLayout) { |
|
866 urlDisplayLayout.setVisibility(View.GONE); |
|
867 urlEditLayout.setVisibility(View.VISIBLE); |
|
868 |
|
869 setCancelVisibility(View.VISIBLE); |
|
870 } |
|
871 } |
|
872 }); |
|
873 } |
|
874 |
|
875 private void setCancelVisibility(final int visibility) { |
|
876 if (editCancel != null) { |
|
877 editCancel.setVisibility(visibility); |
|
878 } |
|
879 } |
|
880 |
|
881 /** |
|
882 * Disables and dims all toolbar elements which are not |
|
883 * related to editing mode. |
|
884 */ |
|
885 private void updateChildrenForEditing() { |
|
886 // This is for the tablet UI only |
|
887 if (!HardwareUtils.isTablet()) { |
|
888 return; |
|
889 } |
|
890 |
|
891 // Disable toolbar elemens while in editing mode |
|
892 final boolean enabled = !isEditing(); |
|
893 |
|
894 // This alpha value has to be in sync with the one used |
|
895 // in setButtonEnabled(). |
|
896 final float alpha = (enabled ? 1.0f : 0.24f); |
|
897 |
|
898 if (!enabled) { |
|
899 tabsCounter.onEnterEditingMode(); |
|
900 } |
|
901 |
|
902 tabsButton.setEnabled(enabled); |
|
903 ViewHelper.setAlpha(tabsCounter, alpha); |
|
904 menuButton.setEnabled(enabled); |
|
905 ViewHelper.setAlpha(menuIcon, alpha); |
|
906 |
|
907 final int actionItemsCount = actionItemBar.getChildCount(); |
|
908 for (int i = 0; i < actionItemsCount; i++) { |
|
909 actionItemBar.getChildAt(i).setEnabled(enabled); |
|
910 } |
|
911 ViewHelper.setAlpha(actionItemBar, alpha); |
|
912 |
|
913 final Tab tab = Tabs.getInstance().getSelectedTab(); |
|
914 if (tab != null) { |
|
915 setButtonEnabled(backButton, canDoBack(tab)); |
|
916 setButtonEnabled(forwardButton, canDoForward(tab)); |
|
917 |
|
918 // Once the editing mode is finished, we have to ensure that the |
|
919 // forward button slides away if necessary. This is because we might |
|
920 // have only disabled it (without hiding it) when the toolbar entered |
|
921 // editing mode. |
|
922 if (!isEditing()) { |
|
923 animateForwardButton(canDoForward(tab) ? |
|
924 ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE); |
|
925 } |
|
926 } |
|
927 } |
|
928 |
|
929 private void setUIMode(final UIMode uiMode) { |
|
930 this.uiMode = uiMode; |
|
931 urlEditLayout.setEnabled(uiMode == UIMode.EDIT); |
|
932 } |
|
933 |
|
934 /** |
|
935 * Returns whether or not the URL bar is in editing mode (url bar is expanded, hiding the new |
|
936 * tab button). Note that selection state is independent of editing mode. |
|
937 */ |
|
938 public boolean isEditing() { |
|
939 return (uiMode == UIMode.EDIT); |
|
940 } |
|
941 |
|
942 public boolean isAnimating() { |
|
943 return isAnimatingEntry; |
|
944 } |
|
945 |
|
946 public void startEditing(String url, PropertyAnimator animator) { |
|
947 if (isEditing()) { |
|
948 return; |
|
949 } |
|
950 |
|
951 urlEditLayout.setText(url != null ? url : ""); |
|
952 |
|
953 setUIMode(UIMode.EDIT); |
|
954 updateChildrenForEditing(); |
|
955 |
|
956 updateProgressVisibility(); |
|
957 |
|
958 if (startEditingListener != null) { |
|
959 startEditingListener.onStartEditing(); |
|
960 } |
|
961 |
|
962 final int curveTranslation = getUrlBarCurveTranslation(); |
|
963 final int entryTranslation = getUrlBarEntryTranslation(); |
|
964 shouldShrinkURLBar = (entryTranslation < 0); |
|
965 |
|
966 if (urlBarTranslatingEdge != null) { |
|
967 urlBarTranslatingEdge.setVisibility(View.VISIBLE); |
|
968 if (shouldShrinkURLBar) { |
|
969 urlBarEntry.setLayoutParams(urlBarEntryShrunkenLayoutParams); |
|
970 } |
|
971 } |
|
972 |
|
973 if (Build.VERSION.SDK_INT < 11) { |
|
974 showEditingWithoutAnimation(entryTranslation, curveTranslation); |
|
975 } else if (HardwareUtils.isTablet()) { |
|
976 // No animation. |
|
977 showUrlEditLayout(); |
|
978 } else { |
|
979 showEditingWithPhoneAnimation(animator, entryTranslation, curveTranslation); |
|
980 } |
|
981 } |
|
982 |
|
983 private void showEditingWithoutAnimation(final int entryTranslation, |
|
984 final int curveTranslation) { |
|
985 showUrlEditLayout(); |
|
986 |
|
987 if (urlBarTranslatingEdge != null) { |
|
988 ViewHelper.setTranslationX(urlBarTranslatingEdge, entryTranslation); |
|
989 } |
|
990 |
|
991 // Prevent taps through the editing mode cancel button (bug 1001243). |
|
992 tabsButton.setEnabled(false); |
|
993 |
|
994 ViewHelper.setTranslationX(tabsButton, curveTranslation); |
|
995 ViewHelper.setTranslationX(tabsCounter, curveTranslation); |
|
996 ViewHelper.setTranslationX(actionItemBar, curveTranslation); |
|
997 |
|
998 if (hasSoftMenuButton) { |
|
999 // Prevent tabs through the editing mode cancel button (bug 1001243). |
|
1000 menuButton.setEnabled(false); |
|
1001 |
|
1002 ViewHelper.setTranslationX(menuButton, curveTranslation); |
|
1003 ViewHelper.setTranslationX(menuIcon, curveTranslation); |
|
1004 } |
|
1005 } |
|
1006 |
|
1007 private void showEditingWithPhoneAnimation(final PropertyAnimator animator, |
|
1008 final int entryTranslation, final int curveTranslation) { |
|
1009 if (isAnimatingEntry) |
|
1010 return; |
|
1011 |
|
1012 urlDisplayLayout.prepareStartEditingAnimation(); |
|
1013 |
|
1014 // Slide toolbar elements. |
|
1015 if (urlBarTranslatingEdge != null) { |
|
1016 animator.attach(urlBarTranslatingEdge, |
|
1017 PropertyAnimator.Property.TRANSLATION_X, |
|
1018 entryTranslation); |
|
1019 } |
|
1020 |
|
1021 animator.attach(tabsButton, |
|
1022 PropertyAnimator.Property.TRANSLATION_X, |
|
1023 curveTranslation); |
|
1024 animator.attach(tabsCounter, |
|
1025 PropertyAnimator.Property.TRANSLATION_X, |
|
1026 curveTranslation); |
|
1027 animator.attach(actionItemBar, |
|
1028 PropertyAnimator.Property.TRANSLATION_X, |
|
1029 curveTranslation); |
|
1030 |
|
1031 if (hasSoftMenuButton) { |
|
1032 animator.attach(menuButton, |
|
1033 PropertyAnimator.Property.TRANSLATION_X, |
|
1034 curveTranslation); |
|
1035 |
|
1036 animator.attach(menuIcon, |
|
1037 PropertyAnimator.Property.TRANSLATION_X, |
|
1038 curveTranslation); |
|
1039 } |
|
1040 |
|
1041 showUrlEditLayout(animator); |
|
1042 |
|
1043 animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { |
|
1044 @Override |
|
1045 public void onPropertyAnimationStart() { |
|
1046 } |
|
1047 |
|
1048 @Override |
|
1049 public void onPropertyAnimationEnd() { |
|
1050 isAnimatingEntry = false; |
|
1051 } |
|
1052 }); |
|
1053 |
|
1054 isAnimatingEntry = true; |
|
1055 } |
|
1056 |
|
1057 /** |
|
1058 * Exits edit mode without updating the toolbar title. |
|
1059 * |
|
1060 * @return the url that was entered |
|
1061 */ |
|
1062 public String cancelEdit() { |
|
1063 Telemetry.stopUISession(TelemetryContract.Session.AWESOMESCREEN); |
|
1064 return stopEditing(); |
|
1065 } |
|
1066 |
|
1067 /** |
|
1068 * Exits edit mode, updating the toolbar title with the url that was just entered. |
|
1069 * |
|
1070 * @return the url that was entered |
|
1071 */ |
|
1072 public String commitEdit() { |
|
1073 final String url = stopEditing(); |
|
1074 if (!TextUtils.isEmpty(url)) { |
|
1075 setTitle(url); |
|
1076 } |
|
1077 return url; |
|
1078 } |
|
1079 |
|
1080 private String stopEditing() { |
|
1081 final String url = urlEditLayout.getText(); |
|
1082 if (!isEditing()) { |
|
1083 return url; |
|
1084 } |
|
1085 setUIMode(UIMode.DISPLAY); |
|
1086 |
|
1087 updateChildrenForEditing(); |
|
1088 |
|
1089 if (stopEditingListener != null) { |
|
1090 stopEditingListener.onStopEditing(); |
|
1091 } |
|
1092 |
|
1093 updateProgressVisibility(); |
|
1094 |
|
1095 // The animation looks cleaner if the text in the URL bar is |
|
1096 // not selected so clear the selection by clearing focus. |
|
1097 urlEditLayout.clearFocus(); |
|
1098 |
|
1099 if (Build.VERSION.SDK_INT < 11) { |
|
1100 stopEditingWithoutAnimation(); |
|
1101 } else if (HardwareUtils.isTablet()) { |
|
1102 // No animation. |
|
1103 hideUrlEditLayout(); |
|
1104 } else { |
|
1105 stopEditingWithPhoneAnimation(); |
|
1106 } |
|
1107 |
|
1108 return url; |
|
1109 } |
|
1110 |
|
1111 private void stopEditingWithoutAnimation() { |
|
1112 hideUrlEditLayout(); |
|
1113 |
|
1114 updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); |
|
1115 |
|
1116 if (urlBarTranslatingEdge != null) { |
|
1117 urlBarTranslatingEdge.setVisibility(View.INVISIBLE); |
|
1118 ViewHelper.setTranslationX(urlBarTranslatingEdge, 0); |
|
1119 if (shouldShrinkURLBar) { |
|
1120 urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams); |
|
1121 } |
|
1122 } |
|
1123 |
|
1124 tabsButton.setEnabled(true); |
|
1125 |
|
1126 ViewHelper.setTranslationX(tabsButton, 0); |
|
1127 ViewHelper.setTranslationX(tabsCounter, 0); |
|
1128 ViewHelper.setTranslationX(actionItemBar, 0); |
|
1129 |
|
1130 if (hasSoftMenuButton) { |
|
1131 menuButton.setEnabled(true); |
|
1132 |
|
1133 ViewHelper.setTranslationX(menuButton, 0); |
|
1134 ViewHelper.setTranslationX(menuIcon, 0); |
|
1135 } |
|
1136 } |
|
1137 |
|
1138 private void stopEditingWithPhoneAnimation() { |
|
1139 final PropertyAnimator contentAnimator = new PropertyAnimator(250); |
|
1140 contentAnimator.setUseHardwareLayer(false); |
|
1141 |
|
1142 // Slide the toolbar back to its original size. |
|
1143 if (urlBarTranslatingEdge != null) { |
|
1144 contentAnimator.attach(urlBarTranslatingEdge, |
|
1145 PropertyAnimator.Property.TRANSLATION_X, |
|
1146 0); |
|
1147 } |
|
1148 |
|
1149 contentAnimator.attach(tabsButton, |
|
1150 PropertyAnimator.Property.TRANSLATION_X, |
|
1151 0); |
|
1152 contentAnimator.attach(tabsCounter, |
|
1153 PropertyAnimator.Property.TRANSLATION_X, |
|
1154 0); |
|
1155 contentAnimator.attach(actionItemBar, |
|
1156 PropertyAnimator.Property.TRANSLATION_X, |
|
1157 0); |
|
1158 |
|
1159 if (hasSoftMenuButton) { |
|
1160 contentAnimator.attach(menuButton, |
|
1161 PropertyAnimator.Property.TRANSLATION_X, |
|
1162 0); |
|
1163 |
|
1164 contentAnimator.attach(menuIcon, |
|
1165 PropertyAnimator.Property.TRANSLATION_X, |
|
1166 0); |
|
1167 } |
|
1168 |
|
1169 hideUrlEditLayout(contentAnimator); |
|
1170 |
|
1171 contentAnimator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { |
|
1172 @Override |
|
1173 public void onPropertyAnimationStart() { |
|
1174 } |
|
1175 |
|
1176 @Override |
|
1177 public void onPropertyAnimationEnd() { |
|
1178 if (urlBarTranslatingEdge != null) { |
|
1179 urlBarTranslatingEdge.setVisibility(View.INVISIBLE); |
|
1180 if (shouldShrinkURLBar) { |
|
1181 urlBarEntry.setLayoutParams(urlBarEntryDefaultLayoutParams); |
|
1182 } |
|
1183 } |
|
1184 |
|
1185 PropertyAnimator buttonsAnimator = new PropertyAnimator(300); |
|
1186 urlDisplayLayout.prepareStopEditingAnimation(buttonsAnimator); |
|
1187 buttonsAnimator.start(); |
|
1188 |
|
1189 isAnimatingEntry = false; |
|
1190 |
|
1191 // Trigger animation to update the tabs counter once the |
|
1192 // tabs button is back on screen. |
|
1193 updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); |
|
1194 } |
|
1195 }); |
|
1196 |
|
1197 isAnimatingEntry = true; |
|
1198 contentAnimator.start(); |
|
1199 } |
|
1200 |
|
1201 private void setButtonEnabled(ImageButton button, boolean enabled) { |
|
1202 final Drawable drawable = button.getDrawable(); |
|
1203 if (drawable != null) { |
|
1204 // This alpha value has to be in sync with the one used |
|
1205 // in updateChildrenForEditing(). |
|
1206 drawable.setAlpha(enabled ? 255 : 61); |
|
1207 } |
|
1208 |
|
1209 button.setEnabled(enabled); |
|
1210 } |
|
1211 |
|
1212 public void updateBackButton(Tab tab) { |
|
1213 setButtonEnabled(backButton, canDoBack(tab)); |
|
1214 } |
|
1215 |
|
1216 private void animateForwardButton(final ForwardButtonAnimation animation) { |
|
1217 // If the forward button is not visible, we must be |
|
1218 // in the phone UI. |
|
1219 if (forwardButton.getVisibility() != View.VISIBLE) { |
|
1220 return; |
|
1221 } |
|
1222 |
|
1223 final boolean showing = (animation == ForwardButtonAnimation.SHOW); |
|
1224 |
|
1225 // if the forward button's margin is non-zero, this means it has already |
|
1226 // been animated to be visible¸ and vice-versa. |
|
1227 MarginLayoutParams fwdParams = (MarginLayoutParams) forwardButton.getLayoutParams(); |
|
1228 if ((fwdParams.leftMargin > defaultForwardMargin && showing) || |
|
1229 (fwdParams.leftMargin == defaultForwardMargin && !showing)) { |
|
1230 return; |
|
1231 } |
|
1232 |
|
1233 // We want the forward button to show immediately when switching tabs |
|
1234 final PropertyAnimator forwardAnim = |
|
1235 new PropertyAnimator(isSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION); |
|
1236 final int width = forwardButton.getWidth() / 2; |
|
1237 |
|
1238 forwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { |
|
1239 @Override |
|
1240 public void onPropertyAnimationStart() { |
|
1241 if (!showing) { |
|
1242 // Set the margin before the transition when hiding the forward button. We |
|
1243 // have to do this so that the favicon isn't clipped during the transition |
|
1244 MarginLayoutParams layoutParams = |
|
1245 (MarginLayoutParams) urlDisplayLayout.getLayoutParams(); |
|
1246 layoutParams.leftMargin = 0; |
|
1247 |
|
1248 // Do the same on the URL edit container |
|
1249 layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams(); |
|
1250 layoutParams.leftMargin = 0; |
|
1251 |
|
1252 requestLayout(); |
|
1253 // Note, we already translated the favicon, site security, and text field |
|
1254 // in prepareForwardAnimation, so they should appear to have not moved at |
|
1255 // all at this point. |
|
1256 } |
|
1257 } |
|
1258 |
|
1259 @Override |
|
1260 public void onPropertyAnimationEnd() { |
|
1261 if (showing) { |
|
1262 MarginLayoutParams layoutParams = |
|
1263 (MarginLayoutParams) urlDisplayLayout.getLayoutParams(); |
|
1264 layoutParams.leftMargin = urlBarViewOffset; |
|
1265 |
|
1266 layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams(); |
|
1267 layoutParams.leftMargin = urlBarViewOffset; |
|
1268 } |
|
1269 |
|
1270 urlDisplayLayout.finishForwardAnimation(); |
|
1271 |
|
1272 MarginLayoutParams layoutParams = (MarginLayoutParams) forwardButton.getLayoutParams(); |
|
1273 layoutParams.leftMargin = defaultForwardMargin + (showing ? width : 0); |
|
1274 ViewHelper.setTranslationX(forwardButton, 0); |
|
1275 |
|
1276 requestLayout(); |
|
1277 } |
|
1278 }); |
|
1279 |
|
1280 prepareForwardAnimation(forwardAnim, animation, width); |
|
1281 forwardAnim.start(); |
|
1282 } |
|
1283 |
|
1284 public void updateForwardButton(Tab tab) { |
|
1285 final boolean enabled = canDoForward(tab); |
|
1286 if (forwardButton.isEnabled() == enabled) |
|
1287 return; |
|
1288 |
|
1289 // Save the state on the forward button so that we can skip animations |
|
1290 // when there's nothing to change |
|
1291 setButtonEnabled(forwardButton, enabled); |
|
1292 animateForwardButton(enabled ? ForwardButtonAnimation.SHOW : ForwardButtonAnimation.HIDE); |
|
1293 } |
|
1294 |
|
1295 private void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) { |
|
1296 if (animation == ForwardButtonAnimation.HIDE) { |
|
1297 anim.attach(forwardButton, |
|
1298 PropertyAnimator.Property.TRANSLATION_X, |
|
1299 -width); |
|
1300 anim.attach(forwardButton, |
|
1301 PropertyAnimator.Property.ALPHA, |
|
1302 0); |
|
1303 |
|
1304 } else { |
|
1305 anim.attach(forwardButton, |
|
1306 PropertyAnimator.Property.TRANSLATION_X, |
|
1307 width); |
|
1308 anim.attach(forwardButton, |
|
1309 PropertyAnimator.Property.ALPHA, |
|
1310 1); |
|
1311 } |
|
1312 |
|
1313 urlDisplayLayout.prepareForwardAnimation(anim, animation, width); |
|
1314 } |
|
1315 |
|
1316 @Override |
|
1317 public boolean addActionItem(View actionItem) { |
|
1318 actionItemBar.addView(actionItem); |
|
1319 return true; |
|
1320 } |
|
1321 |
|
1322 @Override |
|
1323 public void removeActionItem(View actionItem) { |
|
1324 actionItemBar.removeView(actionItem); |
|
1325 } |
|
1326 |
|
1327 @Override |
|
1328 public void setPrivateMode(boolean isPrivate) { |
|
1329 super.setPrivateMode(isPrivate); |
|
1330 |
|
1331 tabsButton.setPrivateMode(isPrivate); |
|
1332 menuButton.setPrivateMode(isPrivate); |
|
1333 menuIcon.setPrivateMode(isPrivate); |
|
1334 urlEditLayout.setPrivateMode(isPrivate); |
|
1335 |
|
1336 if (backButton instanceof BackButton) { |
|
1337 ((BackButton) backButton).setPrivateMode(isPrivate); |
|
1338 } |
|
1339 |
|
1340 if (forwardButton instanceof ForwardButton) { |
|
1341 ((ForwardButton) forwardButton).setPrivateMode(isPrivate); |
|
1342 } |
|
1343 } |
|
1344 |
|
1345 public void show() { |
|
1346 setVisibility(View.VISIBLE); |
|
1347 } |
|
1348 |
|
1349 public void hide() { |
|
1350 setVisibility(View.GONE); |
|
1351 } |
|
1352 |
|
1353 public View getDoorHangerAnchor() { |
|
1354 return urlDisplayLayout.getDoorHangerAnchor(); |
|
1355 } |
|
1356 |
|
1357 public void onDestroy() { |
|
1358 Tabs.unregisterOnTabsChangedListener(this); |
|
1359 |
|
1360 unregisterEventListener("Reader:Click"); |
|
1361 unregisterEventListener("Reader:LongClick"); |
|
1362 } |
|
1363 |
|
1364 public boolean openOptionsMenu() { |
|
1365 if (!hasSoftMenuButton) { |
|
1366 return false; |
|
1367 } |
|
1368 |
|
1369 // Initialize the popup. |
|
1370 if (menuPopup == null) { |
|
1371 View panel = activity.getMenuPanel(); |
|
1372 menuPopup = new MenuPopup(activity); |
|
1373 menuPopup.setPanelView(panel); |
|
1374 |
|
1375 menuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { |
|
1376 @Override |
|
1377 public void onDismiss() { |
|
1378 activity.onOptionsMenuClosed(null); |
|
1379 } |
|
1380 }); |
|
1381 } |
|
1382 |
|
1383 GeckoAppShell.getGeckoInterface().invalidateOptionsMenu(); |
|
1384 if (!menuPopup.isShowing()) { |
|
1385 menuPopup.showAsDropDown(menuButton); |
|
1386 } |
|
1387 |
|
1388 return true; |
|
1389 } |
|
1390 |
|
1391 public boolean closeOptionsMenu() { |
|
1392 if (!hasSoftMenuButton) { |
|
1393 return false; |
|
1394 } |
|
1395 |
|
1396 if (menuPopup != null && menuPopup.isShowing()) { |
|
1397 menuPopup.dismiss(); |
|
1398 } |
|
1399 |
|
1400 return true; |
|
1401 } |
|
1402 |
|
1403 private void registerEventListener(String event) { |
|
1404 GeckoAppShell.getEventDispatcher().registerEventListener(event, this); |
|
1405 } |
|
1406 |
|
1407 private void unregisterEventListener(String event) { |
|
1408 GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this); |
|
1409 } |
|
1410 |
|
1411 @Override |
|
1412 public void handleMessage(String event, JSONObject message) { |
|
1413 Log.d(LOGTAG, "handleMessage: " + event); |
|
1414 if (event.equals("Reader:Click")) { |
|
1415 Tab tab = Tabs.getInstance().getSelectedTab(); |
|
1416 if (tab != null) { |
|
1417 tab.toggleReaderMode(); |
|
1418 } |
|
1419 } else if (event.equals("Reader:LongClick")) { |
|
1420 Tab tab = Tabs.getInstance().getSelectedTab(); |
|
1421 if (tab != null) { |
|
1422 tab.addToReadingList(); |
|
1423 } |
|
1424 } |
|
1425 } |
|
1426 |
|
1427 @Override |
|
1428 public void onLightweightThemeChanged() { |
|
1429 Drawable drawable = theme.getDrawable(this); |
|
1430 if (drawable == null) |
|
1431 return; |
|
1432 |
|
1433 StateListDrawable stateList = new StateListDrawable(); |
|
1434 stateList.addState(PRIVATE_STATE_SET, getColorDrawable(R.color.background_private)); |
|
1435 stateList.addState(EMPTY_STATE_SET, drawable); |
|
1436 |
|
1437 setBackgroundDrawable(stateList); |
|
1438 |
|
1439 if (editCancel != null) { |
|
1440 editCancel.onLightweightThemeChanged(); |
|
1441 } |
|
1442 } |
|
1443 |
|
1444 @Override |
|
1445 public void onLightweightThemeReset() { |
|
1446 setBackgroundResource(R.drawable.url_bar_bg); |
|
1447 if (editCancel != null) { |
|
1448 editCancel.onLightweightThemeReset(); |
|
1449 } |
|
1450 } |
|
1451 } |