Переглянути джерело

Implement MVVM Architecture for Connect/Disconnect functionality.

pull/8/head
Mushegh Sahakyan 4 роки тому
джерело
коміт
cd744b6eda
3 змінених файлів з 123 додано та 98 видалено
  1. +39
    -63
      ui/src/main/java/com/wireguard/android/activity/MainActivity.java
  2. +74
    -33
      ui/src/main/java/com/wireguard/android/repository/DataRepository.java
  3. +10
    -2
      ui/src/main/java/com/wireguard/android/viewmodel/MainViewModel.java

+ 39
- 63
ui/src/main/java/com/wireguard/android/activity/MainActivity.java Переглянути файл

@@ -11,21 +11,13 @@ import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.wireguard.android.Application;
import com.wireguard.android.R;
import com.wireguard.android.backend.GoBackend;
import com.wireguard.android.backend.Tunnel;
import com.wireguard.android.backend.Tunnel.State;
import com.wireguard.android.model.ObservableTunnel;
import com.wireguard.android.viewmodel.MainViewModel;

public class MainActivity extends AppCompatActivity {
private MainViewModel mainViewModel;
private TextView bubbleStatus;
private Button connectButton;
private ObservableTunnel pendingTunnel;
private Boolean pendingTunnelUp;
private boolean connectionStateFlag;

private static final int REQUEST_CODE_VPN_PERMISSION = 23491;
@@ -41,25 +33,20 @@ public class MainActivity extends AppCompatActivity {
startActivity(intent);
finish();
}
mainViewModel.buildRepositoryInstance(this,mainViewModel.getUserURL(this));
pendingTunnel = mainViewModel.getTunnel(this,connectionStateFlag);
mainViewModel.buildRepositoryInstance(this, mainViewModel.getUserURL(this));
initUI();
}

@Override protected void onResume() {
super.onResume();
if(pendingTunnel!=null){
if(pendingTunnel.getState() == State.DOWN)
{
connectionStateFlag = false;
bubbleStatus.setText(getString(R.string.not_connected_bubble));
connectButton.setText(getString(R.string.connect));
}
else {
connectionStateFlag = true;
bubbleStatus.setText(getString(R.string.connected_bubble));
connectButton.setText(getString(R.string.disconnect));
}
if (mainViewModel.isVPNConnected(this, connectionStateFlag)) {
connectionStateFlag = false;
bubbleStatus.setText(getString(R.string.not_connected_bubble));
connectButton.setText(getString(R.string.connect));
} else {
connectionStateFlag = true;
bubbleStatus.setText(getString(R.string.connected_bubble));
connectButton.setText(getString(R.string.disconnect));
}
}

@@ -82,58 +69,47 @@ public class MainActivity extends AppCompatActivity {
}

private void connect() {
mainViewModel.getTunnelState(pendingTunnel).observe(this, new Observer<Boolean>() {
final boolean state = mainViewModel.isVPNConnected(this, connectionStateFlag);
connectionStateFlag = state;
mainViewModel.connect(state, MainActivity.this).observe(MainActivity.this, new Observer<Boolean>() {
@Override public void onChanged(final Boolean state) {
connectionStateFlag = state;
setTunnelState(state);
}
});
}

private void setTunnelState(final Boolean checked) {
if(pendingTunnel!=null) {
final ObservableTunnel tunnel = pendingTunnel;
Application.getBackendAsync().thenAccept(backend -> {
if (backend instanceof GoBackend) {
final Intent intent = GoBackend.VpnService.prepare(this);
if (intent != null) {
pendingTunnelUp = checked;
startActivityForResult(intent, REQUEST_CODE_VPN_PERMISSION);
return;
}
}
setTunnelStateWithPermissionsResult(tunnel, checked);
});
}

}

private void setTunnelStateWithPermissionsResult(final ObservableTunnel tunnel, final boolean checked) {
tunnel.setStateAsync(Tunnel.State.of(checked)).whenComplete((observableTunnel, throwable) ->{
if(throwable==null){
if(observableTunnel == State.DOWN) {
Toast.makeText(this, getString(R.string.not_connected_bubble), Toast.LENGTH_SHORT).show();
bubbleStatus.setText(getString(R.string.not_connected_bubble));
connectButton.setText(getString(R.string.connect));
}
else {
Toast.makeText(this, getString(R.string.connected_bubble), Toast.LENGTH_SHORT).show();
if (state) {
Toast.makeText(MainActivity.this, getString(R.string.connected_bubble), Toast.LENGTH_SHORT).show();
bubbleStatus.setText(getString(R.string.connected_bubble));
connectButton.setText(getString(R.string.disconnect));
} else {
Toast.makeText(MainActivity.this, getString(R.string.not_connected_bubble), Toast.LENGTH_SHORT).show();
bubbleStatus.setText(getString(R.string.not_connected_bubble));
connectButton.setText(getString(R.string.connect));
}
}
else {
Toast.makeText(this,getString(R.string.failed_bubble),Toast.LENGTH_SHORT).show();
}
});
}

@Override protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_VPN_PERMISSION) {
if (pendingTunnel != null && pendingTunnelUp != null) setTunnelStateWithPermissionsResult(pendingTunnel, pendingTunnelUp);
pendingTunnel = null;
pendingTunnelUp = null;
if (resultCode == RESULT_OK) {
mainViewModel.connectWithPermission(connectionStateFlag, this)
.observe(this, new Observer<Boolean>() {
@Override public void onChanged(final Boolean state) {
if (state) {
Toast.makeText(MainActivity.this, getString(R.string.connected_bubble), Toast.LENGTH_SHORT).show();
bubbleStatus.setText(getString(R.string.connected_bubble));
connectButton.setText(getString(R.string.disconnect));
} else {
Toast.makeText(MainActivity.this, getString(R.string.not_connected_bubble), Toast.LENGTH_SHORT).show();
bubbleStatus.setText(getString(R.string.not_connected_bubble));
connectButton.setText(getString(R.string.connect));
}
}
});
} else {
connectionStateFlag = false;
Toast.makeText(this, getString(R.string.not_connected_bubble), Toast.LENGTH_SHORT).show();
bubbleStatus.setText(getString(R.string.not_connected_bubble));
connectButton.setText(getString(R.string.connect));
}
}
}
}

+ 74
- 33
ui/src/main/java/com/wireguard/android/repository/DataRepository.java Переглянути файл

@@ -1,15 +1,21 @@
package com.wireguard.android.repository;

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.widget.Toast;

import com.wireguard.android.Application;
import com.wireguard.android.R;
import com.wireguard.android.activity.MainActivity;
import com.wireguard.android.api.ApiConstants;
import com.wireguard.android.api.network.ClientApi;
import com.wireguard.android.api.network.ClientService;
import com.wireguard.android.api.network.NetworkBoundStatusResource;
import com.wireguard.android.backend.GoBackend;
import com.wireguard.android.backend.Tunnel;
import com.wireguard.android.backend.Tunnel.State;
import com.wireguard.android.configStore.FileConfigStore;
import com.wireguard.android.model.Device;
@@ -34,7 +40,9 @@ import javax.security.cert.CertificateEncodingException;
import javax.security.cert.CertificateException;
import javax.security.cert.X509Certificate;

import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
@@ -51,16 +59,18 @@ public class DataRepository {
private CompositeDisposable compositeDisposable;
private final OkHttpClient client = new OkHttpClient();
private TunnelManager tunnelManager;
private ObservableTunnel pendingTunnel;

public static final String NO_INTERNET_CONNECTION = "no_internet_connection";
private static final String SEPARATOR = ":";
private static final String SPACE = " ";
private static final String DELIMITER = "\\A";
private static final int ANDROID_ID = 1;
private static String BASE_URL = "";
private static String BASE_URL = "";
private static final String TUNNEL_NAME = "BubbleVPN";
private static final int REQUEST_CODE_VPN_PERMISSION = 23491;

private DataRepository(Context context,String url) {
private DataRepository(Context context, String url) {
BASE_URL = url;
clientApi = ClientService.getInstance().createClientApi(url);
compositeDisposable = new CompositeDisposable();
@@ -71,13 +81,13 @@ public class DataRepository {
if (instance == null) {
synchronized (DataRepository.class) {
if (instance == null) {
instance = new DataRepository(context,url);
instance = new DataRepository(context, url);
}
}
}
}

public void buildClientService(String url){
public void buildClientService(String url) {
BASE_URL = url;
clientApi = ClientService.getInstance().createClientApi(url);
}
@@ -261,7 +271,7 @@ public class DataRepository {
Application.getTunnelManager().create(TUNNEL_NAME, config).whenComplete((observableTunnel, throwable) -> {
if (observableTunnel != null) {
TunnelStore.getInstance(context).setTunnel(TUNNEL_NAME, rawConfig);
tunnelManager.setTunnelState(observableTunnel,State.DOWN);
tunnelManager.setTunnelState(observableTunnel, State.DOWN);
setMutableLiveData(StatusResource.success());
} else {
setMutableLiveData(StatusResource.error(throwable.getMessage()));
@@ -322,7 +332,7 @@ public class DataRepository {
}


public ObservableTunnel createTunnel(final Context context, final boolean stateTunnel){
private ObservableTunnel createTunnel(final Context context, final boolean stateTunnel) {
//TODO implement config is null case
Config config = null;
try {
@@ -330,45 +340,31 @@ public class DataRepository {
} catch (final IOException | BadConfigException e) {
return null;
}
final String name = TunnelStore.getInstance(context).getTunnelName();
final String name = TunnelStore.getInstance(context).getTunnelName();
final ObservableTunnel tunnel;
if(stateTunnel){
tunnel = new ObservableTunnel(tunnelManager, name, config, State.UP);
if (stateTunnel) {
tunnel = new ObservableTunnel(tunnelManager, name, config, State.UP);
} else {
tunnel = new ObservableTunnel(tunnelManager, name, config, State.DOWN);
}
else {
tunnel = new ObservableTunnel(tunnelManager, name, config, State.DOWN);
}
tunnelManager.setTunnelState(tunnel,tunnel.getState());
tunnelManager.setTunnelState(tunnel, tunnel.getState());

return tunnel;
}
public void setUserURL(Context context, String url){

public void setUserURL(Context context, String url) {
UserStore.getInstance(context).setUserURL(url);
}

public ObservableTunnel getTunnel(Context context, boolean connectionStateFlag)
{
public ObservableTunnel getTunnel(Context context, boolean connectionStateFlag) {
ObservableTunnel tunnel = tunnelManager.getLastUsedTunnel();
if(tunnel == null) {
if (tunnel == null) {
tunnel = createTunnel(context, connectionStateFlag);
}
return tunnel;
}

public MutableLiveData<Boolean> getTunnelState(ObservableTunnel pendingTunnel)
{
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
tunnelManager.getTunnelState(pendingTunnel).whenComplete((state, throwable) -> {
if (state == State.DOWN) {
liveData.postValue(true);
} else if (state == State.UP) {
liveData.postValue(false);
}
});
return liveData;
}

public MutableLiveData<byte[]> getCertificate(Context context){
public MutableLiveData<byte[]> getCertificate(Context context) {
final MutableLiveData<byte[]> liveData = new MutableLiveData<>();
final Request request = new Request.Builder()
.url(BASE_URL + ApiConstants.CERTIFICATE_URL)
@@ -395,12 +391,57 @@ public class DataRepository {
} catch (final CertificateEncodingException e) {
liveData.postValue(new byte[]{});
}
}
else {
} else {
liveData.postValue(new byte[]{});
}
}
});
return liveData;
}

public boolean isVPNConnected(Context context, boolean connectionStateFlag) {
pendingTunnel = getTunnel(context, connectionStateFlag);
return pendingTunnel.getState() == State.DOWN;
}

public MutableLiveData<Boolean> connect(final Boolean checked , Context context) {
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
if (pendingTunnel != null) {
Application.getBackendAsync().thenAccept(backend -> {
if (backend instanceof GoBackend) {
final Intent intent = GoBackend.VpnService.prepare(context);
if (intent != null) {
if(context instanceof MainActivity) {
((MainActivity)context).startActivityForResult(intent, REQUEST_CODE_VPN_PERMISSION);
return;
}
}
}
connectWithPermission(checked,context).observe((LifecycleOwner) context, new Observer<Boolean>() {
@Override public void onChanged(final Boolean aBoolean) {
liveData.postValue(aBoolean);
}
});
});
}
return liveData;
}

public MutableLiveData<Boolean> connectWithPermission(final boolean checked , Context context) {
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
pendingTunnel.setStateAsync(Tunnel.State.of(checked)).whenComplete((observableTunnel, throwable) ->{
if(throwable==null){
if(observableTunnel == State.DOWN) {
liveData.postValue(false);
}
else {
liveData.postValue(true);
}
}
else {
Toast.makeText(context,context.getString(R.string.failed_bubble),Toast.LENGTH_SHORT).show();
}
});
return liveData;
}
}

+ 10
- 2
ui/src/main/java/com/wireguard/android/viewmodel/MainViewModel.java Переглянути файл

@@ -28,7 +28,15 @@ public class MainViewModel extends ViewModel {
return UserStore.getInstance(context).getUserURL();
}

public MutableLiveData<Boolean> getTunnelState(ObservableTunnel pendingTunnel){
return DataRepository.getRepositoryInstance().getTunnelState(pendingTunnel);
public MutableLiveData<Boolean> connectWithPermission(final boolean checked , Context context) {
return DataRepository.getRepositoryInstance().connectWithPermission(checked,context);
}

public MutableLiveData<Boolean> connect(final Boolean checked , Context context) {
return DataRepository.getRepositoryInstance().connect(checked,context);
}

public boolean isVPNConnected(Context context, boolean connectionStateFlag) {
return DataRepository.getRepositoryInstance().isVPNConnected(context,connectionStateFlag);
}
}

Завантаження…
Відмінити
Зберегти