Bug 894045 - Add gamepad support for search suggestions. r=lucasr

This commit is contained in:
Brian Nicholson 2013-07-26 11:21:39 -07:00
parent 395e7d2114
commit 771cecaa36
4 changed files with 151 additions and 10 deletions

View File

@ -239,6 +239,22 @@ public class BrowserSearch extends HomeFragment
}
});
final ListSelectionListener listener = new ListSelectionListener();
mList.setOnItemSelectedListener(listener);
mList.setOnFocusChangeListener(listener);
mList.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, android.view.KeyEvent event) {
final View selected = mList.getSelectedView();
if (selected instanceof SearchEngineRow) {
return selected.onKeyDown(keyCode, event);
}
return false;
}
});
registerForContextMenu(mList);
registerEventListener("SearchEngines:Data");
@ -434,6 +450,18 @@ public class BrowserSearch extends HomeFragment
yesButton.setOnClickListener(listener);
noButton.setOnClickListener(listener);
// If the prompt gains focus, automatically pass focus to the
// yes button in the prompt.
final View prompt = mSuggestionsOptInPrompt.findViewById(R.id.prompt);
prompt.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
yesButton.requestFocus();
}
}
});
}
private void setSuggestionsEnabled(final boolean enabled) {
@ -632,6 +660,12 @@ public class BrowserSearch extends HomeFragment
@Override
public boolean isEnabled(int position) {
// If we're using a gamepad or keyboard, allow the row to be
// focused so it can pass the focus to its child suggestion views.
if (!mList.isInTouchMode()) {
return true;
}
// If the suggestion row only contains one item (the user-entered
// query), allow the entire row to be clickable; clicking the row
// has the same effect as clicking the single suggestion. If the
@ -801,4 +835,46 @@ public class BrowserSearch extends HomeFragment
setSuggestions(new ArrayList<String>());
}
}
private static class ListSelectionListener implements View.OnFocusChangeListener,
AdapterView.OnItemSelectedListener {
private SearchEngineRow mSelectedEngineRow;
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
View selectedRow = ((ListView) v).getSelectedView();
if (selectedRow != null) {
selectRow(selectedRow);
}
} else {
deselectRow();
}
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
deselectRow();
selectRow(view);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
deselectRow();
}
private void selectRow(View row) {
if (row instanceof SearchEngineRow) {
mSelectedEngineRow = (SearchEngineRow) row;
mSelectedEngineRow.onSelected();
}
}
private void deselectRow() {
if (mSelectedEngineRow != null) {
mSelectedEngineRow.onDeselected();
mSelectedEngineRow = null;
}
}
}
}

View File

@ -16,6 +16,7 @@ import org.mozilla.gecko.widget.FaviconView;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AlphaAnimation;
@ -52,6 +53,9 @@ class SearchEngineRow extends AnimatedHeightLayout {
// On edit suggestion listener
private OnEditSuggestionListener mEditSuggestionListener;
// Selected suggestion view
private int mSelectedView = 0;
public SearchEngineRow(Context context) {
this(context, null);
}
@ -179,5 +183,64 @@ class SearchEngineRow extends AnimatedHeightLayout {
for (int i = suggestionCount + 1; i < recycledSuggestionCount; i++) {
mSuggestionView.getChildAt(i).setVisibility(View.GONE);
}
// Make sure mSelectedView is still valid
if (mSelectedView >= mSuggestionView.getChildCount()) {
mSelectedView = mSuggestionView.getChildCount() - 1;
}
}
@Override
public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
final View suggestion = mSuggestionView.getChildAt(mSelectedView);
if (event.getAction() != android.view.KeyEvent.ACTION_DOWN) {
return false;
}
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_RIGHT:
final View nextSuggestion = mSuggestionView.getChildAt(mSelectedView + 1);
if (nextSuggestion != null) {
changeSelectedSuggestion(suggestion, nextSuggestion);
mSelectedView++;
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
final View prevSuggestion = mSuggestionView.getChildAt(mSelectedView - 1);
if (prevSuggestion != null) {
changeSelectedSuggestion(suggestion, prevSuggestion);
mSelectedView--;
return true;
}
break;
case KeyEvent.KEYCODE_BUTTON_A:
// TODO: handle long pressing for editing suggestions
return suggestion.performClick();
}
return false;
}
private void changeSelectedSuggestion(View oldSuggestion, View newSuggestion) {
oldSuggestion.setDuplicateParentStateEnabled(false);
newSuggestion.setDuplicateParentStateEnabled(true);
oldSuggestion.refreshDrawableState();
newSuggestion.refreshDrawableState();
}
public void onSelected() {
mSelectedView = 0;
mUserEnteredView.setDuplicateParentStateEnabled(true);
mUserEnteredView.refreshDrawableState();
}
public void onDeselected() {
final View suggestion = mSuggestionView.getChildAt(mSelectedView);
suggestion.setDuplicateParentStateEnabled(false);
suggestion.refreshDrawableState();
}
}

View File

@ -7,5 +7,6 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/search_row_height"
android:duplicateParentState="true"
android:paddingTop="7dp"
android:paddingBottom="7dp"/>

View File

@ -5,24 +5,25 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<org.mozilla.gecko.widget.FaviconView android:id="@+id/suggestion_icon"
android:layout_width="@dimen/favicon_bg"
android:layout_height="@dimen/favicon_bg"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_centerVertical="true"
android:minWidth="@dimen/favicon_bg"
android:minHeight="@dimen/favicon_bg"/>
<org.mozilla.gecko.widget.FaviconView android:id="@+id/suggestion_icon"
android:layout_width="@dimen/favicon_bg"
android:layout_height="@dimen/favicon_bg"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_centerVertical="true"
android:minWidth="@dimen/favicon_bg"
android:minHeight="@dimen/favicon_bg"/>
<org.mozilla.gecko.FlowLayout android:id="@+id/suggestion_layout"
android:layout_toRightOf="@id/suggestion_icon"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:layout_marginRight="10dip">
<include layout="@layout/suggestion_item"
android:id="@+id/suggestion_user_entered"/>
<include layout="@layout/suggestion_item"
android:id="@+id/suggestion_user_entered"/>
</org.mozilla.gecko.FlowLayout>