Bug 602818 - Integrate QR code scanner into Fennec. r=liuche

--HG--
rename : mobile/android/base/util/VoiceRecognizerUtils.java => mobile/android/base/util/InputOptionsUtils.java
extra : commitid : HyeUL1YR3Sm
extra : rebase_source : 8f746b9eb776f2e7e28b42975d59f54614760094
extra : histedit_source : a39383a6526b57230c283f4167bf6b550f8a5df7
This commit is contained in:
Karim Benhmida 2015-06-19 13:20:31 -07:00
parent 81fecd2731
commit ba45639acc
16 changed files with 281 additions and 168 deletions

View File

@ -1085,6 +1085,12 @@ public class BrowserApp extends GeckoApp
}
});
}
// Sending a message to the toolbar when the browser window gains focus
// This is needed for qr code input
if (hasFocus) {
mBrowserToolbar.onParentFocus();
}
}
private void setBrowserToolbarListeners() {

View File

@ -81,6 +81,7 @@
<!ENTITY settings "Settings">
<!ENTITY settings_title "Settings">
<!ENTITY pref_category_input_options "Input options">
<!ENTITY pref_category_advanced "Advanced">
<!ENTITY pref_category_customize "Customize">
<!ENTITY pref_category_customize_summary "Home, search, tabs, import">
@ -252,6 +253,8 @@ size. -->
<!ENTITY pref_zoom_force_enabled_summary "Force override so you can zoom any page">
<!ENTITY pref_voice_input "Voice input">
<!ENTITY pref_voice_input_summary "Allow voice dictation in the title bar">
<!ENTITY pref_qrcode_enabled "QR code reader">
<!ENTITY pref_qrcode_enabled_summary "Allow QR scanner in the title bar">
<!ENTITY pref_use_master_password "Use master password">
<!ENTITY pref_sync "Sync">

View File

@ -86,6 +86,7 @@ gujar.sources += [
'util/HardwareUtils.java',
'util/INIParser.java',
'util/INISection.java',
'util/InputOptionsUtils.java',
'util/IOUtils.java',
'util/JSONUtils.java',
'util/MenuUtils.java',
@ -99,7 +100,6 @@ gujar.sources += [
'util/StringUtils.java',
'util/ThreadUtils.java',
'util/UIAsyncTask.java',
'util/VoiceRecognizerUtils.java',
'util/WeakReferenceHandler.java',
'util/WebActivityMapper.java',
'util/WindowUtils.java',

View File

@ -26,7 +26,6 @@ import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.GuestSession;
import org.mozilla.gecko.LocaleManager;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.PrefsHelper;
@ -43,11 +42,10 @@ import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.VoiceRecognizerUtils;
import org.mozilla.gecko.util.InputOptionsUtils;
import org.mozilla.gecko.widget.FloatingHintEditText;
import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Fragment;
@ -130,6 +128,7 @@ OnSharedPreferenceChangeListener
private static final String PREFS_TRACKING_PROTECTION_LEARN_MORE = NON_PREF_PREFIX + "trackingprotection.learn_more";
public static final String PREFS_OPEN_URLS_IN_PRIVATE = NON_PREF_PREFIX + "openExternalURLsPrivately";
public static final String PREFS_VOICE_INPUT_ENABLED = NON_PREF_PREFIX + "voice_input_enabled";
public static final String PREFS_QRCODE_ENABLED = NON_PREF_PREFIX + "qrcode_enabled";
private static final String ACTION_STUMBLER_UPLOAD_PREF = AppConstants.ANDROID_PACKAGE_NAME + ".STUMBLER_PREF";
@ -687,6 +686,14 @@ OnSharedPreferenceChangeListener
// Only change the customize pref screen summary on nightly builds with the tab queue build flag.
pref.setSummary(getString(R.string.pref_category_customize_alt_summary));
}
if (getResources().getString(R.string.pref_category_input_options).equals(key)) {
if (!AppConstants.NIGHTLY_BUILD || (!InputOptionsUtils.supportsVoiceRecognizer(getApplicationContext(), getResources().getString(R.string.voicesearch_prompt)) &&
!InputOptionsUtils.supportsQrCodeReader(getApplicationContext()))) {
preferences.removePreference(pref);
i--;
continue;
}
}
setupPreferences((PreferenceGroup) pref, prefs);
} else {
pref.setOnPreferenceChangeListener(this);
@ -790,11 +797,17 @@ OnSharedPreferenceChangeListener
i--;
continue;
} else if (PREFS_VOICE_INPUT_ENABLED.equals(key) &&
(!AppConstants.NIGHTLY_BUILD || !VoiceRecognizerUtils.supportsVoiceRecognizer(getApplicationContext(), getResources().getString(R.string.voicesearch_prompt)))) {
(!AppConstants.NIGHTLY_BUILD || !InputOptionsUtils.supportsVoiceRecognizer(getApplicationContext(), getResources().getString(R.string.voicesearch_prompt)))) {
// Remove UI for voice input on non nightly builds.
preferences.removePreference(pref);
i--;
continue;
} else if (PREFS_QRCODE_ENABLED.equals(key) &&
(!AppConstants.NIGHTLY_BUILD || !InputOptionsUtils.supportsQrCodeReader(getApplicationContext()))) {
// Remove UI for qr code input on non nightly builds
preferences.removePreference(pref);
i--;
continue;
}
// Some Preference UI elements are not actually preferences,

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View File

@ -21,9 +21,20 @@
android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen"
android:selectAllOnFocus="true"
android:contentDescription="@string/url_bar_default_text"
android:drawableRight="@drawable/ab_mic"
android:drawablePadding="12dp"
android:paddingRight="8dp"
gecko:autoUpdateTheme="false"/>
<ImageButton android:id="@+id/qrcode"
android:layout_width="@dimen/page_action_button_width"
android:layout_height="match_parent"
android:src="@drawable/ab_qrcode"
android:background="@android:color/transparent"/>
<ImageButton android:id="@+id/mic"
android:layout_width="@dimen/page_action_button_width"
android:layout_height="match_parent"
android:src="@drawable/ab_mic"
android:background="@android:color/transparent"/>
</merge>

View File

@ -33,10 +33,20 @@
android:title="@string/pref_zoom_force_enabled"
android:summary="@string/pref_zoom_force_enabled_summary" />
<CheckBoxPreference android:key="android.not_a_preference.voice_search_enabled"
android:title="@string/pref_voice_input"
android:summary="@string/pref_voice_input_summary"
android:defaultValue="true"/>
<PreferenceCategory android:title="@string/pref_category_input_options"
android:key="@string/pref_category_input_options">
<CheckBoxPreference android:key="android.not_a_preference.voice_input_enabled"
android:title="@string/pref_voice_input"
android:summary="@string/pref_voice_input_summary"
android:defaultValue="true"/>
<CheckBoxPreference android:key="android.not_a_preference.qrcode_enabled"
android:title="@string/pref_qrcode_enabled"
android:summary="@string/pref_qrcode_enabled_summary"
android:defaultValue="true"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_category_advanced">

View File

@ -124,6 +124,7 @@
<string name="settings">&settings;</string>
<string name="settings_title">&settings_title;</string>
<string name="pref_category_input_options">&pref_category_input_options;</string>
<string name="pref_category_advanced">&pref_category_advanced;</string>
<string name="pref_category_customize">&pref_category_customize;</string>
<string name="pref_category_customize_summary">&pref_category_customize_summary;</string>
@ -220,6 +221,8 @@
<string name="pref_zoom_force_enabled_summary">&pref_zoom_force_enabled_summary;</string>
<string name="pref_voice_input">&pref_voice_input;</string>
<string name="pref_voice_input_summary">&pref_voice_input_summary;</string>
<string name="pref_qrcode_enabled">&pref_qrcode_enabled;</string>
<string name="pref_qrcode_enabled_summary">&pref_qrcode_enabled_summary;</string>
<string name="pref_reflow_on_zoom">&pref_reflow_on_zoom4;</string>
<string name="pref_restore">&pref_restore;</string>
<string name="pref_restore_always">&pref_restore_always;</string>

View File

@ -351,6 +351,10 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
canvas.drawRect(0, height - shadowSize, getWidth(), height, shadowPaint);
}
public void onParentFocus() {
urlEditLayout.onParentFocus();
}
public void setProgressBar(ToolbarProgressView progressBar) {
this.progressBar = progressBar;
}

View File

@ -5,22 +5,38 @@
package org.mozilla.gecko.toolbar;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.speech.RecognizerIntent;
import android.widget.Button;
import android.widget.ImageButton;
import org.mozilla.gecko.ActivityHandlerHelper;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.R;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.toolbar.BrowserToolbar.OnCommitListener;
import org.mozilla.gecko.toolbar.BrowserToolbar.OnDismissListener;
import org.mozilla.gecko.toolbar.BrowserToolbar.OnFilterListener;
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.InputOptionsUtils;
import org.mozilla.gecko.widget.ThemedLinearLayout;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import java.util.List;
/**
* {@code ToolbarEditLayout} is the UI for when the toolbar is in
* edit state. It controls a text entry ({@code ToolbarEditText})
@ -31,8 +47,13 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
private final ToolbarEditText mEditText;
private final ImageButton mVoiceInput;
private final ImageButton mQrCode;
private OnFocusChangeListener mFocusChangeListener;
private boolean showKeyboardOnFocus = false; // Indicates if we need to show the keyboard after the app resumes
public ToolbarEditLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@ -40,6 +61,9 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
LayoutInflater.from(context).inflate(R.layout.toolbar_edit_layout, this);
mEditText = (ToolbarEditText) findViewById(R.id.url_edit_text);
mVoiceInput = (ImageButton) findViewById(R.id.mic);
mQrCode = (ImageButton) findViewById(R.id.qrcode);
}
@Override
@ -49,9 +73,38 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
public void onFocusChange(View v, boolean hasFocus) {
if (mFocusChangeListener != null) {
mFocusChangeListener.onFocusChange(ToolbarEditLayout.this, hasFocus);
// Checking if voice and QR code input are enabled each time the user taps on the title bar
if (hasFocus) {
if (voiceIsEnabled(getContext(), getResources().getString(R.string.voicesearch_prompt))) {
mVoiceInput.setVisibility(View.VISIBLE);
} else {
mVoiceInput.setVisibility(View.GONE);
}
if (qrCodeIsEnabled(getContext())) {
mQrCode.setVisibility(View.VISIBLE);
} else {
mQrCode.setVisibility(View.GONE);
}
}
}
}
});
mVoiceInput.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
launchVoiceRecognizer();
}
});
mQrCode.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
launchQRCodeReader();
}
});
}
@Override
@ -71,6 +124,30 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
mEditText.setPrivateMode(isPrivate);
}
/**
* Called when the parent gains focus (on app launch and resume)
*/
public void onParentFocus() {
if (showKeyboardOnFocus) {
showKeyboardOnFocus = false;
Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
activity.runOnUiThread(new Runnable() {
public void run() {
mEditText.requestFocus();
showSoftInput();
}
});
}
// Checking if qr code is supported after resuming the app
if (qrCodeIsEnabled(getContext())) {
mQrCode.setVisibility(View.VISIBLE);
} else {
mQrCode.setVisibility(View.GONE);
}
}
void setToolbarPrefs(final ToolbarPrefs prefs) {
mEditText.setToolbarPrefs(prefs);
}
@ -139,4 +216,125 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
mEditText.setText(editingState.lastEditingText);
mEditText.setSelection(editingState.selectionStart, editingState.selectionEnd);
}
private boolean voiceIsEnabled(Context context, String prompt) {
// Voice input is enabled for nightly only
if(!AppConstants.NIGHTLY_BUILD) {
return false;
}
final boolean voiceIsSupported = InputOptionsUtils.supportsVoiceRecognizer(context, prompt);
if (!voiceIsSupported) {
return false;
}
return GeckoSharedPrefs.forApp(context)
.getBoolean(GeckoPreferences.PREFS_VOICE_INPUT_ENABLED, true);
}
private void launchVoiceRecognizer() {
final Intent intent = InputOptionsUtils.createVoiceRecognizerIntent(getResources().getString(R.string.voicesearch_prompt));
Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
ActivityHandlerHelper.startIntentForActivity(activity, intent, new ActivityResultHandler() {
@Override
public void onActivityResult(int resultCode, Intent data) {
switch (resultCode) {
case RecognizerIntent.RESULT_CLIENT_ERROR:
case RecognizerIntent.RESULT_NETWORK_ERROR:
case RecognizerIntent.RESULT_SERVER_ERROR:
// We have an temporarily unrecoverable error.
handleVoiceSearchError(false);
break;
case RecognizerIntent.RESULT_AUDIO_ERROR:
case RecognizerIntent.RESULT_NO_MATCH:
// Maybe the user can say it differently?
handleVoiceSearchError(true);
break;
case Activity.RESULT_CANCELED:
break;
}
if (resultCode != Activity.RESULT_OK) {
return;
}
// We have RESULT_OK, not RESULT_NO_MATCH so it should be safe to assume that
// we have at least one match. We only need one: this will be
// used for showing the user search engines with this search term in it.
List<String> voiceStrings = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
String text = voiceStrings.get(0);
mEditText.setText(text);
mEditText.setSelection(0, text.length());
final InputMethodManager imm =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
}
});
}
private void handleVoiceSearchError(boolean offerRetry) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
.setTitle(R.string.voicesearch_failed_title)
.setIcon(R.drawable.icon).setNeutralButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
if (offerRetry) {
builder.setMessage(R.string.voicesearch_failed_message_recoverable)
.setNegativeButton(R.string.voicesearch_failed_retry, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
launchVoiceRecognizer();
}
});
} else {
builder.setMessage(R.string.voicesearch_failed_message);
}
AlertDialog dialog = builder.create();
dialog.show();
}
private boolean qrCodeIsEnabled(Context context) {
// QR code is enabled for nightly only
if(!AppConstants.NIGHTLY_BUILD) {
return false;
}
final boolean qrCodeIsSupported = InputOptionsUtils.supportsQrCodeReader(context);
if (!qrCodeIsSupported) {
return false;
}
return GeckoSharedPrefs.forApp(context)
.getBoolean(GeckoPreferences.PREFS_QRCODE_ENABLED, true);
}
private void launchQRCodeReader() {
final Intent intent = InputOptionsUtils.createQRCodeReaderIntent();
Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
ActivityHandlerHelper.startIntentForActivity(activity, intent, new ActivityResultHandler() {
@Override
public void onActivityResult(int resultCode, Intent intent) {
if (resultCode == Activity.RESULT_OK) {
String text = intent.getStringExtra("SCAN_RESULT");
if (!StringUtils.isSearchQuery(text, false)) {
mEditText.setText(text);
mEditText.selectAll();
// Queuing up the keyboard show action.
// At this point the app has not resumed yet, and trying to show
// the keyboard will fail.
showKeyboardOnFocus = true;
}
}
// We can get the SCAN_RESULT_FORMAT, SCAN_RESULT_BYTES,
// SCAN_RESULT_ORIENTATION and SCAN_RESULT_ERROR_CORRECTION_LEVEL
// as well as the actual result, which may hold a URL.
}
});
}
}

View File

@ -5,31 +5,17 @@
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.ActivityHandlerHelper;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.CustomEditText;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.InputMethods;
import org.mozilla.gecko.R;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.toolbar.BrowserToolbar.OnCommitListener;
import org.mozilla.gecko.toolbar.BrowserToolbar.OnDismissListener;
import org.mozilla.gecko.toolbar.BrowserToolbar.OnFilterListener;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.VoiceRecognizerUtils;
import org.mozilla.gecko.util.StringUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.speech.RecognizerIntent;
import android.text.Editable;
import android.text.NoCopySpan;
import android.text.Selection;
@ -40,7 +26,6 @@ import android.text.style.BackgroundColorSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
@ -50,8 +35,6 @@ import android.view.inputmethod.InputMethodManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import java.util.List;
/**
* {@code ToolbarEditText} is the text entry used when the toolbar
* is in edit state. It handles all the necessary input method machinery.
@ -68,7 +51,6 @@ public class ToolbarEditText extends CustomEditText
private OnCommitListener mCommitListener;
private OnDismissListener mDismissListener;
private OnFilterListener mFilterListener;
private VoiceSearchOnTouchListener mVoiceOnTouchListener;
private ToolbarPrefs mPrefs;
@ -114,7 +96,6 @@ public class ToolbarEditText extends CustomEditText
if (gainFocus) {
resetAutocompleteState();
configureCompoundDrawables();
return;
}
@ -473,105 +454,6 @@ public class ToolbarEditText extends CustomEditText
};
}
/**
* Detect if we are able to enable the 'buttons' made from compound drawables.
*
* Currently, only voice input.
*/
private void configureCompoundDrawables() {
if (AppConstants.NIGHTLY_BUILD && voiceIsEnabled()) {
final Drawable micImg = getContext().getResources().getDrawable(R.drawable.ab_mic);
setCompoundDrawablesWithIntrinsicBounds(null, null, micImg, null);
if (mVoiceOnTouchListener == null) {
mVoiceOnTouchListener = new VoiceSearchOnTouchListener();
}
setOnTouchListener(mVoiceOnTouchListener);
} else {
// Remove the mic button
setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
setOnTouchListener(null);
}
}
private boolean voiceIsEnabled() {
final boolean voiceIsSupported = VoiceRecognizerUtils.supportsVoiceRecognizer(getContext(), getResources().getString(R.string.voicesearch_prompt));
if (!voiceIsSupported) {
return false;
}
return GeckoSharedPrefs.forApp(getContext())
.getBoolean(GeckoPreferences.PREFS_VOICE_INPUT_ENABLED, true);
}
private void launchVoiceRecognizer() {
final Intent intent = VoiceRecognizerUtils.createVoiceRecognizerIntent(getResources().getString(R.string.voicesearch_prompt));
Activity activity = GeckoAppShell.getGeckoInterface().getActivity();
ActivityHandlerHelper.startIntentForActivity(activity, intent, new ActivityResultHandler() {
@Override
public void onActivityResult(int resultCode, Intent data) {
switch (resultCode) {
case RecognizerIntent.RESULT_CLIENT_ERROR:
case RecognizerIntent.RESULT_NETWORK_ERROR:
case RecognizerIntent.RESULT_SERVER_ERROR:
// We have an temporarily unrecoverable error.
handleVoiceSearchError(false);
break;
case RecognizerIntent.RESULT_AUDIO_ERROR:
case RecognizerIntent.RESULT_NO_MATCH:
// Maybe the user can say it differently?
handleVoiceSearchError(true);
break;
case Activity.RESULT_CANCELED:
break;
}
if (resultCode != Activity.RESULT_OK) {
return;
}
// We have RESULT_OK, not RESULT_NO_MATCH so it should be safe to assume that
// we have at least one match. We only need one: this will be
// used for showing the user search engines with this search term in it.
List<String> voiceStrings = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
String text = voiceStrings.get(0);
setText(text);
setSelection(0, text.length());
final InputMethodManager imm =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(ToolbarEditText.this, InputMethodManager.SHOW_IMPLICIT);
}
});
}
private void handleVoiceSearchError(boolean offerRetry) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
.setTitle(R.string.voicesearch_failed_title)
.setIcon(R.drawable.icon).setNeutralButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
if (offerRetry) {
builder.setMessage(R.string.voicesearch_failed_message_recoverable)
.setNegativeButton(R.string.voicesearch_failed_retry, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
launchVoiceRecognizer();
}
});
} else {
builder.setMessage(R.string.voicesearch_failed_message);
}
AlertDialog dialog = builder.create();
dialog.show();
}
private class SelectionChangeListener implements OnSelectionChangedListener {
@Override
public void onSelectionChanged(final int selStart, final int selEnd) {
@ -715,40 +597,4 @@ public class ToolbarEditText extends CustomEditText
return false;
}
}
private class VoiceSearchOnTouchListener implements View.OnTouchListener {
private int mVoiceSearchIconIndex = -1;
private Drawable mVoiceSearchIcon;
public VoiceSearchOnTouchListener() {
Drawable[] drawables = getCompoundDrawables();
for (int i = 0; i < drawables.length; i++) {
if (drawables[i] != null) {
mVoiceSearchIcon = drawables[i];
mVoiceSearchIconIndex = i;
}
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean tapped;
final int action = event.getActionMasked();
switch (mVoiceSearchIconIndex) {
case 0:
tapped = event.getX() < (getPaddingLeft() + mVoiceSearchIcon.getIntrinsicWidth());
break;
case 2:
tapped = event.getX() > (getWidth() - getPaddingRight() - mVoiceSearchIcon.getIntrinsicWidth());
break;
default:
tapped = false;
}
if (tapped && (action == MotionEvent.ACTION_DOWN)) {
launchVoiceRecognizer();
}
return tapped;
}
}
}

View File

@ -9,7 +9,7 @@ import android.content.Context;
import android.content.Intent;
import android.speech.RecognizerIntent;
public class VoiceRecognizerUtils {
public class InputOptionsUtils {
public static boolean supportsVoiceRecognizer(Context context, String prompt) {
final Intent intent = createVoiceRecognizerIntent(prompt);
return intent.resolveActivity(context.getPackageManager()) != null;
@ -22,4 +22,21 @@ public class VoiceRecognizerUtils {
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
return intent;
}
public static boolean supportsIntent(Intent intent, Context context) {
return intent.resolveActivity(context.getPackageManager()) != null;
}
public static boolean supportsQrCodeReader(Context context) {
final Intent intent = createQRCodeReaderIntent();
return supportsIntent(intent, context);
}
public static Intent createQRCodeReaderIntent() {
// Bug 602818 enables QR code input if you have the particular app below installed in your device
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return intent;
}
}

View File

@ -7,9 +7,11 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.VoiceRecognizerUtils;
import org.mozilla.gecko.util.InputOptionsUtils;
/** This patch tests the Sections present in the Settings Menu and the
* default values for them
@ -228,7 +230,7 @@ public class testSettingsMenuItems extends PixelTest {
}
// Voice input
if (AppConstants.NIGHTLY_BUILD && VoiceRecognizerUtils.supportsVoiceRecognizer(this.getActivity().getApplicationContext(), this.getActivity().getResources())) {
if (AppConstants.NIGHTLY_BUILD && InputOptionsUtils.supportsVoiceRecognizer(this.getActivity().getApplicationContext(), this.getActivity().getResources().getString(R.string.voicesearch_prompt))) {
String[] voiceInputUi = { mStringHelper.VOICE_INPUT_TITLE_LABEL, mStringHelper.VOICE_INPUT_SUMMARY_LABEL };
settingsMap.get(PATH_DISPLAY).add(voiceInputUi);
}