Switch RedPhone view<->service interaction to use event bus

Fixes #4234
// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-11-02 14:32:02 -08:00
parent 3e798a9863
commit 5b6f49c993
12 changed files with 218 additions and 274 deletions

View File

@ -2,4 +2,7 @@
-keepattributes SourceFile,LineNumberTable -keepattributes SourceFile,LineNumberTable
-keep class org.whispersystems.** { *; } -keep class org.whispersystems.** { *; }
-keep class org.thoughtcrime.securesms.** { *; } -keep class org.thoughtcrime.securesms.** { *; }
-keepclassmembers class ** {
public void onEvent*(**);
}

View File

@ -21,34 +21,32 @@ package org.thoughtcrime.redphone;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.support.annotation.NonNull;
import android.os.IBinder;
import android.os.Message;
import android.util.Log; import android.util.Log;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import com.afollestad.materialdialogs.AlertDialogWrapper; import com.afollestad.materialdialogs.AlertDialogWrapper;
import org.thoughtcrime.redphone.crypto.zrtp.SASInfo;
import org.thoughtcrime.redphone.ui.CallControls; import org.thoughtcrime.redphone.ui.CallControls;
import org.thoughtcrime.redphone.ui.CallScreen; import org.thoughtcrime.redphone.ui.CallScreen;
import org.thoughtcrime.redphone.util.AudioUtils; import org.thoughtcrime.redphone.util.AudioUtils;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.events.RedPhoneEvent;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import de.greenrobot.event.EventBus;
/** /**
* The main UI class for RedPhone. Most of the heavy lifting is * The main UI class for RedPhone. Most of the heavy lifting is
* done by RedPhoneService, so this activity is mostly responsible * done by RedPhoneService, so this activity is mostly responsible
@ -62,40 +60,9 @@ public class RedPhone extends Activity {
private static final String TAG = RedPhone.class.getSimpleName(); private static final String TAG = RedPhone.class.getSimpleName();
private static final int REMOTE_TERMINATE = 0; private static final int STANDARD_DELAY_FINISH = 1000;
private static final int LOCAL_TERMINATE = 1;
public static final int STATE_IDLE = 0;
public static final int STATE_RINGING = 2;
public static final int STATE_DIALING = 3;
public static final int STATE_ANSWERING = 4;
public static final int STATE_CONNECTED = 5;
private static final int STANDARD_DELAY_FINISH = 3000;
public static final int BUSY_SIGNAL_DELAY_FINISH = 5500; public static final int BUSY_SIGNAL_DELAY_FINISH = 5500;
public static final int HANDLE_CALL_CONNECTED = 0;
public static final int HANDLE_WAITING_FOR_RESPONDER = 1;
public static final int HANDLE_SERVER_FAILURE = 2;
public static final int HANDLE_PERFORMING_HANDSHAKE = 3;
public static final int HANDLE_HANDSHAKE_FAILED = 4;
public static final int HANDLE_CONNECTING_TO_INITIATOR = 5;
public static final int HANDLE_CALL_DISCONNECTED = 6;
public static final int HANDLE_CALL_RINGING = 7;
public static final int HANDLE_SERVER_MESSAGE = 9;
public static final int HANDLE_RECIPIENT_UNAVAILABLE = 10;
public static final int HANDLE_INCOMING_CALL = 11;
public static final int HANDLE_OUTGOING_CALL = 12;
public static final int HANDLE_CALL_BUSY = 13;
public static final int HANDLE_LOGIN_FAILED = 14;
public static final int HANDLE_CLIENT_FAILURE = 15;
public static final int HANDLE_DEBUG_INFO = 16;
public static final int HANDLE_NO_SUCH_USER = 17;
private final Handler callStateHandler = new CallStateHandler();
private int state;
private RedPhoneService redPhoneService;
private CallScreen callScreen; private CallScreen callScreen;
private BroadcastReceiver bluetoothStateReceiver; private BroadcastReceiver bluetoothStateReceiver;
@ -118,7 +85,7 @@ public class RedPhone extends Activity {
super.onResume(); super.onResume();
initializeScreenshotSecurity(); initializeScreenshotSecurity();
initializeServiceBinding(); EventBus.getDefault().registerSticky(this);
registerBluetoothReceiver(); registerBluetoothReceiver();
} }
@ -127,7 +94,7 @@ public class RedPhone extends Activity {
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
unbindService(serviceConnection); EventBus.getDefault().unregister(this);
unregisterReceiver(bluetoothStateReceiver); unregisterReceiver(bluetoothStateReceiver);
} }
@ -136,12 +103,6 @@ public class RedPhone extends Activity {
super.onConfigurationChanged(newConfiguration); super.onConfigurationChanged(newConfiguration);
} }
private void initializeServiceBinding() {
Log.w(TAG, "Binding to RedPhoneService...");
Intent bindIntent = new Intent(this, RedPhoneService.class);
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void initializeScreenshotSecurity() { private void initializeScreenshotSecurity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
TextSecurePreferences.isScreenSecurityEnabled(this)) TextSecurePreferences.isScreenSecurityEnabled(this))
@ -154,8 +115,6 @@ public class RedPhone extends Activity {
private void initializeResources() { private void initializeResources() {
callScreen = (CallScreen)findViewById(R.id.callScreen); callScreen = (CallScreen)findViewById(R.id.callScreen);
state = STATE_IDLE;
callScreen.setHangupButtonListener(new HangupButtonListener()); callScreen.setHangupButtonListener(new HangupButtonListener());
callScreen.setIncomingCallActionListener(new IncomingCallActionListener()); callScreen.setIncomingCallActionListener(new IncomingCallActionListener());
callScreen.setMuteButtonListener(new MuteButtonListener()); callScreen.setMuteButtonListener(new MuteButtonListener());
@ -170,140 +129,123 @@ public class RedPhone extends Activity {
} }
private void handleAnswerCall() { private void handleAnswerCall() {
state = STATE_ANSWERING; RedPhoneEvent event = EventBus.getDefault().getStickyEvent(RedPhoneEvent.class);
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(org.thoughtcrime.securesms.R.string.RedPhone_answering));
Intent intent = new Intent(this, RedPhoneService.class); if (event != null) {
intent.setAction(RedPhoneService.ACTION_ANSWER_CALL); callScreen.setActiveCall(event.getRecipient(), getString(org.thoughtcrime.securesms.R.string.RedPhone_answering));
startService(intent);
Intent intent = new Intent(this, RedPhoneService.class);
intent.setAction(RedPhoneService.ACTION_ANSWER_CALL);
startService(intent);
}
} }
private void handleDenyCall() { private void handleDenyCall() {
state = STATE_IDLE; RedPhoneEvent event = EventBus.getDefault().getStickyEvent(RedPhoneEvent.class);
Intent intent = new Intent(this, RedPhoneService.class); if (event != null) {
intent.setAction(RedPhoneService.ACTION_DENY_CALL); Intent intent = new Intent(this, RedPhoneService.class);
startService(intent); intent.setAction(RedPhoneService.ACTION_DENY_CALL);
startService(intent);
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(org.thoughtcrime.securesms.R.string.RedPhone_ending_call)); callScreen.setActiveCall(event.getRecipient(), getString(org.thoughtcrime.securesms.R.string.RedPhone_ending_call));
delayedFinish(); delayedFinish();
}
} }
private void handleIncomingCall(Recipient recipient) { private void handleIncomingCall(@NonNull RedPhoneEvent event) {
state = STATE_RINGING; callScreen.setIncomingCall(event.getRecipient());
callScreen.setIncomingCall(redPhoneService.getRecipient());
} }
private void handleOutgoingCall(Recipient recipient) { private void handleOutgoingCall(@NonNull RedPhoneEvent event) {
state = STATE_DIALING; callScreen.setActiveCall(event.getRecipient(), getString(org.thoughtcrime.securesms.R.string.RedPhone_dialing));
callScreen.setActiveCall(recipient, getString(org.thoughtcrime.securesms.R.string.RedPhone_dialing));
} }
private void handleTerminate( int terminationType ) { private void handleTerminate(@NonNull Recipient recipient /*, int terminationType */) {
Log.w(TAG, "handleTerminate called"); Log.w(TAG, "handleTerminate called");
Log.w(TAG, "Termination Stack:", new Exception()); Log.w(TAG, "Termination Stack:", new Exception());
if( state == STATE_DIALING ) { callScreen.setActiveCall(recipient, getString(R.string.RedPhone_ending_call));
if (terminationType == LOCAL_TERMINATE) {
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(org.thoughtcrime.securesms.R.string.RedPhone_canceling_call));
} else {
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_call_rejected));
}
} else if (state != STATE_IDLE) {
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_ending_call));
}
state = STATE_IDLE;
delayedFinish(); delayedFinish();
} }
private void handleCallRinging() { private void handleCallRinging(@NonNull RedPhoneEvent event) {
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_ringing)); callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ringing));
} }
private void handleCallBusy() { private void handleCallBusy(@NonNull RedPhoneEvent event) {
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_busy)); callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_busy));
state = STATE_IDLE;
delayedFinish(BUSY_SIGNAL_DELAY_FINISH); delayedFinish(BUSY_SIGNAL_DELAY_FINISH);
} }
private void handleCallConnected(SASInfo sas) { private void handleCallConnected(@NonNull RedPhoneEvent event) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES); getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_connected), sas); callScreen.setActiveCall(event.getRecipient(),
getString(R.string.RedPhone_connected),
state = STATE_CONNECTED; event.getExtra());
redPhoneService.notifyCallConnectionUIUpdateComplete();
} }
private void handleDebugInfo( String info ) { private void handleConnectingToInitiator(@NonNull RedPhoneEvent event) {
// debugCard.setInfo( info ); callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_connecting));
} }
private void handleConnectingToInitiator() { private void handleHandshakeFailed(@NonNull RedPhoneEvent event) {
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_connecting)); callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_handshake_failed));
}
private void handleHandshakeFailed() {
state = STATE_IDLE;
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_handshake_failed));
delayedFinish(); delayedFinish();
} }
private void handleRecipientUnavailable() { private void handleRecipientUnavailable(@NonNull RedPhoneEvent event) {
state = STATE_IDLE; callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_recipient_unavailable));
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_recipient_unavailable));
delayedFinish(); delayedFinish();
} }
private void handlePerformingHandshake() { private void handlePerformingHandshake(@NonNull RedPhoneEvent event) {
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_performing_handshake)); callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_performing_handshake));
} }
private void handleServerFailure() { private void handleServerFailure(@NonNull RedPhoneEvent event) {
state = STATE_IDLE; callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_network_failed));
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_network_failed));
delayedFinish(); delayedFinish();
} }
private void handleClientFailure(String msg) { private void handleClientFailure(final @NonNull RedPhoneEvent event) {
state = STATE_IDLE; callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_client_failed));
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_client_failed)); if( event.getExtra() != null && !isFinishing() ) {
if( msg != null && !isFinishing() ) {
AlertDialog.Builder ad = new AlertDialog.Builder(this); AlertDialog.Builder ad = new AlertDialog.Builder(this);
ad.setTitle(R.string.RedPhone_fatal_error); ad.setTitle(R.string.RedPhone_fatal_error);
ad.setMessage(msg); ad.setMessage(event.getExtra());
ad.setCancelable(false); ad.setCancelable(false);
ad.setPositiveButton(android.R.string.ok, new OnClickListener() { ad.setPositiveButton(android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int arg) { public void onClick(DialogInterface dialog, int arg) {
RedPhone.this.handleTerminate(LOCAL_TERMINATE); RedPhone.this.handleTerminate(event.getRecipient());
} }
}); });
ad.show(); ad.show();
} }
} }
private void handleLoginFailed() { private void handleLoginFailed(@NonNull RedPhoneEvent event) {
state = STATE_IDLE; callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_login_failed));
callScreen.setActiveCall(redPhoneService.getRecipient(), getString(R.string.RedPhone_login_failed));
delayedFinish(); delayedFinish();
} }
private void handleServerMessage(String message) { private void handleServerMessage(final @NonNull RedPhoneEvent event) {
if( isFinishing() ) return; //we're already shutting down, this might crash if( isFinishing() ) return; //we're already shutting down, this might crash
AlertDialog.Builder ad = new AlertDialog.Builder(this); AlertDialog.Builder ad = new AlertDialog.Builder(this);
ad.setTitle(R.string.RedPhone_message_from_the_server); ad.setTitle(R.string.RedPhone_message_from_the_server);
ad.setMessage(message); ad.setMessage(event.getExtra());
ad.setCancelable(false); ad.setCancelable(false);
ad.setPositiveButton(android.R.string.ok, new OnClickListener() { ad.setPositiveButton(android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int arg) { public void onClick(DialogInterface dialog, int arg) {
RedPhone.this.handleTerminate(LOCAL_TERMINATE); RedPhone.this.handleTerminate(event.getRecipient());
} }
}); });
ad.show(); ad.show();
} }
private void handleNoSuchUser(final Recipient recipient) { private void handleNoSuchUser(final @NonNull RedPhoneEvent event) {
if (isFinishing()) return; // XXX Stuart added this check above, not sure why, so I'm repeating in ignorance. - moxie if (isFinishing()) return; // XXX Stuart added this check above, not sure why, so I'm repeating in ignorance. - moxie
AlertDialogWrapper.Builder dialog = new AlertDialogWrapper.Builder(this); AlertDialogWrapper.Builder dialog = new AlertDialogWrapper.Builder(this);
dialog.setTitle(R.string.RedPhone_number_not_registered); dialog.setTitle(R.string.RedPhone_number_not_registered);
@ -313,13 +255,13 @@ public class RedPhone extends Activity {
dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() { dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
RedPhone.this.handleTerminate(LOCAL_TERMINATE); RedPhone.this.handleTerminate(event.getRecipient());
} }
}); });
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override @Override
public void onCancel(DialogInterface dialog) { public void onCancel(DialogInterface dialog) {
RedPhone.this.handleTerminate(LOCAL_TERMINATE); RedPhone.this.handleTerminate(event.getRecipient());
} }
}); });
dialog.show(); dialog.show();
@ -330,35 +272,33 @@ public class RedPhone extends Activity {
} }
private void delayedFinish(int delayMillis) { private void delayedFinish(int delayMillis) {
callStateHandler.postDelayed(new Runnable() { callScreen.postDelayed(new Runnable() {
public void run() {
public void run() { RedPhone.this.finish();
RedPhone.this.finish(); }
}}, delayMillis); }, delayMillis);
} }
private class CallStateHandler extends Handler { @SuppressWarnings("unused")
@Override public void onEventMainThread(final RedPhoneEvent event) {
public void handleMessage(Message message) { Log.w(TAG, "Got message from service: " + event.getType());
Log.w(TAG, "Got message from service: " + message.what);
switch (message.what) { switch (event.getType()) {
case HANDLE_CALL_CONNECTED: handleCallConnected((SASInfo)message.obj); break; case CALL_CONNECTED: handleCallConnected(event); break;
case HANDLE_SERVER_FAILURE: handleServerFailure(); break; case SERVER_FAILURE: handleServerFailure(event); break;
case HANDLE_PERFORMING_HANDSHAKE: handlePerformingHandshake(); break; case PERFORMING_HANDSHAKE: handlePerformingHandshake(event); break;
case HANDLE_HANDSHAKE_FAILED: handleHandshakeFailed(); break; case HANDSHAKE_FAILED: handleHandshakeFailed(event); break;
case HANDLE_CONNECTING_TO_INITIATOR: handleConnectingToInitiator(); break; case CONNECTING_TO_INITIATOR: handleConnectingToInitiator(event); break;
case HANDLE_CALL_RINGING: handleCallRinging(); break; case CALL_RINGING: handleCallRinging(event); break;
case HANDLE_CALL_DISCONNECTED: handleTerminate( REMOTE_TERMINATE ); break; case CALL_DISCONNECTED: handleTerminate(event.getRecipient()); break;
case HANDLE_SERVER_MESSAGE: handleServerMessage((String)message.obj); break; case SERVER_MESSAGE: handleServerMessage(event); break;
case HANDLE_NO_SUCH_USER: handleNoSuchUser((Recipient)message.obj); break; case NO_SUCH_USER: handleNoSuchUser(event); break;
case HANDLE_RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(); break; case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break;
case HANDLE_INCOMING_CALL: handleIncomingCall((Recipient)message.obj); break; case INCOMING_CALL: handleIncomingCall(event); break;
case HANDLE_OUTGOING_CALL: handleOutgoingCall((Recipient)message.obj); break; case OUTGOING_CALL: handleOutgoingCall(event); break;
case HANDLE_CALL_BUSY: handleCallBusy(); break; case CALL_BUSY: handleCallBusy(event); break;
case HANDLE_LOGIN_FAILED: handleLoginFailed(); break; case LOGIN_FAILED: handleLoginFailed(event); break;
case HANDLE_CLIENT_FAILURE: handleClientFailure((String)message.obj); break; case CLIENT_FAILURE: handleClientFailure(event); break;
case HANDLE_DEBUG_INFO: handleDebugInfo((String)message.obj); break;
}
} }
} }
@ -369,7 +309,11 @@ public class RedPhone extends Activity {
intent.setAction(RedPhoneService.ACTION_HANGUP_CALL); intent.setAction(RedPhoneService.ACTION_HANGUP_CALL);
startService(intent); startService(intent);
RedPhone.this.handleTerminate(LOCAL_TERMINATE); RedPhoneEvent event = EventBus.getDefault().getStickyEvent(RedPhoneEvent.class);
if (event != null) {
RedPhone.this.handleTerminate(event.getRecipient());
}
} }
} }
@ -418,30 +362,11 @@ public class RedPhone extends Activity {
public void onAcceptClick() { public void onAcceptClick() {
RedPhone.this.handleAnswerCall(); RedPhone.this.handleAnswerCall();
} }
@Override @Override
public void onDenyClick() { public void onDenyClick() {
RedPhone.this.handleDenyCall(); RedPhone.this.handleDenyCall();
} }
} }
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
RedPhone.this.redPhoneService = ((RedPhoneService.RedPhoneServiceBinder)service).getService();
redPhoneService.setCallStateHandler(callStateHandler);
Recipient recipient = redPhoneService.getRecipient();
switch (redPhoneService.getState()) {
case STATE_IDLE: callScreen.reset(); break;
case STATE_RINGING: handleIncomingCall(recipient); break;
case STATE_DIALING: handleOutgoingCall(recipient); break;
case STATE_ANSWERING: handleAnswerCall(); break;
case STATE_CONNECTED: handleCallConnected(redPhoneService.getCurrentCallSAS()); break;
}
}
public void onServiceDisconnected(ComponentName name) {
redPhoneService.setCallStateHandler(null);
}
};
} }

View File

@ -22,12 +22,13 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.redphone.audio.IncomingRinger; import org.thoughtcrime.redphone.audio.IncomingRinger;
@ -47,6 +48,8 @@ import org.thoughtcrime.redphone.signaling.SignalingSocket;
import org.thoughtcrime.redphone.ui.NotificationBarManager; import org.thoughtcrime.redphone.ui.NotificationBarManager;
import org.thoughtcrime.redphone.util.UncaughtExceptionHandlerManager; import org.thoughtcrime.redphone.util.UncaughtExceptionHandlerManager;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.events.RedPhoneEvent;
import org.thoughtcrime.securesms.events.RedPhoneEvent.Type;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -57,8 +60,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List; import de.greenrobot.event.EventBus;
/** /**
* The major entry point for all of the heavy lifting associated with * The major entry point for all of the heavy lifting associated with
@ -73,6 +76,12 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
private static final String TAG = RedPhoneService.class.getSimpleName(); private static final String TAG = RedPhoneService.class.getSimpleName();
private static final int STATE_IDLE = 0;
private static final int STATE_RINGING = 2;
private static final int STATE_DIALING = 3;
private static final int STATE_ANSWERING = 4;
private static final int STATE_CONNECTED = 5;
public static final String EXTRA_REMOTE_NUMBER = "remote_number"; public static final String EXTRA_REMOTE_NUMBER = "remote_number";
public static final String EXTRA_SESSION_DESCRIPTOR = "session_descriptor"; public static final String EXTRA_SESSION_DESCRIPTOR = "session_descriptor";
public static final String EXTRA_MUTE = "mute_value"; public static final String EXTRA_MUTE = "mute_value";
@ -84,8 +93,6 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
public static final String ACTION_HANGUP_CALL = "org.thoughtcrime.redphone.RedPhoneService.HANGUP"; public static final String ACTION_HANGUP_CALL = "org.thoughtcrime.redphone.RedPhoneService.HANGUP";
public static final String ACTION_SET_MUTE = "org.thoughtcrime.redphone.RedPhoneService.SET_MUTE"; public static final String ACTION_SET_MUTE = "org.thoughtcrime.redphone.RedPhoneService.SET_MUTE";
private final List<Message> bufferedEvents = new LinkedList<>();
private final IBinder binder = new RedPhoneServiceBinder();
private final Handler serviceHandler = new Handler(); private final Handler serviceHandler = new Handler();
private OutgoingRinger outgoingRinger; private OutgoingRinger outgoingRinger;
@ -98,7 +105,6 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
private LockManager lockManager; private LockManager lockManager;
private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager; private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager;
private Handler handler;
private IncomingPstnCallListener pstnCallListener; private IncomingPstnCallListener pstnCallListener;
@Override @Override
@ -119,7 +125,7 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return binder; return null;
} }
@Override @Override
@ -155,7 +161,7 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
} }
private void initializeResources() { private void initializeResources() {
this.state = RedPhone.STATE_IDLE; this.state = STATE_IDLE;
this.zid = getZID(); this.zid = getZID();
this.lockManager = new LockManager(this); this.lockManager = new LockManager(this);
} }
@ -173,7 +179,7 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
SessionDescriptor session = intent.getParcelableExtra(EXTRA_SESSION_DESCRIPTOR); SessionDescriptor session = intent.getParcelableExtra(EXTRA_SESSION_DESCRIPTOR);
remoteNumber = intent.getStringExtra(EXTRA_REMOTE_NUMBER); remoteNumber = intent.getStringExtra(EXTRA_REMOTE_NUMBER);
state = RedPhone.STATE_RINGING; state = STATE_RINGING;
lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING); lockManager.updatePhoneState(LockManager.PhoneState.PROCESSING);
this.currentCallManager = new ResponderCallManager(this, this, remoteNumber, localNumber, this.currentCallManager = new ResponderCallManager(this, this, remoteNumber, localNumber,
@ -190,9 +196,9 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
if (remoteNumber == null || remoteNumber.length() == 0) if (remoteNumber == null || remoteNumber.length() == 0)
return; return;
sendMessage(RedPhone.HANDLE_OUTGOING_CALL, getRecipient()); sendMessage(Type.OUTGOING_CALL, getRecipient(), null);
state = RedPhone.STATE_DIALING; state = STATE_DIALING;
lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE); lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE);
this.currentCallManager = new InitiatingCallManager(this, this, localNumber, password, this.currentCallManager = new InitiatingCallManager(this, this, localNumber, password,
remoteNumber, zid); remoteNumber, zid);
@ -234,7 +240,7 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
} }
private void handleAnswerCall(Intent intent) { private void handleAnswerCall(Intent intent) {
state = RedPhone.STATE_ANSWERING; state = STATE_ANSWERING;
incomingRinger.stop(); incomingRinger.stop();
DatabaseFactory.getSmsDatabase(this).insertReceivedCall(remoteNumber); DatabaseFactory.getSmsDatabase(this).insertReceivedCall(remoteNumber);
if (currentCallManager != null) { if (currentCallManager != null) {
@ -243,7 +249,7 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
} }
private void handleDenyCall(Intent intent) { private void handleDenyCall(Intent intent) {
state = RedPhone.STATE_IDLE; state = STATE_IDLE;
incomingRinger.stop(); incomingRinger.stop();
DatabaseFactory.getSmsDatabase(this).insertMissedCall(remoteNumber); DatabaseFactory.getSmsDatabase(this).insertMissedCall(remoteNumber);
if(currentCallManager != null) { if(currentCallManager != null) {
@ -266,12 +272,12 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
private boolean isBusy() { private boolean isBusy() {
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE); TelephonyManager telephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
return ((currentCallManager != null && state != RedPhone.STATE_IDLE) || return ((currentCallManager != null && state != STATE_IDLE) ||
telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE); telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE);
} }
private boolean isIdle() { private boolean isIdle() {
return state == RedPhone.STATE_IDLE; return state == STATE_IDLE;
} }
private void shutdownAudio() { private void shutdownAudio() {
@ -283,15 +289,9 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
return state; return state;
} }
public SASInfo getCurrentCallSAS() { public @NonNull Recipient getRecipient() {
if (currentCallManager != null) if (!TextUtils.isEmpty(remoteNumber)) {
return currentCallManager.getSasInfo(); //noinspection ConstantConditions
else
return null;
}
public Recipient getRecipient() {
if (remoteNumber != null) {
return RecipientFactory.getRecipientsFromString(this, remoteNumber, true) return RecipientFactory.getRecipientsFromString(this, remoteNumber, true)
.getPrimaryRecipient(); .getPrimaryRecipient();
} else { } else {
@ -351,22 +351,10 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
shutdownAudio(); shutdownAudio();
state = RedPhone.STATE_IDLE; state = STATE_IDLE;
lockManager.updatePhoneState(LockManager.PhoneState.IDLE); lockManager.updatePhoneState(LockManager.PhoneState.IDLE);
} }
public void setCallStateHandler(Handler handler) {
this.handler = handler;
if (handler != null) {
for (Message message : bufferedEvents) {
handler.sendMessage(message);
}
bufferedEvents.clear();
}
}
///////// CallStateListener Implementation ///////// CallStateListener Implementation
public void notifyCallStale() { public void notifyCallStale() {
@ -377,7 +365,7 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
public void notifyCallFresh() { public void notifyCallFresh() {
Log.w(TAG, "Good call, time to ring and display call card..."); Log.w(TAG, "Good call, time to ring and display call card...");
sendMessage(RedPhone.HANDLE_INCOMING_CALL, getRecipient()); sendMessage(Type.INCOMING_CALL, getRecipient(), null);
lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE); lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE);
@ -389,7 +377,8 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
public void notifyBusy() { public void notifyBusy() {
Log.w("RedPhoneService", "Got busy signal from responder!"); Log.w("RedPhoneService", "Got busy signal from responder!");
sendMessage(RedPhone.HANDLE_CALL_BUSY, null); sendMessage(Type.CALL_BUSY, getRecipient(), null);
outgoingRinger.playBusy(); outgoingRinger.playBusy();
serviceHandler.postDelayed(new Runnable() { serviceHandler.postDelayed(new Runnable() {
@Override @Override
@ -401,124 +390,103 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
public void notifyCallRinging() { public void notifyCallRinging() {
outgoingRinger.playRing(); outgoingRinger.playRing();
sendMessage(RedPhone.HANDLE_CALL_RINGING, null); sendMessage(Type.CALL_RINGING, getRecipient(), null);
} }
public void notifyCallConnected(SASInfo sas) { public void notifyCallConnected(SASInfo sas) {
outgoingRinger.playComplete(); outgoingRinger.playComplete();
lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL); lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL);
state = RedPhone.STATE_CONNECTED; state = STATE_CONNECTED;
synchronized( this ) { sendMessage(Type.CALL_CONNECTED, getRecipient(), sas.getSasText());
sendMessage(RedPhone.HANDLE_CALL_CONNECTED, sas);
try {
wait();
} catch (InterruptedException e) {
throw new AssertionError( "Wait interrupted in RedPhoneService" );
}
}
}
public void notifyCallConnectionUIUpdateComplete() {
synchronized( this ) {
this.notify();
}
}
public void notifyDebugInfo(String info) {
sendMessage(RedPhone.HANDLE_DEBUG_INFO, info);
} }
public void notifyConnectingtoInitiator() { public void notifyConnectingtoInitiator() {
sendMessage(RedPhone.HANDLE_CONNECTING_TO_INITIATOR, null); sendMessage(Type.CONNECTING_TO_INITIATOR, getRecipient(), null);
} }
public void notifyCallDisconnected() { public void notifyCallDisconnected() {
if (state == RedPhone.STATE_RINGING) if (state == STATE_RINGING)
handleMissedCall(remoteNumber); handleMissedCall(remoteNumber);
sendMessage(RedPhone.HANDLE_CALL_DISCONNECTED, null); sendMessage(Type.CALL_DISCONNECTED, getRecipient(), null);
this.terminate(); this.terminate();
} }
public void notifyHandshakeFailed() { public void notifyHandshakeFailed() {
state = RedPhone.STATE_IDLE; state = STATE_IDLE;
outgoingRinger.playFailure(); outgoingRinger.playFailure();
sendMessage(RedPhone.HANDLE_HANDSHAKE_FAILED, null); sendMessage(Type.HANDSHAKE_FAILED, getRecipient(), null);
this.terminate(); this.terminate();
} }
public void notifyRecipientUnavailable() { public void notifyRecipientUnavailable() {
state = RedPhone.STATE_IDLE; state = STATE_IDLE;
outgoingRinger.playFailure(); outgoingRinger.playFailure();
sendMessage(RedPhone.HANDLE_RECIPIENT_UNAVAILABLE, null); sendMessage(Type.RECIPIENT_UNAVAILABLE, getRecipient(), null);
this.terminate(); this.terminate();
} }
public void notifyPerformingHandshake() { public void notifyPerformingHandshake() {
outgoingRinger.playHandshake(); outgoingRinger.playHandshake();
sendMessage(RedPhone.HANDLE_PERFORMING_HANDSHAKE, null); sendMessage(Type.PERFORMING_HANDSHAKE, getRecipient(), null);
} }
public void notifyServerFailure() { public void notifyServerFailure() {
if (state == RedPhone.STATE_RINGING) if (state == STATE_RINGING)
handleMissedCall(remoteNumber); handleMissedCall(remoteNumber);
state = RedPhone.STATE_IDLE; state = STATE_IDLE;
outgoingRinger.playFailure(); outgoingRinger.playFailure();
sendMessage(RedPhone.HANDLE_SERVER_FAILURE, null); sendMessage(Type.SERVER_FAILURE, getRecipient(), null);
this.terminate(); this.terminate();
} }
public void notifyClientFailure() { public void notifyClientFailure() {
if (state == RedPhone.STATE_RINGING) if (state == STATE_RINGING)
handleMissedCall(remoteNumber); handleMissedCall(remoteNumber);
state = RedPhone.STATE_IDLE; state = STATE_IDLE;
outgoingRinger.playFailure(); outgoingRinger.playFailure();
sendMessage(RedPhone.HANDLE_CLIENT_FAILURE, null); sendMessage(Type.CLIENT_FAILURE, getRecipient(), null);
this.terminate(); this.terminate();
} }
public void notifyLoginFailed() { public void notifyLoginFailed() {
if (state == RedPhone.STATE_RINGING) if (state == STATE_RINGING)
handleMissedCall(remoteNumber); handleMissedCall(remoteNumber);
state = RedPhone.STATE_IDLE; state = STATE_IDLE;
outgoingRinger.playFailure(); outgoingRinger.playFailure();
sendMessage(RedPhone.HANDLE_LOGIN_FAILED, null); sendMessage(Type.LOGIN_FAILED, getRecipient(), null);
this.terminate(); this.terminate();
} }
public void notifyNoSuchUser() { public void notifyNoSuchUser() {
sendMessage(RedPhone.HANDLE_NO_SUCH_USER, getRecipient()); sendMessage(Type.NO_SUCH_USER, getRecipient(), null);
this.terminate(); this.terminate();
} }
public void notifyServerMessage(String message) { public void notifyServerMessage(String message) {
sendMessage(RedPhone.HANDLE_SERVER_MESSAGE, message); sendMessage(Type.SERVER_MESSAGE, getRecipient(), message);
this.terminate(); this.terminate();
} }
public void notifyClientError(String msg) { public void notifyClientError(String msg) {
sendMessage(RedPhone.HANDLE_CLIENT_FAILURE,msg); sendMessage(Type.CLIENT_FAILURE, getRecipient(), msg);
this.terminate(); this.terminate();
} }
public void notifyClientError(int messageId) {
notifyClientError(getString(messageId));
}
public void notifyCallConnecting() { public void notifyCallConnecting() {
outgoingRinger.playSonar(); outgoingRinger.playSonar();
} }
public void notifyWaitingForResponder() {} public void notifyWaitingForResponder() {}
private void sendMessage(int code, Object extra) { private void sendMessage(@NonNull Type type,
Message message = Message.obtain(); @NonNull Recipient recipient,
message.what = code; @Nullable String error)
message.obj = extra; {
EventBus.getDefault().postSticky(new RedPhoneEvent(type, recipient, error));
if (handler != null) handler.sendMessage(message);
else bufferedEvents.add(message);
} }
private class IntentRunnable implements Runnable { private class IntentRunnable implements Runnable {
@ -533,21 +501,15 @@ public class RedPhoneService extends Service implements CallStateListener, CallS
} }
} }
public class RedPhoneServiceBinder extends Binder {
public RedPhoneService getService() {
return RedPhoneService.this;
}
}
@Override @Override
public boolean isInCall() { public boolean isInCall() {
switch(state) { switch(state) {
case RedPhone.STATE_IDLE: case STATE_IDLE:
return false; return false;
case RedPhone.STATE_DIALING: case STATE_DIALING:
case RedPhone.STATE_RINGING: case STATE_RINGING:
case RedPhone.STATE_ANSWERING: case STATE_ANSWERING:
case RedPhone.STATE_CONNECTED: case STATE_CONNECTED:
return true; return true;
default: default:
Log.e(TAG, "Unhandled call state: " + state); Log.e(TAG, "Unhandled call state: " + state);

View File

@ -43,10 +43,8 @@ public interface CallStateListener {
public void notifyRecipientUnavailable(); public void notifyRecipientUnavailable();
public void notifyBusy(); public void notifyBusy();
public void notifyLoginFailed(); public void notifyLoginFailed();
public void notifyDebugInfo(String info);
public void notifyCallStale(); public void notifyCallStale();
public void notifyCallFresh(); public void notifyCallFresh();
public void notifyClientError(int msgId);
public void notifyClientError(String message); public void notifyClientError(String message);
public void notifyCallConnecting(); public void notifyCallConnecting();
} }

View File

@ -23,6 +23,7 @@ import android.content.IntentFilter;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -104,9 +105,9 @@ public class CallControls extends RelativeLayout {
sasTextView.setVisibility(View.GONE); sasTextView.setVisibility(View.GONE);
} }
public void setActiveCall(SASInfo sas) { public void setActiveCall(@Nullable String sas) {
setActiveCall(); setActiveCall();
sasTextView.setText(sas.getSasText()); sasTextView.setText(sas);
sasTextView.setVisibility(View.VISIBLE); sasTextView.setVisibility(View.VISIBLE);
} }

View File

@ -18,6 +18,8 @@
package org.thoughtcrime.redphone.ui; package org.thoughtcrime.redphone.ui;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@ -53,7 +55,7 @@ public class CallScreen extends FrameLayout {
initialize(); initialize();
} }
public void setActiveCall(Recipient personInfo, String message, SASInfo sas) { public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message, @Nullable String sas) {
callCard.setCard(personInfo, message); callCard.setCard(personInfo, message);
callControls.setActiveCall(sas); callControls.setActiveCall(sas);
} }

View File

@ -21,7 +21,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer; import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.jobs.PartProgressEvent; import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.SlideClickListener; import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;

View File

@ -22,7 +22,7 @@ import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.jobs.PartProgressEvent; import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.jobs; package org.thoughtcrime.securesms.events;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;

View File

@ -0,0 +1,51 @@
package org.thoughtcrime.securesms.events;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.recipients.Recipient;
public class RedPhoneEvent {
public enum Type {
CALL_CONNECTED,
WAITING_FOR_RESPONDER,
SERVER_FAILURE,
PERFORMING_HANDSHAKE,
HANDSHAKE_FAILED,
CONNECTING_TO_INITIATOR,
CALL_DISCONNECTED,
CALL_RINGING,
SERVER_MESSAGE,
RECIPIENT_UNAVAILABLE,
INCOMING_CALL,
OUTGOING_CALL,
CALL_BUSY,
LOGIN_FAILED,
CLIENT_FAILURE,
DEBUG_INFO,
NO_SUCH_USER
}
private final @NonNull Type type;
private final @NonNull Recipient recipient;
private final @Nullable String extra;
public RedPhoneEvent(@NonNull Type type, @NonNull Recipient recipient, @Nullable String extra) {
this.type = type;
this.recipient = recipient;
this.extra = extra;
}
public @NonNull Type getType() {
return type;
}
public @NonNull Recipient getRecipient() {
return recipient;
}
public @Nullable String getExtra() {
return extra;
}
}

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.crypto.MediaKey;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.MediaNetworkRequirement; import org.thoughtcrime.securesms.jobs.requirements.MediaNetworkRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;

View File

@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.TextSecureDirectory; import org.thoughtcrime.securesms.database.TextSecureDirectory;
import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;