Reviewed-on: https://git.bubblev.org/bubblev/bubble-droid/pulls/8pull/9/head
@@ -60,7 +60,6 @@ class Application : android.app.Application(), OnSharedPreferenceChangeListener | |||
override fun onCreate() { | |||
Log.i(TAG, USER_AGENT) | |||
super.onCreate() | |||
DataRepository.buildRepositoryInstance() | |||
asyncWorker = AsyncWorker(AsyncTask.SERIAL_EXECUTOR, Handler(Looper.getMainLooper())) | |||
rootShell = RootShell(applicationContext) | |||
toolsInstaller = ToolsInstaller(applicationContext, rootShell) | |||
@@ -1,10 +1,12 @@ | |||
package com.wireguard.android.activity; | |||
import androidx.annotation.Nullable; | |||
import androidx.lifecycle.Observer; | |||
import androidx.lifecycle.ViewModelProvider; | |||
import android.content.Intent; | |||
import android.os.Bundle; | |||
import android.security.KeyChain; | |||
import android.util.Log; | |||
import android.view.View; | |||
import android.view.View.OnClickListener; | |||
@@ -14,9 +16,14 @@ import android.widget.Toast; | |||
import com.wireguard.android.R; | |||
import com.wireguard.android.model.User; | |||
import com.wireguard.android.repository.DataRepository; | |||
import com.wireguard.android.resource.StatusResource; | |||
import com.wireguard.android.viewmodel.LoginViewModel; | |||
import javax.security.cert.CertificateEncodingException; | |||
import javax.security.cert.CertificateException; | |||
import javax.security.cert.X509Certificate; | |||
public class LoginActivity extends BaseActivityBubble { | |||
private LoginViewModel loginViewModel; | |||
@@ -25,6 +32,12 @@ public class LoginActivity extends BaseActivityBubble { | |||
private EditText password; | |||
private Button sign; | |||
private static final String BASE_URL_PREFIX = "https://"; | |||
private static final String BASE_URL_SUFFIX = ":1443/api/"; | |||
private static final String SEPARATOR = "\\."; | |||
private static final int REQUEST_CODE = 1555; | |||
private static final String CERTIFICATE_NAME = "Bubble Certificate"; | |||
@Override | |||
protected void onCreate(Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
@@ -41,11 +54,21 @@ public class LoginActivity extends BaseActivityBubble { | |||
private void initListeners() { | |||
sign.setOnClickListener(new OnClickListener() { | |||
@Override public void onClick(final View v) { | |||
final String tunnelName = bubbleName.getText().toString().trim(); | |||
final String username = userName.getText().toString().trim(); | |||
final String password = LoginActivity.this.password.getText().toString().trim(); | |||
final String url = BASE_URL_PREFIX + bubbleName.getText().toString() + BASE_URL_SUFFIX; | |||
if (url.split(SEPARATOR).length != 3) { | |||
Toast.makeText(LoginActivity.this, getResources().getText(R.string.hostname_not_valid), Toast.LENGTH_LONG).show(); | |||
return; | |||
} | |||
if (DataRepository.getRepositoryInstance() == null) { | |||
loginViewModel.buildRepositoryInstance(LoginActivity.this, url); | |||
} else { | |||
loginViewModel.buildClientService(url); | |||
} | |||
loginViewModel.setUserURL(LoginActivity.this, url); | |||
final String usernameInput = userName.getText().toString().trim(); | |||
final String passwordInput = password.getText().toString().trim(); | |||
showLoadingDialog(); | |||
login(tunnelName, username, password); | |||
login(usernameInput, passwordInput); | |||
} | |||
}); | |||
} | |||
@@ -57,28 +80,48 @@ public class LoginActivity extends BaseActivityBubble { | |||
sign = findViewById(R.id.signButton); | |||
} | |||
private void login(String tunnelName, String username, String password) { | |||
loginViewModel.login(tunnelName, username, password, this).observe(this, new Observer<StatusResource<User>>() { | |||
private void login(String username, String password) { | |||
loginViewModel.login(username, password, this).observe(this, new Observer<StatusResource<User>>() { | |||
@Override public void onChanged(final StatusResource<User> userStatusResource) { | |||
switch (userStatusResource.status) { | |||
case SUCCESS: | |||
Toast.makeText(LoginActivity.this, "Success", Toast.LENGTH_SHORT).show(); | |||
Log.d("TAG", "Success"); | |||
closeLoadingDialog(); | |||
Intent intent = new Intent(LoginActivity.this, MainActivity.class); | |||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); | |||
startActivity(intent); | |||
loginViewModel.getCertificate(LoginActivity.this).observe(LoginActivity.this, new Observer<byte[]>() { | |||
@Override public void onChanged(final byte[] encodedCertificate) { | |||
closeLoadingDialog(); | |||
if (encodedCertificate.length == 0) { | |||
Toast.makeText(LoginActivity.this, getString(R.string.failed_bubble), Toast.LENGTH_SHORT).show(); | |||
} else { | |||
final Intent intent = KeyChain.createInstallIntent(); | |||
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, encodedCertificate); | |||
intent.putExtra(KeyChain.EXTRA_NAME, CERTIFICATE_NAME); | |||
startActivityForResult(intent, REQUEST_CODE); | |||
} | |||
} | |||
}); | |||
break; | |||
case LOADING: | |||
Log.d("TAG", "Loading"); | |||
break; | |||
case ERROR: | |||
closeLoadingDialog(); | |||
Toast.makeText(LoginActivity.this, "Login Failed", Toast.LENGTH_SHORT).show(); | |||
Toast.makeText(LoginActivity.this, getString(R.string.login_failed), Toast.LENGTH_SHORT).show(); | |||
Log.d("TAG", "Error"); | |||
break; | |||
} | |||
} | |||
}); | |||
} | |||
@Override protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) { | |||
super.onActivityResult(requestCode, resultCode, data); | |||
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) { | |||
Toast.makeText(this, getString(R.string.success), Toast.LENGTH_SHORT).show(); | |||
Log.d("TAG", "Success"); | |||
final Intent mainActivityIntent = new Intent(this, MainActivity.class); | |||
mainActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); | |||
startActivity(mainActivityIntent); | |||
} else { | |||
Toast.makeText(this, getString(R.string.cerificate_install), Toast.LENGTH_LONG).show(); | |||
} | |||
} | |||
} |
@@ -2,6 +2,7 @@ package com.wireguard.android.activity; | |||
import androidx.annotation.Nullable; | |||
import androidx.appcompat.app.AppCompatActivity; | |||
import androidx.lifecycle.Observer; | |||
import androidx.lifecycle.ViewModelProvider; | |||
import android.content.Intent; | |||
import android.os.Bundle; | |||
@@ -9,22 +10,14 @@ import android.view.View; | |||
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.model.ObservableTunnel; | |||
import com.wireguard.android.viewmodel.MainViewModel; | |||
public class MainActivity extends AppCompatActivity { | |||
private MainViewModel mainViewModel; | |||
private TextView bubbleStatus; | |||
private TextView deviceStatus; | |||
private Button connectButton; | |||
public ObservableTunnel pendingTunnel; | |||
private Boolean pendingTunnelUp; | |||
private boolean connectionStateFlag; | |||
private static final int REQUEST_CODE_VPN_PERMISSION = 23491; | |||
@@ -39,8 +32,19 @@ public class MainActivity extends AppCompatActivity { | |||
startActivity(intent); | |||
finish(); | |||
} | |||
mainViewModel.buildRepositoryInstance(this, mainViewModel.getUserURL(this)); | |||
initUI(); | |||
pendingTunnel = mainViewModel.getTunnel(this); | |||
} | |||
@Override protected void onResume() { | |||
super.onResume(); | |||
if (mainViewModel.isVPNConnected(this, connectionStateFlag)) { | |||
connectionStateFlag = false; | |||
setConnectionStateUI(false); | |||
} else { | |||
connectionStateFlag = true; | |||
setConnectionStateUI(true); | |||
} | |||
} | |||
private void initUI() { | |||
@@ -50,7 +54,6 @@ public class MainActivity extends AppCompatActivity { | |||
private void initViews() { | |||
bubbleStatus = findViewById(R.id.bubbleStatus); | |||
deviceStatus = findViewById(R.id.deviceStatus); | |||
connectButton = findViewById(R.id.connectButton); | |||
} | |||
@@ -63,44 +66,39 @@ public class MainActivity extends AppCompatActivity { | |||
} | |||
private void connect() { | |||
setTunnelState(true); | |||
} | |||
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){ | |||
Toast.makeText(this,"Connected",Toast.LENGTH_SHORT).show(); | |||
} | |||
else { | |||
Toast.makeText(this,"Failed",Toast.LENGTH_SHORT).show(); | |||
} | |||
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) { | |||
setConnectionStateUI(state); | |||
} | |||
}); | |||
} | |||
@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) { | |||
setConnectionStateUI(state); | |||
} | |||
}); | |||
} else { | |||
connectionStateFlag = false; | |||
setConnectionStateUI(false); | |||
} | |||
} | |||
} | |||
private void setConnectionStateUI(boolean state) { | |||
if (state) { | |||
bubbleStatus.setText(getString(R.string.connected_bubble)); | |||
connectButton.setText(getString(R.string.disconnect)); | |||
} else { | |||
bubbleStatus.setText(getString(R.string.not_connected_bubble)); | |||
connectButton.setText(getString(R.string.connect)); | |||
} | |||
} | |||
} |
@@ -1,7 +1,6 @@ | |||
package com.wireguard.android.api; | |||
public class ApiConstants { | |||
public static final String BASE_URL = "https://jtest2.bubblesecure.com:1443/api/"; | |||
public static final String LOGIN_URL = "auth/login"; | |||
public static final String ALL_DEVICES_URL = "me/devices"; | |||
public static final String ADD_DEVICE_URL = "me/devices"; | |||
@@ -12,4 +11,5 @@ public class ApiConstants { | |||
public static final String AUTHORIZATION_HEADER = "X-Bubble-Session"; | |||
public static final String DEVICE_NAME = "name"; | |||
public static final String DEVICE_TYPE = "deviceType"; | |||
public static final String CERTIFICATE_URL = "auth/cacert?deviceType=android"; | |||
} |
@@ -28,7 +28,7 @@ public class ClientService { | |||
} | |||
public ClientApi createClientApi() { | |||
public ClientApi createClientApi(String url) { | |||
OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); | |||
@@ -40,7 +40,7 @@ public class ClientService { | |||
httpClient.addInterceptor(new UserAgentInterceptor(System.getProperty("http.agent"))); | |||
return new Retrofit.Builder() | |||
.baseUrl(ApiConstants.BASE_URL) | |||
.baseUrl(url) | |||
.addConverterFactory(GsonConverterFactory.create()) | |||
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) | |||
.client(httpClient.build()) | |||
@@ -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; | |||
@@ -30,7 +36,13 @@ import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Scanner; | |||
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; | |||
@@ -46,33 +58,45 @@ public class DataRepository { | |||
private ClientApi clientApi; | |||
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 final String TUNNEL_NAME = "BubbleVPN"; | |||
private static final int REQUEST_CODE_VPN_PERMISSION = 23491; | |||
private DataRepository() { | |||
clientApi = ClientService.getInstance().createClientApi(); | |||
private DataRepository(Context context, String url) { | |||
BASE_URL = url; | |||
clientApi = ClientService.getInstance().createClientApi(url); | |||
compositeDisposable = new CompositeDisposable(); | |||
tunnelManager = new TunnelManager(new FileConfigStore(context)); | |||
} | |||
public static void buildRepositoryInstance() { | |||
public static void buildRepositoryInstance(Context context, String url) { | |||
if (instance == null) { | |||
synchronized (DataRepository.class) { | |||
if (instance == null) { | |||
instance = new DataRepository(); | |||
instance = new DataRepository(context, url); | |||
} | |||
} | |||
} | |||
} | |||
public void buildClientService(String url) { | |||
BASE_URL = url; | |||
clientApi = ClientService.getInstance().createClientApi(url); | |||
} | |||
public static DataRepository getRepositoryInstance() { | |||
return instance; | |||
} | |||
public MutableLiveData<StatusResource<User>> login(String tunnelName, String username, String password, Context context) { | |||
public MutableLiveData<StatusResource<User>> login(String username, String password, Context context) { | |||
return new NetworkBoundStatusResource<User>() { | |||
@Override protected void createCall() { | |||
@@ -115,7 +139,7 @@ public class DataRepository { | |||
addDevice(context); | |||
} | |||
}, throwable -> { | |||
setMutableLiveData(StatusResource.error(throwable.getMessage())); | |||
}); | |||
compositeDisposable.add(disposableAllDevices); | |||
} | |||
@@ -223,7 +247,7 @@ public class DataRepository { | |||
final String deviceID = UserStore.getInstance(context).getDeviceID(); | |||
final String token = UserStore.getInstance(context).getToken(); | |||
Request request = new Request.Builder() | |||
.url(ApiConstants.BASE_URL + ApiConstants.CONFIG_DEVICE_URL + deviceID + ApiConstants.CONFIG_VPN_URL) | |||
.url(BASE_URL + ApiConstants.CONFIG_DEVICE_URL + deviceID + ApiConstants.CONFIG_VPN_URL) | |||
.addHeader(ApiConstants.AUTHORIZATION_HEADER, token) | |||
.build(); | |||
client.newCall(request).enqueue(new Callback() { | |||
@@ -235,18 +259,19 @@ public class DataRepository { | |||
final InputStream inputStream = response.body().byteStream(); | |||
final Scanner scanner = new Scanner(inputStream).useDelimiter(DELIMITER); | |||
final String data = scanner.hasNext() ? scanner.next() : ""; | |||
createTunnel(data, tunnelName); | |||
createTunnel(data); | |||
} | |||
}); | |||
} | |||
private void createTunnel(String rawConfig, String tunnelName) { | |||
private void createTunnel(final String rawConfig) { | |||
try { | |||
final byte[] configBytes = rawConfig.getBytes(); | |||
final Config config = Config.parse(new ByteArrayInputStream(configBytes)); | |||
Application.getTunnelManager().create(tunnelName, config).whenComplete((observableTunnel, throwable) -> { | |||
Application.getTunnelManager().create(TUNNEL_NAME, config).whenComplete((observableTunnel, throwable) -> { | |||
if (observableTunnel != null) { | |||
TunnelStore.getInstance(context).setTunnel(tunnelName, rawConfig); | |||
TunnelStore.getInstance(context).setTunnel(TUNNEL_NAME, rawConfig); | |||
tunnelManager.setTunnelState(observableTunnel, State.DOWN); | |||
setMutableLiveData(StatusResource.success()); | |||
} else { | |||
setMutableLiveData(StatusResource.error(throwable.getMessage())); | |||
@@ -300,7 +325,14 @@ public class DataRepository { | |||
} | |||
public ObservableTunnel getTunnel(Context context) { | |||
private Config parseConfig(String data) throws IOException, BadConfigException { | |||
final byte[] configText = data.getBytes(); | |||
final Config config = Config.parse(new ByteArrayInputStream(configText)); | |||
return config; | |||
} | |||
private ObservableTunnel createTunnel(final Context context, final boolean stateTunnel) { | |||
//TODO implement config is null case | |||
Config config = null; | |||
try { | |||
@@ -309,14 +341,107 @@ public class DataRepository { | |||
return null; | |||
} | |||
final String name = TunnelStore.getInstance(context).getTunnelName(); | |||
final TunnelManager tunnelManager = new TunnelManager(new FileConfigStore(context)); | |||
final ObservableTunnel tunnel = new ObservableTunnel(tunnelManager, name, config, State.DOWN); | |||
final ObservableTunnel tunnel; | |||
if (stateTunnel) { | |||
tunnel = new ObservableTunnel(tunnelManager, name, config, State.UP); | |||
} else { | |||
tunnel = new ObservableTunnel(tunnelManager, name, config, State.DOWN); | |||
} | |||
tunnelManager.setTunnelState(tunnel, tunnel.getState()); | |||
return tunnel; | |||
} | |||
private Config parseConfig(String data) throws IOException, BadConfigException { | |||
final byte[] configText = data.getBytes(); | |||
final Config config = Config.parse(new ByteArrayInputStream(configText)); | |||
return config; | |||
public void setUserURL(Context context, String url) { | |||
UserStore.getInstance(context).setUserURL(url); | |||
} | |||
public ObservableTunnel getTunnel(Context context, boolean connectionStateFlag) { | |||
ObservableTunnel tunnel = tunnelManager.getLastUsedTunnel(); | |||
if (tunnel == null) { | |||
tunnel = createTunnel(context, connectionStateFlag); | |||
} | |||
return tunnel; | |||
} | |||
public MutableLiveData<byte[]> getCertificate(Context context) { | |||
final MutableLiveData<byte[]> liveData = new MutableLiveData<>(); | |||
final Request request = new Request.Builder() | |||
.url(BASE_URL + ApiConstants.CERTIFICATE_URL) | |||
.build(); | |||
client.newCall(request).enqueue(new Callback() { | |||
@Override public void onFailure(final okhttp3.Call call, final IOException e) { | |||
liveData.postValue(new byte[]{}); | |||
} | |||
@Override public void onResponse(final okhttp3.Call call, final Response response) throws IOException { | |||
if (response.isSuccessful()) { | |||
final InputStream inputStream = response.body().byteStream(); | |||
final Scanner scanner = new Scanner(inputStream).useDelimiter(DELIMITER); | |||
final String data = scanner.hasNext() ? scanner.next() : ""; | |||
final byte[] cert = data.getBytes(); | |||
X509Certificate x509Certificate = null; | |||
try { | |||
x509Certificate = X509Certificate.getInstance(cert); | |||
} catch (final CertificateException e) { | |||
liveData.postValue(new byte[]{}); | |||
} | |||
try { | |||
liveData.postValue(x509Certificate.getEncoded()); | |||
} catch (final CertificateEncodingException e) { | |||
liveData.postValue(new byte[]{}); | |||
} | |||
} 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; | |||
} | |||
} |
@@ -11,9 +11,11 @@ public class UserStore { | |||
private static final String USER_DATA_KEY = "com.wireguard.android.util.bubbleUserResponse"; | |||
private static final String DEVICE_DATA_KEY = "com.wireguard.android.util.bubbleDeviceResponse"; | |||
private static final String DEVICE_ID_KEY = "com.wireguard.android.util.bubbleDeviceIDResponse"; | |||
private static final String USER_BASE_URL_KEY = "com.wireguard.android.util.bubbleUserURLResponse"; | |||
public static final String USER_TOKEN_DEFAULT_VALUE = ""; | |||
public static final String DEVICE_DEFAULT_VALUE = ""; | |||
public static final String DEVICE_ID_DEFAULT_VALUE = ""; | |||
public static final String USER_BASE_URL_DEFAULT_VALUE = ""; | |||
public static UserStore getInstance(Context context) { | |||
if (instance == null) { | |||
@@ -51,4 +53,12 @@ public class UserStore { | |||
public String getDeviceID(){ | |||
return sharedPreferences.getString(DEVICE_ID_KEY,DEVICE_ID_DEFAULT_VALUE); | |||
} | |||
public void setUserURL(String url){ | |||
sharedPreferences.edit().putString(USER_BASE_URL_KEY,url).apply(); | |||
} | |||
public String getUserURL(){ | |||
return sharedPreferences.getString(USER_BASE_URL_KEY,USER_BASE_URL_DEFAULT_VALUE); | |||
} | |||
} |
@@ -5,17 +5,35 @@ import android.content.Context; | |||
import com.wireguard.android.model.User; | |||
import com.wireguard.android.repository.DataRepository; | |||
import com.wireguard.android.resource.StatusResource; | |||
import com.wireguard.android.util.UserStore; | |||
import androidx.lifecycle.LiveData; | |||
import androidx.lifecycle.MutableLiveData; | |||
import androidx.lifecycle.ViewModel; | |||
public class LoginViewModel extends ViewModel { | |||
public LiveData<StatusResource<User>> login(String tunnelName, String username, String password, Context context) { | |||
return DataRepository.getRepositoryInstance().login(tunnelName,username, password, context); | |||
public LiveData<StatusResource<User>> login(String username, String password, Context context) { | |||
return DataRepository.getRepositoryInstance().login(username, password, context); | |||
} | |||
@Override protected void onCleared() { | |||
super.onCleared(); | |||
DataRepository.getRepositoryInstance().clearDisposable(); | |||
} | |||
public void buildRepositoryInstance(Context context, String url){ | |||
DataRepository.buildRepositoryInstance(context,url); | |||
} | |||
public void setUserURL(Context context, String url){ | |||
DataRepository.getRepositoryInstance().setUserURL(context,url); | |||
} | |||
public void buildClientService(String url){ | |||
DataRepository.getRepositoryInstance().buildClientService(url); | |||
} | |||
public MutableLiveData<byte[]> getCertificate(Context context){ | |||
return DataRepository.getRepositoryInstance().getCertificate(context); | |||
} | |||
} |
@@ -3,15 +3,40 @@ package com.wireguard.android.viewmodel; | |||
import android.content.Context; | |||
import com.wireguard.android.model.ObservableTunnel; | |||
import com.wireguard.android.model.TunnelManager; | |||
import com.wireguard.android.repository.DataRepository; | |||
import com.wireguard.android.util.TunnelStore; | |||
import com.wireguard.android.util.UserStore; | |||
import androidx.lifecycle.MutableLiveData; | |||
import androidx.lifecycle.ViewModel; | |||
public class MainViewModel extends ViewModel { | |||
public boolean isUserLoggedIn(Context context){ | |||
return DataRepository.getRepositoryInstance().isUserLoggedIn(context); | |||
return !UserStore.USER_TOKEN_DEFAULT_VALUE.equals(UserStore.getInstance(context).getToken()); | |||
} | |||
public ObservableTunnel getTunnel(Context context, boolean stateTunnel) { | |||
return DataRepository.getRepositoryInstance().getTunnel(context,stateTunnel); | |||
} | |||
public void buildRepositoryInstance(Context context, String url){ | |||
DataRepository.buildRepositoryInstance(context,url); | |||
} | |||
public String getUserURL(Context context){ | |||
return UserStore.getInstance(context).getUserURL(); | |||
} | |||
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 ObservableTunnel getTunnel(Context context) { | |||
return DataRepository.getRepositoryInstance().getTunnel(context); | |||
public boolean isVPNConnected(Context context, boolean connectionStateFlag) { | |||
return DataRepository.getRepositoryInstance().isVPNConnected(context,connectionStateFlag); | |||
} | |||
} |
@@ -35,40 +35,15 @@ | |||
android:id="@+id/bubbleStatus" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="@string/running" | |||
android:text="@string/not_connected_bubble" | |||
android:textSize="20sp" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.565" | |||
app:layout_constraintHorizontal_bias="0.75" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" | |||
app:layout_constraintVertical_bias="0.183" /> | |||
<TextView | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="@string/this_device_status" | |||
android:textSize="20sp" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.133" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" | |||
app:layout_constraintVertical_bias="0.255" /> | |||
<TextView | |||
android:id="@+id/deviceStatus" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:text="@string/not_connected" | |||
android:textSize="20sp" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.811" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" | |||
app:layout_constraintVertical_bias="0.254" /> | |||
<Button | |||
android:id="@+id/connectButton" | |||
android:layout_width="wrap_content" | |||
@@ -242,4 +242,12 @@ | |||
<string name="disable_apps">Disable Apps</string> | |||
<string name="turnInternet">Please turn on internet connection</string> | |||
<string name="progress_bar_text">Loading</string> | |||
<string name="not_connected_bubble">Not Connected</string> | |||
<string name="connected_bubble">Connected</string> | |||
<string name="failed_bubble">Failed</string> | |||
<string name="disconnect">Disconnect</string> | |||
<string name="hostname_not_valid">Hostname not valid</string> | |||
<string name="login_failed">Login Failed</string> | |||
<string name="cerificate_install">"Please install a certificate"</string> | |||
<string name="success">Success</string> | |||
</resources> |