From cd744b6eda1d41836b1959e13491aedf02a7b2eb Mon Sep 17 00:00:00 2001 From: Mushegh98 Date: Tue, 9 Jun 2020 00:59:54 +0400 Subject: [PATCH] Implement MVVM Architecture for Connect/Disconnect functionality. --- .../android/activity/MainActivity.java | 102 +++++++---------- .../android/repository/DataRepository.java | 107 ++++++++++++------ .../android/viewmodel/MainViewModel.java | 12 +- 3 files changed, 123 insertions(+), 98 deletions(-) diff --git a/ui/src/main/java/com/wireguard/android/activity/MainActivity.java b/ui/src/main/java/com/wireguard/android/activity/MainActivity.java index 3310a08..b44ac95 100644 --- a/ui/src/main/java/com/wireguard/android/activity/MainActivity.java +++ b/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() { + final boolean state = mainViewModel.isVPNConnected(this, connectionStateFlag); + connectionStateFlag = state; + mainViewModel.connect(state, MainActivity.this).observe(MainActivity.this, new Observer() { @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() { + @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)); + } } } } diff --git a/ui/src/main/java/com/wireguard/android/repository/DataRepository.java b/ui/src/main/java/com/wireguard/android/repository/DataRepository.java index 46023dd..9431ac4 100644 --- a/ui/src/main/java/com/wireguard/android/repository/DataRepository.java +++ b/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 getTunnelState(ObservableTunnel pendingTunnel) - { - MutableLiveData 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 getCertificate(Context context){ + public MutableLiveData getCertificate(Context context) { final MutableLiveData 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 connect(final Boolean checked , Context context) { + MutableLiveData 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() { + @Override public void onChanged(final Boolean aBoolean) { + liveData.postValue(aBoolean); + } + }); + }); + } + return liveData; + } + + public MutableLiveData connectWithPermission(final boolean checked , Context context) { + MutableLiveData 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; + } } diff --git a/ui/src/main/java/com/wireguard/android/viewmodel/MainViewModel.java b/ui/src/main/java/com/wireguard/android/viewmodel/MainViewModel.java index f979077..c4285cd 100644 --- a/ui/src/main/java/com/wireguard/android/viewmodel/MainViewModel.java +++ b/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 getTunnelState(ObservableTunnel pendingTunnel){ - return DataRepository.getRepositoryInstance().getTunnelState(pendingTunnel); + public MutableLiveData connectWithPermission(final boolean checked , Context context) { + return DataRepository.getRepositoryInstance().connectWithPermission(checked,context); + } + + public MutableLiveData 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); } }