diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e14b038..f35621a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -74,16 +74,30 @@
+ android:label="@string/activity_call_label" />
+
+
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/CallsHelper.java b/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/CallsHelper.java
index 851a540..a2de7da 100644
--- a/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/CallsHelper.java
+++ b/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/CallsHelper.java
@@ -9,6 +9,7 @@ import org.communiquons.android.comunic.client.data.models.APIRequest;
import org.communiquons.android.comunic.client.data.models.APIResponse;
import org.communiquons.android.comunic.client.data.models.CallInformation;
import org.communiquons.android.comunic.client.data.models.CallMember;
+import org.communiquons.android.comunic.client.data.models.CallResponse;
import org.communiquons.android.comunic.client.data.models.CallsConfiguration;
import org.communiquons.android.comunic.client.data.models.NextPendingCallInformation;
import org.json.JSONArray;
@@ -171,6 +172,26 @@ public class CallsHelper extends BaseHelper {
return name;
}
+ /**
+ * Respond to a call request
+ *
+ * @param response The response to the call
+ * @return TRUE for a success / FALSE else
+ */
+ public boolean respondToCall(@NonNull CallResponse response){
+
+ APIRequest request = new APIRequest(getContext(), "calls/respond");
+ request.addInt("call_id", response.getCallID());
+ request.addBoolean("accept", response.isAccept());
+
+ try {
+ return request.exec().getJSONObject().has("success");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
/**
* Turn a {@link JSONObject} object into a {@link CallsConfiguration} object.
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/models/CallResponse.java b/app/src/main/java/org/communiquons/android/comunic/client/data/models/CallResponse.java
new file mode 100644
index 0000000..1fb6d70
--- /dev/null
+++ b/app/src/main/java/org/communiquons/android/comunic/client/data/models/CallResponse.java
@@ -0,0 +1,34 @@
+package org.communiquons.android.comunic.client.data.models;
+
+/**
+ * Response of a user to a call
+ *
+ * @author Pierre HUBERT
+ */
+public class CallResponse {
+
+ //Private fields
+ private int callID;
+ private boolean accept;
+
+ public CallResponse(int callID, boolean accept) {
+ this.callID = callID;
+ this.accept = accept;
+ }
+
+ public int getCallID() {
+ return callID;
+ }
+
+ public void setCallID(int callID) {
+ this.callID = callID;
+ }
+
+ public boolean isAccept() {
+ return accept;
+ }
+
+ public void setAccept(boolean accept) {
+ this.accept = accept;
+ }
+}
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java
index a8787fc..81df000 100644
--- a/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java
+++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/Constants.java
@@ -72,6 +72,13 @@ public final class Constants {
public static final String ACTION_NOTIFY_NEW_CALLS_AVAILABLE =
"org.communiquons.android.comunic.client.NEW_CALLS_AVAILABLE";
+
+ /**
+ * Intent used to reject incoming call
+ */
+ public static final String ACTION_REJECT_INCOMING_CALL =
+ "org.communiquons.android.comunic.client.REJECT_INCOMING_CALL";
+
}
/**
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java
index 1f38cf9..64cb3bd 100644
--- a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java
+++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/BaseActivity.java
@@ -1,8 +1,8 @@
package org.communiquons.android.comunic.client.ui.activities;
import android.os.Bundle;
-import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
@@ -20,6 +20,12 @@ public abstract class BaseActivity extends AppCompatActivity {
*/
private SafeAsyncTasksManager mSafeAsyncTasksManager = null;
+ /**
+ * Current active class
+ */
+ @Nullable
+ private static String mActiveActivityName = null;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -36,6 +42,22 @@ public abstract class BaseActivity extends AppCompatActivity {
mSafeAsyncTasksManager.unsetAllTasks();
}
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ mActiveActivityName = getClass().getSimpleName();
+ }
+
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ if(getClass().getSimpleName().equals(mActiveActivityName))
+ mActiveActivityName = null;
+ }
+
@NonNull
@Override
public ActionBar getSupportActionBar() {
@@ -51,4 +73,14 @@ public abstract class BaseActivity extends AppCompatActivity {
public SafeAsyncTasksManager getTasksManager() {
return mSafeAsyncTasksManager;
}
+
+ /**
+ * Check out whether an activity is the active activity or not
+ *
+ * @param activity The activity to check
+ * @return True if the activity is the active one / false else
+ */
+ public static boolean IsActiveActivity(Class extends BaseActivity> activity){
+ return activity.getSimpleName().equals(mActiveActivityName);
+ }
}
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/CallActivity.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/CallActivity.java
index 5a3b799..df33bdb 100644
--- a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/CallActivity.java
+++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/CallActivity.java
@@ -1,7 +1,6 @@
package org.communiquons.android.comunic.client.ui.activities;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import org.communiquons.android.comunic.client.R;
@@ -14,7 +13,7 @@ import java.util.Objects;
*
* @author Pierre HUBERT
*/
-public class CallActivity extends AppCompatActivity {
+public class CallActivity extends BaseActivity {
/**
* Mandatory argument that includes call id
@@ -29,9 +28,6 @@ public class CallActivity extends AppCompatActivity {
//Hide call bar
Objects.requireNonNull(getSupportActionBar()).hide();
-
- ((TextView)findViewById(R.id.call_id)).setText(
- "Call " + getIntent().getExtras().getInt(ARGUMENT_CALL_ID));
}
@Override
@@ -40,5 +36,8 @@ public class CallActivity extends AppCompatActivity {
//Hide call notifications
PendingCallsBroadcastReceiver.RemoveCallNotification(this);
+
+ ((TextView)findViewById(R.id.call_id)).setText(
+ "Call " + getIntent().getExtras().getInt(ARGUMENT_CALL_ID));
}
}
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/IncomingCallActivity.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/IncomingCallActivity.java
new file mode 100644
index 0000000..fa0417c
--- /dev/null
+++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/activities/IncomingCallActivity.java
@@ -0,0 +1,159 @@
+package org.communiquons.android.comunic.client.ui.activities;
+
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.support.annotation.Nullable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.communiquons.android.comunic.client.R;
+import org.communiquons.android.comunic.client.data.models.NextPendingCallInformation;
+import org.communiquons.android.comunic.client.data.utils.AccountUtils;
+import org.communiquons.android.comunic.client.ui.Constants;
+import org.communiquons.android.comunic.client.ui.asynctasks.GetNextPendingCallTask;
+import org.communiquons.android.comunic.client.ui.asynctasks.SafeAsyncTask;
+import org.communiquons.android.comunic.client.ui.receivers.PendingCallsBroadcastReceiver;
+import org.communiquons.android.comunic.client.ui.receivers.RejectCallReceiver;
+
+
+/**
+ * Incoming call activity
+ *
+ * @author Pierre HUBERT
+ */
+public class IncomingCallActivity extends BaseActivity implements SafeAsyncTask.OnPostExecuteListener, View.OnClickListener {
+
+ private NextPendingCallInformation mNextPendingCallInformation;
+
+ private RefreshThread mRefreshThread = null;
+
+ private Button mAcceptButton;
+ private Button mRejectButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_incoming_call);
+
+ getSupportActionBar().hide();
+
+ PendingCallsBroadcastReceiver.RemoveCallNotification(this);
+
+ mAcceptButton = findViewById(R.id.accept_button);
+ mRejectButton = findViewById(R.id.reject_button);
+
+ if(IsActiveActivity(IncomingCallActivity.class))
+ finish();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ mRefreshThread = new RefreshThread();
+ mRefreshThread.start();
+
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ mRefreshThread.interrupt();
+ }
+
+ private void getNextPendingCall() {
+ //Get next pending call
+ GetNextPendingCallTask getNextPendingCallTask = new GetNextPendingCallTask(getApplicationContext());
+ getNextPendingCallTask.setOnPostExecuteListener(this);
+ getNextPendingCallTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ getTasksManager().addTask(getNextPendingCallTask);
+ }
+
+ @Override
+ public void OnPostExecute(@Nullable NextPendingCallInformation nextPendingCallInformation) {
+
+ if(nextPendingCallInformation == null){
+ Toast.makeText(this, R.string.err_get_next_pending_call_info, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if(!nextPendingCallInformation.isHasPendingCall()
+ || nextPendingCallInformation.hasAllMembersLeftCallExcept(AccountUtils.getID(this)))
+ finish();
+
+ this.mNextPendingCallInformation = nextPendingCallInformation;
+
+ ((TextView)findViewById(R.id.call_title)).setText(nextPendingCallInformation.getCallName());
+ mAcceptButton.setOnClickListener(this);
+ mRejectButton.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+
+ //Accept call
+ if(v.equals(mAcceptButton)){
+ Intent intent = new Intent(this, CallActivity.class);
+ intent.putExtra(CallActivity.ARGUMENT_CALL_ID, mNextPendingCallInformation.getId());
+ startActivity(intent);
+ finish();
+ }
+
+
+ //Reject calls
+ if(v.equals(mRejectButton)){
+ Intent intent = new Intent(this, RejectCallReceiver.class);
+ intent.setAction(Constants.IntentActions.ACTION_REJECT_INCOMING_CALL);
+ intent.putExtra(RejectCallReceiver.ARGUMENT_CALL_ID, mNextPendingCallInformation.getId());
+ sendBroadcast(intent);
+ finish();
+ }
+
+ }
+
+ /**
+ * Class used to refresh call information
+ */
+ private class RefreshThread extends Thread {
+
+ private boolean stop = false;
+ private final Object o = new Object();
+
+ @Override
+ public void run() {
+ super.run();
+
+ while(!stop){
+
+ //Execute task
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ getNextPendingCall();
+ }
+ });
+
+ synchronized (o) {
+
+ //Looping
+ try {
+ o.wait(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ interrupt();
+ }
+
+ }
+ }
+
+ }
+
+ public void interrupt(){
+ stop = true;
+ }
+ }
+}
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/RespondToCallTask.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/RespondToCallTask.java
new file mode 100644
index 0000000..def31cc
--- /dev/null
+++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/RespondToCallTask.java
@@ -0,0 +1,23 @@
+package org.communiquons.android.comunic.client.ui.asynctasks;
+
+import android.content.Context;
+
+import org.communiquons.android.comunic.client.data.helpers.CallsHelper;
+import org.communiquons.android.comunic.client.data.models.CallResponse;
+
+/**
+ * Respond to call task
+ *
+ * @author Pierre HUBERT
+ */
+public class RespondToCallTask extends SafeAsyncTask {
+
+ public RespondToCallTask(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected Boolean doInBackground(CallResponse... callResponses) {
+ return new CallsHelper(getContext()).respondToCall(callResponses[0]);
+ }
+}
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/receivers/PendingCallsBroadcastReceiver.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/receivers/PendingCallsBroadcastReceiver.java
index 8c847da..d6304b5 100644
--- a/app/src/main/java/org/communiquons/android/comunic/client/ui/receivers/PendingCallsBroadcastReceiver.java
+++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/receivers/PendingCallsBroadcastReceiver.java
@@ -18,7 +18,9 @@ import org.communiquons.android.comunic.client.R;
import org.communiquons.android.comunic.client.data.models.NextPendingCallInformation;
import org.communiquons.android.comunic.client.data.utils.AccountUtils;
import org.communiquons.android.comunic.client.ui.Constants;
+import org.communiquons.android.comunic.client.ui.activities.BaseActivity;
import org.communiquons.android.comunic.client.ui.activities.CallActivity;
+import org.communiquons.android.comunic.client.ui.activities.IncomingCallActivity;
import org.communiquons.android.comunic.client.ui.asynctasks.GetNextPendingCallTask;
import org.communiquons.android.comunic.client.ui.asynctasks.SafeAsyncTask;
import org.communiquons.android.comunic.client.ui.utils.UiUtils;
@@ -50,6 +52,11 @@ public class PendingCallsBroadcastReceiver extends BroadcastReceiver {
*/
private boolean locked = false;
+ /**
+ * The ID of the last shown notification
+ */
+ private static int mLastNotificationCallID = 0;
+
@Override
public void onReceive(final Context context, Intent intent) {
@@ -61,6 +68,12 @@ public class PendingCallsBroadcastReceiver extends BroadcastReceiver {
throw new RuntimeException("Unexpected call of " + TAG);
+ //Check if user is already calling
+ if(CheckIfUseless(context, null)) {
+ Log.v(TAG, "Reported that a new call is available but skipped because considered as useless.");
+ return;
+ }
+
//Check if service is currently locked
if(locked) {
Log.e(TAG, "New call skipped because this class is locked");
@@ -93,29 +106,20 @@ public class PendingCallsBroadcastReceiver extends BroadcastReceiver {
return;
}
- //Check if there is no pending call
- if(!info.isHasPendingCall()) {
-
- //Remove any related notification
- RemoveCallNotification(context);
+ //Check if it is useless to continue
+ if(CheckIfUseless(context, info)) {
return;
-
}
- //Check if all notification members left the call
- if(info.hasAllMembersLeftCallExcept(AccountUtils.getID(context)))
- return;
-
-
//Create notification
- //Accept intent
- Intent acceptIntent = new Intent(context, CallActivity.class);
- acceptIntent.putExtra(CallActivity.ARGUMENT_CALL_ID, info.getId());
- PendingIntent pendingAcceptIntent
- = PendingIntent.getActivity(context, 0, acceptIntent, 0);
+ //Full screen call request intent
+ Intent fullScreenCallRequestIntent = new Intent(context, IncomingCallActivity.class);
+ PendingIntent pendingFullScreenRequestIntent = PendingIntent.getActivity(context,
+ 0, fullScreenCallRequestIntent, 0);
+
//Create and show notification
NotificationCompat.Builder builder =
@@ -123,14 +127,9 @@ public class PendingCallsBroadcastReceiver extends BroadcastReceiver {
.setSmallIcon(R.drawable.ic_call)
.setContentTitle(info.getCallName())
.setContentText(UiUtils.getString(context, R.string.notification_call_content, info.getCallName()))
- .setContentIntent(pendingAcceptIntent)
- .setFullScreenIntent(pendingAcceptIntent, true)
- .setPriority(PRIORITY_HIGH)
-
- //Accept action
- .addAction(R.drawable.ic_call,
- UiUtils.getString(context, R.string.notification_call_accept), pendingAcceptIntent);
- //.addAction(R.drawable.ic_call, R.string.notification_call_reject, null)
+ //.setContentIntent(pendingAcceptIntent)
+ .setFullScreenIntent(pendingFullScreenRequestIntent, true)
+ .setPriority(PRIORITY_HIGH);
//Create notification channel if required
@@ -146,7 +145,28 @@ public class PendingCallsBroadcastReceiver extends BroadcastReceiver {
Notification n = builder.build();
n.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
- NotificationManagerCompat.from(context).notify(CALL_NOTIFICATION_ID, n);
+ mLastNotificationCallID = info.getId();
+ NotificationManagerCompat.from(context).notify(GetNotificationID(mLastNotificationCallID), n);
+ }
+
+ /**
+ * Check if calling this receiver is useless for now
+ *
+ * @param context Context of application
+ * @param call Optional information about a pending call
+ * @return TRUE if useless / FALSE else
+ */
+ public static boolean CheckIfUseless(Context context, @Nullable NextPendingCallInformation call){
+ boolean useless = BaseActivity.IsActiveActivity(IncomingCallActivity.class)
+ || BaseActivity.IsActiveActivity(CallActivity.class)
+ || (call != null && (
+ !call.isHasPendingCall()
+ || call.hasAllMembersLeftCallExcept(AccountUtils.getID(context))));
+
+ if(useless)
+ RemoveCallNotification(context);
+
+ return useless;
}
/**
@@ -155,6 +175,15 @@ public class PendingCallsBroadcastReceiver extends BroadcastReceiver {
* @param context The context of the application
*/
public static void RemoveCallNotification(Context context){
- NotificationManagerCompat.from(context).cancel(CALL_NOTIFICATION_ID);
+ NotificationManagerCompat.from(context).cancel(GetNotificationID(mLastNotificationCallID));
+ }
+
+ /**
+ * Get the ID of a notification for a call
+ *
+ * @param callID The ID of the target call
+ */
+ private static int GetNotificationID(int callID){
+ return CALL_NOTIFICATION_ID*1000 + callID;
}
}
diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/receivers/RejectCallReceiver.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/receivers/RejectCallReceiver.java
new file mode 100644
index 0000000..2e760b7
--- /dev/null
+++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/receivers/RejectCallReceiver.java
@@ -0,0 +1,49 @@
+package org.communiquons.android.comunic.client.ui.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import org.communiquons.android.comunic.client.data.models.CallResponse;
+import org.communiquons.android.comunic.client.ui.asynctasks.RespondToCallTask;
+
+import java.util.Objects;
+
+import static org.communiquons.android.comunic.client.ui.Constants.IntentActions.ACTION_REJECT_INCOMING_CALL;
+
+/**
+ * This broadcast receiver is used to reject incoming calls
+ *
+ * @author Pierre HUBERT
+ */
+public class RejectCallReceiver extends BroadcastReceiver {
+
+ /**
+ * Debug tag
+ */
+ private static final String TAG = RejectCallReceiver.class.getSimpleName();
+
+ /**
+ * Mandatory argument that includes call id
+ */
+ public static final String ARGUMENT_CALL_ID = "call_id";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ if(intent == null || !ACTION_REJECT_INCOMING_CALL.equals(intent.getAction()))
+ throw new RuntimeException(TAG + " was incorrectly triggered!");
+
+ int callID = Objects.requireNonNull(intent.getExtras()).getInt(ARGUMENT_CALL_ID);
+
+ //Send the response back to the server
+ Log.v(TAG, "Reject call " + callID);
+ RespondToCallTask respondToCallTask = new RespondToCallTask(context);
+ respondToCallTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new CallResponse(
+ callID,
+ false
+ ));
+ }
+}
diff --git a/app/src/main/res/layout/activity_incoming_call.xml b/app/src/main/res/layout/activity_incoming_call.xml
new file mode 100644
index 0000000..2b8c061
--- /dev/null
+++ b/app/src/main/res/layout/activity_incoming_call.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 13ec0b6..6613d36 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -325,4 +325,8 @@
Rejeter
%s vous appelle.
Accélérer le rafraîchissement des notifications
+ Rejeter
+ Répondre
+ Appel entrant
+ Impossible de récupérer les informations de l\'appel en cours !
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index def4d1c..37ebf88 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -8,7 +8,7 @@
#aaa
#5b5b5b
#303f9f
- #ffcc0000
+ #cc0000
#000000
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5e97afd..a0eea37 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -324,4 +324,8 @@
Reject call
%s is calling you
Accelerate notifications refresh
+ Reject call
+ Accept call
+ Incoming call
+ Could not get pending call information!