mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-03 15:13:50 +00:00
Support for using Signal without Play Services
This is now possible with beta calling, so non-GCM users are a part of beta calling by default. // FREEBIE
This commit is contained in:
@@ -54,6 +54,7 @@ import android.view.ViewGroup;
|
||||
import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener;
|
||||
import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator;
|
||||
import org.thoughtcrime.securesms.components.reminder.DefaultSmsReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.DozeReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.OutdatedBuildReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.PushRegistrationReminder;
|
||||
@@ -184,6 +185,8 @@ public class ConversationListFragment extends Fragment
|
||||
return Optional.of((new PushRegistrationReminder(context, masterSecret)));
|
||||
} else if (ShareReminder.isEligible(context)) {
|
||||
return Optional.of(new ShareReminder(context));
|
||||
} else if (DozeReminder.isEligible(context)) {
|
||||
return Optional.of(new DozeReminder(context));
|
||||
} else {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -20,6 +21,7 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
import com.google.i18n.phonenumbers.AsYouTypeFormatter;
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
@@ -44,6 +46,13 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
||||
private static final int PICK_COUNTRY = 1;
|
||||
private static final String TAG = RegistrationActivity.class.getSimpleName();
|
||||
|
||||
private enum PlayServicesStatus {
|
||||
SUCCESS,
|
||||
MISSING,
|
||||
NEEDS_UPDATE,
|
||||
TRANSIENT_ERROR
|
||||
}
|
||||
|
||||
private AsYouTypeFormatter countryFormatter;
|
||||
private ArrayAdapter<String> countrySpinnerAdapter;
|
||||
private Spinner countrySpinner;
|
||||
@@ -211,28 +220,32 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
int gcmStatus = GooglePlayServicesUtil.isGooglePlayServicesAvailable(self);
|
||||
PlayServicesStatus gcmStatus = checkPlayServices(self);
|
||||
|
||||
if (gcmStatus != ConnectionResult.SUCCESS) {
|
||||
if (GooglePlayServicesUtil.isUserRecoverableError(gcmStatus)) {
|
||||
GooglePlayServicesUtil.getErrorDialog(gcmStatus, self, 9000).show();
|
||||
} else {
|
||||
Dialogs.showAlertDialog(self, getString(R.string.RegistrationActivity_unsupported),
|
||||
getString(R.string.RegistrationActivity_sorry_this_device_is_not_supported_for_data_messaging));
|
||||
}
|
||||
return;
|
||||
if (gcmStatus == PlayServicesStatus.SUCCESS) {
|
||||
promptForRegistrationStart(self, e164number, true);
|
||||
} else if (gcmStatus == PlayServicesStatus.MISSING) {
|
||||
promptForNoPlayServices(self, e164number);
|
||||
} else if (gcmStatus == PlayServicesStatus.NEEDS_UPDATE) {
|
||||
GoogleApiAvailability.getInstance().getErrorDialog(self, ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED, 0);
|
||||
} else {
|
||||
Dialogs.showAlertDialog(self, getString(R.string.RegistrationActivity_play_services_error),
|
||||
getString(R.string.RegistrationActivity_google_play_services_is_updating_or_unavailable));
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(self);
|
||||
private void promptForRegistrationStart(final Context context, final String e164number, final boolean gcmSupported) {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
||||
dialog.setTitle(PhoneNumberFormatter.getInternationalFormatFromE164(e164number));
|
||||
dialog.setMessage(R.string.RegistrationActivity_we_will_now_verify_that_the_following_number_is_associated_with_your_device_s);
|
||||
dialog.setPositiveButton(getString(R.string.RegistrationActivity_continue),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = new Intent(self, RegistrationProgressActivity.class);
|
||||
intent.putExtra("e164number", e164number);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
Intent intent = new Intent(context, RegistrationProgressActivity.class);
|
||||
intent.putExtra(RegistrationProgressActivity.NUMBER_EXTRA, e164number);
|
||||
intent.putExtra(RegistrationProgressActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(RegistrationProgressActivity.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
@@ -240,6 +253,48 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
||||
dialog.setNegativeButton(getString(R.string.RegistrationActivity_edit), null);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void promptForNoPlayServices(final Context context, final String e164number) {
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
||||
dialog.setTitle(R.string.RegistrationActivity_missing_google_play_services);
|
||||
dialog.setMessage(R.string.RegistrationActivity_this_device_is_missing_google_play_services);
|
||||
dialog.setPositiveButton(R.string.RegistrationActivity_i_understand, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
promptForRegistrationStart(context, e164number, false);
|
||||
}
|
||||
});
|
||||
dialog.setNegativeButton(android.R.string.cancel, null);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private PlayServicesStatus checkPlayServices(Context context) {
|
||||
int gcmStatus = 0;
|
||||
|
||||
try {
|
||||
gcmStatus = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, t);
|
||||
return PlayServicesStatus.MISSING;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Play Services: " + gcmStatus);
|
||||
|
||||
switch (gcmStatus) {
|
||||
case ConnectionResult.SUCCESS:
|
||||
return PlayServicesStatus.SUCCESS;
|
||||
case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
|
||||
return PlayServicesStatus.NEEDS_UPDATE;
|
||||
case ConnectionResult.SERVICE_DISABLED:
|
||||
case ConnectionResult.SERVICE_MISSING:
|
||||
case ConnectionResult.SERVICE_INVALID:
|
||||
case ConnectionResult.API_UNAVAILABLE:
|
||||
case ConnectionResult.SERVICE_MISSING_PERMISSION:
|
||||
return PlayServicesStatus.MISSING;
|
||||
default:
|
||||
return PlayServicesStatus.TRANSIENT_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CountryCodeChangedListener implements TextWatcher {
|
||||
|
||||
@@ -49,6 +49,10 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
|
||||
private static final String TAG = RegistrationProgressActivity.class.getSimpleName();
|
||||
|
||||
public static final String NUMBER_EXTRA = "e164number";
|
||||
public static final String MASTER_SECRET_EXTRA = "master_secret";
|
||||
public static final String GCM_SUPPORTED_EXTRA = "gcm_supported";
|
||||
|
||||
private static final int FOCUSED_COLOR = Color.parseColor("#ff333333");
|
||||
private static final int UNFOCUSED_COLOR = Color.parseColor("#ff808080");
|
||||
|
||||
@@ -89,6 +93,7 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
private EditText codeEditText;
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private boolean gcmSupported;
|
||||
private volatile boolean visible;
|
||||
|
||||
@Override
|
||||
@@ -131,7 +136,8 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.masterSecret = getIntent().getParcelableExtra("master_secret");
|
||||
this.masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
|
||||
this.gcmSupported = getIntent().getBooleanExtra(GCM_SUPPORTED_EXTRA, true);
|
||||
this.registrationLayout = (LinearLayout)findViewById(R.id.registering_layout);
|
||||
this.verificationFailureLayout = (LinearLayout)findViewById(R.id.verification_failure_layout);
|
||||
this.connectivityFailureLayout = (LinearLayout)findViewById(R.id.connectivity_failure_layout);
|
||||
@@ -199,8 +205,9 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
if (hasNumberDirective()) {
|
||||
Intent intent = new Intent(this, RegistrationService.class);
|
||||
intent.setAction(RegistrationService.REGISTER_NUMBER_ACTION);
|
||||
intent.putExtra("e164number", getNumberDirective());
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
intent.putExtra(RegistrationService.NUMBER_EXTRA, getNumberDirective());
|
||||
intent.putExtra(RegistrationService.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(RegistrationService.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startService(intent);
|
||||
} else {
|
||||
Intent intent = new Intent(this, RegistrationActivity.class);
|
||||
@@ -295,7 +302,7 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
|
||||
private void handleVerificationRequestedVoice(RegistrationState state) {
|
||||
handleVerificationTimeout(state);
|
||||
verifyButton.setOnClickListener(new VerifyClickListener(state.number, state.password));
|
||||
verifyButton.setOnClickListener(new VerifyClickListener(state.number, state.password, gcmSupported));
|
||||
verifyButton.setEnabled(true);
|
||||
codeEditText.setEnabled(true);
|
||||
}
|
||||
@@ -354,11 +361,11 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
}
|
||||
|
||||
private boolean hasNumberDirective() {
|
||||
return getIntent().getStringExtra("e164number") != null;
|
||||
return getIntent().getStringExtra(NUMBER_EXTRA) != null;
|
||||
}
|
||||
|
||||
private String getNumberDirective() {
|
||||
return getIntent().getStringExtra("e164number");
|
||||
return getIntent().getStringExtra(NUMBER_EXTRA);
|
||||
}
|
||||
|
||||
private void shutdownServiceBinding() {
|
||||
@@ -426,7 +433,8 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
shutdownService();
|
||||
|
||||
Intent activityIntent = new Intent(RegistrationProgressActivity.this, RegistrationActivity.class);
|
||||
activityIntent.putExtra("master_secret", masterSecret);
|
||||
activityIntent.putExtra(RegistrationProgressActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
activityIntent.putExtra(RegistrationProgressActivity.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startActivity(activityIntent);
|
||||
finish();
|
||||
}
|
||||
@@ -447,17 +455,19 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
private static final int VERIFICATION_ERROR = 3;
|
||||
private static final int MULTI_REGISTRATION_ERROR = 4;
|
||||
|
||||
private final String e164number;
|
||||
private final String password;
|
||||
private final String signalingKey;
|
||||
private final String e164number;
|
||||
private final String password;
|
||||
private final String signalingKey;
|
||||
private final boolean gcmSupported;
|
||||
private final Context context;
|
||||
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
public VerifyClickListener(String e164number, String password) {
|
||||
public VerifyClickListener(String e164number, String password, boolean gcmSupported) {
|
||||
this.e164number = e164number;
|
||||
this.password = password;
|
||||
this.signalingKey = Util.getSecret(52);
|
||||
this.gcmSupported = gcmSupported;
|
||||
this.context = RegistrationProgressActivity.this;
|
||||
}
|
||||
|
||||
@@ -490,10 +500,11 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
case SUCCESS:
|
||||
Intent intent = new Intent(context, RegistrationService.class);
|
||||
intent.setAction(RegistrationService.VOICE_REGISTER_ACTION);
|
||||
intent.putExtra("e164number", e164number);
|
||||
intent.putExtra("password", password);
|
||||
intent.putExtra("signaling_key", signalingKey);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
intent.putExtra(RegistrationService.NUMBER_EXTRA, e164number);
|
||||
intent.putExtra(RegistrationService.PASSWORD_EXTRA, password);
|
||||
intent.putExtra(RegistrationService.SIGNALING_KEY_EXTRA, signalingKey);
|
||||
intent.putExtra(RegistrationService.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(RegistrationService.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startService(intent);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
@@ -522,7 +533,7 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
|
||||
boolean video = TextSecurePreferences.isWebrtcCallingEnabled(context);
|
||||
|
||||
accountManager.verifyAccountWithCode(code, signalingKey, registrationId, true, video);
|
||||
accountManager.verifyAccountWithCode(code, signalingKey, registrationId, true, video, !gcmSupported);
|
||||
|
||||
return SUCCESS;
|
||||
} catch (ExpectationFailedException e) {
|
||||
@@ -578,9 +589,10 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
case SUCCESS:
|
||||
Intent intent = new Intent(context, RegistrationService.class);
|
||||
intent.setAction(RegistrationService.VOICE_REQUESTED_ACTION);
|
||||
intent.putExtra("e164number", e164number);
|
||||
intent.putExtra("password", password);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
intent.putExtra(RegistrationService.NUMBER_EXTRA, e164number);
|
||||
intent.putExtra(RegistrationService.PASSWORD_EXTRA, password);
|
||||
intent.putExtra(RegistrationService.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(RegistrationService.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startService(intent);
|
||||
|
||||
callButton.setEnabled(false);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.thoughtcrime.securesms.components.reminder;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.view.View;
|
||||
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
public class DozeReminder extends Reminder {
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public DozeReminder(@NonNull final Context context) {
|
||||
super("Optimize for missing Play Services",
|
||||
"This device does not support Play Services. Tap to disable system battery optimizations that prevent Signal from retrieving messages while inactive.");
|
||||
|
||||
setOkListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TextSecurePreferences.setPromptedOptimizeDoze(context, true);
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
Uri.parse("package:" + context.getPackageName()));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
setDismissListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TextSecurePreferences.setPromptedOptimizeDoze(context, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isEligible(Context context) {
|
||||
return TextSecurePreferences.isGcmDisabled(context) &&
|
||||
!TextSecurePreferences.hasPromptedOptimizeDoze(context) &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
!((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isIgnoringBatteryOptimizations(context.getPackageName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -60,6 +60,8 @@ public class GcmRefreshJob extends ContextJob implements InjectableType {
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
if (TextSecurePreferences.isGcmDisabled(context)) return;
|
||||
|
||||
String registrationId = TextSecurePreferences.getGcmRegistrationId(context);
|
||||
|
||||
if (registrationId == null) {
|
||||
|
||||
@@ -43,11 +43,12 @@ public class RefreshAttributesJob extends ContextJob implements InjectableType {
|
||||
String gcmRegistrationId = TextSecurePreferences.getGcmRegistrationId(context);
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
|
||||
boolean video = TextSecurePreferences.isWebrtcCallingEnabled(context);
|
||||
boolean fetchesMessages = TextSecurePreferences.isGcmDisabled(context);
|
||||
|
||||
String token = signalAccountManager.getAccountVerificationToken();
|
||||
|
||||
redPhoneAccountManager.createAccount(token, new RedPhoneAccountAttributes(signalingKey, gcmRegistrationId));
|
||||
signalAccountManager.setAccountAttributes(signalingKey, registrationId, true, video);
|
||||
signalAccountManager.setAccountAttributes(signalingKey, registrationId, true, video || fetchesMessages, fetchesMessages);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -58,9 +58,11 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.service.MessageRetrievalService;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.webrtc.CallNotificationManager;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -155,7 +157,12 @@ public class MessageNotifier {
|
||||
for (StatusBarNotification notification : activeNotifications) {
|
||||
boolean validNotification = false;
|
||||
|
||||
if (notification.getId() != SUMMARY_NOTIFICATION_ID && notification.getId() != NotificationBarManager.RED_PHONE_NOTIFICATION) {
|
||||
if (notification.getId() != SUMMARY_NOTIFICATION_ID &&
|
||||
notification.getId() != NotificationBarManager.RED_PHONE_NOTIFICATION &&
|
||||
notification.getId() != CallNotificationManager.WEBRTC_NOTIFICATION &&
|
||||
notification.getId() != KeyCachingService.SERVICE_RUNNING_ID &&
|
||||
notification.getId() != MessageRetrievalService.FOREGROUND_ID)
|
||||
{
|
||||
for (NotificationItem item : notificationState.getNotifications()) {
|
||||
if (notification.getId() == (SUMMARY_NOTIFICATION_ID + item.getThreadId())) {
|
||||
validNotification = true;
|
||||
|
||||
@@ -99,7 +99,9 @@ public class AdvancedPreferenceFragment extends PreferenceFragment {
|
||||
}
|
||||
|
||||
private void initializeWebrtcCallingToggle() {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
if (TextSecurePreferences.isGcmDisabled(getContext())) {
|
||||
getPreferenceScreen().removePreference(findPreference(TextSecurePreferences.WEBRTC_CALLING_PREF));
|
||||
} else if (Build.VERSION.SDK_INT >= 11) {
|
||||
this.findPreference(TextSecurePreferences.WEBRTC_CALLING_PREF)
|
||||
.setOnPreferenceChangeListener(new WebRtcClickListener());
|
||||
} else {
|
||||
|
||||
@@ -5,9 +5,11 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.gcm.GcmBroadcastReceiver;
|
||||
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
||||
@@ -24,16 +26,20 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class MessageRetrievalService extends Service implements Runnable, InjectableType, RequirementListener {
|
||||
public class MessageRetrievalService extends Service implements InjectableType, RequirementListener {
|
||||
|
||||
private static final String TAG = MessageRetrievalService.class.getSimpleName();
|
||||
|
||||
public static final String ACTION_ACTIVITY_STARTED = "ACTIVITY_STARTED";
|
||||
public static final String ACTION_ACTIVITY_FINISHED = "ACTIVITY_FINISHED";
|
||||
public static final String ACTION_PUSH_RECEIVED = "PUSH_RECEIVED";
|
||||
public static final String ACTION_INITIALIZE = "INITIALIZE";
|
||||
public static final int FOREGROUND_ID = 313399;
|
||||
|
||||
private static final long REQUEST_TIMEOUT_MINUTES = 1;
|
||||
|
||||
private NetworkRequirement networkRequirement;
|
||||
@@ -42,8 +48,9 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
@Inject
|
||||
public SignalServiceMessageReceiver receiver;
|
||||
|
||||
private int activeActivities = 0;
|
||||
private List<Intent> pushPending = new LinkedList<>();
|
||||
private int activeActivities = 0;
|
||||
private List<Intent> pushPending = new LinkedList<>();
|
||||
private MessageRetrievalThread retrievalThread = null;
|
||||
|
||||
public static SignalServiceMessagePipe pipe = null;
|
||||
|
||||
@@ -56,7 +63,11 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
networkRequirementProvider = new NetworkRequirementProvider(this);
|
||||
|
||||
networkRequirementProvider.setListener(this);
|
||||
new Thread(this, "MessageRetrievalService").start();
|
||||
|
||||
retrievalThread = new MessageRetrievalThread();
|
||||
retrievalThread.start();
|
||||
|
||||
setForegroundIfNecessary();
|
||||
}
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
@@ -70,44 +81,11 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
Log.w(TAG, "Waiting for websocket state change....");
|
||||
waitForConnectionNecessary();
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
Log.w(TAG, "Making websocket connection....");
|
||||
pipe = receiver.createMessagePipe();
|
||||
|
||||
try {
|
||||
while (isConnectionNecessary()) {
|
||||
try {
|
||||
Log.w(TAG, "Reading message...");
|
||||
pipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
|
||||
new SignalServiceMessagePipe.MessagePipeCallback() {
|
||||
@Override
|
||||
public void onMessage(SignalServiceEnvelope envelope) {
|
||||
Log.w(TAG, "Retrieved envelope! " + envelope.getSource());
|
||||
|
||||
PushContentReceiveJob receiveJob = new PushContentReceiveJob(MessageRetrievalService.this);
|
||||
receiveJob.handle(envelope, false);
|
||||
|
||||
decrementPushReceived();
|
||||
}
|
||||
});
|
||||
} catch (TimeoutException e) {
|
||||
Log.w(TAG, "Application level read timeout...");
|
||||
} catch (InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.w(TAG, e);
|
||||
} finally {
|
||||
Log.w(TAG, "Shutting down pipe...");
|
||||
shutdown(pipe);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Looping...");
|
||||
if (retrievalThread != null) {
|
||||
retrievalThread.stopThread();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +101,18 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setForegroundIfNecessary() {
|
||||
if (TextSecurePreferences.isGcmDisabled(this)) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setContentTitle(getString(R.string.MessageRetrievalService_signal));
|
||||
builder.setContentText(getString(R.string.MessageRetrievalService_background_connection_enabled));
|
||||
builder.setPriority(NotificationCompat.PRIORITY_MIN);
|
||||
builder.setWhen(0);
|
||||
builder.setSmallIcon(R.drawable.ic_signal_grey_24dp);
|
||||
startForeground(FOREGROUND_ID, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void incrementActive() {
|
||||
activeActivities++;
|
||||
Log.w(TAG, "Active Count: " + activeActivities);
|
||||
@@ -149,11 +139,13 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
}
|
||||
|
||||
private synchronized boolean isConnectionNecessary() {
|
||||
Log.w(TAG, String.format("Network requirement: %s, active activities: %s, push pending: %s",
|
||||
networkRequirement.isPresent(), activeActivities, pushPending.size()));
|
||||
boolean isGcmDisabled = TextSecurePreferences.isGcmDisabled(this);
|
||||
|
||||
return TextSecurePreferences.isWebsocketRegistered(this) &&
|
||||
(activeActivities > 0 || !pushPending.isEmpty()) &&
|
||||
Log.w(TAG, String.format("Network requirement: %s, active activities: %s, push pending: %s, gcm disabled: %b",
|
||||
networkRequirement.isPresent(), activeActivities, pushPending.size(), isGcmDisabled));
|
||||
|
||||
return TextSecurePreferences.isWebsocketRegistered(this) &&
|
||||
(activeActivities > 0 || !pushPending.isEmpty() || isGcmDisabled) &&
|
||||
networkRequirement.isPresent();
|
||||
}
|
||||
|
||||
@@ -188,4 +180,59 @@ public class MessageRetrievalService extends Service implements Runnable, Inject
|
||||
public static @Nullable SignalServiceMessagePipe getPipe() {
|
||||
return pipe;
|
||||
}
|
||||
|
||||
private class MessageRetrievalThread extends Thread {
|
||||
|
||||
private AtomicBoolean stopThread = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!stopThread.get()) {
|
||||
Log.w(TAG, "Waiting for websocket state change....");
|
||||
waitForConnectionNecessary();
|
||||
|
||||
Log.w(TAG, "Making websocket connection....");
|
||||
pipe = receiver.createMessagePipe();
|
||||
|
||||
SignalServiceMessagePipe localPipe = pipe;
|
||||
|
||||
try {
|
||||
while (isConnectionNecessary() && !stopThread.get()) {
|
||||
try {
|
||||
Log.w(TAG, "Reading message...");
|
||||
localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES,
|
||||
new SignalServiceMessagePipe.MessagePipeCallback() {
|
||||
@Override
|
||||
public void onMessage(SignalServiceEnvelope envelope) {
|
||||
Log.w(TAG, "Retrieved envelope! " + envelope.getSource());
|
||||
|
||||
PushContentReceiveJob receiveJob = new PushContentReceiveJob(MessageRetrievalService.this);
|
||||
receiveJob.handle(envelope, false);
|
||||
|
||||
decrementPushReceived();
|
||||
}
|
||||
});
|
||||
} catch (TimeoutException e) {
|
||||
Log.w(TAG, "Application level read timeout...");
|
||||
} catch (InvalidVersionException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.w(TAG, e);
|
||||
} finally {
|
||||
Log.w(TAG, "Shutting down pipe...");
|
||||
shutdown(localPipe);
|
||||
}
|
||||
|
||||
Log.w(TAG, "Looping...");
|
||||
}
|
||||
|
||||
Log.w(TAG, "Exiting...");
|
||||
}
|
||||
|
||||
public void stopThread() {
|
||||
stopThread.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
|
||||
public class PersistentConnectionBootListener extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TextSecurePreferences.isGcmDisabled(context)) {
|
||||
Intent serviceIntent = new Intent(context, MessageRetrievalService.class);
|
||||
serviceIntent.setAction(MessageRetrievalService.ACTION_INITIALIZE);
|
||||
context.startService(serviceIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,12 @@ public class RegistrationService extends Service {
|
||||
public static final String CHALLENGE_EVENT = "org.thoughtcrime.securesms.CHALLENGE_EVENT";
|
||||
public static final String REGISTRATION_EVENT = "org.thoughtcrime.securesms.REGISTRATION_EVENT";
|
||||
|
||||
public static final String CHALLENGE_EXTRA = "CAAChallenge";
|
||||
public static final String NUMBER_EXTRA = "e164number";
|
||||
public static final String MASTER_SECRET_EXTRA = "master_secret";
|
||||
public static final String GCM_SUPPORTED_EXTRA = "gcm_supported";
|
||||
public static final String PASSWORD_EXTRA = "password";
|
||||
public static final String SIGNALING_KEY_EXTRA = "signaling_key";
|
||||
public static final String CHALLENGE_EXTRA = "CAAChallenge";
|
||||
|
||||
private static final long REGISTRATION_TIMEOUT_MILLIS = 120000;
|
||||
|
||||
@@ -149,21 +154,22 @@ public class RegistrationService extends Service {
|
||||
|
||||
private void handleVoiceRequestedIntent(Intent intent) {
|
||||
setState(new RegistrationState(RegistrationState.STATE_VOICE_REQUESTED,
|
||||
intent.getStringExtra("e164number"),
|
||||
intent.getStringExtra("password")));
|
||||
intent.getStringExtra(NUMBER_EXTRA),
|
||||
intent.getStringExtra(PASSWORD_EXTRA)));
|
||||
}
|
||||
|
||||
private void handleVoiceRegistrationIntent(Intent intent) {
|
||||
markAsVerifying(true);
|
||||
|
||||
String number = intent.getStringExtra("e164number");
|
||||
String password = intent.getStringExtra("password");
|
||||
String signalingKey = intent.getStringExtra("signaling_key");
|
||||
String number = intent.getStringExtra(NUMBER_EXTRA);
|
||||
String password = intent.getStringExtra(PASSWORD_EXTRA);
|
||||
String signalingKey = intent.getStringExtra(SIGNALING_KEY_EXTRA);
|
||||
boolean supportsGcm = intent.getBooleanExtra(GCM_SUPPORTED_EXTRA, true);
|
||||
|
||||
try {
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(this, number, password);
|
||||
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey);
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey, supportsGcm);
|
||||
|
||||
markAsVerified(number, password, signalingKey);
|
||||
|
||||
@@ -183,8 +189,10 @@ public class RegistrationService extends Service {
|
||||
private void handleSmsRegistrationIntent(Intent intent) {
|
||||
markAsVerifying(true);
|
||||
|
||||
String number = intent.getStringExtra("e164number");
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(this);
|
||||
String number = intent.getStringExtra(NUMBER_EXTRA);
|
||||
boolean supportsGcm = intent.getBooleanExtra(GCM_SUPPORTED_EXTRA, true);
|
||||
int registrationId = TextSecurePreferences.getLocalRegistrationId(this);
|
||||
boolean supportsVideo = TextSecurePreferences.isWebrtcCallingEnabled(this) || !supportsGcm;
|
||||
|
||||
if (registrationId == 0) {
|
||||
registrationId = KeyHelper.generateRegistrationId(false);
|
||||
@@ -203,9 +211,9 @@ public class RegistrationService extends Service {
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_VERIFYING, number));
|
||||
String challenge = waitForChallenge();
|
||||
accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId, true, TextSecurePreferences.isWebrtcCallingEnabled(this));
|
||||
accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId, true, supportsVideo, !supportsGcm);
|
||||
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey);
|
||||
handleCommonRegistration(accountManager, number, password, signalingKey, supportsGcm);
|
||||
markAsVerified(number, password, signalingKey);
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number));
|
||||
@@ -231,7 +239,7 @@ public class RegistrationService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCommonRegistration(SignalServiceAccountManager accountManager, String number, String password, String signalingKey)
|
||||
private void handleCommonRegistration(SignalServiceAccountManager accountManager, String number, String password, String signalingKey, boolean supportsGcm)
|
||||
throws IOException
|
||||
{
|
||||
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
|
||||
@@ -244,21 +252,29 @@ public class RegistrationService extends Service {
|
||||
|
||||
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
|
||||
|
||||
String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID);
|
||||
accountManager.setGcmId(Optional.of(gcmRegistrationId));
|
||||
if (supportsGcm) {
|
||||
String gcmRegistrationId = GoogleCloudMessaging.getInstance(this).register(GcmRefreshJob.REGISTRATION_ID);
|
||||
accountManager.setGcmId(Optional.of(gcmRegistrationId));
|
||||
|
||||
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
|
||||
TextSecurePreferences.setGcmDisabled(this, false);
|
||||
} else {
|
||||
TextSecurePreferences.setGcmDisabled(this, true);
|
||||
}
|
||||
|
||||
TextSecurePreferences.setGcmRegistrationId(this, gcmRegistrationId);
|
||||
TextSecurePreferences.setWebsocketRegistered(this, true);
|
||||
|
||||
DatabaseFactory.getIdentityDatabase(this).saveIdentity(self.getRecipientId(), identityKey.getPublicKey());
|
||||
DirectoryHelper.refreshDirectory(this, accountManager, number);
|
||||
|
||||
RedPhoneAccountManager redPhoneAccountManager = new RedPhoneAccountManager(BuildConfig.REDPHONE_MASTER_URL,
|
||||
new RedPhoneTrustStore(this),
|
||||
number, password);
|
||||
if (supportsGcm) {
|
||||
RedPhoneAccountManager redPhoneAccountManager = new RedPhoneAccountManager(BuildConfig.REDPHONE_MASTER_URL,
|
||||
new RedPhoneTrustStore(this),
|
||||
number, password);
|
||||
|
||||
String verificationToken = accountManager.getAccountVerificationToken();
|
||||
redPhoneAccountManager.createAccount(verificationToken, new RedPhoneAccountAttributes(signalingKey, gcmRegistrationId));
|
||||
String verificationToken = accountManager.getAccountVerificationToken();
|
||||
redPhoneAccountManager.createAccount(verificationToken, new RedPhoneAccountAttributes(signalingKey, TextSecurePreferences.getGcmRegistrationId(this)));
|
||||
}
|
||||
|
||||
DirectoryRefreshListener.schedule(this);
|
||||
RotateSignedPreKeyListener.schedule(this);
|
||||
|
||||
@@ -69,6 +69,7 @@ public class TextSecurePreferences {
|
||||
private static final String GCM_PASSWORD_PREF = "pref_gcm_password";
|
||||
private static final String PROMPTED_PUSH_REGISTRATION_PREF = "pref_prompted_push_registration";
|
||||
private static final String PROMPTED_DEFAULT_SMS_PREF = "pref_prompted_default_sms";
|
||||
private static final String PROMPTED_OPTIMIZE_DOZE_PREF = "pref_prompted_optimize_doze";
|
||||
private static final String PROMPTED_SHARE_PREF = "pref_prompted_share";
|
||||
private static final String SIGNALING_KEY_PREF = "pref_signaling_key";
|
||||
private static final String DIRECTORY_FRESH_TIME_PREF = "pref_directory_refresh_time";
|
||||
@@ -80,6 +81,7 @@ public class TextSecurePreferences {
|
||||
private static final String SIGNED_PREKEY_REGISTERED_PREF = "pref_signed_prekey_registered";
|
||||
private static final String WIFI_SMS_PREF = "pref_wifi_sms";
|
||||
|
||||
private static final String GCM_DISABLED_PREF = "pref_gcm_disabled";
|
||||
private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id";
|
||||
private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version";
|
||||
private static final String WEBSOCKET_REGISTERED_PREF = "pref_websocket_registered";
|
||||
@@ -105,6 +107,14 @@ public class TextSecurePreferences {
|
||||
return getBooleanPreference(context, ALWAYS_RELAY_CALLS_PREF, false);
|
||||
}
|
||||
|
||||
public static boolean isGcmDisabled(Context context) {
|
||||
return getBooleanPreference(context, GCM_DISABLED_PREF, false);
|
||||
}
|
||||
|
||||
public static void setGcmDisabled(Context context, boolean disabled) {
|
||||
setBooleanPreference(context, GCM_DISABLED_PREF, disabled);
|
||||
}
|
||||
|
||||
public static boolean isWebrtcCallingEnabled(Context context) {
|
||||
return getBooleanPreference(context, WEBRTC_CALLING_PREF, false);
|
||||
}
|
||||
@@ -481,6 +491,14 @@ public class TextSecurePreferences {
|
||||
setBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, value);
|
||||
}
|
||||
|
||||
public static void setPromptedOptimizeDoze(Context context, boolean value) {
|
||||
setBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, value);
|
||||
}
|
||||
|
||||
public static boolean hasPromptedOptimizeDoze(Context context) {
|
||||
return getBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, false);
|
||||
}
|
||||
|
||||
public static boolean hasPromptedShare(Context context) {
|
||||
return getBooleanPreference(context, PROMPTED_SHARE_PREF, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user