Author: kmorin Date: 2014-04-02 13:59:25 +0200 (Wed, 02 Apr 2014) New Revision: 40 Url: http://forge.chorem.org/projects/say-my-texts/repository/revisions/40 Log: refs #999 Answer to a sms Added: trunk/src/org/chorem/android/saymytexts/SMS.java Modified: trunk/AndroidManifest.xml trunk/pom.xml trunk/res/values-fr/strings.xml trunk/res/values/strings.xml trunk/src/org/chorem/android/saymytexts/NewTextBroadcastReceiver.java trunk/src/org/chorem/android/saymytexts/SayMyTextService.java trunk/src/org/chorem/android/saymytexts/SettingsActivity.java Modified: trunk/AndroidManifest.xml =================================================================== --- trunk/AndroidManifest.xml 2014-03-31 08:54:08 UTC (rev 39) +++ trunk/AndroidManifest.xml 2014-04-02 11:59:25 UTC (rev 40) @@ -16,6 +16,7 @@ <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-feature android:name="android.hardware.telephony" android:required="true"/> Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2014-03-31 08:54:08 UTC (rev 39) +++ trunk/pom.xml 2014-04-02 11:59:25 UTC (rev 40) @@ -108,7 +108,7 @@ <repositories> <repository> - <id>tutti-public-group</id> + <id>saymytexts-public-group</id> <url>https://nexus.nuiton.org/nexus/content/groups/say-my-texts-group/</url> <snapshots> <enabled>true</enabled> @@ -120,9 +120,10 @@ </releases> </repository> </repositories> + <pluginRepositories> <pluginRepository> - <id>tutti-public-group</id> + <id>saymytexts-public-group</id> <url>https://nexus.nuiton.org/nexus/content/groups/say-my-texts-group/</url> <snapshots> <enabled>true</enabled> Modified: trunk/res/values/strings.xml =================================================================== --- trunk/res/values/strings.xml 2014-03-31 08:54:08 UTC (rev 39) +++ trunk/res/values/strings.xml 2014-04-02 11:59:25 UTC (rev 40) @@ -28,4 +28,13 @@ <string name="crash_dialog_comment_prompt">You might add your comments about the problem below:</string> <string name="crash_dialog_ok_toast">Thank you !</string> + <!-- actions --> + <string name="call_action">call</string> + <string name="answer_action">answer</string> + <string name="quit_action">quit</string> + + <string name="confirm_action">confirm</string> + <string name="modifiy_action">modify</string> + <string name="cancel_action">cancel</string> + </resources> Modified: trunk/res/values-fr/strings.xml =================================================================== --- trunk/res/values-fr/strings.xml 2014-03-31 08:54:08 UTC (rev 39) +++ trunk/res/values-fr/strings.xml 2014-04-02 11:59:25 UTC (rev 40) @@ -27,4 +27,13 @@ <string name="crash_dialog_comment_prompt">Vous pouvez ajouter un commentaire ci-dessous:</string> <string name="crash_dialog_ok_toast">Merci !</string> + <!-- actions --> + <string name="call_action">appeler</string> + <string name="answer_action">répondre</string> + <string name="quit_action">quitter</string> + + <string name="confirm_action">confirmer</string> + <string name="modifiy_action">corriger</string> + <string name="cancel_action">annuler</string> + </resources> Modified: trunk/src/org/chorem/android/saymytexts/NewTextBroadcastReceiver.java =================================================================== --- trunk/src/org/chorem/android/saymytexts/NewTextBroadcastReceiver.java 2014-03-31 08:54:08 UTC (rev 39) +++ trunk/src/org/chorem/android/saymytexts/NewTextBroadcastReceiver.java 2014-04-02 11:59:25 UTC (rev 40) @@ -27,9 +27,14 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; +import android.provider.BaseColumns; +import android.provider.ContactsContract; import android.telephony.SmsMessage; import android.util.Log; @@ -66,11 +71,12 @@ // Get the Sender Phone Number String senderPhoneNumber = msgs[0].getDisplayOriginatingAddress(); + String senderName = getContactDisplayNameByNumber(context, senderPhoneNumber); + SMS sms = new SMS(senderPhoneNumber, senderName, messageReceived); Log.d(TAG,messageReceived); // start the service to say it out loud - serviceIntent.putExtra(SayMyTextService.INTENT_EXTRA_SMS_BODY, messageReceived); - serviceIntent.putExtra(SayMyTextService.INTENT_EXTRA_SMS_SENDER, senderPhoneNumber); + serviceIntent.putExtra(SayMyTextService.INTENT_EXTRA_SMS, sms); context.startService(serviceIntent); } @@ -92,4 +98,32 @@ } } } + + /** + * Finds the contact name in the contact book + * @param number the number of the contact + * @return the name if the contact is known, the number otherwise + */ + protected String getContactDisplayNameByNumber(Context context, String number) { + Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); + String name = number; + + ContentResolver contentResolver = context.getContentResolver(); + Cursor contactLookup = contentResolver.query(uri, + new String[] { BaseColumns._ID, ContactsContract.PhoneLookup.DISPLAY_NAME }, + null, null, null); + + try { + if (contactLookup != null && contactLookup.getCount() > 0) { + contactLookup.moveToNext(); + name = contactLookup.getString(contactLookup.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); + } + } finally { + if (contactLookup != null) { + contactLookup.close(); + } + } + + return name; + } } Added: trunk/src/org/chorem/android/saymytexts/SMS.java =================================================================== --- trunk/src/org/chorem/android/saymytexts/SMS.java (rev 0) +++ trunk/src/org/chorem/android/saymytexts/SMS.java 2014-04-02 11:59:25 UTC (rev 40) @@ -0,0 +1,44 @@ +package org.chorem.android.saymytexts; + +import java.io.Serializable; + +/** + * @author Kevin Morin (Code Lutin) + * @since x.x + */ +public class SMS implements Serializable { + + protected String senderNumber; + protected String senderName; + protected String message; + + public SMS(String senderNumber, String senderName, String message) { + this.senderNumber = senderNumber; + this.senderName = senderName; + this.message = message; + } + + public String getSenderNumber() { + return senderNumber; + } + + public void setSenderNumber(String senderNumber) { + this.senderNumber = senderNumber; + } + + public String getSenderName() { + return senderName; + } + + public void setSenderName(String senderName) { + this.senderName = senderName; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} Modified: trunk/src/org/chorem/android/saymytexts/SayMyTextService.java =================================================================== --- trunk/src/org/chorem/android/saymytexts/SayMyTextService.java 2014-03-31 08:54:08 UTC (rev 39) +++ trunk/src/org/chorem/android/saymytexts/SayMyTextService.java 2014-04-02 11:59:25 UTC (rev 40) @@ -24,8 +24,10 @@ * #L% */ +import android.app.PendingIntent; import android.app.Service; import android.bluetooth.BluetoothDevice; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -68,10 +70,8 @@ private static final String TAG = "SayMyTextService"; - /** Body of the SMS */ - public static final String INTENT_EXTRA_SMS_BODY = "smsBody"; - /** Number of the sender of the SMS */ - public static final String INTENT_EXTRA_SMS_SENDER = "smsSender"; + /** SMS to read */ + public static final String INTENT_EXTRA_SMS = "sms"; /** Bluetooth device which has just connected or disconnected */ public static final String INTENT_EXTRA_BT_DEVICE = "btDevice"; /** If true, the device has just connected, else disconnected */ @@ -88,8 +88,10 @@ protected TextToSpeech textToSpeech; + protected SpeechRecognizer speechRecognizer; + /** texts to read, received before the textospeech is ready or while a call is in progress */ - protected List<String> awaitingTexts = new ArrayList<>(); + protected List<SMS> awaitingTexts = new ArrayList<>(); /** bluetooth devices which are currently connected */ protected Map<BluetoothDevice, Integer> bluetoothDevices = new HashMap<>(); @@ -112,6 +114,7 @@ super.onCreate(); textToSpeech = new TextToSpeech(this, this); + speechRecognizer = SpeechRecognizer.createSpeechRecognizer(SayMyTextService.this); audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); @@ -157,15 +160,11 @@ // if the headset is plugged, or if there is a bluetooth device connected } else if (readingEnabled && (audioManager.isWiredHeadsetOn() || !bluetoothDevices.isEmpty())) { - String sms = intent.getStringExtra(INTENT_EXTRA_SMS_BODY); - String sender = intent.getStringExtra(INTENT_EXTRA_SMS_SENDER); - sender = getContactDisplayNameByNumber(sender); - String text = getString(R.string.sms_received, sender, sms); - + SMS sms = (SMS) intent.getSerializableExtra(INTENT_EXTRA_SMS); if (canSpeak != null && canSpeak) { - requestReading(text); + requestReading(sms); } else { - awaitingTexts.add(text); + awaitingTexts.add(sms); } } @@ -196,6 +195,7 @@ audioManager.stopBluetoothSco(); audioManager.setMode(AudioManager.MODE_NORMAL); } + waiting = false; } }); @@ -225,36 +225,36 @@ /** * Requests the reading of one text through the wired headset or bluetooth device - * @param text the text to read + * @param sms the text to read */ - protected void requestReading(String text) { - requestReading(Collections.singletonList(text)); + protected void requestReading(SMS sms) { + requestReading(Collections.singletonList(sms)); } /** * Requests the reading of a list of texts through the wired headset or bluetooth device - * @param texts the texts to read + * @param smsList the texts to read */ - protected void requestReading(List<String> texts) { + protected void requestReading(List<SMS> smsList) { if (bluetoothDevices.isEmpty()) { - readText(texts, false); + readText(smsList, false); } else { - requestReadingOverBt(texts); + requestReadingOverBt(smsList); } } /** * Starts the connection with the bluetooth device and requests the reading - * @param texts the texts to read + * @param smsList the texts to read */ - protected void requestReadingOverBt(final List<String> texts) { + protected void requestReadingOverBt(final List<SMS> smsList) { registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int state = intent.getExtras().getInt(AudioManager.EXTRA_SCO_AUDIO_STATE); if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) { context.unregisterReceiver(this); - readText(texts, true); + readText(smsList, true); } } }, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)); @@ -264,10 +264,10 @@ /** * Reads the texts out loud - * @param texts the texts to read + * @param smsList the texts to read * @param btConnected if true, adds the utterance id for the bluetooth device */ - protected void readText(List<String> texts, boolean btConnected) { + protected void readText(List<SMS> smsList, boolean btConnected) { HashMap<String, String> params = new HashMap<>(); params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, String.valueOf(AudioManager.STREAM_VOICE_CALL)); params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, btConnected ? BT_UTTERANCE_ID : OTHER_UTTERANCE_ID); @@ -276,7 +276,8 @@ String heisendroidModeEnabledKey = getString(R.string.preference_enable_heisendroid_mode_key); boolean heisendroidModeEnabled = sharedPref.getBoolean(heisendroidModeEnabledKey, true); - for (String text : texts) { + for (SMS sms : smsList) { + waiting = true; if (heisendroidModeEnabled) { textToSpeech.setLanguage(Locale.US); textToSpeech.setSpeechRate(0.3f); @@ -287,6 +288,7 @@ textToSpeech.setLanguage(Locale.getDefault()); textToSpeech.setSpeechRate(1f); textToSpeech.setPitch(1f); + String text = getString(R.string.sms_received, sms.getSenderName(), sms.getMessage()); textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, params); if (heisendroidModeEnabled) { @@ -295,35 +297,92 @@ textToSpeech.setPitch(0.1f); textToSpeech.speak("You're goddamn right.", TextToSpeech.QUEUE_ADD, params); } + + while (waiting) { + } + waiting = true; + recognizeVoice(sms); + while (waiting) { + } } } - /** - * Finds the contact name in the contact book - * @param number the number of the contact - * @return the name if the contact is known, the number otherwise - */ - protected String getContactDisplayNameByNumber(String number) { - Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); - String name = number; + protected void recognizeVoice(final SMS sms) { + Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + // Specify free form input + intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); - ContentResolver contentResolver = getContentResolver(); - Cursor contactLookup = contentResolver.query(uri, - new String[] { BaseColumns._ID, ContactsContract.PhoneLookup.DISPLAY_NAME }, - null, null, null); + speechRecognizer.setRecognitionListener(new RecognitionListener() { + @Override + public void onReadyForSpeech(Bundle params) { + } - try { - if (contactLookup != null && contactLookup.getCount() > 0) { - contactLookup.moveToNext(); - name = contactLookup.getString(contactLookup.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); + @Override + public void onBeginningOfSpeech() { } - } finally { - if (contactLookup != null) { - contactLookup.close(); + + @Override + public void onRmsChanged(float rmsdB) { } - } - return name; + @Override + public void onBufferReceived(byte[] buffer) { + } + + @Override + public void onEndOfSpeech() { + } + + @Override + public void onError(int error) { + Log.d(TAG, "onError " + error); + waiting = false; + } + + @Override + public void onResults(Bundle data) { + List<String> results = data.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); + + Log.d(TAG, "results " + results); + if (results != null) { + + if (results.contains(getString(R.string.call_action))) { + try { + Intent callIntent = new Intent(Intent.ACTION_CALL); + callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + callIntent.setData(Uri.parse("tel:" + sms.getSenderNumber())); + startActivity(callIntent); + + } catch (ActivityNotFoundException activityException) { + Log.e("Calling a Phone Number", "Call failed", activityException); + } + + } else if (results.contains(getString(R.string.answer_action))) { + //TODO ask the user to dictate the message + } else if (results.contains(getString(R.string.quit_action))) { + // do nothing + } else { + //TODO add a counter to ask only twice (or 3 times, or it should be configurable) + recognizeVoice(sms); + } + } + + waiting = false; + } + + @Override + public void onPartialResults(Bundle partialResults) { + } + + @Override + public void onEvent(int eventType, Bundle params) { + } + }); + + speechRecognizer.startListening(intent); } + boolean waiting = false; + } Modified: trunk/src/org/chorem/android/saymytexts/SettingsActivity.java =================================================================== --- trunk/src/org/chorem/android/saymytexts/SettingsActivity.java 2014-03-31 08:54:08 UTC (rev 39) +++ trunk/src/org/chorem/android/saymytexts/SettingsActivity.java 2014-04-02 11:59:25 UTC (rev 40) @@ -162,6 +162,8 @@ PendingIntent pi = PendingIntent.getActivity(context, -1, new Intent(context, SettingsActivity.class), 0); SmsManager sms = SmsManager.getDefault(); sms.sendTextMessage(phoneNumber, null, message, pi, null); + } + } }