I implemented video call in Android using WebRTC.
Call will be made if two users come to same room as their will.
What I want to achieve now, is someone be able to enter a user ID and "Call" him and the other user's phone ring (So there is no problem with webRTC implementation, I just want to implement Ringing behavior).
What I have done so far using Firebase's Realtime database, is that I defined a branch called 'calls', consisting of childs named room name by two user id combination. (so if user1 calls user2, room name will be user1user2).
If user1 calls user2, it sets reqId to 1, and then as user2 listens to any change. he understands that user1 is calling him (and I show incoming call screen) and then it responses by setting reqId to 2, this conversation continues until user2 accepts or cancels the call.
I'm searching for a better solution to achieve this, cause it doesn't seem such a good method and has many problems.
i found the solution.
as if anyone have same question.
for every user, i created a branch called 'call' which is responsible for incoming calls.
and this two functions are what i implemented to perform, or listen for a call:
performCall function:
private DatabaseReference mDatabase;
private static int count = 0;
private void performCall(String s) {
if(count>0)return;
mDatabase = FirebaseDatabase.getInstance().getReference().child("users/"+"USERIDTOCALL"+"/call");
mDatabase.child("roomName").setValue(s);
mDatabase.child("answer").setValue("none");
mDatabase.child("answer").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
switch (Objects.requireNonNull(dataSnapshot.getValue()).toString()){
case "none":
break;
case "yes":
t.cancel();
t.purge();
count =0;
//The Call Should Begin...
break;
case "no":
t.cancel();
t.purge();
count =0;
//RejectedCall
break;
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
//Declare the timer
t = new Timer();
count =0;
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
mDatabase.child("signal").setValue(new Random().nextInt());
count++;
if(count >= 20){
t.cancel();
t.purge();
count =0;
}
}
}, 0, 2000);
}
and listenForCalls function:
private int count =-1;
private boolean isCalling = false;
Runnable runnable = null;
private boolean callingScreenShowed;
AlertDialog alertDialog;
private void listenForCalls() {
mDatabase = FirebaseDatabase.getInstance().getReference().child("users/"+GlobalVars.userName+"/call");
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String answer = dataSnapshot.child("answer").getValue().toString();
if(answer == "yes" || answer =="no") return;
count++;
if(count >= 1){
isCalling = true;
}
if(count == 1 ){
callingScreenShowed= false;
//every 5 seconds check if signaling is active
final int interval = 5000;
Handler handler = new Handler();
runnable = () -> {
if(isCalling){
if(!callingScreenShowed){
//Show A dialog for calling
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setMessage("user with id" + dataSnapshot.child("roomName").getValue() + " Is Calling");
dialog.setTitle("Incoming Call");
dialog.setPositiveButton("YES",
(dialog1, which) -> {
mDatabase.child("answer").setValue("yes");
callingScreenShowed =false;
isCalling = false;
count = -1;
handler.removeCallbacks(runnable);
//Start VideoCall
}
);
dialog.setNegativeButton("cancel", (dialog12, which) -> {
mDatabase.child("answer").setValue("no");
callingScreenShowed =false;
isCalling = false;
count = -1;
handler.removeCallbacks(runnable);
//Clling Rejected
});
alertDialog=dialog.create();
alertDialog.show();
callingScreenShowed = true;
}
}
else {
if(callingScreenShowed){
alertDialog.hide();
}
Log.e("Called","Call Request Ended");
count = -1;
handler.removeCallbacks(runnable);
return;
//Hide Calling Screen
}
isCalling = false;
handler.postDelayed(runnable, interval);
};
runnable.run();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
I have an android app which sends likes to the server.
What i want to do is not to send a like to server immediately but send after 2 secs if user still likes the post.
My like void;
public void rotationAnimation(ImageView button, int source1, int source2){
if(isLikeClicked){
button.setImageResource(source1);
button.startAnimation(rotate_backward);
isLikeClicked = false;
}else{
button.setImageResource(source2);
button.startAnimation(rotate_forward);
isLikeClicked = true;
}
ChangeLikeCount();
if(isReadyToPost)
if(!isLikeClicked){
Like like = new Like();
like.execute(ServerCons.HOST + "unlike");
}else{
Like like = new Like();
like.execute(ServerCons.HOST + "like");
}
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// I thought the solution could be there
}
}, 2000);
}
Do it using isReadyToPost flag:
if(isReadyToPost){
isReadyToPost=false;
}else{
// try after 2 secs for next like
}
and in Handler. postDelayed change isReadyToPost to true after 2 secs:
#Override
public void run() {
isReadyToPost=true;
}
isReadyToPost default value is true.
Try this approach:
boolean isReadyToPost= false;
boolean liked = false;//control like click (witch)
public void onLikePressed() {
if (liked && isReadyToPost) {
sendLikeToServer();//send to server after 2 secs
return;
}
this.isReadyToPost= false;
Toast.makeText(this, "waiting for any dislike... in 2 secs", Toast.LENGTH_SHORT).show();
if (liked){
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
isReadyToPost=true;
onLikePressed();
}
}, 2000);
}//end if
} //end onlikepress
You can try to create a thread that sleeps for 2 sec and after that it checks if user still likes the post then update your database
Can I use a thread for increment a counter and shows it in a frame of Android activity.
Public class MainActivity extendsActivity {
TextView counter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
counter = (TextView) findViewById(R.id.TV_counter);
Thread t = new Thread() {
public void run() {
runOnUiThread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
counter.setText("" + i);
System.out.println("Value of i= " + i);
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
};
t.start();
}
}
I wrote this code, but it run properly in console, but the text view displays i=4 in the terminal, I modified the time to sleep(3000) and the problem persists.
First you don't ever want to put sleep in UI Thread that can lead to unresponsive user interface and that is never good. You should use it just to update your graphics. Try replacing your code with this
Thread t = new Thread() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
final int a = i;
runOnUiThread(new Runnable() {
public void run() {
counter.setText("" + a);
}
});
System.out.println("Value of i= " + i);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
You are going to notice that sleep and for loop is outside UIThread and in your first thread, so basically all of your math is done outside and you just display the results.
This is just a correction of your code and suggestion for further thinking
EDIT: And for you to better understand why your code is not working, you set some value on your TextView, and immediately after you set UIThread to sleep, UIThread blocks instead of giving it time to finish updating graphics, after he finish sleep you set new value, and he never got to update previous one so in the end you see just the last one.
Hope this helps and enjoy your work.
you can use a CountDownTimer, and update your UI in the onTick() method ( this method is executed on the UI Thread):
int i=0;
CountDownTimer timer = new CountDownTimer(5000,1000) {
#Override
public void onTick(long millisUntilFinished) {
// this method will be executed every second ( 1000 ms : the second parameter in the CountDownTimer constructor)
i++;
txt.setText(i);
}
#Override
public void onFinish() {
// TODO Auto-generated method stub
}
};
timer.start();
I am having difficulty figuring out how to resolve this issue, I am not sure if I am not setting up threads correctly or if it is even possible to resolve things properly.
This is an Android app that reads certain strings out as TTS (using the native Android TTS) at certain timings. During this TTS reading, the user should be able to barge-in with instructions such as "Stop" or "Pause." This recognition is done by using the iSpeech API.
Our current solution is to have the TTS running as a Thread that will output the proper strings. Once the user presses a button to begin the voice recognition (using an Intent), the app does voice recognition and handles it perfectly, but then TTS never again outputs anything. Logcat shows the following error:
11-28 02:18:57.072: W/TextToSpeech(16383): speak failed: not bound to TTS engine
I have thought about making the voice recognition a thread of its own that pauses the TTS, but the problem would then be that the timer controlling the TTS would become unsynced with what it should be.
Any advice or help would be appreciated.
Relevant code regarding the thread and the intent are below:
Thread
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Prevent device from sleeping mid build.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_build_order);
mPlayer = MediaPlayer.create(BuildOrderActivity.this, R.raw.bing);
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,"stringId");
tts = new TextToSpeech(BuildOrderActivity.this, new TextToSpeech.OnInitListener() {
#SuppressWarnings("deprecation")
public void onInit(int status) {
if(status != TextToSpeech.ERROR)
{
tts.setLanguage(Locale.US);
tts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener() {
public void onUtteranceCompleted(String utteranceId) {
mPlayer.start();
}
});
}
}
});
buttonStart = (Button) findViewById(R.id.buttonStartBuild);
buttonStart.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startBuild = new StartBuildRunnable();
Thread t = new Thread(startBuild);
t.start();
}
});
...//code continues oncreate setup for the view}
public class StartBuildRunnable implements Runnable {
public void run() {
double delay;
buildActions = parseBuildXMLAction();
buildTimes = parseBuildXMLTime();
say("Build has started");
delayForNextAction((getSeconds(buildTimes.get(0)) * 1000));
say(buildActions.get(0));
for (int i = 1; i < buildActions.size(); i++)
{
delay = calcDelayUntilNextAction(buildTimes.get(i - 1), buildTimes.get(i));
delayForNextAction((long) (delay * 1000));
say(buildActions.get(i));
//listViewBuildItems.setSelection(i);
}
say("Build has completed");
}
}
Intent
/**
* Fire an intent to start the speech recognition activity.
* #throws InvalidApiKeyException
*/
private void startRecognition() {
setupFreeFormDictation();
try {
recognizer.startRecord(new SpeechRecognizerEvent() {
#Override
public void onRecordingComplete() {
updateInfoMessage("Recording completed.");
}
#Override
public void onRecognitionComplete(SpeechResult result) {
Log.v(TAG, "Recognition complete");
//TODO: Once something is recognized, tie it to an action and continue recognizing.
// currently recognizes something in the grammar and then stops listening until
// the next button press.
if (result != null) {
Log.d(TAG, "Text Result:" + result.getText());
Log.d(TAG, "Text Conf:" + result.getConfidence());
updateInfoMessage("Result: " + result.getText() + "\n\nconfidence: " + result.getConfidence());
} else
Log.d(TAG, "Result is null...");
}
#Override
public void onRecordingCancelled() {
updateInfoMessage("Recording cancelled.");
}
#Override
public void onError(Exception exception) {
updateInfoMessage("ERROR: " + exception.getMessage());
exception.printStackTrace();
}
});
} catch (BusyException e) {
e.printStackTrace();
} catch (NoNetworkException e) {
e.printStackTrace();
}
}
I'm playing text with android TTS - android.speech.tts.TextToSpeech
I use: TextToSpeech.speak to speak and .stop to stop. Is there a way to pause the text also?
The TTS SDK doesn't have any pause functionality that I know of. But you could use synthesizeToFile() to create an audio file that contains the TTS output. Then, you would use a MediaPlayer object to play, pause, and stop playing the file. Depending on how long the text string is, it might take a little longer for audio to be produced because the synthesizeToFile() function would have to complete the entire file before you could play it, but this delay should be acceptable for most applications.
I used splitting of string and used playsilence() like below:
public void speakSpeech(String speech) {
HashMap<String, String> myHash = new HashMap<String, String>();
myHash.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "done");
String[] splitspeech = speech.split("\\.");
for (int i = 0; i < splitspeech.length; i++) {
if (i == 0) { // Use for the first splited text to flush on audio stream
textToSpeech.speak(splitspeech[i].toString().trim(),TextToSpeech.QUEUE_FLUSH, myHash);
} else { // add the new test on previous then play the TTS
textToSpeech.speak(splitspeech[i].toString().trim(), TextToSpeech.QUEUE_ADD,myHash);
}
textToSpeech.playSilence(750, TextToSpeech.QUEUE_ADD, null);
}
}
You can make the TTS pause between sentences, or anywhere you want by adding up to three periods (".") all followed by a single space " ". The example below has a long pause at the beginning, and again before the message body. I'm not sure that is what you are after though.
private final BroadcastReceiver SMScatcher = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(
"android.provider.Telephony.SMS_RECEIVED")) {
// if(message starts with SMStretcher recognize BYTE)
StringBuilder sb = new StringBuilder();
/*
* The SMS-Messages are 'hiding' within the extras of the
* Intent.
*/
Bundle bundle = intent.getExtras();
if (bundle != null) {
/* Get all messages contained in the Intent */
Object[] pdusObj = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdusObj.length];
for (int i = 0; i < pdusObj.length; i++) {
messages[i] = SmsMessage
.createFromPdu((byte[]) pdusObj[i]);
}
/* Feed the StringBuilder with all Messages found. */
for (SmsMessage currentMessage : messages) {
// periods are to pause
sb.append("... Message From: ");
/* Sender-Number */
sb.append(currentMessage.getDisplayOriginatingAddress());
sb.append(".. ");
/* Actual Message-Content */
sb.append(currentMessage.getDisplayMessageBody());
}
// Toast.makeText(application, sb.toString(),
// Toast.LENGTH_LONG).show();
if (mTtsReady) {
try {
mTts.speak(sb.toString(), TextToSpeech.QUEUE_ADD,
null);
} catch (Exception e) {
Toast.makeText(application, "TTS Not ready",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
}
}
};
If you omit the space after the last period it will (or may) not work as expected.
In the absence of a pause option, you can add silence for the duration of when you want to delay the TTS Engine speaking. This of course would have to be a predetermined 'pause' and wouldn't help to include functionality of a pause button, for example.
For API < 21 : public int playSilence (long durationInMs, int queueMode, HashMap params)
For > 21 : public int playSilentUtterance (long durationInMs, int queueMode, String utteranceId)
Remember to use TextToSpeech.QUEUE_ADD rather than TextToSpeech.QUEUE_FLUSH otherwise it will clear the previously started speech.
I used a different approach.
Seperate your text into sentences
Speak every sentence one by one and keep track of the spoken sentence
pause will stop the text instantly
resume will start at the beginning of the last spoken sentence
Kotlin code:
class VoiceService {
private lateinit var textToSpeech: TextToSpeech
var sentenceCounter: Int = 0
var myList: List<String> = ArrayList()
fun resume() {
sentenceCounter -= 1
speakText()
}
fun pause() {
textToSpeech.stop()
}
fun stop() {
sentenceCounter = 0
textToSpeech.stop()
}
fun speakText() {
var myText = "This is some text to speak. This is more text to speak."
myList =myText.split(".")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, null, utteranceId)
sentenceCounter++
} else {
var map: HashMap<String, String> = LinkedHashMap<String, String>()
map[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = utteranceId
textToSpeech.speak(myList[sentenceCounter], TextToSpeech.QUEUE_FLUSH, map)
sentenceCounter++
}
}
override fun onDone(p0: String?) {
if (sentenceCounter < myList.size) {
speakText()
} else {
speakNextText()
}
}
}
I haven't yet tried this, but I need to do the same thing. My thinking is to first split your speech text into an array of words.
Then create a recursive function that plays the next word after the current word is finished, while keeping a counter of the current word.
divide the messages into parts and listen for last utterance by using onutteranceprogress listener
tts.playSilence(1250, TextToSpeech.QUEUE_ADD, null);
It seems that if you put a period after a word AND start the next word with a capital letter, just like a new sentence, like this:
after we came home. We ate dinner.
the "home. We" will then have a pause in it.
This becomes a grammatically strange way of writing it.
So far I have only tested this in my own language, Swedish.
It might be important that the space is there.
Also, an escaped quote (\") seems to have it pause somewhat as well - at least, if you put it around a word it adds space around the word.
This solution is not perfect, but an alternative to #Aaron C's solution may be to create a custom text to speech class like the below. This solution may work well enough if your text is relatively short and spoken words per minute is accurate enough for the language you are using.
private class CustomTextToSpeech extends TextToSpeech {
private static final double WORDS_PER_MS = (double)190/60/1000;
long startTimestamp = 0;
long pauseTimestamp = 0;
private Handler handler;
private Runnable speakRunnable;
StringBuilder textToSpeechBuilder;
private boolean isPaused = false;
public CustomTextToSpeech(Context context, OnInitListener initListener){
super(context, initListener);
setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String arg0) {
Log.d(TAG, "tts done. " + arg0);
startTimestamp = 0;
pauseTimestamp = 0;
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
#Override
public void onError(String arg0) {
Log.e(TAG, "tts error. " + arg0);
}
#Override
public void onStart(String arg0) {
Log.d(TAG, "tts start. " + arg0);
setStartTimestamp(System.currentTimeMillis());
}
});
handler = new Handler();
speakRunnable = new Runnable() {
#Override
public void run() {
speak();
}
};
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
public void setStartTimestamp(long timestamp) {
startTimestamp = timestamp;
}
public void setPauseTimestamp(long timestamp) {
pauseTimestamp = timestamp;
}
public boolean isPaused(){
return (startTimestamp > 0 && pauseTimestamp > 0);
}
public void resume(){
if(handler != null && isPaused){
if(startTimestamp > 0 && pauseTimestamp > 0){
handler.postDelayed(speakRunnable, TTS_SETUP_TIME_MS);
} else {
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
isPaused = false;
}
public void pause(){
isPaused = true;
if (handler != null) {
handler.removeCallbacks(speakRunnable);
handler.removeMessages(1);
}
if(isSpeaking()){
setPauseTimestamp(System.currentTimeMillis());
}
stop();
}
public void utter(){
if(handler != null){
handler.postDelayed(speakRunnable, TTS_INTERVAL_MS);
}
}
public void speak(){
Log.d(TAG, "textToSpeechBuilder: " + textToSpeechBuilder.toString());
if(isPaused()){
String[] words = textToSpeechBuilder.toString().split(" ");
int wordsAlreadySpoken = (int)Math.round((pauseTimestamp - startTimestamp)*WORDS_PER_MS);
words = Arrays.copyOfRange(words, wordsAlreadySpoken-1, words.length);
textToSpeechBuilder = new StringBuilder();
for(String s : words){
textToSpeechBuilder.append(s);
textToSpeechBuilder.append(" ");
}
} else {
textToSpeechBuilder = new StringBuilder(getResources().getString(R.string.talkback_tips));
}
if (tts != null && languageAvailable)
speak(textToSpeechBuilder.toString(), TextToSpeech.QUEUE_FLUSH, new Bundle(), "utter");
}
}