소스 검색

Added launcher button

Fixed disposing data on logout
dev
Denys Podymskyy 4 년 전
부모
커밋
347d820c4c
10개의 변경된 파일302개의 추가작업 그리고 13개의 파일을 삭제
  1. +2
    -0
      ui/src/main/AndroidManifest.xml
  2. +8
    -0
      ui/src/main/java/com/getbubblenow/android/activity/LoginActivity.java
  3. +6
    -0
      ui/src/main/java/com/getbubblenow/android/activity/MainActivity.java
  4. +98
    -0
      ui/src/main/java/com/getbubblenow/android/activity/SetLauncherActivity.java
  5. +42
    -12
      ui/src/main/java/com/getbubblenow/android/repository/DataRepository.java
  6. +8
    -1
      ui/src/main/java/com/getbubblenow/android/viewmodel/MainViewModel.java
  7. +13
    -0
      ui/src/main/java/com/getbubblenow/android/viewmodel/SetLauncherViewModel.java
  8. +12
    -0
      ui/src/main/res/layout/activity_login.xml
  9. +110
    -0
      ui/src/main/res/layout/activity_login_sage.xml
  10. +3
    -0
      ui/src/main/res/values/strings.xml

+ 2
- 0
ui/src/main/AndroidManifest.xml 파일 보기

@@ -46,6 +46,8 @@
</activity>
<activity android:name=".activity.LoginActivity"
android:screenOrientation="portrait"/>
<activity android:name=".activity.SetLauncherActivity"
android:screenOrientation="portrait"/>
<activity
android:name=".activity.MFAVerifyActivity"
android:screenOrientation="portrait" />


+ 8
- 0
ui/src/main/java/com/getbubblenow/android/activity/LoginActivity.java 파일 보기

@@ -37,6 +37,7 @@ public class LoginActivity extends BaseActivityBubble {
private EditText password;
private AppCompatButton sign;
private TextView signUpButton;
private TextView setLauncherButton;

private String resource;
private boolean userNameStateFlag;
@@ -69,6 +70,12 @@ public class LoginActivity extends BaseActivityBubble {
passwordStateListener();
sendListener();
signUpButton.setOnClickListener(v -> signUp());
setLauncherButton.setOnClickListener(v -> setLauncher());
}

private void setLauncher() {
final Intent intent = new Intent(this, SetLauncherActivity.class);
startActivity(intent);
}

private void signUp() {
@@ -106,6 +113,7 @@ public class LoginActivity extends BaseActivityBubble {
password = findViewById(R.id.password);
sign = findViewById(R.id.signButton);
signUpButton = findViewById(R.id.signUpButton);
setLauncherButton = findViewById(R.id.setLauncherButton);
}




+ 6
- 0
ui/src/main/java/com/getbubblenow/android/activity/MainActivity.java 파일 보기

@@ -103,6 +103,11 @@ public class MainActivity extends BaseActivityBubble {
});
}

@Override protected void onStop() {
super.onStop();
mainViewModel.disposeProgress();
}

private void startBubbleSession() {
if (mainViewModel.isHaveSageURL(this)) {
if (mainViewModel.isHaveHostName(this)) {
@@ -463,6 +468,7 @@ public class MainActivity extends BaseActivityBubble {
mainViewModel.deleteTunnel();
mainViewModel.removeSharedPreferences(this);
mainViewModel.clearCredentials(getApplicationContext());
mainViewModel.disposeBubbleCheck();
startActivity(intent);
}



+ 98
- 0
ui/src/main/java/com/getbubblenow/android/activity/SetLauncherActivity.java 파일 보기

@@ -0,0 +1,98 @@
package com.getbubblenow.android.activity;

import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;

import com.getbubblenow.android.R;
import com.getbubblenow.android.viewmodel.SetLauncherViewModel;
import com.jakewharton.rxbinding4.widget.RxTextView;

import androidx.appcompat.widget.AppCompatButton;
import androidx.lifecycle.ViewModelProvider;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;


public class SetLauncherActivity extends BaseActivityBubble {

private static final String PREFIX = "https://";

private SetLauncherViewModel setLauncherViewModel;
private EditText launcherUrl;
private AppCompatButton setLauncherButton;

private final CompositeDisposable compositeSubscription = new CompositeDisposable();


@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_sage);
initUI();
setLauncherViewModel = new ViewModelProvider(this).get(SetLauncherViewModel.class);
}

@Override protected void onDestroy() {
super.onDestroy();
compositeSubscription.dispose();
}

private void initUI() {
initViews();
initListeners();
}

private void initListeners() {
setLauncherButton.setOnClickListener(v -> setLauncher());
sendListener();
launcherUrlStateListener();
}

private void initViews() {
launcherUrl = findViewById(R.id.launcher_url);
setLauncherButton = findViewById(R.id.setLauncherButton);
}

private void sendListener() {
launcherUrl.setOnEditorActionListener((v, actionId, event) -> {
if ((event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP)
|| (actionId == EditorInfo.IME_ACTION_SEND)) {
setLauncher();
return true;
}
return false;
});
}

private void setLauncher() {
String launcherInput = launcherUrl.getText().toString().trim();

if (!launcherInput.isEmpty()) {
if (!launcherInput.startsWith(PREFIX)) {
launcherInput = PREFIX + launcherInput;
}
}
setLauncherViewModel.setSageHost(this, launcherInput);
finish();
}

private void launcherUrlStateListener() {
final Disposable subscribe = RxTextView
.afterTextChangeEvents(launcherUrl)
.subscribe(view -> setButtonState(!launcherUrl.getText().toString().trim().isEmpty()));
compositeSubscription.add(subscribe);
}

private void setButtonState(final boolean launcherUrlStateFlag) {
if (launcherUrlStateFlag) {
setLauncherButton.setBackgroundDrawable(getDrawable(R.drawable.sign_in_enable));
setLauncherButton.setEnabled(true);
} else {
setLauncherButton.setBackgroundDrawable(getDrawable(R.drawable.sign_in_disable));
setLauncherButton.setEnabled(false);
}
}

}

+ 42
- 12
ui/src/main/java/com/getbubblenow/android/repository/DataRepository.java 파일 보기

@@ -11,12 +11,10 @@ import android.widget.Toast;
import com.getbubblenow.android.Application;
import com.getbubblenow.android.R;
import com.getbubblenow.android.api.enums.MFAuthType;
import com.getbubblenow.android.configStore.FileConfigStore;
import com.getbubblenow.android.model.LoginErrBody;
import com.getbubblenow.android.model.Network;
import com.getbubblenow.android.model.NetworkStatus;
import com.getbubblenow.android.model.ObservableTunnel;
import com.getbubblenow.android.model.TunnelManager;
import com.getbubblenow.android.activity.MainActivity;
import com.getbubblenow.android.api.ApiConstants;
import com.getbubblenow.android.api.network.ClientApi;
@@ -39,6 +37,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -56,6 +55,7 @@ import io.reactivex.observers.DisposableSingleObserver;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subscribers.DisposableSubscriber;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import okio.Buffer;
import retrofit2.Call;
@@ -97,6 +97,8 @@ public final class DataRepository {
private static volatile DataRepository instance;

private String baseUrl = "";
private Disposable bubbleCheckDisposable;
private Disposable progressDisposable;
private String deviceName;
private String deviceID;

@@ -135,6 +137,24 @@ public final class DataRepository {
return instance;
}

public void disposeBubbleCheck() {
if (bubbleCheckDisposable == null) {
return;
}
if (!bubbleCheckDisposable.isDisposed()) {
bubbleCheckDisposable.dispose();
}
}

public void disposeProgress() {
if (progressDisposable == null) {
return;
}
if (!progressDisposable.isDisposed()) {
progressDisposable.dispose();
}
}

public MutableLiveData<StatusResource<List<Network>>> checkBubbleOnce(final Context context) {
final MutableLiveData<StatusResource<List<Network>>> data = new MutableLiveData<>();
final HashMap<String, String> header = new HashMap<>();
@@ -161,12 +181,13 @@ public final class DataRepository {
final MutableLiveData<StatusResource<List<Network>>> data = new MutableLiveData<>();
final HashMap<String, String> header = new HashMap<>();
header.put(ApiConstants.AUTHORIZATION_HEADER, UserStore.getInstance(context).getSageToken());
clientApi.getNodeBaseURI(header)
bubbleCheckDisposable = clientApi.getNodeBaseURI(header)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.repeatWhen(success -> success.delay(DELAY_VALUE, TimeUnit.SECONDS))
.retryWhen(error -> error.delay(NET_UNAVAILABLE_DELAY_VALUE, TimeUnit.SECONDS))
.subscribe(new DisposableSubscriber<List<Network>>() {
.subscribeWith(new DisposableSubscriber<List<Network>>() {

@Override public void onNext(final List<Network> networks) {
final List<Network> alive = getAliveBubbles(networks);
if (!alive.isEmpty()) {
@@ -366,7 +387,7 @@ public final class DataRepository {

final HashMap<String, String> header = new HashMap<>();
header.put(ApiConstants.AUTHORIZATION_HEADER, UserStore.getInstance(context).getSageToken());
final Disposable getNetworkStateDisposable = clientApi.getNetworkState(
progressDisposable = clientApi.getNetworkState(
CredentialRepository.getInstance(context).getUsername(), network.getUuid(), header
)
.subscribeOn(Schedulers.newThread())
@@ -393,7 +414,6 @@ public final class DataRepository {
stopNetworkStatusLiveData = true;
setErrorMessage(throwable, getMutableLiveData());
});
compositeDisposable.add(getNetworkStateDisposable);
}

private void loginToNetworkWithCheck() {
@@ -492,6 +512,13 @@ public final class DataRepository {
return new NetworkBoundStatusResource<byte[]>() {

@Override protected void createCall() {
final String sageHostName = UserStore.getInstance(context).getSageHostname();
if (!sageHostName.isEmpty()) {
nodes = Collections.singletonList(sageHostName);
getNodeIndex(0, username, password, context, this);
return;
}

final Disposable sagesDisposable = clientApi.getSages()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
@@ -985,6 +1012,7 @@ public final class DataRepository {
liveData.postValue(StatusResource.error(LOGIN_FAILED));
}
} else {
Log.e("ERR", "Unhandled http error.", throwable);
liveData.postValue(StatusResource.error(LOGIN_FAILED));
}
}
@@ -995,27 +1023,29 @@ public final class DataRepository {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
final RequestBody body = copy.body();
if (body != null) {
body.writeTo(buffer);
}
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}

public void removeSharedPreferences(Context context) {
public void removeSharedPreferences(final 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() {
ArrayList<ObservableTunnel> tunnels = new ArrayList<>();
if(pendingTunnel != null) {
tunnels.add(pendingTunnel);
if (pendingTunnel != null) {
Application.getTunnelManager().delete(pendingTunnel);
pendingTunnel = null;
}
}

public MutableLiveData<StatusResource<Boolean>> isHaveTunnel(Context context) {
public MutableLiveData<StatusResource<Boolean>> isHaveTunnel(final Context context) {
return new NetworkBoundStatusResource<Boolean>(){
@Override protected void createCall() {
final byte[] configBytes = UserStore.getInstance(context).getConfig().getBytes();


+ 8
- 1
ui/src/main/java/com/getbubblenow/android/viewmodel/MainViewModel.java 파일 보기

@@ -5,7 +5,6 @@ import android.content.Context;
import com.getbubblenow.android.model.Network;
import com.getbubblenow.android.repository.CredentialRepository;
import com.getbubblenow.android.repository.DataRepository;
import com.getbubblenow.android.model.ObservableTunnel;
import com.getbubblenow.android.repository.RestringMessageRepository;
import com.getbubblenow.android.resource.StatusResource;
import com.getbubblenow.android.util.UserStore;
@@ -22,6 +21,14 @@ public class MainViewModel extends BaseViewModel {
CredentialRepository.getInstance(context).clear();
}

public void disposeBubbleCheck() {
DataRepository.getRepositoryInstance().disposeBubbleCheck();
}

public void disposeProgress() {
DataRepository.getRepositoryInstance().disposeProgress();
}

public boolean isUserLoggedInToSage(final Context context) {
return !UserStore.SAGE_TOKEN_DEFAULT_VALUE.equals(UserStore.getInstance(context).getSageToken());
}


+ 13
- 0
ui/src/main/java/com/getbubblenow/android/viewmodel/SetLauncherViewModel.java 파일 보기

@@ -0,0 +1,13 @@
package com.getbubblenow.android.viewmodel;

import android.content.Context;

import com.getbubblenow.android.util.UserStore;

public class SetLauncherViewModel extends BaseViewModel {

public void setSageHost(final Context context, final String sageHost) {
UserStore.getInstance(context).setSageHostname(sageHost);
}

}

+ 12
- 0
ui/src/main/res/layout/activity_login.xml 파일 보기

@@ -177,5 +177,17 @@
android:textStyle="normal"
android:clickable="true"
android:focusable="true" />

<TextView
android:id="@+id/setLauncherButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_start_and_end"
android:text="@string/set_launcher"
android:textAlignment="center"
android:textSize="14sp"
android:textStyle="normal"
android:clickable="true"
android:focusable="true" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

+ 110
- 0
ui/src/main/res/layout/activity_login_sage.xml 파일 보기

@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
tools:context="com.getbubblenow.android.activity.LoginActivity">

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/imageConstraint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<ImageView
android:id="@+id/color_curve"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="@color/white"
android:scaleType="fitXY"
android:src="@drawable/color_curves"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_start_and_end"
android:layout_marginTop="24dp"
android:layout_marginEnd="@dimen/margin_start_and_end"
android:src="@drawable/bubble_top"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.51"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>


<TextView
android:id="@+id/set_launcher_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_start_and_end"
android:layout_marginTop="50dp"
android:layout_marginEnd="@dimen/margin_start_and_end"
android:fontFamily="@font/josefin_sans_bold"
android:text="@string/set_launcher_title"
android:textColor="@color/black"
android:textSize="@dimen/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageConstraint" />

<EditText
android:id="@+id/launcher_url"
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="64dp"
android:layout_marginEnd="@dimen/margin_start_and_end"
android:background="@drawable/rectangle"
android:hint="@string/launcher_url_to_sign_in"
android:inputType="textUri"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="@dimen/text_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/set_launcher_title"
android:importantForAutofill="no" />

<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="50dp"
android:layout_marginEnd="@dimen/margin_start_and_end"
android:background="@color/white"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/launcher_url">

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/setLauncherButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/sign_in_disable"
android:enabled="false"
android:text="@string/set_launcher_title"
android:textAlignment="center"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="@dimen/text_title" />
</LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

+ 3
- 0
ui/src/main/res/values/strings.xml 파일 보기

@@ -269,6 +269,7 @@
<string name="building_bubble_title">We Are Building</string>
<string name="private_bubble">Your Private Bubble!</string>
<string name="no_account_yet">No account yet? <u>Create one.</u></string>
<string name="set_launcher">Running your own Bubble? <u>Set Launcher.</u></string>
<string name="launch_new_bubble">LAUNCH NEW BUBBLE</string>
<string name="select_your_bubble">Select your Bubble</string>
<string name="select_your_bubble_ok">OK</string>
@@ -277,4 +278,6 @@
<string name="enter_verification_code">Enter Verification Code</string>
<string name="enter_6_digit_code">Enter 6-digit Code</string>
<string name="invalid_mfa_token_format">Invalid MFA token format</string>
<string name="set_launcher_title">Set launcher</string>
<string name="launcher_url_to_sign_in">Launcher URL to Sign In</string>
</resources>

불러오는 중...
취소
저장