| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.browser; |
| |
| import com.android.browser.UI.DropdownChangeListener; |
| import com.android.browser.UrlInputView.UrlInputListener; |
| import com.android.browser.autocomplete.SuggestedTextController.TextChangeWatcher; |
| |
| import android.animation.Animator; |
| import android.animation.Animator.AnimatorListener; |
| import android.animation.ObjectAnimator; |
| import android.app.SearchManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.graphics.Bitmap; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.speech.RecognizerResultsIntent; |
| import android.text.TextUtils; |
| import android.view.ContextThemeWrapper; |
| import android.view.Gravity; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.View.OnFocusChangeListener; |
| import android.view.ViewGroup; |
| import android.view.animation.Animation; |
| import android.view.animation.Animation.AnimationListener; |
| import android.view.animation.AnimationUtils; |
| import android.webkit.WebView; |
| import android.widget.AbsoluteLayout; |
| import android.widget.ArrayAdapter; |
| import android.widget.Button; |
| import android.widget.FrameLayout; |
| import android.widget.ImageView; |
| import android.widget.PopupMenu; |
| import android.widget.PopupMenu.OnMenuItemClickListener; |
| import android.widget.ProgressBar; |
| import android.widget.RelativeLayout; |
| import android.widget.Spinner; |
| import android.widget.TextView; |
| |
| import java.util.List; |
| |
| /** |
| * Base class for a title bar used by the browser. |
| */ |
| public class TitleBarBase extends RelativeLayout |
| implements OnClickListener, OnFocusChangeListener, UrlInputListener, |
| TextChangeWatcher, DeviceAccountLogin.AutoLoginCallback, |
| OnMenuItemClickListener { |
| |
| protected static final int PROGRESS_MAX = 100; |
| |
| // These need to be set by the subclass. |
| protected ImageView mFavicon; |
| protected ImageView mLockIcon; |
| |
| protected UiController mUiController; |
| protected BaseUi mBaseUi; |
| protected FrameLayout mParent; |
| protected PageProgressView mProgress; |
| protected UrlInputView mUrlInput; |
| protected boolean mInVoiceMode; |
| protected View mContainer; |
| private View mUaSwitcher; |
| |
| // Auto-login UI |
| protected View mAutoLogin; |
| protected Spinner mAutoLoginAccount; |
| protected Button mAutoLoginLogin; |
| protected ProgressBar mAutoLoginProgress; |
| protected TextView mAutoLoginError; |
| protected View mAutoLoginCancel; |
| protected DeviceAccountLogin mAutoLoginHandler; |
| protected ArrayAdapter<String> mAccountsAdapter; |
| protected boolean mUseQuickControls; |
| |
| //state |
| protected boolean mShowing; |
| protected boolean mInLoad; |
| protected boolean mSkipTitleBarAnimations; |
| private Animator mTitleBarAnimator; |
| |
| public TitleBarBase(Context context, UiController controller, BaseUi ui, |
| FrameLayout parent) { |
| super(context, null); |
| mUiController = controller; |
| mBaseUi = ui; |
| mParent = parent; |
| } |
| |
| protected void initLayout(Context context, int layoutId) { |
| LayoutInflater factory = LayoutInflater.from(context); |
| factory.inflate(layoutId, this); |
| mContainer = findViewById(R.id.taburlbar); |
| mProgress = (PageProgressView) findViewById(R.id.progress); |
| mUrlInput = (UrlInputView) findViewById(R.id.url); |
| mLockIcon = (ImageView) findViewById(R.id.lock); |
| mUrlInput.setUrlInputListener(this); |
| mUrlInput.setController(mUiController); |
| mUrlInput.setOnFocusChangeListener(this); |
| mUrlInput.setSelectAllOnFocus(true); |
| mUrlInput.addQueryTextWatcher(this); |
| mAutoLogin = findViewById(R.id.autologin); |
| mAutoLoginAccount = (Spinner) findViewById(R.id.autologin_account); |
| mAutoLoginLogin = (Button) findViewById(R.id.autologin_login); |
| mAutoLoginLogin.setOnClickListener(this); |
| mAutoLoginProgress = (ProgressBar) findViewById(R.id.autologin_progress); |
| mAutoLoginError = (TextView) findViewById(R.id.autologin_error); |
| mAutoLoginCancel = mAutoLogin.findViewById(R.id.autologin_close); |
| mAutoLoginCancel.setOnClickListener(this); |
| } |
| |
| protected void setupUrlInput() { |
| } |
| |
| protected void setUseQuickControls(boolean use) { |
| mUseQuickControls = use; |
| setLayoutParams(makeLayoutParams()); |
| } |
| |
| void setShowProgressOnly(boolean progress) { |
| if (progress && !inAutoLogin()) { |
| mContainer.setVisibility(View.GONE); |
| } else { |
| mContainer.setVisibility(View.VISIBLE); |
| } |
| } |
| |
| void setSkipTitleBarAnimations(boolean skip) { |
| mSkipTitleBarAnimations = skip; |
| } |
| |
| void show() { |
| if (mUseQuickControls) { |
| mParent.addView(this); |
| } else { |
| if (!mSkipTitleBarAnimations) { |
| cancelTitleBarAnimation(false); |
| int visibleHeight = getVisibleTitleHeight(); |
| float startPos = (-getEmbeddedHeight() + visibleHeight); |
| if (getTranslationY() != 0) { |
| startPos = Math.max(startPos, getTranslationY()); |
| } |
| mTitleBarAnimator = ObjectAnimator.ofFloat(this, |
| "translationY", |
| startPos, 0); |
| mTitleBarAnimator.start(); |
| } |
| mBaseUi.setTitleGravity(Gravity.TOP); |
| } |
| mShowing = true; |
| } |
| |
| void hide() { |
| if (mUseQuickControls) { |
| mParent.removeView(this); |
| } else { |
| if (!mSkipTitleBarAnimations) { |
| cancelTitleBarAnimation(false); |
| int visibleHeight = getVisibleTitleHeight(); |
| mTitleBarAnimator = ObjectAnimator.ofFloat(this, |
| "translationY", getTranslationY(), |
| (-getEmbeddedHeight() + visibleHeight)); |
| mTitleBarAnimator.addListener(mHideTileBarAnimatorListener); |
| mTitleBarAnimator.start(); |
| } else { |
| mBaseUi.setTitleGravity(Gravity.NO_GRAVITY); |
| } |
| } |
| mShowing = false; |
| } |
| |
| boolean isShowing() { |
| return mShowing; |
| } |
| |
| void cancelTitleBarAnimation(boolean reset) { |
| if (mTitleBarAnimator != null) { |
| mTitleBarAnimator.cancel(); |
| mTitleBarAnimator = null; |
| } |
| if (reset) { |
| setTranslationY(0); |
| } |
| } |
| |
| private AnimatorListener mHideTileBarAnimatorListener = new AnimatorListener() { |
| |
| boolean mWasCanceled; |
| @Override |
| public void onAnimationStart(Animator animation) { |
| mWasCanceled = false; |
| } |
| |
| @Override |
| public void onAnimationRepeat(Animator animation) { |
| } |
| |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| if (!mWasCanceled) { |
| setTranslationY(0); |
| } |
| mBaseUi.setTitleGravity(Gravity.NO_GRAVITY); |
| } |
| |
| @Override |
| public void onAnimationCancel(Animator animation) { |
| mWasCanceled = true; |
| } |
| }; |
| |
| private int getVisibleTitleHeight() { |
| Tab tab = mBaseUi.getActiveTab(); |
| WebView webview = tab != null ? tab.getWebView() : null; |
| return webview != null ? webview.getVisibleTitleHeight() : 0; |
| } |
| |
| /** |
| * Update the progress, from 0 to 100. |
| */ |
| void setProgress(int newProgress) { |
| if (newProgress >= PROGRESS_MAX) { |
| mProgress.setProgress(PageProgressView.MAX_PROGRESS); |
| mProgress.setVisibility(View.GONE); |
| mInLoad = false; |
| onProgressStopped(); |
| // check if needs to be hidden |
| if (!isEditingUrl() && !inAutoLogin()) { |
| hide(); |
| if (mUseQuickControls) { |
| setShowProgressOnly(false); |
| } |
| } |
| } else { |
| if (!mInLoad) { |
| mProgress.setVisibility(View.VISIBLE); |
| mInLoad = true; |
| onProgressStarted(); |
| } |
| mProgress.setProgress(newProgress * PageProgressView.MAX_PROGRESS |
| / PROGRESS_MAX); |
| if (!mShowing) { |
| if (mUseQuickControls && !isEditingUrl()) { |
| setShowProgressOnly(true); |
| } |
| show(); |
| } |
| } |
| } |
| |
| protected void onProgressStarted() { |
| } |
| |
| protected void onProgressStopped() { |
| } |
| |
| /* package */ void setLock(Drawable d) { |
| assert mLockIcon != null; |
| if (null == d) { |
| mLockIcon.setVisibility(View.GONE); |
| } else { |
| mLockIcon.setImageDrawable(d); |
| mLockIcon.setVisibility(View.VISIBLE); |
| } |
| } |
| |
| /* package */ void setFavicon(Bitmap icon) { |
| mFavicon.setImageDrawable(mBaseUi.getFaviconDrawable(icon)); |
| } |
| |
| public int getEmbeddedHeight() { |
| int height = mContainer.getHeight(); |
| if (mAutoLogin.getVisibility() == View.VISIBLE) { |
| height += mAutoLogin.getHeight(); |
| } |
| return height; |
| } |
| |
| protected void updateAutoLogin(Tab tab, boolean animate) { |
| DeviceAccountLogin login = tab.getDeviceAccountLogin(); |
| if (login != null) { |
| mAutoLoginHandler = login; |
| ContextThemeWrapper wrapper = new ContextThemeWrapper(mContext, |
| android.R.style.Theme_Holo_Light); |
| mAccountsAdapter = new ArrayAdapter<String>(wrapper, |
| android.R.layout.simple_spinner_item, login.getAccountNames()); |
| mAccountsAdapter.setDropDownViewResource( |
| android.R.layout.simple_spinner_dropdown_item); |
| mAutoLoginAccount.setAdapter(mAccountsAdapter); |
| mAutoLoginAccount.setSelection(0); |
| mAutoLoginAccount.setEnabled(true); |
| mAutoLoginLogin.setEnabled(true); |
| mAutoLoginProgress.setVisibility(View.INVISIBLE); |
| mAutoLoginError.setVisibility(View.GONE); |
| switch (login.getState()) { |
| case DeviceAccountLogin.PROCESSING: |
| mAutoLoginAccount.setEnabled(false); |
| mAutoLoginLogin.setEnabled(false); |
| mAutoLoginProgress.setVisibility(View.VISIBLE); |
| break; |
| case DeviceAccountLogin.FAILED: |
| mAutoLoginProgress.setVisibility(View.INVISIBLE); |
| mAutoLoginError.setVisibility(View.VISIBLE); |
| break; |
| case DeviceAccountLogin.INITIAL: |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| showAutoLogin(animate); |
| } else { |
| hideAutoLogin(animate); |
| } |
| } |
| |
| protected void showAutoLogin(boolean animate) { |
| if (mUseQuickControls) { |
| mBaseUi.showTitleBar(); |
| } |
| mAutoLogin.setVisibility(View.VISIBLE); |
| if (animate) { |
| mAutoLogin.startAnimation(AnimationUtils.loadAnimation( |
| getContext(), R.anim.autologin_enter)); |
| } |
| } |
| |
| protected void hideAutoLogin(boolean animate) { |
| mAutoLoginHandler = null; |
| if (mUseQuickControls) { |
| mBaseUi.hideTitleBar(); |
| mAutoLogin.setVisibility(View.GONE); |
| mBaseUi.refreshWebView(); |
| } else { |
| if (animate) { |
| Animation anim = AnimationUtils.loadAnimation(getContext(), |
| R.anim.autologin_exit); |
| anim.setAnimationListener(new AnimationListener() { |
| @Override |
| public void onAnimationEnd(Animation a) { |
| mAutoLogin.setVisibility(View.GONE); |
| mBaseUi.refreshWebView(); |
| } |
| |
| @Override |
| public void onAnimationStart(Animation a) { |
| } |
| |
| @Override |
| public void onAnimationRepeat(Animation a) { |
| } |
| }); |
| mAutoLogin.startAnimation(anim); |
| } else if (mAutoLogin.getAnimation() == null) { |
| mAutoLogin.setVisibility(View.GONE); |
| mBaseUi.refreshWebView(); |
| } |
| } |
| } |
| |
| @Override |
| public void loginFailed() { |
| mAutoLoginAccount.setEnabled(true); |
| mAutoLoginLogin.setEnabled(true); |
| mAutoLoginProgress.setVisibility(View.INVISIBLE); |
| mAutoLoginError.setVisibility(View.VISIBLE); |
| } |
| |
| |
| protected boolean inAutoLogin() { |
| return mAutoLoginHandler != null; |
| } |
| |
| public void setUaSwitcher(View v) { |
| if (mUaSwitcher != null) { |
| mUaSwitcher.setOnClickListener(null); |
| } |
| mUaSwitcher = v; |
| mUaSwitcher.setOnClickListener(this); |
| } |
| |
| @Override |
| public void onClick(View v) { |
| if (mAutoLoginCancel == v) { |
| if (mAutoLoginHandler != null) { |
| mAutoLoginHandler.cancel(); |
| mAutoLoginHandler = null; |
| } |
| hideAutoLogin(true); |
| } else if (mAutoLoginLogin == v) { |
| if (mAutoLoginHandler != null) { |
| mAutoLoginAccount.setEnabled(false); |
| mAutoLoginLogin.setEnabled(false); |
| mAutoLoginProgress.setVisibility(View.VISIBLE); |
| mAutoLoginError.setVisibility(View.GONE); |
| mAutoLoginHandler.login( |
| mAutoLoginAccount.getSelectedItemPosition(), this); |
| } |
| } else if (mUaSwitcher == v) { |
| BrowserSettings settings = BrowserSettings.getInstance(); |
| WebView web = getCurrentWebView(); |
| if (web == null) return; |
| boolean desktop = settings.hasDesktopUseragent(web); |
| PopupMenu popup = new PopupMenu(mContext, mUaSwitcher); |
| Menu menu = popup.getMenu(); |
| popup.getMenuInflater().inflate(R.menu.ua_switcher, menu); |
| menu.findItem(R.id.ua_mobile_menu_id).setChecked(!desktop); |
| menu.findItem(R.id.ua_desktop_menu_id).setChecked(desktop); |
| popup.setOnMenuItemClickListener(this); |
| popup.show(); |
| } |
| } |
| |
| @Override |
| public boolean onMenuItemClick(MenuItem item) { |
| BrowserSettings settings = BrowserSettings.getInstance(); |
| WebView web = getCurrentWebView(); |
| if (web == null) return false; |
| boolean desktop = settings.hasDesktopUseragent(web); |
| switch (item.getItemId()) { |
| case R.id.ua_mobile_menu_id: |
| if (desktop) { |
| settings.toggleDesktopUseragent(web); |
| web.loadUrl(web.getOriginalUrl()); |
| } |
| return true; |
| case R.id.ua_desktop_menu_id: |
| if (!desktop) { |
| settings.toggleDesktopUseragent(web); |
| web.loadUrl(web.getOriginalUrl()); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void onFocusChange(View view, boolean hasFocus) { |
| // if losing focus and not in touch mode, leave as is |
| if (hasFocus || view.isInTouchMode() || mUrlInput.needsUpdate()) { |
| setFocusState(hasFocus); |
| } |
| if (hasFocus) { |
| mUrlInput.forceIme(); |
| if (mInVoiceMode) { |
| mUrlInput.forceFilter(); |
| } |
| } else if (!mUrlInput.needsUpdate()) { |
| mUrlInput.dismissDropDown(); |
| mUrlInput.hideIME(); |
| if (mUrlInput.getText().length() == 0) { |
| Tab currentTab = mUiController.getTabControl().getCurrentTab(); |
| if (currentTab != null) { |
| mUrlInput.setText(currentTab.getUrl(), false); |
| } |
| } |
| } |
| mUrlInput.clearNeedsUpdate(); |
| } |
| |
| protected void setFocusState(boolean focus) { |
| if (focus) { |
| updateSearchMode(false); |
| } |
| } |
| |
| protected void updateSearchMode(boolean userEdited) { |
| setSearchMode(!userEdited || TextUtils.isEmpty(mUrlInput.getUserText())); |
| } |
| |
| protected void setSearchMode(boolean voiceSearchEnabled) {} |
| |
| boolean isEditingUrl() { |
| return mUrlInput.hasFocus(); |
| } |
| |
| void stopEditingUrl() { |
| mUrlInput.clearFocus(); |
| } |
| |
| void setDisplayTitle(String title) { |
| if (!isEditingUrl()) { |
| mUrlInput.setText(title, false); |
| } |
| } |
| |
| // UrlInput text watcher |
| |
| @Override |
| public void onTextChanged(String newText) { |
| if (mUrlInput.hasFocus()) { |
| // check if input field is empty and adjust voice search state |
| updateSearchMode(true); |
| // clear voice mode when user types |
| setInVoiceMode(false, null); |
| } |
| } |
| |
| // voicesearch |
| |
| public void setInVoiceMode(boolean voicemode, List<String> voiceResults) { |
| mInVoiceMode = voicemode; |
| mUrlInput.setVoiceResults(voiceResults); |
| } |
| |
| void setIncognitoMode(boolean incognito) { |
| mUrlInput.setIncognitoMode(incognito); |
| } |
| |
| void clearCompletions() { |
| mUrlInput.setSuggestedText(null); |
| } |
| |
| // UrlInputListener implementation |
| |
| /** |
| * callback from suggestion dropdown |
| * user selected a suggestion |
| */ |
| @Override |
| public void onAction(String text, String extra, String source) { |
| mUiController.getCurrentTopWebView().requestFocus(); |
| mBaseUi.hideTitleBar(); |
| Intent i = new Intent(); |
| String action = null; |
| if (UrlInputView.VOICE.equals(source)) { |
| action = RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS; |
| source = null; |
| } else { |
| action = Intent.ACTION_SEARCH; |
| } |
| i.setAction(action); |
| i.putExtra(SearchManager.QUERY, text); |
| if (extra != null) { |
| i.putExtra(SearchManager.EXTRA_DATA_KEY, extra); |
| } |
| if (source != null) { |
| Bundle appData = new Bundle(); |
| appData.putString(com.android.common.Search.SOURCE, source); |
| i.putExtra(SearchManager.APP_DATA, appData); |
| } |
| mUiController.handleNewIntent(i); |
| setDisplayTitle(text); |
| } |
| |
| @Override |
| public void onDismiss() { |
| final Tab currentTab = mBaseUi.getActiveTab(); |
| mBaseUi.hideTitleBar(); |
| post(new Runnable() { |
| public void run() { |
| clearFocus(); |
| if ((currentTab != null) && !mInVoiceMode) { |
| setDisplayTitle(currentTab.getUrl()); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * callback from the suggestion dropdown |
| * copy text to input field and stay in edit mode |
| */ |
| @Override |
| public void onCopySuggestion(String text) { |
| mUrlInput.setText(text, true); |
| if (text != null) { |
| mUrlInput.setSelection(text.length()); |
| } |
| } |
| |
| public void setCurrentUrlIsBookmark(boolean isBookmark) { |
| } |
| |
| @Override |
| public boolean dispatchKeyEventPreIme(KeyEvent evt) { |
| if (evt.getKeyCode() == KeyEvent.KEYCODE_BACK) { |
| // catch back key in order to do slightly more cleanup than usual |
| mUrlInput.clearFocus(); |
| return true; |
| } |
| return super.dispatchKeyEventPreIme(evt); |
| } |
| |
| protected WebView getCurrentWebView() { |
| Tab t = mBaseUi.getActiveTab(); |
| if (t != null) { |
| return t.getWebView(); |
| } else { |
| return null; |
| } |
| } |
| |
| void registerDropdownChangeListener(DropdownChangeListener d) { |
| mUrlInput.registerDropdownChangeListener(d); |
| } |
| |
| /** |
| * called from the Ui when the user wants to edit |
| * @param clearInput clear the input field |
| */ |
| void startEditingUrl(boolean clearInput) { |
| // editing takes preference of progress |
| mContainer.setVisibility(View.VISIBLE); |
| if (mUseQuickControls) { |
| mProgress.setVisibility(View.GONE); |
| } |
| if (!mUrlInput.hasFocus()) { |
| mUrlInput.requestFocus(); |
| } |
| if (clearInput) { |
| mUrlInput.setText(""); |
| } else if (mInVoiceMode) { |
| mUrlInput.showDropDown(); |
| } |
| } |
| |
| private ViewGroup.LayoutParams makeLayoutParams() { |
| if (mUseQuickControls) { |
| return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, |
| LayoutParams.WRAP_CONTENT); |
| } else { |
| return new AbsoluteLayout.LayoutParams( |
| LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, |
| 0, 0); |
| } |
| } |
| |
| } |