2015-11-19 18:21:19 +00:00
|
|
|
package org.thoughtcrime.securesms;
|
|
|
|
|
2017-11-25 06:00:30 +00:00
|
|
|
import android.Manifest;
|
2017-11-12 21:54:37 +00:00
|
|
|
import android.annotation.SuppressLint;
|
2015-11-19 18:21:19 +00:00
|
|
|
import android.content.Context;
|
|
|
|
import android.net.Uri;
|
2017-10-23 20:03:32 +00:00
|
|
|
import android.os.AsyncTask;
|
2015-11-19 18:21:19 +00:00
|
|
|
import android.os.Build;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.os.Vibrator;
|
2020-08-19 00:06:26 +00:00
|
|
|
import androidx.annotation.NonNull;
|
2015-11-22 17:28:36 +00:00
|
|
|
import android.text.TextUtils;
|
2015-11-19 18:21:19 +00:00
|
|
|
import android.transition.TransitionInflater;
|
|
|
|
import android.view.MenuItem;
|
|
|
|
import android.view.View;
|
|
|
|
import android.widget.Button;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
2017-08-25 19:00:52 +00:00
|
|
|
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
2019-07-24 02:30:23 +00:00
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
2017-11-25 06:00:30 +00:00
|
|
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
2016-12-20 17:55:52 +00:00
|
|
|
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
2016-08-25 01:51:45 +00:00
|
|
|
import org.thoughtcrime.securesms.qr.ScanListener;
|
2015-11-19 18:21:19 +00:00
|
|
|
import org.thoughtcrime.securesms.util.Base64;
|
|
|
|
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
|
|
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
|
|
import org.thoughtcrime.securesms.util.Util;
|
2017-08-25 19:00:52 +00:00
|
|
|
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
2016-03-23 17:34:41 +00:00
|
|
|
import org.whispersystems.libsignal.IdentityKeyPair;
|
|
|
|
import org.whispersystems.libsignal.InvalidKeyException;
|
|
|
|
import org.whispersystems.libsignal.ecc.Curve;
|
|
|
|
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
2017-08-25 19:00:52 +00:00
|
|
|
import org.whispersystems.libsignal.util.guava.Optional;
|
2016-03-23 17:34:41 +00:00
|
|
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
|
|
|
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
|
|
|
|
import org.whispersystems.signalservice.internal.push.DeviceLimitExceededException;
|
2015-11-19 18:21:19 +00:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
2019-07-24 02:30:23 +00:00
|
|
|
import network.loki.messenger.R;
|
|
|
|
|
2015-11-19 18:21:19 +00:00
|
|
|
public class DeviceActivity extends PassphraseRequiredActionBarActivity
|
2016-08-25 01:51:45 +00:00
|
|
|
implements Button.OnClickListener, ScanListener, DeviceLinkFragment.LinkClickedListener
|
2015-11-19 18:21:19 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
private static final String TAG = DeviceActivity.class.getSimpleName();
|
|
|
|
|
|
|
|
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
|
|
|
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
|
|
|
|
|
|
|
private DeviceAddFragment deviceAddFragment;
|
|
|
|
private DeviceListFragment deviceListFragment;
|
|
|
|
private DeviceLinkFragment deviceLinkFragment;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPreCreate() {
|
|
|
|
dynamicTheme.onCreate(this);
|
|
|
|
dynamicLanguage.onCreate(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-02-02 03:22:48 +00:00
|
|
|
public void onCreate(Bundle bundle, boolean ready) {
|
2015-11-19 18:21:19 +00:00
|
|
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
2016-02-11 16:27:30 +00:00
|
|
|
getSupportActionBar().setTitle(R.string.AndroidManifest__linked_devices);
|
2015-11-19 18:21:19 +00:00
|
|
|
this.deviceAddFragment = new DeviceAddFragment();
|
|
|
|
this.deviceListFragment = new DeviceListFragment();
|
|
|
|
this.deviceLinkFragment = new DeviceLinkFragment();
|
|
|
|
|
|
|
|
this.deviceListFragment.setAddDeviceButtonListener(this);
|
|
|
|
this.deviceAddFragment.setScanListener(this);
|
|
|
|
|
2019-08-23 03:22:52 +00:00
|
|
|
// if (getIntent().getBooleanExtra("add", false)) {
|
2018-02-02 03:22:48 +00:00
|
|
|
initFragment(android.R.id.content, deviceAddFragment, dynamicLanguage.getCurrentLocale());
|
2019-08-23 03:22:52 +00:00
|
|
|
// } else {
|
|
|
|
// initFragment(android.R.id.content, deviceListFragment, dynamicLanguage.getCurrentLocale());
|
|
|
|
// }
|
2015-11-19 18:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
dynamicTheme.onResume(this);
|
|
|
|
dynamicLanguage.onResume(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
case android.R.id.home: finish(); return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
2017-11-25 06:00:30 +00:00
|
|
|
Permissions.with(this)
|
|
|
|
.request(Manifest.permission.CAMERA)
|
|
|
|
.ifNecessary()
|
|
|
|
.withPermanentDenialDialog(getString(R.string.DeviceActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code))
|
|
|
|
.onAllGranted(() -> {
|
|
|
|
getSupportFragmentManager().beginTransaction()
|
|
|
|
.replace(android.R.id.content, deviceAddFragment)
|
|
|
|
.addToBackStack(null)
|
|
|
|
.commitAllowingStateLoss();
|
|
|
|
})
|
|
|
|
.onAnyDenied(() -> Toast.makeText(this, R.string.DeviceActivity_unable_to_scan_a_qr_code_without_the_camera_permission, Toast.LENGTH_LONG).show())
|
|
|
|
.execute();
|
2015-11-19 18:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2016-08-25 01:51:45 +00:00
|
|
|
public void onQrDataFound(final String data) {
|
2017-11-25 06:00:30 +00:00
|
|
|
Util.runOnMain(() -> {
|
|
|
|
((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
|
|
|
|
Uri uri = Uri.parse(data);
|
|
|
|
deviceLinkFragment.setLinkClickedListener(uri, DeviceActivity.this);
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
|
|
deviceAddFragment.setSharedElementReturnTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared));
|
|
|
|
deviceAddFragment.setExitTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade));
|
|
|
|
|
|
|
|
deviceLinkFragment.setSharedElementEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared));
|
|
|
|
deviceLinkFragment.setEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade));
|
|
|
|
|
|
|
|
getSupportFragmentManager().beginTransaction()
|
|
|
|
.addToBackStack(null)
|
|
|
|
.addSharedElement(deviceAddFragment.getDevicesImage(), "devices")
|
|
|
|
.replace(android.R.id.content, deviceLinkFragment)
|
|
|
|
.commit();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
getSupportFragmentManager().beginTransaction()
|
|
|
|
.setCustomAnimations(R.anim.slide_from_bottom, R.anim.slide_to_bottom,
|
|
|
|
R.anim.slide_from_bottom, R.anim.slide_to_bottom)
|
|
|
|
.replace(android.R.id.content, deviceLinkFragment)
|
|
|
|
.addToBackStack(null)
|
|
|
|
.commit();
|
2015-11-19 18:21:19 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-11-25 06:00:30 +00:00
|
|
|
@Override
|
|
|
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
|
|
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
|
|
|
}
|
|
|
|
|
2017-11-12 21:54:37 +00:00
|
|
|
@SuppressLint("StaticFieldLeak")
|
2015-11-19 18:21:19 +00:00
|
|
|
@Override
|
|
|
|
public void onLink(final Uri uri) {
|
|
|
|
new ProgressDialogAsyncTask<Void, Void, Integer>(this,
|
|
|
|
R.string.DeviceProvisioningActivity_content_progress_title,
|
|
|
|
R.string.DeviceProvisioningActivity_content_progress_content)
|
|
|
|
{
|
|
|
|
private static final int SUCCESS = 0;
|
|
|
|
private static final int NO_DEVICE = 1;
|
|
|
|
private static final int NETWORK_ERROR = 2;
|
|
|
|
private static final int KEY_ERROR = 3;
|
|
|
|
private static final int LIMIT_EXCEEDED = 4;
|
2015-11-22 17:28:36 +00:00
|
|
|
private static final int BAD_CODE = 5;
|
2015-11-19 18:21:19 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Integer doInBackground(Void... params) {
|
2017-11-12 21:54:37 +00:00
|
|
|
boolean isMultiDevice = TextSecurePreferences.isMultiDevice(DeviceActivity.this);
|
|
|
|
|
2015-11-19 18:21:19 +00:00
|
|
|
try {
|
2016-03-23 17:34:41 +00:00
|
|
|
Context context = DeviceActivity.this;
|
2016-12-20 17:55:52 +00:00
|
|
|
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
|
2016-03-23 17:34:41 +00:00
|
|
|
String verificationCode = accountManager.getNewDeviceVerificationCode();
|
|
|
|
String ephemeralId = uri.getQueryParameter("uuid");
|
|
|
|
String publicKeyEncoded = uri.getQueryParameter("pub_key");
|
2015-11-22 17:28:36 +00:00
|
|
|
|
|
|
|
if (TextUtils.isEmpty(ephemeralId) || TextUtils.isEmpty(publicKeyEncoded)) {
|
|
|
|
Log.w(TAG, "UUID or Key is empty!");
|
|
|
|
return BAD_CODE;
|
|
|
|
}
|
|
|
|
|
2017-08-25 19:00:52 +00:00
|
|
|
ECPublicKey publicKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0);
|
|
|
|
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context);
|
|
|
|
Optional<byte[]> profileKey = Optional.of(ProfileKeyUtil.getProfileKey(getContext()));
|
2015-11-19 18:21:19 +00:00
|
|
|
|
2017-11-12 21:54:37 +00:00
|
|
|
TextSecurePreferences.setMultiDevice(DeviceActivity.this, true);
|
2018-10-11 23:45:22 +00:00
|
|
|
TextSecurePreferences.setIsUnidentifiedDeliveryEnabled(context, false);
|
2017-08-25 19:00:52 +00:00
|
|
|
accountManager.addDevice(ephemeralId, publicKey, identityKeyPair, profileKey, verificationCode);
|
2017-11-12 21:54:37 +00:00
|
|
|
|
2015-11-19 18:21:19 +00:00
|
|
|
return SUCCESS;
|
|
|
|
} catch (NotFoundException e) {
|
|
|
|
Log.w(TAG, e);
|
2017-11-12 21:54:37 +00:00
|
|
|
TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice);
|
2015-11-19 18:21:19 +00:00
|
|
|
return NO_DEVICE;
|
|
|
|
} catch (DeviceLimitExceededException e) {
|
|
|
|
Log.w(TAG, e);
|
2017-11-12 21:54:37 +00:00
|
|
|
TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice);
|
2015-11-19 18:21:19 +00:00
|
|
|
return LIMIT_EXCEEDED;
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, e);
|
2017-11-12 21:54:37 +00:00
|
|
|
TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice);
|
2015-11-19 18:21:19 +00:00
|
|
|
return NETWORK_ERROR;
|
|
|
|
} catch (InvalidKeyException e) {
|
|
|
|
Log.w(TAG, e);
|
2017-11-12 21:54:37 +00:00
|
|
|
TextSecurePreferences.setMultiDevice(DeviceActivity.this, isMultiDevice);
|
2015-11-19 18:21:19 +00:00
|
|
|
return KEY_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Integer result) {
|
|
|
|
super.onPostExecute(result);
|
|
|
|
|
|
|
|
Context context = DeviceActivity.this;
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
case SUCCESS:
|
|
|
|
Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_success, Toast.LENGTH_SHORT).show();
|
|
|
|
finish();
|
|
|
|
return;
|
|
|
|
case NO_DEVICE:
|
|
|
|
Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_no_device, Toast.LENGTH_LONG).show();
|
|
|
|
break;
|
|
|
|
case NETWORK_ERROR:
|
|
|
|
Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_network_error, Toast.LENGTH_LONG).show();
|
|
|
|
break;
|
|
|
|
case KEY_ERROR:
|
|
|
|
Toast.makeText(context, R.string.DeviceProvisioningActivity_content_progress_key_error, Toast.LENGTH_LONG).show();
|
|
|
|
break;
|
|
|
|
case LIMIT_EXCEEDED:
|
|
|
|
Toast.makeText(context, R.string.DeviceProvisioningActivity_sorry_you_have_too_many_devices_linked_already, Toast.LENGTH_LONG).show();
|
|
|
|
break;
|
2015-11-22 17:28:36 +00:00
|
|
|
case BAD_CODE:
|
|
|
|
Toast.makeText(context, R.string.DeviceActivity_sorry_this_is_not_a_valid_device_link_qr_code, Toast.LENGTH_LONG).show();
|
|
|
|
break;
|
2015-11-19 18:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getSupportFragmentManager().popBackStackImmediate();
|
|
|
|
}
|
2017-10-23 20:03:32 +00:00
|
|
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
2015-11-19 18:21:19 +00:00
|
|
|
}
|
|
|
|
}
|