/*
* Protocoder
* A prototyping platform for Android devices
*
* Victor Diaz Barrales victormdb@gmail.com
*
* Copyright (C) 2014 Victor Diaz
* Copyright (C) 2013 Motorola Mobility LLC
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
package org.protocoderrunner.apprunner.api;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.speech.RecognizerIntent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import org.json.JSONArray;
import org.protocoderrunner.apidoc.annotation.APIMethod;
import org.protocoderrunner.apidoc.annotation.APIParam;
import org.protocoderrunner.apprunner.AppRunnerActivity;
import org.protocoderrunner.apprunner.AppRunnerSettings;
import org.protocoderrunner.apprunner.PInterface;
import org.protocoderrunner.apprunner.ProtocoderScript;
import org.protocoderrunner.apprunner.api.other.PMidi;
import org.protocoderrunner.apprunner.api.other.PPureData;
import org.protocoderrunner.media.Audio;
import org.protocoderrunner.media.AudioService;
import org.protocoderrunner.sensors.WhatIsRunning;
import org.protocoderrunner.utils.MLog;
import org.puredata.android.service.PdService;
import org.puredata.android.utils.PdUiDispatcher;
import org.puredata.core.PdBase;
import org.puredata.core.PdReceiver;
import org.puredata.core.utils.PdDispatcher;
import java.io.File;
import java.util.Arrays;
import java.util.Locale;
public class PMedia extends PInterface {
String TAG = "PMedia";
private HeadSetReceiver headsetPluggedReceiver;
private MicPluggedCB headsetCallbackfn;
public PMedia(Context a) {
super(a);
WhatIsRunning.getInstance().add(this);
}
@ProtocoderScript
@APIMethod(description = "Play a sound file", example = "media.playSound(fileName);")
@APIParam(params = { "fileName" })
public MediaPlayer playSound(String url) {
if (url.startsWith("http://") == false) {
url = AppRunnerSettings.get().project.getStoragePath() + File.separator + url;
}
MediaPlayer player = Audio.playSound(url, 100);
WhatIsRunning.getInstance().add(player);
return player;
}
@ProtocoderScript
@APIMethod(description = "Set the main volume", example = "media.playSound(fileName);")
@APIParam(params = { "volume" })
public void setVolume(int volume) {
appRunnerActivity.get().setVolume(volume);
}
@ProtocoderScript
@APIMethod(description = "Routes the audio through the speakers", example = "media.playSound(fileName);")
@APIParam(params = { "" })
public void setAudioOnSpeakers(boolean b) {
AudioManager audioManager = (AudioManager) a.get().getSystemService(Context.AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(!b);
}
@ProtocoderScript
@APIMethod(description = "Enable sounds effects (default false)", example = "")
@APIParam(params = { "boolean" })
public void enableSoundEffects(boolean b) {
appRunnerActivity.get().setEnableSoundEffects(b);
}
// --------- initPDPatch ---------//
interface initPDPatchCB {
void event(PDReturn o);
}
class PDReturn {
String type;
protected String source;
protected Object data;
}
@ProtocoderScript
@APIMethod(description = "Loads and initializes a PureData patch http://www.puredata.info", example = "")
@APIParam(params = { "fileName", "function(objectType, value)" })
public PPureData initPDPatch(String fileName, final initPDPatchCB callbackfn) {
String filePath = AppRunnerSettings.get().project.getStoragePath() + File.separator + fileName;
PdReceiver receiver = new PdReceiver() {
@Override
public void print(String s) {
MLog.d(TAG, "pd >>" + s);
PDReturn o = new PDReturn();
o.type = "print";
o.data = s;
callbackfn.event(o);
}
@Override
public void receiveBang(String source) {
MLog.d(TAG, "bang");
PDReturn o = new PDReturn();
o.type = "bang";
o.source = source;
callbackfn.event(o);
}
@Override
public void receiveFloat(String source, float x) {
MLog.d(TAG, "float: " + x);
PDReturn o = new PDReturn();
o.type = "float";
o.source = source;
o.data = x;
callbackfn.event(o);
}
@Override
public void receiveList(String source, Object... args) {
MLog.d(TAG, "list: " + Arrays.toString(args));
JSONArray jsonArray = new JSONArray();
for (Object arg : args) {
jsonArray.put(arg);
}
PDReturn o = new PDReturn();
o.type = "list";
o.source = source;
o.data = jsonArray;
callbackfn.event(o);
}
@Override
public void receiveMessage(String source, String symbol, Object... args) {
MLog.d(TAG, "message: " + Arrays.toString(args));
JSONArray jsonArray = new JSONArray();
for (Object arg : args) {
jsonArray.put(arg);
}
PDReturn o = new PDReturn();
o.type = "message";
o.source = source;
o.data = jsonArray;
callbackfn.event(o);
}
@Override
public void receiveSymbol(String source, String symbol) {
MLog.d(TAG, "symbol: " + symbol);
PDReturn o = new PDReturn();
o.type = "symbol";
o.source = source;
o.data = symbol;
callbackfn.event(o);
}
public void stop() {
a.get().unbindService(AudioService.pdConnection);
}
};
// create and install the dispatcher
PdDispatcher dispatcher = new PdUiDispatcher() {
@Override
public void print(String s) {
Log.i("Pd print", s);
}
};
PdBase.setReceiver(dispatcher);
// PdBase.setReceiver(receiver);
PdBase.subscribe("android");
// start pure data sound engine
AudioService.file = filePath;
Intent intent = new Intent((a.get()), PdService.class);
// intent.putExtra("file", "qq.pd");
(a.get()).bindService(intent, AudioService.pdConnection, Context.BIND_AUTO_CREATE);
initSystemServices();
WhatIsRunning.getInstance().add(AudioService.pdConnection);
return new PPureData();
}
private void initSystemServices() {
TelephonyManager telephonyManager = (TelephonyManager) a.get().getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (AudioService.pdService == null) {
return;
}
if (state == TelephonyManager.CALL_STATE_IDLE) {
AudioService.start();
} else {
AudioService.pdService.stopAudio();
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
MediaRecorder recorder;
ProgressDialog mProgressDialog;
boolean showProgress = false;
@ProtocoderScript
@APIMethod(description = "Record a sound with the microphone", example = "")
@APIParam(params = { "fileName", "showProgressBoolean" })
public void recordAudio(String fileName, boolean showProgress) {
this.showProgress = showProgress;
recorder = new MediaRecorder();
// ContentValues values = new ContentValues(3);
// values.put(MediaStore.MediaColumns.TITLE, fileName);
recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
// recorder.setAudioEncoder(MediaRecorder.getAudioSourceMax());
recorder.setAudioEncodingBitRate(16);
recorder.setAudioSamplingRate(44100);
recorder.setOutputFile(AppRunnerSettings.get().project.getStoragePath() + File.separator + fileName);
try {
recorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mProgressDialog = new ProgressDialog(a.get());
mProgressDialog.setTitle("Record!");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Stop recording",
new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int whichButton) {
mProgressDialog.dismiss();
stopRecording();
}
});
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface p1) {
stopRecording();
}
});
recorder.start();
if (showProgress == true) {
mProgressDialog.show();
}
}
@ProtocoderScript
@APIMethod(description = "Stops recording", example = "")
@APIParam(params = { "" })
public void stopRecording() {
try {
if (recorder != null) {
recorder.stop();
recorder.reset();
recorder.release();
recorder = null;
}
} catch (Exception e) {
}
if (showProgress) {
mProgressDialog.dismiss();
showProgress = false;
}
}
@ProtocoderScript
@APIMethod(description = "Says a text with voice", example = "media.textToSpeech('hello world');")
@APIParam(params = { "text" })
public void textToSpeech(String text) {
Audio.speak(a.get(), text, Locale.getDefault());
}
@ProtocoderScript
@APIMethod(description = "Says a text with voice using a defined locale", example = "media.textToSpeech('hello world');")
@APIParam(params = { "text", "Locale" })
public void textToSpeech(String text, Locale locale) {
Audio.speak(a.get(), text, locale);
}
// --------- startVoiceRecognition ---------//
interface StartVoiceRecognitionCB {
void event(String responseString);
}
@ProtocoderScript
@APIMethod(description = "Fires the voice recognition and returns the best match", example = "media.startVoiceRecognition(function(text) { console.log(text) } );")
@APIParam(params = { "function(recognizedText)" })
public void startVoiceRecognition(final StartVoiceRecognitionCB callbackfn) {
((AppRunnerActivity)a.get()).addVoiceRecognitionListener(new onVoiceRecognitionListener() {
@Override
public void onNewResult(String text) {
MLog.d(TAG, "" + text);
callbackfn.event(text);
}
});
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Tell me something!");
appRunnerActivity.get().startActivityForResult(intent, AppRunnerActivity.VOICE_RECOGNITION_REQUEST_CODE);
}
public interface onVoiceRecognitionListener {
public void onNewResult(String text);
}
public void stop() {
stopRecording();
a.get().unregisterReceiver(headsetPluggedReceiver);
}
@ProtocoderScript
@APIMethod(description = "Start a connected midi device", example = "media.startVoiceRecognition(function(text) { console.log(text) } );")
@APIParam(params = { "function(recognizedText)" })
public void startMidiDevice(final PMidi.MidiDeviceEventCB callbackfn) {
PMidi pMidi = new PMidi(a.get(), callbackfn);
}
public boolean isHeadsetPlugged() {
AudioManager audioManager = (AudioManager) a.get().getSystemService(Context.AUDIO_SERVICE);
return audioManager.isWiredHeadsetOn();
}
interface MicPluggedCB {
void event(boolean b);
}
public void startHeadsetListener(MicPluggedCB callbackfn) {
WhatIsRunning.getInstance().add(this);
headsetCallbackfn = callbackfn;
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
headsetPluggedReceiver = new HeadSetReceiver();
a.get().registerReceiver(headsetPluggedReceiver, filter);
}
private class HeadSetReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", -1);
switch (state) {
case 0:
Log.d(TAG, "Headset unplugged");
headsetCallbackfn.event(false);
break;
case 1:
Log.d(TAG, "Headset plugged");
headsetCallbackfn.event(true);
break;
}
}
}
}
}