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) {
}
});
}
Related
As I have a text longer than the 4000 chars limit for TTS, I divided the string to more parts and I am adding these parts in cycle to tts queue, for example:
int pos = 0;
while(true) {
String var = "";
try {
var = str.substring(pos, 3999);
pos += 3999;
} catch(Exception e) {
var = str.substring(pos, str.length());
break;
}
Bundle params = new Bundle();
params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "");
tts.speak(var, TextToSpeech.QUEUE_ADD, params, "myID");
}
However this works, but after the END of speech I need to change the stop button again for play button.
I am doing this button change in onDone().
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String utteranceId) {
Log.d("Speak", "TTS finished");
runOnUiThread(new Runnable() {
public void run() {
Button view2 = findViewById(R.id.speech);
view2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.play, 0, 0, 0);
}
});
}
#Override
public void onError(String utteranceId) {
}
#Override
public void onStart(String utteranceId) {
}
});
The problem is, the onDone is calling after each Queue finishes. So if there are more parts in the queue, the onDone is called many times. I can't determine, when the last queue is processed so when to change the button.
I think I have found a solution - by sending a different utteranceId in
tts.speak(var, TextToSpeech.QUEUE_ADD, params, id1);
and in next condition id2. Then checking in onDone the utteranceId and according it change the button.
Seems working very well.
So, I have a boolean called nuanceWaiting it's initially set to true. I immediate run a runnable loop that checks if nuanceWaiting is true or false.
protected void onCreate(Bundle savedInstanceState) {
...
nuanceWaiting = true;
...
}
#Override
protected void onStart() {
....
soundMeterLoop();
}
public void soundMeterLoop() {
soundMeterHandler = new Handler();
soundMeterRunnable = new Runnable() {
#Override
public void run() {
if(nuanceWaiting) {
//do my stuff
amplitude = soundMeter.getAmplitude();
if (amplitude > threshold) {
decibelLevelOutput.setTextColor(Color.RED);
startNuance();
} else {
decibelLevelOutput.setTextColor(Color.BLACK);
}
}
soundMeterHandler.postDelayed(this, 100);
}
};
soundMeterHandler.postDelayed(soundMeterRunnable, 100);
}
public void startNuance() {
nuanceWaiting = false;
nuance.toggleReco();
}
public void stopNuance() {
Log.d("SpeechKit", "stopNuance");
nuanceWaiting = true;
Log.d("SpeechKit", "nuanceWaiting " + nuanceWaiting);
}
Now, for some reason, once I call false, now, nuance.toggleReco() goes to another class and when it's finished, it calls stopNuance();
nuanceWaiting becomes false (showing in the second log), but when I check a log in the runnable, it still says true and never "stays" false when running the runnable again. Any idea as to why it doesn't stick to being false?
Below is what nuance.toggleReco(); does
public void toggleReco() {
Log.d("SpeechKit", "In "+state);
switch (state) {
case IDLE:
recognize();
break;
case LISTENING:
stopRecording();
break;
case PROCESSING:
cancel();
break;
}
}
It's usually in the IDLE state, so I'll follow that method,
private void recognize() {
//Setup our ASR transaction options.
Transaction.Options options = new Transaction.Options();
options.setRecognitionType(RecognitionType.DICTATION);
options.setDetection(DetectionType.Short);
options.setLanguage(new Language("eng-USA"));
options.setEarcons(startEarcon, stopEarcon, errorEarcon, cancelEarcon);
//Start listening
recoTransaction = session.recognize(options, recoListener);
}
private Transaction.Listener recoListener = new Transaction.Listener() {
#Override
public void onStartedRecording(Transaction transaction) {
Log.d("SpeechKit", "onStartedRecording");
//We have started recording the users voice.
//We should update our state and start polling their volume.
state = State.LISTENING;
startAudioLevelPoll();
}
#Override
public void onFinishedRecording(Transaction transaction) {
Log.d("SpeechKit", "onFinishedRecording");
//We have finished recording the users voice.
//We should update our state and stop polling their volume.
state = State.PROCESSING;
stopAudioLevelPoll();
avatar.stopNuance();
}
#Override
public void onRecognition(Transaction transaction, Recognition recognition) {
//We have received a transcription of the users voice from the server.
state = State.IDLE;
Log.d("SpeechKit", "onRecognition: " + recognition.getText());
voiceRecognizeText = recognition.getText();
voiceRecognize = (TextView) activity.findViewById(R.id.voiceRecognize);
voiceRecognize.setText(voiceRecognizeText);
}
#Override
public void onSuccess(Transaction transaction, String s) {
Log.d("SpeechKit", "onSuccess");
//Notification of a successful transaction. Nothing to do here.
}
#Override
public void onError(Transaction transaction, String s, TransactionException e) {
Log.e("SpeechKit", "onError: " + e.getMessage() + ". " + s);
//Something went wrong. Ensure that your credentials are correct.
//The user could also be offline, so be sure to handle this case appropriately.
//We will simply reset to the idle state.
state = State.IDLE;
avatar.stopNuance();
}
};
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
Android question:
When I set text to a view, when the view will update the UI?
Here is my case 1:
for(int i=0;i< 2000; i++){
aTextView.setText("a"+i);
}
aTextView is a MyTextView, that extends from TextView. I overwirte the onDraw as:
public static int counterP = 0;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
counterP++;
Log.d("MYButton", "onDraw: " + counterP);
}
From the log, I can see, the printed counterP is not consequent. In the loop, the onDraw method is called only 2-4 times.
I did another test, case 2:
boolean notInit = true;
List<String> cmdList = null;
long stSelfRefresh = 0;
String contentStr;
TextView selfTv;
public void onSelfRefreshTv(View v) {
if (cmdList == null || cmdList.isEmpty()) {
Log.e(TAG, "empty now, reset");
notInit = true;
}
if (notInit) {
cmdList = new ArrayList<String>();
for (int i = 0; i < 2000; i++) {
cmdList.add("a" + i);
}
stSelfRefresh = SystemClock.uptimeMillis();
notInit = false;
selfTv = (MyTextView) findViewById(R.id.mytv);
}
contentStr = cmdList.remove(0);
Log.d(TAG, "contentStr = " + contentStr);
selfTv.setText(contentStr);
if (!cmdList.isEmpty()) {
if (!mHandler.sendEmptyMessage(99)) {
Log.e(TAG, "SKIP my self");
}
} else {
Log.d(TAG, "Cost time: " + (SystemClock.uptimeMillis() - stSelfRefresh));
}
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 99:
onSelfRefreshTv(null);
break;
default:
break;
}
}
};
The result of case 2 is the counterP is printed out consequent. 1-2000.
I don't know why the textView is updated for each time?
Do you have any idea for this?
Thanks in advance.
******add case 3***********
for(int i=0;i< 2000;i++){
contentStr = cmdList.remove(0);
mHandler.sendEmptyMessage(100);
}
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 100:
selfTv.setText(contentStr);
break;
default:
break;
}
}
};
******end of case 3**********
The test result for case 3 is similar with case 1.
It looks like you are running your code on the UI Thread.
This means: as long as your code is running, the drawing code can not run. The onDraw() is called after your loop finished counting and only the last value is actually drawn on the display.
It's as simple as that.
If you want something count up on your display, you can use a AsyncTask to move the counting part into a non-UI Thread:
new AsyncTask<Void,Integer, Void>() {
#Override
protected Void doInBackground(Void... voids) {
for (int i =0; i<2000; i++) {
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
aTextView.setText("a"+values[0]);
}
};
Your second case is like this:
You prepare the list with all 2000 elements.
Set the first element of the list to the TextView and remove the item from the list.
Send yourself a message to run on the UI Thread as soon as the system thinks it is appropriate.
Let go the control of the UI Thread.
Your code is actually finished and the system calls onDraw().
The message from 4 gets processed and you start again with 2.
You see that the system gets control over the UI Thread in the middle of your loop. That's why it's counting visibly.
Case 3 is different from 2:
You are sending 2000 messages in on loop without letting the system handle it.
In my application, I have a button that starts an AsyncTask that downloads data with coordinates for google maps, then draws a marker on the map at the following coordinates. I want to run this every 10 seconds until the user presses the button again.
Here's my code for the handler:
class handleMap{
Handler mHandler = new Handler();
Runnable mTask = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
while(btnRefreshPressed == false){
try{
new getGoogleMap().execute();
mHandler.postDelayed(mTask, INTERVAL);
Thread.sleep(INTERVAL);
} catch(Exception e){
System.out.println(e.toString());
}
}
}
};
public void starReapetingClass (){
hMap.starReapetingClass();
}
public void stopDoing(){
mHandler.stopDoing();
}
}
And in the menubutton where it is called:
case R.id.id_Refresh:
handleMap hMap = new handleMap();
if(btnRefreshPressed == true){
menuItem = item;
menuItem.setActionView(R.layout.progressbar);
menuItem.expandActionView();
fRun += 1;
btnRefreshPressed = false;
hMap.run();
}else if(btnRefreshPressed == false){
if(fRun > 0){
menuItem.collapseActionView();
menuItem.setActionView(null);
}
btnRefreshPressed = true;
hMap.stopHandler();
}
This currently causes the application to freeze, and the system outputs a dialog saying that the app isn't responding, and asking if I want to close or wait.
I suspect it has to with the while statement, but I don't get any errors in logcat.
Thanks in advance.
Just use:
private int mSampleDurationTime = 10000;
private boolean continueToRun = true;
mHandler.postDelayed(mRunnable, mSampleDurationTime);
where mRunnable is your task:
private final Runnable mRunnable = new Runnable() {
//...
public void run() {
...
if(continueToRun == true){
mHandler.postDelayed(mRunnable, mSampleDurationTime);
}
}
...
};
First time you call postDelayed and invoke new Runnable(). After, if you want to continue,
call the same method into run()