mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 769145 - Part 5: Search suggestion opt-in animations. r=lucasr
--HG-- rename : mobile/android/base/resources/layout/awesomebar_suggestion_row.xml => mobile/android/base/resources/layout/awesomebar_suggestion_row.xml.in
This commit is contained in:
parent
d6318a0457
commit
34d3b2814d
52
mobile/android/base/AnimatedHeightLayout.java
Normal file
52
mobile/android/base/AnimatedHeightLayout.java
Normal file
@ -0,0 +1,52 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
public class AnimatedHeightLayout extends RelativeLayout {
|
||||
private static final String LOGTAG = "GeckoAnimatedHeightLayout";
|
||||
private static final int ANIMATION_DURATION = 100;
|
||||
private boolean mAnimating = false;
|
||||
|
||||
public AnimatedHeightLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int oldHeight = getMeasuredHeight();
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int newHeight = getMeasuredHeight();
|
||||
|
||||
if (!mAnimating && oldHeight != 0 && oldHeight != newHeight) {
|
||||
mAnimating = true;
|
||||
setMeasuredDimension(getMeasuredWidth(), oldHeight);
|
||||
|
||||
// Animate the difference of suggestion row height
|
||||
Animation anim = new HeightChangeAnimation(this, oldHeight, newHeight);
|
||||
anim.setDuration(ANIMATION_DURATION);
|
||||
anim.setInterpolator(new DecelerateInterpolator());
|
||||
anim.setAnimationListener(new Animation.AnimationListener() {
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
getLayoutParams().height = LayoutParams.WRAP_CONTENT;
|
||||
mAnimating = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
startAnimation(anim);
|
||||
}
|
||||
}
|
||||
}
|
27
mobile/android/base/HeightChangeAnimation.java
Normal file
27
mobile/android/base/HeightChangeAnimation.java
Normal file
@ -0,0 +1,27 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Transformation;
|
||||
|
||||
public class HeightChangeAnimation extends Animation {
|
||||
int mFromHeight;
|
||||
int mToHeight;
|
||||
View mView;
|
||||
|
||||
public HeightChangeAnimation(View view, int fromHeight, int toHeight) {
|
||||
mView = view;
|
||||
mFromHeight = fromHeight;
|
||||
mToHeight = toHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
mView.getLayoutParams().height = Math.round((mFromHeight * (1 - interpolatedTime)) + (mToHeight * interpolatedTime));
|
||||
mView.requestLayout();
|
||||
}
|
||||
}
|
@ -41,6 +41,7 @@ FENNEC_JAVA_FILES = \
|
||||
AndroidImport.java \
|
||||
AndroidImportPreference.java \
|
||||
AlertNotification.java \
|
||||
AnimatedHeightLayout.java \
|
||||
AwesomeBar.java \
|
||||
AwesomebarResultHandler.java \
|
||||
AwesomeBarTabs.java \
|
||||
@ -86,6 +87,7 @@ FENNEC_JAVA_FILES = \
|
||||
GeckoThread.java \
|
||||
GlobalHistory.java \
|
||||
GeckoViewsFactory.java \
|
||||
HeightChangeAnimation.java \
|
||||
InputMethods.java \
|
||||
LinkPreference.java \
|
||||
LinkTextView.java \
|
||||
@ -201,6 +203,7 @@ FENNEC_PP_JAVA_FILES = \
|
||||
|
||||
FENNEC_PP_XML_FILES = \
|
||||
res/layout/abouthome_content.xml \
|
||||
res/layout/awesomebar_suggestion_row.xml \
|
||||
res/layout/browser_toolbar.xml \
|
||||
res/layout/browser_toolbar_menu.xml \
|
||||
res/layout-land-v14/browser_toolbar.xml \
|
||||
@ -331,7 +334,6 @@ RES_LAYOUT = \
|
||||
res/layout/awesomebar_row.xml \
|
||||
res/layout/awesomebar_suggestion_item.xml \
|
||||
res/layout/awesomebar_suggestion_prompt.xml \
|
||||
res/layout/awesomebar_suggestion_row.xml \
|
||||
res/layout/awesomebar_search.xml \
|
||||
res/layout/awesomebar_tab_indicator.xml \
|
||||
res/layout/awesomebar_tabs.xml \
|
||||
|
@ -39,6 +39,10 @@ public class SuggestClient {
|
||||
// used by robocop for testing; referenced via reflection
|
||||
private boolean mCheckNetwork;
|
||||
|
||||
// used to make suggestions appear instantly after opt-in
|
||||
private String mPrevQuery;
|
||||
private ArrayList<String> mPrevResults;
|
||||
|
||||
public SuggestClient(Context context, String suggestTemplate, int timeout, int maxResults) {
|
||||
mContext = context;
|
||||
mMaxResults = maxResults;
|
||||
@ -55,6 +59,9 @@ public class SuggestClient {
|
||||
* Queries for a given search term and returns an ArrayList of suggestions.
|
||||
*/
|
||||
public ArrayList<String> query(String query) {
|
||||
if (query.equals(mPrevQuery))
|
||||
return mPrevResults;
|
||||
|
||||
ArrayList<String> suggestions = new ArrayList<String>();
|
||||
if (TextUtils.isEmpty(mSuggestTemplate) || TextUtils.isEmpty(query)) {
|
||||
return suggestions;
|
||||
@ -113,6 +120,9 @@ public class SuggestClient {
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Error", e);
|
||||
}
|
||||
|
||||
mPrevQuery = query;
|
||||
mPrevResults = suggestions;
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,10 @@ import android.os.AsyncTask;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
@ -31,8 +35,10 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.FilterQueryProvider;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
@ -50,6 +56,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
|
||||
private static final int SUGGESTION_TIMEOUT = 3000;
|
||||
private static final int SUGGESTION_MAX = 3;
|
||||
private static final int ANIMATION_DURATION = 250;
|
||||
|
||||
private String mSearchTerm;
|
||||
private ArrayList<SearchEngine> mSearchEngines;
|
||||
@ -59,6 +66,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
private AwesomeBarCursorAdapter mCursorAdapter = null;
|
||||
private boolean mTelemetrySent = false;
|
||||
private LinearLayout mAllPagesView;
|
||||
private boolean mAnimateSuggestions;
|
||||
private View mSuggestionsOptInPrompt;
|
||||
|
||||
private class SearchEntryViewHolder {
|
||||
@ -145,6 +153,17 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for suggestions, but don't show them yet.
|
||||
*/
|
||||
private void primeSuggestions() {
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
mSuggestClient.query(mSearchTerm);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void filterSuggestions(String searchTerm) {
|
||||
// cancel previous query
|
||||
if (mSuggestTask != null) {
|
||||
@ -341,7 +360,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
SearchEntryViewHolder viewHolder = null;
|
||||
|
||||
if (convertView == null) {
|
||||
convertView = getInflater().inflate(R.layout.awesomebar_suggestion_row, null);
|
||||
convertView = getInflater().inflate(R.layout.awesomebar_suggestion_row, getListView(), false);
|
||||
|
||||
viewHolder = new SearchEntryViewHolder();
|
||||
viewHolder.suggestionView = (FlowLayout) convertView.findViewById(R.id.suggestion_layout);
|
||||
@ -422,6 +441,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
// add additional suggestions given by this engine
|
||||
int recycledSuggestionCount = suggestionView.getChildCount();
|
||||
int suggestionCount = engine.suggestions.size();
|
||||
boolean showedSuggestions = false;
|
||||
|
||||
for (int i = 0; i < suggestionCount; i++) {
|
||||
String suggestion = engine.suggestions.get(i);
|
||||
View suggestionItem = null;
|
||||
@ -439,12 +460,23 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
|
||||
suggestionItem.setOnClickListener(clickListener);
|
||||
suggestionItem.setOnLongClickListener(longClickListener);
|
||||
|
||||
if (mAnimateSuggestions) {
|
||||
showedSuggestions = true;
|
||||
AlphaAnimation anim = new AlphaAnimation(0, 1);
|
||||
anim.setDuration(ANIMATION_DURATION);
|
||||
anim.setStartOffset(i * ANIMATION_DURATION);
|
||||
suggestionItem.startAnimation(anim);
|
||||
}
|
||||
}
|
||||
|
||||
// hide extra suggestions that have been recycled
|
||||
for (int i = suggestionCount + 1; i < recycledSuggestionCount; i++) {
|
||||
suggestionView.getChildAt(i).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (showedSuggestions)
|
||||
mAnimateSuggestions = false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -546,21 +578,57 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
|
||||
}
|
||||
|
||||
private void setSuggestionsEnabled(final boolean enabled) {
|
||||
// Make suggestions appear immediately after the user opts in
|
||||
primeSuggestions();
|
||||
|
||||
// Pref observer in gecko will also set prompted = true
|
||||
PrefsHelper.setPref("browser.search.suggest.enabled", enabled);
|
||||
|
||||
getAllPagesView().post(new Runnable() {
|
||||
public void run() {
|
||||
getAllPagesView().removeView(mSuggestionsOptInPrompt);
|
||||
mSuggestionsOptInPrompt = null;
|
||||
TranslateAnimation anim1 = new TranslateAnimation(0, mSuggestionsOptInPrompt.getWidth(), 0, 0);
|
||||
anim1.setDuration(ANIMATION_DURATION);
|
||||
anim1.setInterpolator(new AccelerateInterpolator());
|
||||
anim1.setFillAfter(true);
|
||||
mSuggestionsOptInPrompt.setAnimation(anim1);
|
||||
|
||||
if (enabled) {
|
||||
mSuggestionsEnabled = enabled;
|
||||
getCursorAdapter().notifyDataSetChanged();
|
||||
filterSuggestions(mSearchTerm);
|
||||
}
|
||||
TranslateAnimation anim2 = new TranslateAnimation(0, 0, 0, -1 * mSuggestionsOptInPrompt.getHeight());
|
||||
anim2.setDuration(ANIMATION_DURATION);
|
||||
anim2.setFillAfter(true);
|
||||
anim2.setStartOffset(anim1.getDuration());
|
||||
anim2.setAnimationListener(new Animation.AnimationListener() {
|
||||
public void onAnimationStart(Animation a) {
|
||||
// Increase the height of the view so a gap isn't shown during animation
|
||||
getAllPagesView().getLayoutParams().height = getAllPagesView().getHeight() +
|
||||
mSuggestionsOptInPrompt.getHeight();
|
||||
getAllPagesView().requestLayout();
|
||||
}
|
||||
public void onAnimationRepeat(Animation a) {}
|
||||
public void onAnimationEnd(Animation a) {
|
||||
// Removing the view immediately results in a NPE in
|
||||
// dispatchDraw(), possibly because this callback executes
|
||||
// before drawing is finished. Posting this as a Runnable fixes
|
||||
// the issue.
|
||||
getAllPagesView().post(new Runnable() {
|
||||
public void run() {
|
||||
getAllPagesView().removeView(mSuggestionsOptInPrompt);
|
||||
getListView().clearAnimation();
|
||||
mSuggestionsOptInPrompt = null;
|
||||
|
||||
if (enabled) {
|
||||
// Reset the view height
|
||||
getAllPagesView().getLayoutParams().height = LayoutParams.FILL_PARENT;
|
||||
|
||||
mSuggestionsEnabled = enabled;
|
||||
mAnimateSuggestions = true;
|
||||
getCursorAdapter().notifyDataSetChanged();
|
||||
filterSuggestions(mSearchTerm);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
mSuggestionsOptInPrompt.startAnimation(anim1);
|
||||
getListView().startAnimation(anim2);
|
||||
}
|
||||
|
||||
public void handleMessage(String event, final JSONObject message) {
|
||||
|
@ -1,14 +1,15 @@
|
||||
#filter substitution
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.mozilla.gecko.AnimatedHeightLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res/@ANDROID_PACKAGE_NAME@"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/awesomebar_row_height"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="6dip">
|
||||
android:padding="7dip">
|
||||
|
||||
<ImageView android:id="@+id/suggestion_icon"
|
||||
android:layout_width="32dip"
|
||||
@ -29,4 +30,4 @@
|
||||
|
||||
</org.mozilla.gecko.FlowLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</org.mozilla.gecko.AnimatedHeightLayout>
|
Loading…
Reference in New Issue
Block a user