domingo, 28 de octubre de 2012

Reconocimiento de Voz

Si en nuestra aplicación es necesario un buscador web, de aplicaciones o similares, podemos usar la opción de búsqueda por voz. Este tipo de búsqueda se puede configurar para que sea interna al teléfono, por ejemplo aplicaciones, o bien, en la web.

En el siguiente ejemplo veremos que está configurado para web y que el número máximo de elementos que se van a mostrar en la lista son 10. Esta configuración se puede hacer dinámica: en la búsqueda por web o aplicaciones se puede realizar mediante la inserción de un RadioButton en el código. En el segundo caso podemos poner un contador, un spinner con el número de búsquedas limitadas...

A continuación les dejo el código:

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.blogvoicerecognition"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".VoiceRecognitionActivity"
            android:label="@string/title_activity_voice_recognition" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

activity_voice_recognition.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/etTextHint"
        android:gravity="top"
        android:inputType="textMultiLine"
        android:lines="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Speech hint here"/>

    <Button
        android:id="@+id/btSpeak"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="speak"
        android:padding="@dimen/padding_medium"
        android:text="Speak"
        tools:context=".VoiceRecognitionActivity" />

    <ListView
        android:id="@+id/lvTextMatches"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

VoiceRecognitionActivity.java
package com.example.blogvoicerecognition;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class VoiceRecognitionActivity extends Activity {

private static final int VOICE_RECOGNITION_REQUEST_CODE = 0;
private static final int MATCHES = 10;

private EditText mEditText;
private ListView mListViewResults;
private Button mButtonSpeak;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_voice_recognition);
mEditText = (EditText) findViewById(R.id.etTextHint);
mListViewResults = (ListView) findViewById(R.id.lvTextMatches);
mButtonSpeak = (Button) findViewById(R.id.btSpeak);
voiceRecognition();
}

public void voiceRecognition() {
// Check if voice recognition is present
PackageManager pm = getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(new Intent(
RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
if (activities.size() == 0) {
mButtonSpeak.setEnabled(false);
mButtonSpeak.setText("Voice recognizer not present");
Toast.makeText(this, "Voice recognizer not present",
Toast.LENGTH_SHORT).show();
}
}

public void speak(View view) {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

// Specify the calling package to identify your application
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getClass()
.getPackage().getName());

// Display an hint to the user about what he should say.
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, mEditText.getText()
.toString());

// Given an hint to the recognizer about what the user is going to say
// There are two form of language model available
// 1.LANGUAGE_MODEL_WEB_SEARCH : For short phrases
// 2.LANGUAGE_MODEL_FREE_FORM : If not sure about the words or phrases
// and its domain.
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);

// Specify how many results you want to receive. The results will be
// sorted where the first result is the one with higher confidence.
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, MATCHES);
// Start the Voice recognizer activity for the result.
startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == VOICE_RECOGNITION_REQUEST_CODE)

// If Voice recognition is successful then it returns RESULT_OK
if (resultCode == RESULT_OK) {

ArrayList<String> textMatchList = data
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);

if (!textMatchList.isEmpty()) {
// If first Match contains the 'search' word
// Then start web search.
if (textMatchList.get(0).contains("search")) {

String searchQuery = textMatchList.get(0);
searchQuery = searchQuery.replace("search", "");
Intent search = new Intent(Intent.ACTION_WEB_SEARCH);
search.putExtra(SearchManager.QUERY, searchQuery);
startActivity(search);
} else {
// populate the Matches
mListViewResults.setAdapter(new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1,
textMatchList));
}

}
// Result code for various error.
} else if (resultCode == RecognizerIntent.RESULT_AUDIO_ERROR) {
showToastMessage("Audio Error");
} else if (resultCode == RecognizerIntent.RESULT_CLIENT_ERROR) {
showToastMessage("Client Error");
} else if (resultCode == RecognizerIntent.RESULT_NETWORK_ERROR) {
showToastMessage("Network Error");
} else if (resultCode == RecognizerIntent.RESULT_NO_MATCH) {
showToastMessage("No Match");
} else if (resultCode == RecognizerIntent.RESULT_SERVER_ERROR) {
showToastMessage("Server Error");
}
super.onActivityResult(requestCode, resultCode, data);
}

/**
* Helper method to show the toast message
**/
void showToastMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}

A continuación les dejo la imagen de la búsqueda de la palabra "Android":


It works!
Roger Sala,



viernes, 26 de octubre de 2012

ActionBarSherlock: Usar, personalizar y Tabs

ActionBarSherlock es un proyecto que cualquier desarrollador puede descargar e integrar en sus proyectos. Para saber exactamente que es y que posibilidades tiene mejor consultarlo en la web. En este post nos vamos a centrar en como usar la ActionBar que ofrece.
Cómo hacerlo es muy simple:
1.- Descargar el proyecto de la web. ZIP
2.- Descomprimir...dentro de la carpeta creada existe la carpeta "library". Este carpeta es la que tenemos que importar a nuestro workspace de Eclipse.

Una vez importado el proyecto tenemos que crear un nuevo proyecto e indicarle que vamos usar el "library" como librería. Pasos:
1.- Crear proyecto.
2.- Una vez creado...Clic derecho sobre el proyecto --> Properties --> Android --> (A la parte inferior exite la opción de añadir librería) Add --> Seleccionamos el "library".

Una vez importado ya podemos usarlo. Para ello tenemos el siguiente código:

res/menu/activity_main.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_settings"
        android:orderInCategory="100"
        android:showAsAction="ifRoom"
        android:title="@string/menu_settings">
        <menu>
            <item
                android:id="@+id/form1"
                android:title="Form 1"/>
            <item
                android:id="@+id/form2"
                android:title="Form 2"/>
        </menu>
    </item>
    <item
        android:id="@+id/menu_map"
        android:orderInCategory="200"
        android:showAsAction="ifRoom"
        android:title="map">
    </item>

</menu>


res/values/styles.xml

<resources>
    <style name="AppTheme" parent="android:Theme.Light" />
<!-- Usaremos nuestro propio estilo sino-->
    <style name="Theme.Styled" parent="Theme.Sherlock.Light.DarkActionBar">
        <item name="actionBarStyle">@style/Widget.Styled.ActionBar</item>
        <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item>
    </style>
<!-- Sobrescribimos la actionBarSherlock con nuestro propio background-->
    <style name="Widget.Styled.ActionBar" parent="Widget.Sherlock.Light.ActionBar.Solid.Inverse">
        <item name="android:background">@drawable/comun_actionbar</item>
    </style>
</resources>


//Como podemos ver el Theme.Styled es custom y así nos permite personalizar el estilo de la ActionBar. NOTA: Si no lo personalizamos ponemos :Theme.Sherlock
AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.blogactionbarsherlock"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.Styled"
        <activity
            android:name=".ChooserActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".SingleActivity"/>
        <activity android:name=".TabsActivity" android:label="TabsView" />
    </application>
</manifest>

activity_chooser.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button_single"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="40dip"
        android:gravity="center_horizontal"
        android:padding="@dimen/padding_medium"
        android:text="Single"
        tools:context=".MainActivity" />

    <Button
        android:id="@+id/button_tabs"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center_horizontal"
        android:padding="@dimen/padding_medium"
        android:text="Tabs"
        tools:context=".MainActivity" />

</LinearLayout>

//Clase inicial (indice de la aplicación) que nos permite seleccionar entre una vista simple o bien la de los Tabs. 
ChooserActivity.java
package com.example.blogactionbarsherlock;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.actionbarsherlock.app.SherlockActivity;

public class ChooserActivity extends SherlockActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chooser);

((Button) findViewById(R.id.button_single))
.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
startActivity(new Intent(ChooserActivity.this, SingleActivity.class));
}
});

((Button) findViewById(R.id.button_tabs))
.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
startActivity(new Intent(ChooserActivity.this, TabsActivity.class));
}
});

}

}

activity_single.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</RelativeLayout>


SingleActivity.java
package com.example.blogactionbarsherlock;

import android.os.Bundle;
import android.widget.Toast;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;

public class SingleActivity extends SherlockActivity {

private Menu mMenu;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single);

// Customize
ActionBar actionBar = getSupportActionBar();
actionBar.setBackgroundDrawable(getResources().getDrawable(
R.drawable.comun_background_grey));
actionBar.setTitle("Custom Title");
actionBar.setDisplayShowHomeEnabled(false);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

MenuInflater menuInflater = getSupportMenuInflater();
menuInflater.inflate(R.menu.activity_main, menu);

// Manage Menu - Customize
mMenu = menu;
mMenu.findItem(R.id.menu_map).setVisible(false);

return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_settings:
Toast.makeText(this, "Settings", Toast.LENGTH_SHORT).show();
break;

default:
break;
}

return true;
}
}

tabs_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView android:id="@+id/textView_tab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>


TabsActivity.java
package com.example.blogactionbarsherlock;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.widget.Toast;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;

public class TabsActivity extends SherlockFragmentActivity implements
ActionBar.TabListener {

private Menu mMenu;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single);

ActionBar actionBar = getSupportActionBar();

// Navigation
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

// create tabs
ActionBar.Tab newTab0 = getSupportActionBar().newTab();
newTab0.setText("Tab 0");
newTab0.setTag("0");
ActionBar.Tab newTab1 = getSupportActionBar().newTab();
newTab1.setText("Tab 1");
newTab1.setTag("1");

// Listeners
newTab0.setTabListener(this);
newTab1.setTabListener(this);

// add tabs
getSupportActionBar().addTab(newTab0);
getSupportActionBar().addTab(newTab1);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

MenuInflater menuInflater = getSupportMenuInflater();
menuInflater.inflate(R.menu.activity_main, menu);

// Manage Menu
mMenu = menu;
mMenu.findItem(R.id.menu_settings).setVisible(false);

return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_settings:
Toast.makeText(this, "Settings", Toast.LENGTH_SHORT).show();
break;

default:
break;
}

return true;
}

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
                
                //Añadimos el fragment en el tab que queremos. En este ejemplo añadimos el mismo.
Fragment ef = (Fragment) new ExampleFragment();
FragmentManager fragMgr = getSupportFragmentManager();
ft = fragMgr.beginTransaction();

switch (Integer.valueOf((String) tab.getTag())) {
case 0:
Log.d("SimpleActionBarTabsActivity",
"tab " + String.valueOf(tab.getPosition()) + " 0 clicked");
ft.add(android.R.id.content, ef, null);
break;
case 1:
Log.d("SimpleActionBarTabsActivity",
"tab " + String.valueOf(tab.getPosition()) + " 1 clicked");
ft.add(android.R.id.content, ef, null);
break;
}

ft.commit();
Log.d("SimpleActionBarTabsActivity",
"tab " + String.valueOf(tab.getPosition()) + " clicked");
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// switch (Integer.valueOf((String) tab.getTag())) {
// case 0:
// Log.d("SimpleActionBarTabsActivity",
// "tab " + String.valueOf(tab.getPosition())
// + " 0 un-clicked");
// break;
// case 1:
// Log.d("SimpleActionBarTabsActivity",
// "tab " + String.valueOf(tab.getPosition())
// + " 1 un-clicked");
// break;
// }
// Log.d("SimpleActionBarTabsActivity",
// "tab " + String.valueOf(tab.getPosition()) + " un-clicked");

}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// switch (Integer.valueOf((String) tab.getTag())) {
// case 0:
// Log.d("SimpleActionBarTabsActivity",
// "tab " + String.valueOf(tab.getPosition())
// + " 0 re-clicked");
// break;
// case 1:
// Log.d("SimpleActionBarTabsActivity",
// "tab " + String.valueOf(tab.getPosition())
// + " 1 re-clicked");
// break;
// }
// Log.d("SimpleActionBarTabsActivity",
// "tab " + String.valueOf(tab.getPosition()) + " re-clicked");

}
}

fragment_example.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView android:id="@+id/textView_tab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

ExampleFragment.java

package com.example.blogactionbarsherlock;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.actionbarsherlock.app.SherlockFragment;

public class ExampleFragment extends SherlockFragment {

public ExampleFragment(){
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_example, container, false);
((TextView) view.findViewById(R.id.textView_tab)).setText("Custom tab");
return view;
}

}


A continuación las capturas de pantalla de la aplicación que podemos ver:

Chooser




SingleActivity + CustomActionBar + Menú




TabsActivity + ActionBar en Theme.Styled



It works!
Roger Sala


martes, 16 de octubre de 2012

Searchable en tu aplicación

Si tu aplicación realiza búsquedas, ya sea dentro del dispositivo, webservices... seguramente tendrás que añadir la barra de buscar objetos nativa de Android. Para ello, puedes seguir el siguiente ejemplo. En este podremos ver como se nos muestran las distintas aplicación que tenemos instaladas en el dispositivo.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.blogsearchable"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".SearchActivity"
            android:label="@string/title_activity_search" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

           <--IMPORTANTE AÑADIR ESTE META EN LA ACTIVITY QUE CONTIENE LA BARRA-->
            <meta-data
                android:name="com.example.blogsearchable.searchable"
                android:resource="@xml/searchable" />
        </activity>
    </application>

</manifest>

strings.xml

<resources>
    <string name="app_name">BlogSearchable</string>
    <string name="hello_world">Hello world!</string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_search">BlogSearchable</string>
    <string name="search">Search</string>
</resources>

res/xml/searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:hint="@string/search"
    android:label="@string/app_name" />

res/menu/options_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/search"
          android:title="@string/search"
          android:icon="@drawable/ic_action_search"
          android:showAsAction="collapseActionView|ifRoom"
          android:actionViewClass="android.widget.SearchView" />
</menu>

SearchActivity.java
package com.example.blogsearchable;

import java.util.List;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.SearchView;

public class SearchActivity extends Activity {

SearchView mSearchView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
}

@SuppressLint({ "NewApi", "NewApi" })
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);

// Associate searchable configuration with the SearchView
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
MenuItem search = (MenuItem) menu.findItem(R.id.search);
mSearchView = (SearchView) search.getActionView();
mSearchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
setupSearchView(search);
return true;
}

@SuppressLint({ "NewApi", "NewApi" })
private void setupSearchView(MenuItem searchItem) {

SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
if (searchManager != null) {
List<SearchableInfo> searchables = searchManager
.getSearchablesInGlobalSearch();
SearchableInfo info = searchManager
.getSearchableInfo(getComponentName());
for (SearchableInfo inf : searchables) {
if (inf.getSuggestAuthority() != null
&& inf.getSuggestAuthority().startsWith("applications")) {
info = inf;
}
}
mSearchView.setSearchableInfo(info);
}
}

}

Aquí les dejo una imagen de una búsqueda. Por cierto, notad que no se ha modificado ni el layout por defecto que viene de Android.






It works!
Roger Sala,