progress_meter
into dev
4 years ago
@@ -8,20 +8,21 @@ import android.view.View; | |||
import android.widget.Toast; | |||
import com.getbubblenow.android.R; | |||
import com.getbubblenow.android.api.ApiConstants; | |||
import com.getbubblenow.android.fragment.ErrorDialogFragment; | |||
import com.getbubblenow.android.fragment.LoadingDialogFragment; | |||
import com.getbubblenow.android.fragment.ErrorDialogFragment; | |||
import com.getbubblenow.android.fragment.LoadingDialogFragment; | |||
import com.getbubblenow.android.repository.DataRepository; | |||
import androidx.annotation.Nullable; | |||
import androidx.appcompat.app.AppCompatActivity; | |||
import androidx.lifecycle.Lifecycle; | |||
public class BaseActivityBubble extends AppCompatActivity { | |||
public class BaseActivityBubble extends AppCompatActivity{ | |||
public static final String LOADING_TAG = "loading_tag"; | |||
public static final String NO_CONNECTION_TAG = "no_connection_tag"; | |||
public static final String ERROR_TAG = "error_tag"; | |||
public static final String PROGRESS_TAG = "progress_tag"; | |||
public static final String RATE_TAG = "rate tag"; | |||
private final long LOADER_DELAY = 1000; | |||
@@ -32,6 +33,11 @@ public class BaseActivityBubble extends AppCompatActivity { | |||
@Override | |||
protected void onCreate(@Nullable Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
if (DataRepository.getRepositoryInstance() == null) { | |||
DataRepository.buildRepositoryInstance(this, ApiConstants.BOOTSTRAP_URL); | |||
} else { | |||
DataRepository.getRepositoryInstance().buildClientService(ApiConstants.BOOTSTRAP_URL); | |||
} | |||
} | |||
@@ -87,5 +93,4 @@ public class BaseActivityBubble extends AppCompatActivity { | |||
errorDialog.setArguments(bundle); | |||
getSupportFragmentManager().beginTransaction().add(errorDialog,ERROR_TAG).commitAllowingStateLoss(); | |||
} | |||
} |
@@ -1,14 +1,12 @@ | |||
package com.getbubblenow.android.activity; | |||
import androidx.annotation.NonNull; | |||
import androidx.annotation.Nullable; | |||
import androidx.appcompat.widget.AppCompatButton; | |||
import androidx.lifecycle.Observer; | |||
import androidx.lifecycle.ViewModelProvider; | |||
import android.content.Intent; | |||
import android.os.Bundle; | |||
import android.security.KeyChain; | |||
import android.text.Editable; | |||
import android.text.TextUtils; | |||
import android.text.TextWatcher; | |||
@@ -28,7 +26,6 @@ import com.getbubblenow.android.repository.DataRepository; | |||
import com.getbubblenow.android.resource.StatusResource; | |||
import com.getbubblenow.android.viewmodel.LoginViewModel; | |||
import java.util.List; | |||
public class LoginActivity extends BaseActivityBubble { | |||
@@ -37,8 +34,6 @@ public class LoginActivity extends BaseActivityBubble { | |||
private EditText password; | |||
private AppCompatButton sign; | |||
private static final int REQUEST_CODE = 1555; | |||
private static final String CERTIFICATE_NAME = "Bubble Certificate"; | |||
private boolean userNameStateFlag = false; | |||
private boolean passwordStateFlag = false; | |||
private static final String USER_NAME_KEY = "userName"; | |||
@@ -46,9 +41,6 @@ public class LoginActivity extends BaseActivityBubble { | |||
private static final String NO_INTERNET_CONNECTION = "no internet connection"; | |||
private static final String LOGIN_FAILED = "Login Failed"; | |||
private String config = ""; | |||
@Override | |||
protected void onCreate(Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
@@ -144,49 +136,22 @@ public class LoginActivity extends BaseActivityBubble { | |||
} | |||
private void signIn() { | |||
if (DataRepository.getRepositoryInstance() == null) { | |||
loginViewModel.buildRepositoryInstance(LoginActivity.this, ApiConstants.BOOTSTRAP_URL); | |||
if (loginViewModel.checkRepositoryInstance()) { | |||
loginViewModel.buildRepositoryInstance(this, ApiConstants.BOOTSTRAP_URL); | |||
} else { | |||
loginViewModel.buildClientService(ApiConstants.BOOTSTRAP_URL); | |||
} | |||
final String usernameInput = userName.getText().toString().trim(); | |||
final String passwordInput = password.getText().toString().trim(); | |||
showLoadingDialog(); | |||
loginViewModel.login(LoginActivity.this,usernameInput,passwordInput).observe(LoginActivity.this, new Observer<StatusResource<byte[]>>() { | |||
loginViewModel.login(this,usernameInput,passwordInput).observe(LoginActivity.this, new Observer<StatusResource<byte[]>>() { | |||
@Override public void onChanged(final StatusResource<byte[]> stringStatusResource) { | |||
switch (stringStatusResource.status) { | |||
case SUCCESS: | |||
byte[] certificateEncode = stringStatusResource.data; | |||
loginViewModel.getConfig(LoginActivity.this).observe(LoginActivity.this, new Observer<StatusResource<String>>() { | |||
@Override public void onChanged(final StatusResource<String> stringStatusResource) { | |||
switch (stringStatusResource.status){ | |||
case SUCCESS: | |||
closeLoadingDialog(); | |||
config = stringStatusResource.data; | |||
if (loginViewModel.isHaveCertificate(LoginActivity.this)) { | |||
createTunnel(); | |||
} else { | |||
final Intent intent = KeyChain.createInstallIntent(); | |||
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, certificateEncode); | |||
intent.putExtra(KeyChain.EXTRA_NAME, CERTIFICATE_NAME); | |||
startActivityForResult(intent, REQUEST_CODE); | |||
} | |||
break; | |||
case LOADING: | |||
break; | |||
case ERROR: | |||
closeLoadingDialog(); | |||
if (stringStatusResource.message.equals(NO_INTERNET_CONNECTION)) { | |||
showNetworkNotAvailableMessage(); | |||
} else if (stringStatusResource.message.equals(LOGIN_FAILED)) { | |||
Toast.makeText(LoginActivity.this, LOGIN_FAILED, Toast.LENGTH_LONG).show(); | |||
} else { | |||
showErrorDialog(stringStatusResource.message); | |||
} | |||
break; | |||
} | |||
} | |||
}); | |||
closeLoadingDialog(); | |||
final Intent mainActivityIntent = new Intent(LoginActivity.this, MainActivity.class); | |||
mainActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); | |||
startActivity(mainActivityIntent); | |||
break; | |||
case LOADING: | |||
Log.d("TAG", "Loading"); | |||
@@ -217,47 +182,6 @@ public class LoginActivity extends BaseActivityBubble { | |||
} | |||
} | |||
@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) { | |||
createTunnel(); | |||
} | |||
else { | |||
Toast.makeText(this, getString(R.string.cerificate_install), Toast.LENGTH_LONG).show(); | |||
} | |||
} | |||
private void createTunnel(){ | |||
showLoadingDialog(); | |||
loginViewModel.createTunnel(this,config).observe(this, new Observer<StatusResource<Object>>() { | |||
@Override public void onChanged(final StatusResource<Object> objectStatusResource) { | |||
closeLoadingDialog(); | |||
switch (objectStatusResource.status){ | |||
case SUCCESS: | |||
Toast.makeText(LoginActivity.this, getString(R.string.success), Toast.LENGTH_SHORT).show(); | |||
Log.d("TAG", "Success"); | |||
final Intent mainActivityIntent = new Intent(LoginActivity.this, MainActivity.class); | |||
mainActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); | |||
startActivity(mainActivityIntent); | |||
break; | |||
case LOADING: | |||
break; | |||
case ERROR: | |||
if(objectStatusResource.message.equals(NO_INTERNET_CONNECTION)){ | |||
showNetworkNotAvailableMessage(); | |||
} | |||
else if(objectStatusResource.message.equals(LOGIN_FAILED)){ | |||
Toast.makeText(LoginActivity.this,LOGIN_FAILED,Toast.LENGTH_LONG).show(); | |||
} | |||
else { | |||
showErrorDialog(objectStatusResource.message); | |||
} | |||
break; | |||
} | |||
} | |||
}); | |||
} | |||
@Override protected void onSaveInstanceState(@NonNull final Bundle outState) { | |||
outState.putBoolean(USER_NAME_KEY,userNameStateFlag); | |||
outState.putBoolean(PASSWORD_KEY,passwordStateFlag); | |||
@@ -1,24 +1,32 @@ | |||
package com.getbubblenow.android.activity; | |||
import androidx.annotation.Nullable; | |||
import androidx.appcompat.app.AppCompatActivity; | |||
import androidx.constraintlayout.widget.ConstraintLayout; | |||
import androidx.lifecycle.Observer; | |||
import androidx.lifecycle.ViewModelProvider; | |||
import android.content.Intent; | |||
import android.net.Uri; | |||
import android.os.Bundle; | |||
import android.security.KeyChain; | |||
import android.text.TextUtils; | |||
import android.util.Log; | |||
import android.view.View; | |||
import android.view.View.OnClickListener; | |||
import android.widget.Button; | |||
import android.widget.ImageButton; | |||
import android.widget.ImageView; | |||
import android.widget.TextView; | |||
import android.widget.Toast; | |||
import com.airbnb.lottie.LottieAnimationView; | |||
import com.getbubblenow.android.model.Network; | |||
import com.getbubblenow.android.resource.StatusResource; | |||
import com.getbubblenow.android.util.UserStore; | |||
import com.getbubblenow.android.viewmodel.MainViewModel; | |||
import com.getbubblenow.android.R; | |||
import com.getbubblenow.android.viewmodel.MainViewModel; | |||
public class MainActivity extends AppCompatActivity { | |||
public class MainActivity extends BaseActivityBubble { | |||
private MainViewModel mainViewModel; | |||
private TextView bubbleStatus; | |||
@@ -26,8 +34,8 @@ public class MainActivity extends AppCompatActivity { | |||
private Button connectButton; | |||
private ImageView imageMyBubble; | |||
private ImageView mark; | |||
private ImageButton myBubbleButton; | |||
private ImageButton accountButton; | |||
private ImageButton supportButton; | |||
private TextView logout; | |||
private boolean connectionStateFlag; | |||
@@ -39,32 +47,138 @@ public class MainActivity extends AppCompatActivity { | |||
public static final int CONNECTED_TEXT_VIEW_BOTTOM_MARGIN = 100; | |||
private static final String BASE_URL_PREFIX = "https://"; | |||
private static final String BASE_URL_SUFFIX_MY_BUBBLE = ":1443/appLogin?session="; | |||
private static final String URI_MY_BUBBLE = "&uri=/"; | |||
private static final String BASE_URL_SUFFIX_ACCOUNT = "/appLogin?session="; | |||
private static final String URI_ACCOUNT = "&uri=/me"; | |||
private static final String NO_INTERNET_CONNECTION = "no internet connection"; | |||
private static final String LOGIN_FAILED = "Login Failed"; | |||
private static final int REQUEST_CODE = 1555; | |||
private static final String CERTIFICATE_NAME = "Bubble Certificate"; | |||
private String config = ""; | |||
private byte[] certificateEncode; | |||
private final String SUPPORT_URL = "https://support.getbubblenow.com"; | |||
private ConstraintLayout constraint_layout_connected_title; | |||
private ConstraintLayout constraints_layout_connected_bubble; | |||
private ConstraintLayout constraint_layout_disconnected_title; | |||
private ConstraintLayout constraints_layout_disconnected_bubble; | |||
private ImageView union; | |||
private LottieAnimationView network_progress; | |||
private TextView progress_title; | |||
@Override | |||
protected void onCreate(Bundle savedInstanceState) { | |||
super.onCreate(savedInstanceState); | |||
mainViewModel = new ViewModelProvider(this).get(MainViewModel.class); | |||
if (mainViewModel.isUserLoggedIn(this)) { | |||
setContentView(R.layout.activity_main); | |||
mainViewModel.buildRepositoryInstance(this, mainViewModel.getUserURL(this)); | |||
initUI(); | |||
} else { | |||
showLoadingDialog(); | |||
if (mainViewModel.isHaveSageURL(this)) { | |||
if (mainViewModel.isHaveHostName(this)) { | |||
closeLoadingDialog(); | |||
setContentView(R.layout.activity_main); | |||
mainViewModel.buildRepositoryInstance(this, mainViewModel.getSageURL(this)); | |||
initUI(); | |||
logout.setEnabled(true); | |||
connectButton.setEnabled(true); | |||
} else { | |||
setContentView(R.layout.activity_main); | |||
initUI(); | |||
if (mainViewModel.checkRepositoryInstance()) { | |||
mainViewModel.buildRepositoryInstance(this, mainViewModel.getSageURL(this)); | |||
getCertificateData(); | |||
} else { | |||
mainViewModel.buildClientService(mainViewModel.getSageURL(this)); | |||
getCertificateData(); | |||
} | |||
} | |||
} | |||
else{ | |||
closeLoadingDialog(); | |||
Intent intent = new Intent(this, LoginActivity.class); | |||
startActivity(intent); | |||
finish(); | |||
} | |||
} | |||
private void getCertificateData() { | |||
mainViewModel.getCertificateData(this).observe(this, new Observer<StatusResource<byte[]>>() { | |||
@Override public void onChanged(final StatusResource<byte[]> statusResource) { | |||
switch (statusResource.status) { | |||
case SUCCESS: | |||
certificateEncode = statusResource.data; | |||
setConnectedBubbleUI(); | |||
mainViewModel.getConfig(MainActivity.this).observe(MainActivity.this, new Observer<StatusResource<String>>() { | |||
@Override public void onChanged(final StatusResource<String> stringStatusResource) { | |||
switch (stringStatusResource.status) { | |||
case SUCCESS: | |||
config = stringStatusResource.data; | |||
mainViewModel.setConfig(config,MainActivity.this); | |||
if (mainViewModel.isHaveCertificate(MainActivity.this)) { | |||
createTunnel(); | |||
} else { | |||
final Intent intent = KeyChain.createInstallIntent(); | |||
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, certificateEncode); | |||
intent.putExtra(KeyChain.EXTRA_NAME, CERTIFICATE_NAME); | |||
closeLoadingDialog(); | |||
startActivityForResult(intent, REQUEST_CODE); | |||
} | |||
break; | |||
case LOADING: | |||
break; | |||
case ERROR: | |||
closeLoadingDialog(); | |||
if (stringStatusResource.message.equals(NO_INTERNET_CONNECTION)) { | |||
showNetworkNotAvailableMessage(); | |||
} else if (stringStatusResource.message.equals(LOGIN_FAILED)) { | |||
Toast.makeText(MainActivity.this, LOGIN_FAILED, Toast.LENGTH_LONG).show(); | |||
} else { | |||
showErrorDialog(stringStatusResource.message); | |||
} | |||
break; | |||
} | |||
} | |||
}); | |||
break; | |||
case LOADING: | |||
break; | |||
case ERROR: | |||
if (statusResource.message.equals(NO_INTERNET_CONNECTION)) { | |||
showNetworkNotAvailableMessage(); | |||
} else if (statusResource.message.equals(LOGIN_FAILED)) { | |||
Toast.makeText(MainActivity.this, LOGIN_FAILED, Toast.LENGTH_LONG).show(); | |||
} else { | |||
showErrorDialog(statusResource.message); | |||
} | |||
break; | |||
} | |||
} | |||
}); | |||
mainViewModel.getNetworkStatusLiveData().observe(this, new Observer<StatusResource<Network>>() { | |||
@Override public void onChanged(final StatusResource<Network> networkStatusResource) { | |||
if (!networkStatusResource.data.isRunning()) { | |||
setProgressBubbleUI(); | |||
mainViewModel.getProgressLiveData().observe(MainActivity.this, new Observer<Long>() { | |||
@Override public void onChanged(final Long percent) { | |||
setProgress(percent); | |||
} | |||
}); | |||
} | |||
} | |||
}); | |||
} | |||
@Override protected void onResume() { | |||
super.onResume(); | |||
if (mainViewModel.isVPNConnected(this, connectionStateFlag)) { | |||
connectionStateFlag = false; | |||
setConnectionStateUI(false); | |||
} else { | |||
connectionStateFlag = true; | |||
setConnectionStateUI(true); | |||
if (mainViewModel.isHaveSageURL(this)) { | |||
if (mainViewModel.isHaveHostName(this)) { | |||
if (mainViewModel.isVPNConnected(this, connectionStateFlag)) { | |||
connectionStateFlag = false; | |||
setConnectionStateUI(false); | |||
} else { | |||
connectionStateFlag = true; | |||
setConnectionStateUI(true); | |||
} | |||
} | |||
} | |||
} | |||
@@ -77,11 +191,18 @@ public class MainActivity extends AppCompatActivity { | |||
bubbleStatus = findViewById(R.id.bubbleStatus); | |||
connectButton = findViewById(R.id.connectButton); | |||
imageMyBubble = findViewById(R.id.imageMyBubble); | |||
supportButton = findViewById(R.id.supportButton); | |||
mark = findViewById(R.id.mark); | |||
titleMyBubble = findViewById(R.id.titleMyBubble); | |||
myBubbleButton = findViewById(R.id.myBubbleButton); | |||
accountButton = findViewById(R.id.accountButton); | |||
logout = findViewById(R.id.logout); | |||
constraint_layout_connected_title = findViewById(R.id.constraint_layout_connected_title); | |||
constraints_layout_connected_bubble = findViewById(R.id.constraints_layout_connected_bubble); | |||
constraint_layout_disconnected_title = findViewById(R.id.constraint_layout_disconnected_title); | |||
constraints_layout_disconnected_bubble = findViewById(R.id.constraints_layout_disconnected_bubble); | |||
union = findViewById(R.id.union); | |||
network_progress = findViewById(R.id.network_progress); | |||
progress_title = findViewById(R.id.progress_title); | |||
} | |||
private void initListeners() { | |||
@@ -90,11 +211,6 @@ public class MainActivity extends AppCompatActivity { | |||
connect(); | |||
} | |||
}); | |||
myBubbleButton.setOnClickListener(new OnClickListener() { | |||
@Override public void onClick(final View v) { | |||
showMyBubble(); | |||
} | |||
}); | |||
accountButton.setOnClickListener(new OnClickListener() { | |||
@Override public void onClick(final View v) { | |||
showAccount(); | |||
@@ -105,32 +221,96 @@ public class MainActivity extends AppCompatActivity { | |||
logout(); | |||
} | |||
}); | |||
supportButton.setOnClickListener(new OnClickListener() { | |||
@Override public void onClick(final View v) { | |||
showSupport(); | |||
} | |||
}); | |||
} | |||
private void showAccount() { | |||
String token = mainViewModel.getToken(this); | |||
final String hostname = mainViewModel.getHostname(this); | |||
final String url = BASE_URL_PREFIX + hostname + BASE_URL_SUFFIX_MY_BUBBLE + token + URI_ACCOUNT; | |||
final Intent intent = new Intent(Intent.ACTION_VIEW); | |||
intent.setData(Uri.parse(url)); | |||
startActivity(intent); | |||
final String hostname = mainViewModel.getHostNameForAccount(this); | |||
if(!TextUtils.isEmpty(hostname)) { | |||
String token = mainViewModel.getToken(this); | |||
final String url = BASE_URL_PREFIX + hostname + BASE_URL_SUFFIX_MY_BUBBLE + token + URI_ACCOUNT; | |||
final Intent intent = new Intent(Intent.ACTION_VIEW); | |||
intent.setData(Uri.parse(url)); | |||
startActivity(intent); | |||
} | |||
else { | |||
String sageToken = mainViewModel.getSageToken(this); | |||
final String sageHostname = mainViewModel.getSageHostname(this); | |||
final String url = sageHostname + BASE_URL_SUFFIX_ACCOUNT + sageToken + URI_ACCOUNT; | |||
final Intent intent = new Intent(Intent.ACTION_VIEW); | |||
intent.setData(Uri.parse(url)); | |||
startActivity(intent); | |||
} | |||
} | |||
private void showMyBubble() { | |||
String token = mainViewModel.getToken(this); | |||
final String hostname = mainViewModel.getHostname(this); | |||
final String url = BASE_URL_PREFIX + hostname + BASE_URL_SUFFIX_MY_BUBBLE + token + URI_MY_BUBBLE; | |||
private void showSupport(){ | |||
final Intent intent = new Intent(Intent.ACTION_VIEW); | |||
intent.setData(Uri.parse(url)); | |||
intent.setData(Uri.parse(SUPPORT_URL)); | |||
startActivity(intent); | |||
} | |||
private void connect() { | |||
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); | |||
if(mainViewModel.isHaveCertificate(this)) { | |||
mainViewModel.isHaveTunnel(this).observe(this, new Observer<StatusResource<Boolean>>() { | |||
@Override public void onChanged(final StatusResource<Boolean> booleanStatusResource) { | |||
if(booleanStatusResource.data!=null){ | |||
if (booleanStatusResource.data) { | |||
final boolean state = mainViewModel.isVPNConnected(MainActivity.this, connectionStateFlag); | |||
connectionStateFlag = state; | |||
mainViewModel.connect(state, MainActivity.this).observe(MainActivity.this, new Observer<Boolean>() { | |||
@Override public void onChanged(final Boolean state) { | |||
setConnectionStateUI(state); | |||
} | |||
}); | |||
} else { | |||
createTunnel(); | |||
connect(); | |||
} | |||
} | |||
} | |||
}); | |||
} | |||
else { | |||
final Intent intent = KeyChain.createInstallIntent(); | |||
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, certificateEncode); | |||
intent.putExtra(KeyChain.EXTRA_NAME, CERTIFICATE_NAME); | |||
startActivityForResult(intent, REQUEST_CODE); | |||
} | |||
} | |||
private void createTunnel(){ | |||
showLoadingDialog(); | |||
mainViewModel.createTunnel(this,UserStore.getInstance(this).getConfig()).observe(this, new Observer<StatusResource<Object>>() { | |||
@Override public void onChanged(final StatusResource<Object> objectStatusResource) { | |||
closeLoadingDialog(); | |||
switch (objectStatusResource.status){ | |||
case SUCCESS: | |||
closeLoadingDialog(); | |||
logout.setEnabled(true); | |||
connectButton.setEnabled(true); | |||
Toast.makeText(MainActivity.this, getString(R.string.success), Toast.LENGTH_SHORT).show(); | |||
Log.d("TAG", "Success"); | |||
break; | |||
case LOADING: | |||
break; | |||
case ERROR: | |||
closeLoadingDialog(); | |||
if(objectStatusResource.message.equals(NO_INTERNET_CONNECTION)){ | |||
showNetworkNotAvailableMessage(); | |||
} | |||
else if(objectStatusResource.message.equals(LOGIN_FAILED)){ | |||
Toast.makeText(MainActivity.this,LOGIN_FAILED,Toast.LENGTH_LONG).show(); | |||
} | |||
else { | |||
showErrorDialog(objectStatusResource.message); | |||
} | |||
break; | |||
} | |||
} | |||
}); | |||
} | |||
@@ -150,6 +330,16 @@ public class MainActivity extends AppCompatActivity { | |||
setConnectionStateUI(false); | |||
} | |||
} | |||
else if (requestCode == REQUEST_CODE) { | |||
if(resultCode == RESULT_OK) { | |||
createTunnel(); | |||
} | |||
else { | |||
logout.setEnabled(true); | |||
connectButton.setEnabled(true); | |||
Toast.makeText(this, getString(R.string.cerificate_install), Toast.LENGTH_LONG).show(); | |||
} | |||
} | |||
} | |||
private void setConnectionStateUI(boolean state) { | |||
@@ -172,11 +362,39 @@ public class MainActivity extends AppCompatActivity { | |||
} | |||
} | |||
private void logout(){ | |||
final Intent intent = new Intent(MainActivity.this, LoginActivity.class); | |||
private void logout() { | |||
final Intent intent = new Intent(this, LoginActivity.class); | |||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); | |||
mainViewModel.deleteTunnel(this); | |||
mainViewModel.removeSharedPreferences(MainActivity.this); | |||
mainViewModel.removeSharedPreferences(this); | |||
startActivity(intent); | |||
} | |||
private void setConnectedBubbleUI() { | |||
union.setImageResource(R.drawable.union); | |||
constraint_layout_connected_title.setVisibility(View.VISIBLE); | |||
constraints_layout_connected_bubble.setVisibility(View.VISIBLE); | |||
constraint_layout_disconnected_title.setVisibility(View.GONE); | |||
constraints_layout_disconnected_bubble.setVisibility(View.GONE); | |||
} | |||
private void setProgressBubbleUI() { | |||
closeLoadingDialog(); | |||
union.setImageResource(R.drawable.progress_background); | |||
constraint_layout_connected_title.setVisibility(View.GONE); | |||
constraints_layout_connected_bubble.setVisibility(View.GONE); | |||
constraint_layout_disconnected_title.setVisibility(View.VISIBLE); | |||
constraints_layout_disconnected_bubble.setVisibility(View.VISIBLE); | |||
} | |||
private void setProgress(long percent) { | |||
final long textProgress = percent; | |||
if (percent % 10 >= 5) { | |||
percent = percent + 10 - (percent % 10); | |||
} else { | |||
percent -= (percent % 10); | |||
} | |||
network_progress.setProgress(percent / 100f); | |||
progress_title.setText(textProgress + "%"); | |||
} | |||
} |
@@ -16,4 +16,5 @@ public class ApiConstants { | |||
public static final String DEVICE_NAME = "name"; | |||
public static final String DEVICE_TYPE = "deviceType"; | |||
public static final String CERTIFICATE_URL = "auth/cacert?deviceType=android"; | |||
public static final String NETWORK_STATUS = "users/{userId}/networks/{networkId}/actions/status"; | |||
} |
@@ -2,6 +2,7 @@ package com.getbubblenow.android.api.network; | |||
import com.getbubblenow.android.model.Device; | |||
import com.getbubblenow.android.model.Network; | |||
import com.getbubblenow.android.model.NetworkStatus; | |||
import com.getbubblenow.android.model.Sages; | |||
import com.getbubblenow.android.model.User; | |||
import com.getbubblenow.android.api.ApiConstants; | |||
@@ -47,4 +48,7 @@ public interface ClientApi { | |||
@GET(ApiConstants.NODE_BASE_URI) | |||
Single<List<Network>> getNodeBaseURI(@HeaderMap HashMap<String,String> header); | |||
@GET(ApiConstants.NETWORK_STATUS) | |||
Single<List<NetworkStatus>> getNetworkState(@Path("userId") String userId , @Path("networkId") String networkId , @HeaderMap HashMap<String,String> header); | |||
} |
@@ -225,4 +225,8 @@ public class Network { | |||
public void setShortId(final String shortId) { | |||
this.shortId = shortId; | |||
} | |||
public boolean isRunning(){ | |||
return state.equals("running"); | |||
} | |||
} |
@@ -0,0 +1,78 @@ | |||
package com.getbubblenow.android.model; | |||
import com.google.gson.annotations.Expose; | |||
import com.google.gson.annotations.SerializedName; | |||
public class NetworkStatus { | |||
@SerializedName("account") | |||
@Expose | |||
private String account; | |||
@SerializedName("network") | |||
@Expose | |||
private String network; | |||
@SerializedName("pattern") | |||
@Expose | |||
private String pattern; | |||
@SerializedName("match") | |||
@Expose | |||
private String match; | |||
@SerializedName("percent") | |||
@Expose | |||
private long percent; | |||
@SerializedName("messageKey") | |||
@Expose | |||
private String messageKey; | |||
@SerializedName("details") | |||
@Expose | |||
private String details; | |||
public String getPattern() { | |||
return pattern; | |||
} | |||
public void setPattern(final String pattern) { | |||
this.pattern = pattern; | |||
} | |||
public String getMatch() { | |||
return match; | |||
} | |||
public void setMatch(final String match) { | |||
this.match = match; | |||
} | |||
public String getDetails() { | |||
return details; | |||
} | |||
public void setDetails(final String details) { | |||
this.details = details; | |||
} | |||
public String getAccount() { | |||
return account; | |||
} | |||
public void setAccount(final String account) { | |||
this.account = account; | |||
} | |||
public String getNetwork() { | |||
return network; | |||
} | |||
public void setNetwork(final String network) { | |||
this.network = network; | |||
} | |||
public long getPercent() { | |||
return percent; | |||
} | |||
public void setPercent(final long percent) { | |||
this.percent = percent; | |||
} | |||
public String getMessageKey() { | |||
return messageKey; | |||
} | |||
public void setMessageKey(final String messageKey) { | |||
this.messageKey = messageKey; | |||
} | |||
} |
@@ -3,6 +3,7 @@ package com.getbubblenow.android.repository; | |||
import android.content.Context; | |||
import android.content.Intent; | |||
import android.os.Build; | |||
import android.os.Handler; | |||
import android.provider.Settings; | |||
import android.provider.Settings.Secure; | |||
import android.util.Log; | |||
@@ -77,12 +78,16 @@ public class DataRepository { | |||
private static final String RUNNING = "running"; | |||
private static final String BASE_URL_PREFIX = "https://"; | |||
private static final String BASE_URL_SUFFIX = ":1443/api/"; | |||
private static String token = ""; | |||
private static String deviceName; | |||
private static String deviceID; | |||
private List<String> nodes; | |||
private int nodeIndex = 0; | |||
private String hostname = ""; | |||
private MutableLiveData<StatusResource<Network>> networkStatusLiveData = new MutableLiveData<>(); | |||
private MutableLiveData<Long> progressLiveData = new MutableLiveData<>(); | |||
private Network _network; | |||
private final long PROGRESS_DELAY = 5000; | |||
private final int FULL_PROGRESS = 100; | |||
private DataRepository(Context context, String url) { | |||
BASE_URL = url; | |||
@@ -110,108 +115,185 @@ public class DataRepository { | |||
return instance; | |||
} | |||
private void getNodeIndex(int index, String username , String password, Context context,NetworkBoundStatusResource<byte[]> liveData){ | |||
if(index<nodes.size()) | |||
{ | |||
getNetwork(nodes.get(index),username,password,context,liveData); | |||
} | |||
else { | |||
private void getNodeIndex(int index, String username, String password, Context context, NetworkBoundStatusResource<byte[]> liveData) { | |||
if (index < nodes.size()) { | |||
getNetwork(nodes.get(index), username, password, context, liveData); | |||
} else { | |||
liveData.postMutableLiveData(StatusResource.error(LOGIN_FAILED)); | |||
} | |||
} | |||
private void getNetwork(String node, String username , String password, Context context,NetworkBoundStatusResource<byte[]> liveData){ | |||
public MutableLiveData<StatusResource<Network>> getNetworkStatusLiveData() { | |||
return networkStatusLiveData; | |||
} | |||
public MutableLiveData<Long> getProgressLiveData() { | |||
return progressLiveData; | |||
} | |||
private void getNetwork(String node, String username, String password, Context context, NetworkBoundStatusResource<byte[]> liveData) { | |||
HashMap<String, String> data = new HashMap<>(); | |||
data.put(ApiConstants.USERNAME, username); | |||
data.put(ApiConstants.PASSWORD, password); | |||
buildClientService(node + URL_SUFFIX); | |||
Disposable disposableLogin = clientApi.login(data) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(user -> { | |||
UserStore.getInstance(context).setSageToken(user.getToken()); | |||
// UserStore.getInstance(context).setToken(user.getToken()); | |||
UserStore.getInstance(context).setUserData(username, password); | |||
UserStore.getInstance(context).setSageURL(node + URL_SUFFIX); | |||
UserStore.getInstance(context).setSageHostname(node); | |||
BASE_URL = node + URL_SUFFIX; | |||
ApiConstants.BASE_URL = node + URL_SUFFIX; | |||
buildClientService(ApiConstants.BASE_URL); | |||
liveData.postMutableLiveData(StatusResource.success(null)); | |||
}, throwable -> { | |||
nodeIndex++; | |||
getNodeIndex(nodeIndex, username, password, context, liveData); | |||
Log.d("ERR", "getSages-login"); | |||
}); | |||
compositeDisposable.add(disposableLogin); | |||
} | |||
HashMap<String, String> data = new HashMap<>(); | |||
data.put(ApiConstants.USERNAME, username); | |||
data.put(ApiConstants.PASSWORD, password); | |||
buildClientService(node + URL_SUFFIX); | |||
Disposable disposableLogin = clientApi.login(data) | |||
.subscribeOn(Schedulers.newThread()) | |||
public MutableLiveData<StatusResource<byte[]>> getCertificateData(Context context) { | |||
return new NetworkBoundStatusResource<byte[]>() { | |||
@Override protected void createCall() { | |||
final HashMap<String, String> header = new HashMap<>(); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, UserStore.getInstance(context).getSageToken()); | |||
NetworkBoundStatusResource<byte[]> liveData = this; | |||
buildClientService(UserStore.getInstance(context).getSageURL()); | |||
Disposable getNodeBaseURIDisposable = clientApi.getNodeBaseURI(header) | |||
.subscribeOn(Schedulers.io()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(user -> { | |||
token = user.getToken(); | |||
ApiConstants.BASE_URL = node + URL_SUFFIX; | |||
buildClientService(ApiConstants.BASE_URL); | |||
final HashMap<String, String> header = new HashMap<>(); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, token); | |||
Disposable getNodeBaseURIDisposable = clientApi.getNodeBaseURI(header) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(networks -> { | |||
for (Network network : networks) { | |||
if (network.getState().equals(RUNNING)) { | |||
ApiConstants.BASE_URL = network.getName() + "." + network.getDomainName(); | |||
BASE_URL = ApiConstants.BASE_URL; | |||
hostname = BASE_URL; | |||
setHostName(context,BASE_URL); | |||
buildClientService(BASE_URL_PREFIX + BASE_URL + BASE_URL_SUFFIX); | |||
setUserURL(context, BASE_URL_PREFIX + BASE_URL + BASE_URL_SUFFIX); | |||
login(username,password,context,liveData); | |||
break; | |||
} | |||
.subscribe(networks -> { | |||
for (Network network : networks) { | |||
if (network.getState().equals(RUNNING)) { | |||
ApiConstants.BASE_URL = network.getName() + "." + network.getDomainName(); | |||
UserStore.getInstance(context).setHostNameForAccount(network.getName() + "." + network.getDomainName()); | |||
hostname = network.getName() + "." + network.getDomainName(); | |||
BASE_URL = BASE_URL_PREFIX + ApiConstants.BASE_URL + BASE_URL_SUFFIX; | |||
buildClientService(BASE_URL); | |||
_network = network; | |||
login(UserStore.getInstance(context).getUsername(), UserStore.getInstance(context).getPassword(), context, this); | |||
} else { | |||
networkStatusLiveData.postValue(StatusResource.success(network)); | |||
Handler handler = new Handler(); | |||
handler.postDelayed(new Runnable() { | |||
@Override public void run() { | |||
final HashMap<String, String> header = new HashMap<>(); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, UserStore.getInstance(context).getSageToken()); | |||
Disposable getNetworkStateDisposable = clientApi.getNetworkState(UserStore.getInstance(context).getUsername(), network.getUuid(), header) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(networkStatus -> { | |||
int indexNetworkState = 0; | |||
for (int i = 0; i < networkStatus.size(); i++) { | |||
if (networkStatus.get(i).getNetwork().equals(network.getUuid())) { | |||
indexNetworkState = i; | |||
break; | |||
} else { | |||
//TODO Handle when don't have equals network | |||
} | |||
} | |||
if(networkStatus.get(indexNetworkState).getPercent() != FULL_PROGRESS){ | |||
progressLiveData.postValue(networkStatus.get(indexNetworkState).getPercent()); | |||
handler.postDelayed(this, PROGRESS_DELAY); | |||
} | |||
else if(networkStatus.get(indexNetworkState).getPercent() == FULL_PROGRESS) { | |||
progressLiveData.postValue(networkStatus.get(indexNetworkState).getPercent()); | |||
Disposable getNodeBaseURIDisposable = clientApi.getNodeBaseURI(header) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(networks -> { | |||
for (Network network : networks) { | |||
if (network.getState().equals(RUNNING)) { | |||
handler.removeCallbacks(this); | |||
_network = network; | |||
ApiConstants.BASE_URL = network.getName() + "." + network.getDomainName(); | |||
UserStore.getInstance(context).setHostNameForAccount(network.getName() + "." + network.getDomainName()); | |||
hostname = network.getName() + "." + network.getDomainName(); | |||
BASE_URL = ApiConstants.BASE_URL; | |||
buildClientService(BASE_URL_PREFIX + BASE_URL + BASE_URL_SUFFIX); | |||
login(UserStore.getInstance(context).getUsername(), UserStore.getInstance(context).getPassword(), context, liveData); | |||
} | |||
else { | |||
handler.postDelayed(this, PROGRESS_DELAY); | |||
} | |||
} | |||
}, throwable -> { | |||
Log.d("ERR", "getNodeBaseURI"); | |||
setErrorMessage(throwable, liveData); | |||
}); | |||
} | |||
}, throwable -> { | |||
Log.d("ERR", "getNetworkState"); | |||
setErrorMessage(throwable, liveData); | |||
}); | |||
} | |||
}, throwable -> { | |||
Log.d("ERR", "getNodeBaseURI"); | |||
nodeIndex++; | |||
getNodeIndex(nodeIndex,username,password,context,liveData); | |||
}); | |||
compositeDisposable.add(getNodeBaseURIDisposable); | |||
}, PROGRESS_DELAY); | |||
} | |||
} | |||
}, throwable -> { | |||
nodeIndex++; | |||
getNodeIndex(nodeIndex,username,password,context,liveData); | |||
Log.d("ERR", "getSages-login"); | |||
Log.d("ERR", "getNodeBaseURI2"); | |||
setErrorMessage(throwable, liveData); | |||
}); | |||
compositeDisposable.add(disposableLogin); | |||
} | |||
}.getMutableLiveData(); | |||
} | |||
public MutableLiveData<StatusResource<byte[]>> login(Context context, String username, String password){ | |||
return new NetworkBoundStatusResource<byte[]>(){ | |||
public MutableLiveData<StatusResource<byte[]>> login(Context context, String username, String password) { | |||
return new NetworkBoundStatusResource<byte[]>() { | |||
@Override protected void createCall() { | |||
Disposable sagesDisposable = clientApi.getSages() | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(sages -> { | |||
nodeIndex = 0; | |||
nodes = sages.getSages(); | |||
getNodeIndex(nodeIndex,username,password,context,this); | |||
},throwable -> { | |||
Log.d("ERR","getSages"); | |||
setErrorMessage(throwable,this); | |||
}); | |||
compositeDisposable.add(sagesDisposable); | |||
} | |||
}.getMutableLiveData(); | |||
@Override protected void createCall() { | |||
Disposable sagesDisposable = clientApi.getSages() | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(sages -> { | |||
nodeIndex = 0; | |||
nodes = sages.getSages(); | |||
getNodeIndex(nodeIndex, username, password, context, this); | |||
}, throwable -> { | |||
Log.d("ERR", "getSages"); | |||
setErrorMessage(throwable, this); | |||
}); | |||
compositeDisposable.add(sagesDisposable); | |||
} | |||
}.getMutableLiveData(); | |||
} | |||
private MutableLiveData<StatusResource<byte[]>> login(String username, String password, Context context, NetworkBoundStatusResource<byte[]> liveData) { | |||
return new NetworkBoundStatusResource<byte[]>() { | |||
@Override protected void createCall() { | |||
HashMap<String, String> data = new HashMap<>(); | |||
buildClientService(BASE_URL); | |||
data.put(ApiConstants.USERNAME, username); | |||
data.put(ApiConstants.PASSWORD, password); | |||
Disposable disposableLogin = clientApi.login(data) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(user -> { | |||
token = user.getToken(); | |||
UserStore.getInstance(context).setToken(user.getToken()); | |||
if (!isDeviceLoggedIn(context)) { | |||
addDevice(context); | |||
} else { | |||
getAllDevices(context); | |||
} | |||
}, throwable -> { | |||
setErrorMessage(throwable,liveData); | |||
Log.d("ERR", "login"); | |||
setErrorMessage(throwable, liveData); | |||
}); | |||
compositeDisposable.add(disposableLogin); | |||
} | |||
private void getAllDevices(final Context context) { | |||
final HashMap<String, String> header = new HashMap<>(); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, token); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, UserStore.getInstance(context).getToken()); | |||
Disposable disposableAllDevices = clientApi.getAllDevices(header) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
@@ -221,6 +303,7 @@ public class DataRepository { | |||
//TODO Add errbit for this case | |||
if (UserStore.getInstance(context).getDeviceID().equals(item.getUuid())) { | |||
// UserStore.getInstance(context).setToken(token); | |||
Log.i("INF", "SUCCESS NULL"); | |||
setMutableLiveData(StatusResource.success(null)); | |||
hasDevice = true; | |||
break; | |||
@@ -230,7 +313,8 @@ public class DataRepository { | |||
addDevice(context); | |||
} | |||
}, throwable -> { | |||
setErrorMessage(throwable,liveData); | |||
Log.d("ERR", "getAllDevices"); | |||
setErrorMessage(throwable, liveData); | |||
}); | |||
compositeDisposable.add(disposableAllDevices); | |||
} | |||
@@ -241,7 +325,7 @@ public class DataRepository { | |||
final String imei = getDeviceID(context); | |||
final String deviceName = brand + SPACE + model + SPACE + SEPARATOR + SPACE + imei; | |||
final HashMap<String, String> header = new HashMap<>(); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, token); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, UserStore.getInstance(context).getToken()); | |||
final Disposable disposableAllDevices = clientApi.getAllDevices(header) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
@@ -260,9 +344,10 @@ public class DataRepository { | |||
hasDevice = true; | |||
getCertificate(context).observe((LifecycleOwner) context, new Observer<StatusResource<byte[]>>() { | |||
@Override public void onChanged(final StatusResource<byte[]> statusResource) { | |||
switch (statusResource.status){ | |||
switch (statusResource.status) { | |||
case SUCCESS: | |||
liveData.postMutableLiveData(StatusResource.success(statusResource.data)); | |||
networkStatusLiveData.postValue(StatusResource.success(_network)); | |||
break; | |||
case ERROR: | |||
liveData.postMutableLiveData(StatusResource.error(statusResource.message)); | |||
@@ -293,12 +378,13 @@ public class DataRepository { | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(device -> { | |||
UserStore.getInstance(context).setDevice(device.getName(), device.getUuid()); | |||
deviceID = device.getUuid(); | |||
getCertificate(context).observe((LifecycleOwner) context, new Observer<StatusResource<byte[]>>() { | |||
@Override public void onChanged(final StatusResource<byte[]> statusResource) { | |||
switch (statusResource.status){ | |||
switch (statusResource.status) { | |||
case SUCCESS: | |||
liveData.postMutableLiveData(StatusResource.success(statusResource.data)); | |||
networkStatusLiveData.postValue(StatusResource.success(_network)); | |||
break; | |||
case ERROR: | |||
liveData.postMutableLiveData(StatusResource.error(statusResource.message)); | |||
@@ -308,8 +394,9 @@ public class DataRepository { | |||
}); | |||
// getConfig(context); | |||
}, throwable -> { | |||
setErrorMessage(throwable,liveData); | |||
// setMutableLiveData(StatusResource.error(throwable.getMessage())); | |||
setErrorMessage(throwable, liveData); | |||
Log.d("ERR", "addDevice5"); | |||
// setMutableLiveData(StatusResource.error(throwable.getMessage())); | |||
}); | |||
compositeDisposable.add(disposableAddDevice); | |||
} else { | |||
@@ -346,12 +433,13 @@ public class DataRepository { | |||
.subscribe(device -> { | |||
DataRepository.deviceName = device.getName(); | |||
DataRepository.deviceID = device.getUuid(); | |||
// UserStore.getInstance(context).setDevice(device.getName(), device.getUuid()); | |||
// UserStore.getInstance(context).setDevice(device.getName(), device.getUuid()); | |||
getCertificate(context).observe((LifecycleOwner) context, new Observer<StatusResource<byte[]>>() { | |||
@Override public void onChanged(final StatusResource<byte[]> statusResource) { | |||
switch (statusResource.status){ | |||
switch (statusResource.status) { | |||
case SUCCESS: | |||
liveData.postMutableLiveData(StatusResource.success(statusResource.data)); | |||
networkStatusLiveData.postValue(StatusResource.success(_network)); | |||
break; | |||
case ERROR: | |||
liveData.postMutableLiveData(StatusResource.error(statusResource.message)); | |||
@@ -361,16 +449,18 @@ public class DataRepository { | |||
}); | |||
// getConfig(context); | |||
}, throwable -> { | |||
setErrorMessage(throwable,liveData); | |||
// setMutableLiveData(StatusResource.error(throwable.getMessage())); | |||
Log.d("ERR", "addDevice10"); | |||
setErrorMessage(throwable, liveData); | |||
// setMutableLiveData(StatusResource.error(throwable.getMessage())); | |||
}); | |||
compositeDisposable.add(disposableAddDevice); | |||
} | |||
} | |||
} | |||
}, throwable -> { | |||
setErrorMessage(throwable,liveData); | |||
// setMutableLiveData(StatusResource.error(NO_INTERNET_CONNECTION)); | |||
setErrorMessage(throwable, liveData); | |||
Log.d("ERR", "getAllDevices2"); | |||
// setMutableLiveData(StatusResource.error(NO_INTERNET_CONNECTION)); | |||
}); | |||
compositeDisposable.add(disposableAllDevices); | |||
} | |||
@@ -378,31 +468,34 @@ public class DataRepository { | |||
} | |||
public MutableLiveData<StatusResource<String>> getConfig(Context context){ | |||
return new NetworkBoundStatusResource<String>(){ | |||
public MutableLiveData<StatusResource<String>> getConfig(Context context) { | |||
return new NetworkBoundStatusResource<String>() { | |||
@Override protected void createCall() { | |||
NetworkBoundStatusResource<String> liveData = this; | |||
final HashMap<String, String> header = new HashMap<>(); | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, token); | |||
final Disposable configDisposable = clientApi.getConfig(deviceID,header) | |||
header.put(ApiConstants.AUTHORIZATION_HEADER, UserStore.getInstance(context).getToken()); | |||
buildClientService(BASE_URL); | |||
final Disposable configDisposable = clientApi.getConfig(deviceID, header) | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(dataConfig->{ | |||
.subscribe(dataConfig -> { | |||
final InputStream inputStream = dataConfig.byteStream(); | |||
final Scanner scanner = new Scanner(inputStream).useDelimiter(DELIMITER); | |||
final String data = scanner.hasNext() ? scanner.next() : ""; | |||
postMutableLiveData(StatusResource.success(data)); | |||
},throwable -> { | |||
setErrorMessage(throwable,this); | |||
}, throwable -> { | |||
Log.d("ERR", "getConfig"); | |||
setErrorMessage(throwable, this); | |||
}); | |||
compositeDisposable.add(configDisposable); | |||
} | |||
}.getMutableLiveData(); | |||
} | |||
public MutableLiveData<StatusResource<Object>> createTunnel(Context context, String dataConfig){ | |||
return new NetworkBoundStatusResource<Object>(){ | |||
public MutableLiveData<StatusResource<Object>> createTunnel(Context context, String dataConfig) { | |||
return new NetworkBoundStatusResource<Object>() { | |||
@Override protected void createCall() { | |||
try { | |||
@@ -412,14 +505,17 @@ public class DataRepository { | |||
if (observableTunnel != null) { | |||
TunnelStore.getInstance(context).setTunnel(TUNNEL_NAME, dataConfig); | |||
Application.getTunnelManager().setTunnelState(observableTunnel, State.DOWN); | |||
UserStore.getInstance(context).setToken(token); | |||
UserStore.getInstance(context).setToken(UserStore.getInstance(context).getToken()); | |||
UserStore.getInstance(context).setDevice(deviceName, deviceID); | |||
setHostName(context, ApiConstants.BASE_URL); | |||
postMutableLiveData(StatusResource.success(null)); | |||
} else { | |||
Log.d("ERR", "createTunnel11"); | |||
setErrorMessage(throwable, this); | |||
} | |||
}); | |||
} catch (Exception e) { | |||
Log.d("ERR", "createTunnel12"); | |||
setErrorMessage(e, this); | |||
} | |||
} | |||
@@ -463,7 +559,7 @@ public class DataRepository { | |||
} | |||
public void clearDisposable() { | |||
if(compositeDisposable!=null) { | |||
if (compositeDisposable != null) { | |||
compositeDisposable.clear(); | |||
} | |||
} | |||
@@ -496,10 +592,6 @@ public class DataRepository { | |||
return tunnel; | |||
} | |||
public void setUserURL(Context context, String url) { | |||
UserStore.getInstance(context).setUserURL(url); | |||
} | |||
public ObservableTunnel getTunnel(Context context, boolean connectionStateFlag) { | |||
ObservableTunnel tunnel = Application.getTunnelManager().getLastUsedTunnel(); | |||
if (tunnel == null) { | |||
@@ -509,18 +601,18 @@ public class DataRepository { | |||
return tunnel; | |||
} | |||
public List<String> getAllCertificates(final Context context){ | |||
public List<String> getAllCertificates(final Context context) { | |||
final List<String> certificates = new ArrayList<>(); | |||
try { | |||
final KeyStore keyStore = KeyStore.getInstance("AndroidCAStore"); | |||
if(keyStore!=null){ | |||
keyStore.load(null,null); | |||
if (keyStore != null) { | |||
keyStore.load(null, null); | |||
final Enumeration<String> aliases = keyStore.aliases(); | |||
while (aliases.hasMoreElements()){ | |||
while (aliases.hasMoreElements()) { | |||
final String alias = aliases.nextElement(); | |||
final java.security.cert.X509Certificate certificate = (java.security.cert.X509Certificate)keyStore.getCertificate(alias); | |||
certificates.add(certificate.getIssuerDN().getName()); | |||
final java.security.cert.X509Certificate certificate = (java.security.cert.X509Certificate) keyStore.getCertificate(alias); | |||
certificates.add(certificate.getIssuerDN().getName()); | |||
} | |||
} | |||
} catch (final KeyStoreException | NoSuchAlgorithmException | java.security.cert.CertificateException | IOException e) { | |||
@@ -532,40 +624,43 @@ public class DataRepository { | |||
private MutableLiveData<StatusResource<byte[]>> getCertificate(Context context) { | |||
return new NetworkBoundStatusResource<byte[]>() { | |||
@Override protected void createCall() { | |||
final Disposable certificateDisposable = clientApi.getCertificate() | |||
final Disposable certificateDisposable = clientApi.getCertificate() | |||
.subscribeOn(Schedulers.newThread()) | |||
.observeOn(AndroidSchedulers.mainThread()) | |||
.subscribe(certificate->{ | |||
.subscribe(certificate -> { | |||
final InputStream inputStream = certificate.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) { | |||
setErrorMessage(e, this); | |||
} | |||
try { | |||
if (x509Certificate != null) { | |||
String name = x509Certificate.getSubjectDN().getName(); | |||
postMutableLiveData(StatusResource.success(x509Certificate.getEncoded())); | |||
} | |||
} catch (final CertificateEncodingException e) { | |||
setErrorMessage(e, this); | |||
} | |||
},throwable -> { | |||
setErrorMessage(throwable,this); | |||
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) { | |||
Log.d("ERR", "getCertificate12"); | |||
setErrorMessage(e, this); | |||
} | |||
try { | |||
if (x509Certificate != null) { | |||
String name = x509Certificate.getSubjectDN().getName(); | |||
postMutableLiveData(StatusResource.success(x509Certificate.getEncoded())); | |||
} | |||
} catch (final CertificateEncodingException e) { | |||
Log.d("ERR", "getCertificate13"); | |||
setErrorMessage(e, this); | |||
} | |||
}, throwable -> { | |||
Log.d("ERR", "getCertificate14"); | |||
setErrorMessage(throwable, this); | |||
}); | |||
compositeDisposable.add(certificateDisposable); | |||
compositeDisposable.add(certificateDisposable); | |||
} | |||
}.getMutableLiveData(); | |||
} | |||
public boolean isHaveCertificate(Context context){ | |||
public boolean isHaveCertificate(Context context) { | |||
final List<String> certificates = getAllCertificates(context); | |||
for (final String certificate:certificates) { | |||
if(certificate.contains("."+hostname)) { | |||
for (final String certificate : certificates) { | |||
if (certificate.contains("." + hostname)) { | |||
return true; | |||
} | |||
} | |||
@@ -577,20 +672,20 @@ public class DataRepository { | |||
return pendingTunnel.getState() == State.DOWN; | |||
} | |||
public MutableLiveData<Boolean> connect(final Boolean checked , Context context) { | |||
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); | |||
if (context instanceof MainActivity) { | |||
((MainActivity) context).startActivityForResult(intent, REQUEST_CODE_VPN_PERMISSION); | |||
return; | |||
} | |||
} | |||
} | |||
connectWithPermission(checked,context).observe((LifecycleOwner) context, new Observer<Boolean>() { | |||
connectWithPermission(checked, context).observe((LifecycleOwner) context, new Observer<Boolean>() { | |||
@Override public void onChanged(final Boolean aBoolean) { | |||
liveData.postValue(aBoolean); | |||
} | |||
@@ -600,55 +695,53 @@ public class DataRepository { | |||
return liveData; | |||
} | |||
public MutableLiveData<Boolean> connectWithPermission(final boolean checked , Context context) { | |||
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) { | |||
pendingTunnel.setStateAsync(Tunnel.State.of(checked)).whenComplete((observableTunnel, throwable) -> { | |||
if (throwable == null) { | |||
if (observableTunnel == State.DOWN) { | |||
liveData.postValue(false); | |||
} | |||
else { | |||
} else { | |||
liveData.postValue(true); | |||
} | |||
} | |||
else { | |||
Toast.makeText(context,context.getString(R.string.failed_bubble),Toast.LENGTH_SHORT).show(); | |||
} else { | |||
Toast.makeText(context, context.getString(R.string.failed_bubble), Toast.LENGTH_SHORT).show(); | |||
} | |||
}); | |||
return liveData; | |||
} | |||
public void setHostName(Context context, String hostname){ | |||
public void setHostName(Context context, String hostname) { | |||
UserStore.getInstance(context).setHostname(hostname); | |||
} | |||
public String getHostname(Context context){ | |||
public String getHostname(Context context) { | |||
return UserStore.getInstance(context).getHostname(); | |||
} | |||
private <T> void setErrorMessage(Throwable throwable , NetworkBoundStatusResource<T> liveData){ | |||
if (throwable instanceof IOException) { | |||
liveData.postMutableLiveData(StatusResource.error(NO_INTERNET_CONNECTION)); | |||
} | |||
if (throwable instanceof HttpException) { | |||
if (((HttpException) throwable).code() == 500) { | |||
final String requestURL = ((HttpException) throwable).response().raw().request().url().toString(); | |||
final String requestMethod = ((HttpException) throwable).response().raw().request().method(); | |||
final String requestBody = bodyToString(((HttpException) throwable).response().raw().request()); | |||
final String stackTrace = Arrays.toString(throwable.getStackTrace()); | |||
final String message = "URL:" + requestURL + '\n' + | |||
"BODY:" + requestBody + '\n' + | |||
"METHOD:" + requestMethod + '\n' + | |||
"STACK_TRACE:" + stackTrace; | |||
liveData.postMutableLiveData(StatusResource.error(message)); | |||
} else { | |||
liveData.postMutableLiveData(StatusResource.error(LOGIN_FAILED)); | |||
} | |||
private <T> void setErrorMessage(Throwable throwable, NetworkBoundStatusResource<T> liveData) { | |||
if (throwable instanceof IOException) { | |||
liveData.postMutableLiveData(StatusResource.error(NO_INTERNET_CONNECTION)); | |||
} | |||
if (throwable instanceof HttpException) { | |||
if (((HttpException) throwable).code() == 500) { | |||
final String requestURL = ((HttpException) throwable).response().raw().request().url().toString(); | |||
final String requestMethod = ((HttpException) throwable).response().raw().request().method(); | |||
final String requestBody = bodyToString(((HttpException) throwable).response().raw().request()); | |||
final String stackTrace = Arrays.toString(throwable.getStackTrace()); | |||
final String message = "URL:" + requestURL + '\n' + | |||
"BODY:" + requestBody + '\n' + | |||
"METHOD:" + requestMethod + '\n' + | |||
"STACK_TRACE:" + stackTrace; | |||
liveData.postMutableLiveData(StatusResource.error(message)); | |||
} else { | |||
liveData.postMutableLiveData(StatusResource.error(LOGIN_FAILED)); | |||
} | |||
} | |||
} | |||
private String bodyToString(final Request request){ | |||
private String bodyToString(final Request request) { | |||
try { | |||
final Request copy = request.newBuilder().build(); | |||
final Buffer buffer = new Buffer(); | |||
@@ -659,18 +752,48 @@ public class DataRepository { | |||
} | |||
} | |||
public void removeSharedPreferences(Context context){ | |||
context.getSharedPreferences(UserStore.USER_SHARED_PREF,0).edit().clear().apply(); | |||
context.getSharedPreferences(TunnelStore.TUNNEL_SHARED_PREF,0).edit().clear().apply(); | |||
public void removeSharedPreferences(Context context) { | |||
context.getSharedPreferences(UserStore.USER_SHARED_PREF, 0).edit().clear().apply(); | |||
context.getSharedPreferences(TunnelStore.TUNNEL_SHARED_PREF, 0).edit().clear().apply(); | |||
} | |||
public void deleteTunnel(Context context){ | |||
public void deleteTunnel(Context context) { | |||
ArrayList<ObservableTunnel> tunnels = new ArrayList<>(); | |||
tunnels.add(pendingTunnel); | |||
if(pendingTunnel!=null) { | |||
tunnels.add(pendingTunnel); | |||
// UtilKt.deleteTunnel(tunnels); | |||
Application.getTunnelManager().delete(pendingTunnel); | |||
Application.getTunnelManager().delete(pendingTunnel); | |||
} | |||
// ArrayList<ObservableTunnel> tunnels = new ArrayList<>(); | |||
// tunnels.add(tunnelManager.getLastUsedTunnel()); | |||
// UtilKt.deleteTunnel(tunnels); | |||
} | |||
public MutableLiveData<StatusResource<Boolean>> isHaveTunnel(Context context) { | |||
return new NetworkBoundStatusResource<Boolean>(){ | |||
@Override protected void createCall() { | |||
final byte[] configBytes = UserStore.getInstance(context).getConfig().getBytes(); | |||
Config config = null; | |||
try { | |||
config = Config.parse(new ByteArrayInputStream(configBytes)); | |||
} catch (IOException ex) { | |||
ex.printStackTrace(); | |||
} catch (BadConfigException ex) { | |||
ex.printStackTrace(); | |||
} | |||
Application.getTunnelManager().create(TUNNEL_NAME, config).whenComplete((observableTunnel, throwable) -> { | |||
if (observableTunnel != null) { | |||
TunnelStore.getInstance(context).setTunnel(TUNNEL_NAME, UserStore.getInstance(context).getConfig()); | |||
Application.getTunnelManager().setTunnelState(observableTunnel, State.DOWN); | |||
UserStore.getInstance(context).setToken(UserStore.getInstance(context).getToken()); | |||
UserStore.getInstance(context).setDevice(deviceName, deviceID); | |||
setHostName(context, ApiConstants.BASE_URL); | |||
postMutableLiveData(StatusResource.success(false)); | |||
} else { | |||
postMutableLiveData(StatusResource.success(true)); | |||
} | |||
}); | |||
} | |||
}.getMutableLiveData(); | |||
} | |||
} |
@@ -11,13 +11,25 @@ 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"; | |||
private static final String HOSTNAME_KEY = "com.wireguard.android.util.bubbleHostnameResponse"; | |||
private static final String USERNAME_KEY = "com.wireguard.android.util.bubbleUSERNAMEResponse"; | |||
private static final String PASSWORD_KEY = "com.wireguard.android.util.bubblePASSWORDResponse"; | |||
private static final String SAGE_TOKEN_KEY = "com.wireguard.android.util.bubbleSageTokenResponse"; | |||
private static final String CONFIG_KEY = "com.wireguard.android.util.bubbleConfigResponse"; | |||
private static final String SAGE_HOSTNAME_KEY = "com.wireguard.android.util.bubbleSageHostNameResponse"; | |||
private static final String SAGE_KEY = "com.wireguard.android.util.bubbleSageResponse"; | |||
private static final String HOSTNAME_FOR_ACCOUNT_KEY = "com.wireguard.android.util.bubbleHostNameForAccountResponse"; | |||
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 = ""; | |||
private static final String HOSTNAME_DEFAULT_VALUE = ""; | |||
public static final String USERNAME_DEFAULT_VALUE = ""; | |||
private static final String PASSWORD_DEFAULT_VALUE = ""; | |||
private static final String SAGE_TOKEN_DEFAULT_VALUE = ""; | |||
private static final String CONFIG_DEFAULT_VALUE = ""; | |||
private static final String SAGE_HOSTNAME_DEFAULT_VALUE = ""; | |||
private static final String SAGE_DEFAULT_VALUE = ""; | |||
private static final String HOSTNAME_FOR_ACCOUNT_DEFAULT_VALUE = ""; | |||
public static UserStore getInstance(Context context) { | |||
if (instance == null) { | |||
@@ -56,14 +68,6 @@ public class UserStore { | |||
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); | |||
} | |||
public void setHostname(String hostName){ | |||
sharedPreferences.edit().putString(HOSTNAME_KEY,hostName).apply(); | |||
} | |||
@@ -71,4 +75,57 @@ public class UserStore { | |||
public String getHostname(){ | |||
return sharedPreferences.getString(HOSTNAME_KEY,HOSTNAME_DEFAULT_VALUE); | |||
} | |||
public void setUserData(String username , String password){ | |||
sharedPreferences.edit().putString(USERNAME_KEY, username).apply(); | |||
sharedPreferences.edit().putString(PASSWORD_KEY,password).apply(); | |||
} | |||
public String getUsername(){ | |||
return sharedPreferences.getString(USERNAME_KEY,USERNAME_DEFAULT_VALUE); | |||
} | |||
public String getPassword(){ | |||
return sharedPreferences.getString(PASSWORD_KEY,PASSWORD_DEFAULT_VALUE); | |||
} | |||
public void setSageURL(String sage){ | |||
sharedPreferences.edit().putString(SAGE_KEY, sage).apply(); | |||
} | |||
public String getSageURL(){ | |||
return sharedPreferences.getString(SAGE_KEY,SAGE_DEFAULT_VALUE); | |||
} | |||
public void setConfig(String config){ | |||
sharedPreferences.edit().putString(CONFIG_KEY, config).apply(); | |||
} | |||
public String getConfig(){ | |||
return sharedPreferences.getString(CONFIG_KEY,CONFIG_DEFAULT_VALUE); | |||
} | |||
public void setSageHostname(String sageHostname){ | |||
sharedPreferences.edit().putString(SAGE_HOSTNAME_KEY, sageHostname).apply(); | |||
} | |||
public String getSageHostname(){ | |||
return sharedPreferences.getString(SAGE_HOSTNAME_KEY,SAGE_HOSTNAME_DEFAULT_VALUE); | |||
} | |||
public void setSageToken(String sageToken){ | |||
sharedPreferences.edit().putString(SAGE_TOKEN_KEY, sageToken).apply(); | |||
} | |||
public String getSageToken(){ | |||
return sharedPreferences.getString(SAGE_TOKEN_KEY,SAGE_TOKEN_DEFAULT_VALUE); | |||
} | |||
public void setHostNameForAccount(String hostNameForAccount){ | |||
sharedPreferences.edit().putString(HOSTNAME_FOR_ACCOUNT_KEY, hostNameForAccount).apply(); | |||
} | |||
public String getHostNameForAccount(){ | |||
return sharedPreferences.getString(HOSTNAME_FOR_ACCOUNT_KEY,HOSTNAME_FOR_ACCOUNT_DEFAULT_VALUE); | |||
} | |||
} |
@@ -22,10 +22,6 @@ public class LoginViewModel extends ViewModel { | |||
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); | |||
} | |||
@@ -49,4 +45,8 @@ public class LoginViewModel extends ViewModel { | |||
public boolean isHaveCertificate(Context context){ | |||
return DataRepository.getRepositoryInstance().isHaveCertificate(context); | |||
} | |||
public boolean checkRepositoryInstance(){ | |||
return DataRepository.getRepositoryInstance() == null; | |||
} | |||
} |
@@ -2,11 +2,10 @@ package com.getbubblenow.android.viewmodel; | |||
import android.content.Context; | |||
import com.getbubblenow.android.model.Network; | |||
import com.getbubblenow.android.repository.DataRepository; | |||
import com.getbubblenow.android.model.ObservableTunnel; | |||
import com.getbubblenow.android.model.TunnelManager; | |||
import com.getbubblenow.android.repository.DataRepository; | |||
import com.getbubblenow.android.util.TunnelStore; | |||
import com.getbubblenow.android.resource.StatusResource; | |||
import com.getbubblenow.android.util.UserStore; | |||
import androidx.lifecycle.MutableLiveData; | |||
@@ -25,10 +24,6 @@ public class MainViewModel extends ViewModel { | |||
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); | |||
} | |||
@@ -56,4 +51,68 @@ public class MainViewModel extends ViewModel { | |||
public String getToken(Context context) { | |||
return UserStore.getInstance(context).getToken(); | |||
} | |||
public void buildClientService(String url){ | |||
DataRepository.getRepositoryInstance().buildClientService(url); | |||
} | |||
public MutableLiveData<StatusResource<String>> getConfig(Context context){ | |||
return DataRepository.getRepositoryInstance().getConfig(context); | |||
} | |||
public MutableLiveData<StatusResource<Object>> createTunnel(Context context, String dataConfig){ | |||
return DataRepository.getRepositoryInstance().createTunnel(context, dataConfig); | |||
} | |||
public boolean isHaveCertificate(Context context) { | |||
return DataRepository.getRepositoryInstance().isHaveCertificate(context); | |||
} | |||
public boolean isHaveSageURL(Context context){ | |||
return !UserStore.getInstance(context).getSageURL().isEmpty(); | |||
} | |||
public boolean isHaveHostName(Context context){ | |||
return !UserStore.getInstance(context).getHostname().isEmpty(); | |||
} | |||
public String getSageURL(Context context){ | |||
return UserStore.getInstance(context).getSageURL(); | |||
} | |||
public void setConfig(String config, Context context){ | |||
UserStore.getInstance(context).setConfig(config); | |||
} | |||
public MutableLiveData<StatusResource<byte[]>> getCertificateData(Context context) { | |||
return DataRepository.getRepositoryInstance().getCertificateData(context); | |||
} | |||
public MutableLiveData<StatusResource<Network>> getNetworkStatusLiveData(){ | |||
return DataRepository.getRepositoryInstance().getNetworkStatusLiveData(); | |||
} | |||
public MutableLiveData<Long> getProgressLiveData(){ | |||
return DataRepository.getRepositoryInstance().getProgressLiveData(); | |||
} | |||
public MutableLiveData<StatusResource<Boolean>> isHaveTunnel(Context context) { | |||
return DataRepository.getRepositoryInstance().isHaveTunnel(context); | |||
} | |||
public String getSageHostname(Context context){ | |||
return UserStore.getInstance(context).getSageHostname(); | |||
} | |||
public boolean checkRepositoryInstance(){ | |||
return DataRepository.getRepositoryInstance() == null; | |||
} | |||
public String getSageToken(Context context){ | |||
return UserStore.getInstance(context).getSageToken(); | |||
} | |||
public String getHostNameForAccount(Context context){ | |||
return UserStore.getInstance(context).getHostNameForAccount(); | |||
} | |||
} |
@@ -20,158 +20,311 @@ | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" /> | |||
<TextView | |||
android:id="@+id/bubbleConnectionTitle" | |||
<androidx.constraintlayout.widget.ConstraintLayout | |||
android:id="@+id/constraint_layout_connected_title" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="@dimen/bubble_connection_marginTop" | |||
android:layout_marginTop="@dimen/margin_top" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:fontFamily="@font/josefin_sans" | |||
android:letterSpacing="-0.02" | |||
android:text="@string/my_bubble" | |||
android:textColor="@color/black" | |||
android:textSize="@dimen/bubble_connection_title_text_size" | |||
android:textStyle="bold" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.498" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
app:layout_constraintTop_toTopOf="parent"> | |||
<TextView | |||
android:id="@+id/bubbleConnectionTitle" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="@dimen/bubble_connection_marginTop" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:fontFamily="@font/josefin_sans" | |||
android:letterSpacing="-0.02" | |||
android:text="@string/my_bubble" | |||
android:textColor="@color/black" | |||
android:textSize="@dimen/bubble_connection_title_text_size" | |||
android:textStyle="bold" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.498" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
<TextView | |||
android:id="@+id/title" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:text="@string/your_private_bubble" | |||
android:textAlignment="center" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/bubbleConnectionTitle" /> | |||
<TextView | |||
android:id="@+id/logout" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="8dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:enabled="false" | |||
android:text="@string/log_out" | |||
android:textColor="@color/logout" | |||
android:textSize="@dimen/text_title" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/title" /> | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
<TextView | |||
android:id="@+id/title" | |||
<androidx.constraintlayout.widget.ConstraintLayout | |||
android:id="@+id/constraints_layout_connected_bubble" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:text="@string/your_private_bubble" | |||
android:textAlignment="center" | |||
android:layout_marginStart="16dp" | |||
android:layout_marginEnd="16dp" | |||
app:layout_constraintTop_toTopOf="@id/constraints_middle" | |||
app:layout_constraintBottom_toTopOf="@+id/constraints_middle" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/bubbleConnectionTitle" /> | |||
app:layout_constraintStart_toStartOf="parent"> | |||
<ImageView | |||
android:id="@+id/imageMyBubble" | |||
android:layout_width="285.56dp" | |||
android:layout_height="378.72dp" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:layout_marginBottom="120dp" | |||
android:src="@drawable/bubble_disconnected" | |||
app:layout_constraintBottom_toTopOf="@+id/linearLayout" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="@+id/constraints" /> | |||
<TextView | |||
android:id="@+id/logout" | |||
<TextView | |||
android:id="@+id/titleMyBubble" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginBottom="45dp" | |||
android:letterSpacing="-0.05" | |||
android:text="@string/bubble_status" | |||
android:textColor="@android:color/black" | |||
android:textSize="@dimen/myBubble_text_size" | |||
android:textStyle="bold" | |||
app:layout_constraintBottom_toTopOf="@+id/constraints" | |||
app:layout_constraintEnd_toEndOf="@+id/imageMyBubble" | |||
app:layout_constraintStart_toStartOf="@+id/imageMyBubble" /> | |||
<ImageView | |||
android:id="@+id/mark" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="80dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:src="@drawable/mark" | |||
android:visibility="gone" | |||
app:layout_constraintEnd_toEndOf="@+id/imageMyBubble" | |||
app:layout_constraintStart_toStartOf="@+id/imageMyBubble" | |||
app:layout_constraintTop_toTopOf="@+id/imageMyBubble" /> | |||
<TextView | |||
android:id="@+id/bubbleStatus" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="150dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:layout_marginBottom="120dp" | |||
android:text="@string/not_connected_bubble" | |||
android:textColor="@color/gray" | |||
android:textSize="12sp" | |||
app:layout_constraintBottom_toTopOf="@+id/linearLayout" | |||
app:layout_constraintEnd_toEndOf="@+id/imageMyBubble" | |||
app:layout_constraintStart_toStartOf="@+id/imageMyBubble" | |||
app:layout_constraintTop_toTopOf="@+id/imageMyBubble" /> | |||
<LinearLayout | |||
android:id="@+id/linearLayout" | |||
android:layout_width="@dimen/edit_text_width" | |||
android:layout_height="@dimen/edit_text_height" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="100dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:background="@android:color/transparent" | |||
android:orientation="vertical" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/constraints"> | |||
<androidx.appcompat.widget.AppCompatButton | |||
android:id="@+id/connectButton" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/sign_in_enable" | |||
android:enabled="false" | |||
android:text="@string/connect" | |||
android:textAlignment="center" | |||
android:textAllCaps="false" | |||
android:textColor="@color/white" | |||
android:textSize="@dimen/text_title" /> | |||
</LinearLayout> | |||
<androidx.constraintlayout.widget.Constraints | |||
android:id="@+id/constraints" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:layout_marginBottom="@dimen/margin_start_and_end" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
<androidx.constraintlayout.widget.ConstraintLayout | |||
android:id="@+id/constraint_layout_disconnected_title" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:visibility="gone" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="8dp" | |||
android:layout_marginTop="@dimen/margin_top" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:text="@string/log_out" | |||
android:textColor="@color/logout" | |||
android:textSize="@dimen/text_title" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/title" /> | |||
app:layout_constraintTop_toTopOf="parent" | |||
> | |||
<ImageView | |||
android:id="@+id/imageMyBubble" | |||
android:layout_width="285.56dp" | |||
android:layout_height="378.72dp" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:layout_marginBottom="120dp" | |||
android:src="@drawable/bubble_disconnected" | |||
app:layout_constraintBottom_toTopOf="@+id/linearLayout" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="@+id/constraints" /> | |||
<TextView | |||
android:id="@+id/bubble_building_title" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="@dimen/bubble_connection_marginTop" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:fontFamily="@font/lato" | |||
android:letterSpacing="-0.02" | |||
android:text="@string/building_bubble_title" | |||
android:textAlignment="center" | |||
android:textColor="@color/black" | |||
android:textSize="@dimen/title" | |||
android:textStyle="bold" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.498" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
<TextView | |||
android:id="@+id/titleMyBubble" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginBottom="45dp" | |||
android:letterSpacing="-0.05" | |||
android:text="@string/bubble_status" | |||
android:textColor="@android:color/black" | |||
android:textSize="@dimen/myBubble_text_size" | |||
android:textStyle="bold" | |||
app:layout_constraintBottom_toTopOf="@+id/constraints" | |||
app:layout_constraintEnd_toEndOf="@+id/imageMyBubble" | |||
app:layout_constraintStart_toStartOf="@+id/imageMyBubble" /> | |||
<TextView | |||
android:id="@+id/private_bubble" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="40dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:fontFamily="@font/lato" | |||
android:letterSpacing="-0.02" | |||
android:text="@string/private_bubble" | |||
android:textAlignment="center" | |||
android:textColor="@color/black" | |||
android:textSize="@dimen/title" | |||
android:textStyle="bold" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.498" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="@id/bubble_building_title" /> | |||
<ImageView | |||
android:id="@+id/mark" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="80dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:src="@drawable/mark" | |||
android:visibility="gone" | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
app:layout_constraintEnd_toEndOf="@+id/imageMyBubble" | |||
app:layout_constraintStart_toStartOf="@+id/imageMyBubble" | |||
app:layout_constraintTop_toTopOf="@+id/imageMyBubble" /> | |||
<TextView | |||
android:id="@+id/bubbleStatus" | |||
<androidx.constraintlayout.widget.ConstraintLayout | |||
android:id="@+id/constraints_layout_disconnected_bubble" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="150dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:layout_marginBottom="120dp" | |||
android:text="@string/not_connected_bubble" | |||
android:textColor="@color/gray" | |||
android:textSize="12sp" | |||
app:layout_constraintBottom_toTopOf="@+id/linearLayout" | |||
app:layout_constraintEnd_toEndOf="@+id/imageMyBubble" | |||
app:layout_constraintStart_toStartOf="@+id/imageMyBubble" | |||
app:layout_constraintTop_toTopOf="@+id/imageMyBubble" /> | |||
<LinearLayout | |||
android:id="@+id/linearLayout" | |||
android:layout_width="@dimen/edit_text_width" | |||
android:layout_height="@dimen/edit_text_height" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="100dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:background="@android:color/transparent" | |||
android:orientation="vertical" | |||
android:visibility="gone" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toBottomOf="@+id/constraints"> | |||
<androidx.appcompat.widget.AppCompatButton | |||
android:id="@+id/connectButton" | |||
android:layout_width="match_parent" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/sign_in_enable" | |||
android:enabled="true" | |||
android:text="@string/connect" | |||
android:textAlignment="center" | |||
android:textAllCaps="false" | |||
android:textColor="@color/white" | |||
android:textSize="@dimen/text_title" /> | |||
</LinearLayout> | |||
app:layout_constraintTop_toTopOf="@id/constraints_middle" | |||
app:layout_constraintBottom_toTopOf="@+id/constraints_middle" | |||
> | |||
<com.airbnb.lottie.LottieAnimationView | |||
android:id="@+id/network_progress" | |||
android:layout_width="300dp" | |||
android:layout_height="300dp" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:background="@android:color/transparent" | |||
android:backgroundTint="@android:color/transparent" | |||
android:foregroundGravity="center_horizontal" | |||
app:layout_constraintBottom_toBottomOf="@+id/progress_constraints" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="@+id/progress_constraints" | |||
app:lottie_rawRes="@raw/progress_loading" /> | |||
<TextView | |||
android:id="@+id/progress_title" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_gravity="center_horizontal" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="250dp" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:fontFamily="@font/lato" | |||
android:paddingTop="10dp" | |||
android:textColor="@android:color/black" | |||
android:textStyle="bold" | |||
android:textSize="@dimen/myBubble_text_size" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="@+id/network_progress" /> | |||
<androidx.constraintlayout.widget.Constraints | |||
android:id="@+id/progress_constraints" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginTop="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:layout_marginBottom="@dimen/margin_start_and_end" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" /> | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
<ImageButton | |||
android:id="@+id/myBubbleButton" | |||
android:id="@+id/accountButton" | |||
android:layout_width="@dimen/icons_size" | |||
android:layout_height="@dimen/icons_size" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginBottom="44dp" | |||
android:background="@drawable/my_bubble" | |||
android:background="@drawable/account" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toStartOf="@+id/accountButton" | |||
app:layout_constraintEnd_toStartOf="@+id/supportButton" | |||
app:layout_constraintHorizontal_bias="0.5" | |||
app:layout_constraintStart_toStartOf="parent" /> | |||
<ImageButton | |||
android:id="@+id/accountButton" | |||
android:id="@+id/supportButton" | |||
android:layout_width="@dimen/icons_size" | |||
android:layout_height="@dimen/icons_size" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
android:layout_marginEnd="@dimen/margin_start_and_end" | |||
android:background="@drawable/account" | |||
app:layout_constraintBottom_toBottomOf="@+id/myBubbleButton" | |||
android:background="@drawable/support" | |||
app:layout_constraintBottom_toBottomOf="@+id/accountButton" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintHorizontal_bias="0.5" | |||
app:layout_constraintStart_toEndOf="@+id/myBubbleButton" | |||
app:layout_constraintTop_toTopOf="@+id/myBubbleButton" /> | |||
app:layout_constraintStart_toEndOf="@+id/accountButton" | |||
app:layout_constraintTop_toTopOf="@+id/accountButton" /> | |||
<ImageButton | |||
android:id="@+id/imageButton" | |||
@@ -180,12 +333,12 @@ | |||
android:layout_marginTop="32dp" | |||
android:background="@drawable/home" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toStartOf="@+id/accountButton" | |||
app:layout_constraintStart_toEndOf="@+id/myBubbleButton" | |||
app:layout_constraintTop_toTopOf="@+id/myBubbleButton" /> | |||
app:layout_constraintEnd_toStartOf="@+id/supportButton" | |||
app:layout_constraintStart_toEndOf="@+id/accountButton" | |||
app:layout_constraintTop_toTopOf="@+id/accountButton" /> | |||
<androidx.constraintlayout.widget.Constraints | |||
android:id="@+id/constraints" | |||
android:id="@+id/constraints_middle" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
android:layout_marginStart="@dimen/margin_start_and_end" | |||
@@ -44,4 +44,5 @@ | |||
<color name="sign_in_enable_end_color">#C037C0</color> | |||
<color name="toast_color">#4E4E4E</color> | |||
<color name="logout">#159F97</color> | |||
<color name="background_progress">#F7F8FC</color> | |||
</resources> |
@@ -262,4 +262,6 @@ | |||
<string name="log_out">Log out</string> | |||
<string name="bubble_app_name">Bubble</string> | |||
<string name="empty_message">"Email and password should not be empty"</string> | |||
<string name="building_bubble_title">We Are Building</string> | |||
<string name="private_bubble">Your Private Bubble!</string> | |||
</resources> |