mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-18 03:48:08 +00:00
414 lines
13 KiB
Java
414 lines
13 KiB
Java
package org.linphone.assistant;
|
|
/*
|
|
RemoteProvisioningLoginActivity.java
|
|
Copyright (C) 2017 Belledonne Communications, Grenoble, France
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
import org.linphone.LinphoneManager;
|
|
import org.linphone.LinphonePreferences;
|
|
import org.linphone.R;
|
|
import org.linphone.activities.LinphoneActivity;
|
|
import org.linphone.core.ConfiguringState;
|
|
import org.linphone.core.Core;
|
|
import org.linphone.core.CoreListenerStub;
|
|
import org.linphone.core.ProxyConfig;
|
|
import org.linphone.core.RegistrationState;
|
|
import org.linphone.mediastream.Log;
|
|
import org.linphone.mediastream.video.AndroidVideoWindowImpl;
|
|
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
|
|
|
|
import android.Manifest;
|
|
import android.app.Activity;
|
|
import android.app.ProgressDialog;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.graphics.drawable.ColorDrawable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Bundle;
|
|
import android.support.v4.app.ActivityCompat;
|
|
import android.support.v4.content.ContextCompat;
|
|
import android.util.Base64;
|
|
import android.view.SurfaceView;
|
|
import android.view.View;
|
|
import android.view.View.OnClickListener;
|
|
import android.view.WindowManager;
|
|
import android.widget.Button;
|
|
import android.widget.EditText;
|
|
import android.widget.ImageView;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.RelativeLayout;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.math.BigInteger;
|
|
import java.security.spec.KeySpec;
|
|
|
|
import javax.crypto.Cipher;
|
|
import javax.crypto.SecretKey;
|
|
import javax.crypto.SecretKeyFactory;
|
|
import javax.crypto.spec.IvParameterSpec;
|
|
import javax.crypto.spec.PBEKeySpec;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
|
|
import static android.os.SystemClock.sleep;
|
|
|
|
public class RemoteProvisioningLoginActivity extends Activity implements OnClickListener {
|
|
private static RemoteProvisioningLoginActivity instance;
|
|
private EditText code_sms;
|
|
private TextView step, instruction;
|
|
private Button ok, back;
|
|
private ProgressDialog progress;
|
|
private String qrcodeString;
|
|
private String remoteUrl;
|
|
private LinearLayout bottom;
|
|
private RelativeLayout layout_button;
|
|
private CoreListenerStub mListener;
|
|
private SurfaceView mQrcodeView;
|
|
private ImageView mImageMask;
|
|
private AndroidVideoWindowImpl androidVideoWindowImpl;
|
|
private boolean cameraAuthorize = false;
|
|
private boolean readQRCode = true;
|
|
private boolean backCamera = true;
|
|
private int PERMISSION_CAMERA = 108;
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
instance = this;
|
|
setContentView(R.layout.assistant_remote_provisioning_login);
|
|
|
|
if (getPackageManager().checkPermission(Manifest.permission.CAMERA,
|
|
getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
|
checkAndRequestVideoPermission();
|
|
} else {
|
|
cameraAuthorize = true;
|
|
}
|
|
|
|
mQrcodeView = (SurfaceView) findViewById(R.id.qrcodeCaptureSurface);
|
|
bottom = (LinearLayout) findViewById(R.id.bottom_text);
|
|
code_sms = (EditText) findViewById(R.id.code_sms);
|
|
step = (TextView) findViewById(R.id.step);
|
|
instruction = (TextView) findViewById(R.id.instruction);
|
|
mImageMask = (ImageView) findViewById(R.id.imageView4);
|
|
layout_button = (RelativeLayout) findViewById(R.id.layout_button);
|
|
|
|
ok = (Button) findViewById(R.id.valider);
|
|
back = (Button) findViewById(R.id.retour);
|
|
|
|
ok.setOnClickListener(this);
|
|
back.setOnClickListener(this);
|
|
|
|
mImageMask.setOnClickListener(new OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
if (readQRCode && getPackageManager().checkPermission(Manifest.permission.CAMERA,
|
|
getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
|
checkAndRequestVideoPermission();
|
|
} else {
|
|
instance.setBackCamera(!backCamera);
|
|
}
|
|
}
|
|
});
|
|
|
|
mListener = new CoreListenerStub() {
|
|
@Override
|
|
public void onQrcodeFound(Core lc, String result) {
|
|
instance.qrcodeString = result;
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
instance.enableQrcodeReader(false);
|
|
instance.displayCodeSms();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onConfiguringStatus(Core lc, final ConfiguringState state, String message) {
|
|
if (state == ConfiguringState.Successful) {
|
|
//TODO
|
|
} else if (state == ConfiguringState.Failed) {
|
|
Toast.makeText(RemoteProvisioningLoginActivity.this, R.string.remote_provisioning_failure, Toast.LENGTH_LONG).show();
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
instance.ok.setEnabled(true);
|
|
}
|
|
});
|
|
if (progress != null) progress.dismiss();
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
instance.displayQrCode();
|
|
instance.launchQrcodeReader();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onRegistrationStateChanged(Core lc, ProxyConfig proxy, RegistrationState state, String smessage) {
|
|
if (state.equals(RegistrationState.Ok)) {
|
|
LinphonePreferences.instance().firstLaunchSuccessful();
|
|
startActivity(new Intent().setClass(RemoteProvisioningLoginActivity.this, LinphoneActivity.class).setData(getIntent().getData()));
|
|
finish();
|
|
}
|
|
if (progress != null) progress.dismiss();
|
|
}
|
|
|
|
};
|
|
|
|
|
|
displayQrCode();
|
|
}
|
|
|
|
void checkAndRequestVideoPermission() {
|
|
int permissionGranted = getPackageManager().checkPermission(Manifest.permission.CAMERA, getPackageName());
|
|
Log.i("[Permission] " + Manifest.permission.CAMERA + " is " + (permissionGranted == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
|
|
|
|
if (permissionGranted != PackageManager.PERMISSION_GRANTED) {
|
|
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_CAMERA);
|
|
}
|
|
}
|
|
|
|
private void cancelWizard(boolean bypassCheck) {
|
|
if (bypassCheck || getResources().getBoolean(R.bool.allow_cancel_remote_provisioning_login_activity)) {
|
|
LinphonePreferences.instance().disableProvisioningLoginView();
|
|
setResult(bypassCheck ? Activity.RESULT_OK : Activity.RESULT_CANCELED);
|
|
finish();
|
|
}
|
|
}
|
|
|
|
private void displayQrCode() {
|
|
mQrcodeView.setVisibility(View.VISIBLE);
|
|
mImageMask.setVisibility(View.VISIBLE);
|
|
code_sms.setVisibility(View.GONE);
|
|
ok.setVisibility(View.GONE);
|
|
back.setVisibility(View.GONE);
|
|
layout_button.setVisibility(View.GONE);
|
|
|
|
step.setText(getString(R.string.assistant_step1));
|
|
instruction.setText(getString(R.string.assistant_instruction1));
|
|
|
|
readQRCode = true;
|
|
}
|
|
|
|
private void displayCodeSms() {
|
|
mQrcodeView.setVisibility(View.GONE);
|
|
mImageMask.setVisibility(View.GONE);
|
|
code_sms.setVisibility(View.VISIBLE);
|
|
ok.setVisibility(View.VISIBLE);
|
|
back.setVisibility(View.VISIBLE);
|
|
layout_button.setVisibility(View.VISIBLE);
|
|
|
|
step.setText(getString(R.string.assistant_step2));
|
|
instruction.setText(getString(R.string.assistant_instruction2));
|
|
|
|
readQRCode = false;
|
|
}
|
|
|
|
private void enableQrcodeReader(boolean enable) {
|
|
if (cameraAuthorize && readQRCode) {
|
|
LinphoneManager.getLc().enableQrcodeVideoPreview(enable);
|
|
LinphoneManager.getLc().enableVideoPreview(enable);
|
|
}
|
|
}
|
|
|
|
private void setBackCamera(boolean useBackCamera) {
|
|
int camId = 0;
|
|
AndroidCameraConfiguration.AndroidCamera[] cameras = AndroidCameraConfiguration.retrieveCameras();
|
|
for (AndroidCameraConfiguration.AndroidCamera androidCamera : cameras) {
|
|
if (androidCamera.frontFacing == !useBackCamera)
|
|
camId = androidCamera.id;
|
|
}
|
|
String[] devices = LinphoneManager.getLc().getVideoDevicesList();
|
|
String newDevice = devices[camId];
|
|
LinphoneManager.getLc().setVideoDevice(newDevice);
|
|
backCamera = useBackCamera;
|
|
}
|
|
|
|
private void launchQrcodeReader() {
|
|
setBackCamera(true);
|
|
|
|
androidVideoWindowImpl = new AndroidVideoWindowImpl(null, mQrcodeView, new AndroidVideoWindowImpl.VideoWindowListener() {
|
|
public void onVideoRenderingSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) {}
|
|
|
|
public void onVideoRenderingSurfaceDestroyed(AndroidVideoWindowImpl vw) {}
|
|
|
|
public void onVideoPreviewSurfaceReady(AndroidVideoWindowImpl vw, SurfaceView surface) {
|
|
LinphoneManager.getLc().setNativePreviewWindowId(androidVideoWindowImpl);
|
|
}
|
|
|
|
public void onVideoPreviewSurfaceDestroyed(AndroidVideoWindowImpl vw) {}
|
|
});
|
|
|
|
enableQrcodeReader(true);
|
|
}
|
|
|
|
@Override
|
|
public void onStart() {
|
|
super.onStart();
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
launchQrcodeReader();
|
|
if (androidVideoWindowImpl != null) {
|
|
synchronized (androidVideoWindowImpl) {
|
|
LinphoneManager.getLc().setNativePreviewWindowId(androidVideoWindowImpl);
|
|
}
|
|
}
|
|
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
|
if (lc != null) {
|
|
lc.addListener(mListener);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
|
|
if (lc != null) {
|
|
lc.removeListener(mListener);
|
|
}
|
|
if (androidVideoWindowImpl != null) {
|
|
synchronized (androidVideoWindowImpl) {
|
|
LinphoneManager.getLc().setNativePreviewWindowId(null);
|
|
}
|
|
}
|
|
enableQrcodeReader(false);
|
|
setBackCamera(false);
|
|
super.onPause();
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
if (androidVideoWindowImpl != null) {
|
|
androidVideoWindowImpl.release();
|
|
androidVideoWindowImpl = null;
|
|
}
|
|
super.onDestroy();
|
|
}
|
|
|
|
@Override
|
|
public void onClick(View v) {
|
|
int id = v.getId();
|
|
|
|
if (id == R.id.cancel) {
|
|
cancelWizard(false);
|
|
}
|
|
if (id == R.id.retour) {
|
|
displayQrCode();
|
|
enableQrcodeReader(true);
|
|
}
|
|
if (id == R.id.valider) {
|
|
displayRemoteProvisioningInProgressDialog();
|
|
ok.setEnabled(false);
|
|
if (qrcodeString != null
|
|
&& (qrcodeString.startsWith("http://")
|
|
|| qrcodeString.startsWith("https://"))) {
|
|
storeAccount(qrcodeString);
|
|
} else {
|
|
if (decryptQrcode()) {
|
|
storeAccount(remoteUrl);
|
|
} else {
|
|
ok.setEnabled(true);
|
|
if (progress != null) progress.cancel();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
for (int i = 0; i < permissions.length; i++) {
|
|
Log.i("[Permission] " + permissions[i] + " is " + (grantResults[i] == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
|
|
}
|
|
|
|
if (requestCode == PERMISSION_CAMERA) {
|
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
cameraAuthorize = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private byte[] removeUselessByte(byte[] tab, int wantedSize) {
|
|
if (wantedSize == tab.length) return tab;
|
|
byte[] newTab = new byte[wantedSize];
|
|
for (int i = 1 ; i < tab.length ; i++) {
|
|
newTab[i-1] = tab[i];
|
|
}
|
|
return newTab;
|
|
}
|
|
|
|
private boolean decryptQrcode() {
|
|
try {
|
|
byte[] unBased64Data = qrcodeString.getBytes();
|
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(unBased64Data);
|
|
|
|
byte[] contentToDecrypt = new byte[unBased64Data.length];
|
|
|
|
inputStream.read(contentToDecrypt);
|
|
|
|
BigInteger saltHex = new BigInteger("F000000000000000", 16);
|
|
BigInteger ivHex = new BigInteger("F58B8C9A49B321DBA000000000000000", 16);
|
|
|
|
byte[] saltByte = removeUselessByte(saltHex.toByteArray(), 8);
|
|
byte[] ivByte = removeUselessByte(ivHex.toByteArray(), 16);
|
|
|
|
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEwithSHA256AND256BITAES-CBC-BC");
|
|
KeySpec keySpec = new PBEKeySpec(code_sms.getText().toString().toCharArray(), saltByte, 10000, 128);
|
|
SecretKey tmpSecretKey = factory.generateSecret(keySpec);
|
|
SecretKeySpec secretKeySpec = new SecretKeySpec(tmpSecretKey.getEncoded(), "AES");
|
|
|
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(ivByte));
|
|
remoteUrl = new String(cipher.doFinal(Base64.decode(contentToDecrypt, Base64.DEFAULT)));
|
|
} catch (Exception ex) {
|
|
Toast.makeText(RemoteProvisioningLoginActivity.this, "Code mauvais", Toast.LENGTH_LONG).show();
|
|
Log.e("RemoteProvisioningLoginActivity: Decrypt problem: " + ex);
|
|
remoteUrl = null;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean storeAccount(String url) {
|
|
LinphonePreferences.instance().setRemoteProvisioningUrl(url);
|
|
//TODO
|
|
LinphoneManager.getLc().iterate();
|
|
sleep(1000);
|
|
LinphoneManager.getLc().iterate();
|
|
//TODO
|
|
|
|
LinphoneManager.getInstance().restartCore();
|
|
LinphoneManager.getLc().addListener(mListener);
|
|
|
|
return true;
|
|
}
|
|
|
|
private void displayRemoteProvisioningInProgressDialog() {
|
|
progress = ProgressDialog.show(this, null, null);
|
|
Drawable d = new ColorDrawable(ContextCompat.getColor(this, R.color.colorE));
|
|
d.setAlpha(200);
|
|
progress.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
|
|
progress.getWindow().setBackgroundDrawable(d);
|
|
progress.setContentView(R.layout.progress_dialog);
|
|
progress.show();
|
|
}
|
|
}
|