diff --git a/build.gradle b/build.gradle
index 6e60a78..6555854 100644
--- a/build.gradle
+++ b/build.gradle
@@ -38,6 +38,9 @@ buildscript {
constraintlayoutVersion = '1.1.3'
lifecycleExtensionVersion = '2.2.0'
lifecycleViewModelVersion = '2.2.0'
+ rxandroidVersion = '2.0.1'
+ rxjavaVersion = '2.1.6'
+ adapterrxjava2Version = '2.3.0'
groupName = 'com.wireguard.android'
}
diff --git a/ui/build.gradle b/ui/build.gradle
index a855be0..8bd3f6a 100644
--- a/ui/build.gradle
+++ b/ui/build.gradle
@@ -90,6 +90,10 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout:$constraintlayoutVersion"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleExtensionVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycleViewModelVersion"
+ //RxJava
+ implementation "io.reactivex.rxjava2:rxandroid:$rxandroidVersion"
+ implementation "io.reactivex.rxjava2:rxjava:$rxjavaVersion"
+ implementation "com.squareup.retrofit2:adapter-rxjava2:$adapterrxjava2Version"
}
tasks.withType(JavaCompile) {
diff --git a/ui/src/main/AndroidManifest.xml b/ui/src/main/AndroidManifest.xml
index 299ac2c..a708955 100644
--- a/ui/src/main/AndroidManifest.xml
+++ b/ui/src/main/AndroidManifest.xml
@@ -72,7 +72,6 @@
-
>() {
@Override public void onChanged(final StatusResource userStatusResource) {
- switch (userStatusResource.status){
+ switch (userStatusResource.status) {
case SUCCESS:
- Toast.makeText(LoginActivity.this,"Success",Toast.LENGTH_SHORT).show();
- Log.d("TAG","Success");
- break;
- case LOADING:
- Log.d("TAG","Loading");
- break;
- case ERROR:
- Toast.makeText(LoginActivity.this,"Login Failed",Toast.LENGTH_SHORT).show();
- Log.d("TAG","Error");
- break;
+ Toast.makeText(LoginActivity.this, "Success", Toast.LENGTH_SHORT).show();
+ Log.d("TAG", "Success");
+ closeLoadingDialog();
+ break;
+ case LOADING:
+ Log.d("TAG", "Loading");
+ break;
+ case ERROR:
+ closeLoadingDialog();
+ Toast.makeText(LoginActivity.this, "Login Failed", Toast.LENGTH_SHORT).show();
+ Log.d("TAG", "Error");
+ break;
}
}
});
diff --git a/ui/src/main/java/com/wireguard/android/api/ApiConstants.java b/ui/src/main/java/com/wireguard/android/api/ApiConstants.java
index 6b35206..6498069 100644
--- a/ui/src/main/java/com/wireguard/android/api/ApiConstants.java
+++ b/ui/src/main/java/com/wireguard/android/api/ApiConstants.java
@@ -3,6 +3,11 @@ package com.wireguard.android.api;
public class ApiConstants {
public static final String BASE_URL = "https://jtest2.bubblesecure.com:1443/api/";
public static final String LOGIN_URL = "auth/login";
+ public static final String ALL_DEVICES_URL = "me/devices";
+ public static final String ADD_DEVICE_URL = "me/devices";
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
+ public static final String AUTHORIZATION_HEADER = "X-Bubble-Session";
+ public static final String DEVICE_NAME = "name";
+ public static final String DEVICE_TYPE = "deviceType";
}
diff --git a/ui/src/main/java/com/wireguard/android/api/network/ClientApi.java b/ui/src/main/java/com/wireguard/android/api/network/ClientApi.java
index c28f08a..4a40cfc 100644
--- a/ui/src/main/java/com/wireguard/android/api/network/ClientApi.java
+++ b/ui/src/main/java/com/wireguard/android/api/network/ClientApi.java
@@ -1,13 +1,20 @@
package com.wireguard.android.api.network;
import com.wireguard.android.api.ApiConstants;
+import com.wireguard.android.model.Device;
import com.wireguard.android.model.User;
import java.util.HashMap;
+import java.util.List;
+import io.reactivex.Single;
import retrofit2.Call;
import retrofit2.http.Body;
+import retrofit2.http.GET;
+import retrofit2.http.HeaderMap;
import retrofit2.http.POST;
+import retrofit2.http.PUT;
+
/**
Interface for API Calls
@@ -15,5 +22,11 @@ import retrofit2.http.POST;
public interface ClientApi {
@POST(ApiConstants.LOGIN_URL)
- Call login(@Body HashMap params);
+ Single login(@Body HashMap params);
+
+ @GET(ApiConstants.ALL_DEVICES_URL)
+ Single> getAllDevices(@HeaderMap HashMap header);
+
+ @PUT(ApiConstants.ADD_DEVICE_URL)
+ Single addDevice(@HeaderMap HashMap header , @Body HashMap body);
}
diff --git a/ui/src/main/java/com/wireguard/android/api/network/ClientService.java b/ui/src/main/java/com/wireguard/android/api/network/ClientService.java
index 4fc0d7a..2b2b419 100644
--- a/ui/src/main/java/com/wireguard/android/api/network/ClientService.java
+++ b/ui/src/main/java/com/wireguard/android/api/network/ClientService.java
@@ -6,6 +6,7 @@ import com.wireguard.android.api.interceptor.UserAgentInterceptor;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class ClientService {
@@ -41,6 +42,7 @@ public class ClientService {
return new Retrofit.Builder()
.baseUrl(ApiConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
+ .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient.build())
.build().create(ClientApi.class);
}
diff --git a/ui/src/main/java/com/wireguard/android/fragment/LoadingDialogFragment.java b/ui/src/main/java/com/wireguard/android/fragment/LoadingDialogFragment.java
new file mode 100644
index 0000000..1bcfea1
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/fragment/LoadingDialogFragment.java
@@ -0,0 +1,57 @@
+package com.wireguard.android.fragment;
+
+import android.app.Dialog;
+import android.graphics.PorterDuff;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import com.wireguard.android.R;
+import com.wireguard.android.activity.BaseActivityBubble;
+import com.wireguard.android.activity.LoginActivity;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.DialogFragment;
+
+public class LoadingDialogFragment extends DialogFragment {
+
+
+ private ProgressBar progressBar;
+
+ public static LoadingDialogFragment newInstance() {
+ LoadingDialogFragment fragment = new LoadingDialogFragment();
+ Bundle args = new Bundle();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ final Dialog dialog = new Dialog(getActivity(), getTheme()) {
+ @Override
+ public void onBackPressed() {
+ if (getActivity().getSupportFragmentManager().findFragmentByTag(BaseActivityBubble.LOADING_TAG) != null) {
+ getActivity().onBackPressed();
+ }
+ super.onBackPressed();
+ }
+ };
+ return dialog;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_loading_dialog, container, false);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ progressBar = view.findViewById(R.id.progress);
+ progressBar.getIndeterminateDrawable().setColorFilter(ContextCompat.getColor(getContext(), R.color.colorPrimaryDark), PorterDuff.Mode.SRC_IN);
+ }
+}
diff --git a/ui/src/main/java/com/wireguard/android/model/Device.java b/ui/src/main/java/com/wireguard/android/model/Device.java
new file mode 100644
index 0000000..7820e68
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/model/Device.java
@@ -0,0 +1,106 @@
+package com.wireguard.android.model;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class Device {
+ @SerializedName("uuid")
+ @Expose
+ private String uuid;
+ @SerializedName("related")
+ @Expose
+ private String related;
+ @SerializedName("ctime")
+ @Expose
+ private long ctime;
+ @SerializedName("name")
+ @Expose
+ private String name;
+ @SerializedName("account")
+ @Expose
+ private String account;
+ @SerializedName("deviceType")
+ @Expose
+ private String deviceType;
+ @SerializedName("enabled")
+ @Expose
+ private boolean enabled;
+ @SerializedName("network")
+ @Expose
+ private String network;
+ @SerializedName("shortId")
+ @Expose
+ private String shortId;
+
+ public void setUuid(final String uuid) {
+ this.uuid = uuid;
+ }
+
+ public void setRelated(final String related) {
+ this.related = related;
+ }
+
+ public void setCtime(final long ctime) {
+ this.ctime = ctime;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public void setAccount(final String account) {
+ this.account = account;
+ }
+
+ public void setDeviceType(final String deviceType) {
+ this.deviceType = deviceType;
+ }
+
+ public void setEnabled(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public void setNetwork(final String network) {
+ this.network = network;
+ }
+
+ public void setShortId(final String shortId) {
+ this.shortId = shortId;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public String getRelated() {
+ return related;
+ }
+
+ public long getCtime() {
+ return ctime;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAccount() {
+ return account;
+ }
+
+ public String getDeviceType() {
+ return deviceType;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public String getNetwork() {
+ return network;
+ }
+
+ public String getShortId() {
+ return shortId;
+ }
+}
diff --git a/ui/src/main/java/com/wireguard/android/repository/DataRepository.java b/ui/src/main/java/com/wireguard/android/repository/DataRepository.java
index 6e855c6..a8c4873 100644
--- a/ui/src/main/java/com/wireguard/android/repository/DataRepository.java
+++ b/ui/src/main/java/com/wireguard/android/repository/DataRepository.java
@@ -1,30 +1,40 @@
package com.wireguard.android.repository;
import android.content.Context;
-
+import android.os.Build;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
import com.wireguard.android.api.ApiConstants;
import com.wireguard.android.api.network.ClientApi;
import com.wireguard.android.api.network.ClientService;
import com.wireguard.android.api.network.NetworkBoundStatusResource;
+import com.wireguard.android.model.Device;
import com.wireguard.android.model.User;
import com.wireguard.android.resource.StatusResource;
import com.wireguard.android.util.UserStore;
-
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import androidx.lifecycle.MutableLiveData;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
import retrofit2.Call;
-import retrofit2.Callback;
-import retrofit2.Response;
public class DataRepository {
private static volatile DataRepository instance;
private ClientApi clientApi;
+ private CompositeDisposable compositeDisposable;
public static final String NO_INTERNET_CONNECTION = "no_internet_connection";
+ private static final String SEPARATOR = ":";
+ private static final String SPACE = " ";
+ private static final int ANDROID_ID = 1;
- private DataRepository()
- {
+ private DataRepository() {
clientApi = ClientService.getInstance().createClientApi();
+ compositeDisposable = new CompositeDisposable();
}
public static void buildRepositoryInstance() {
@@ -41,36 +51,163 @@ public class DataRepository {
return instance;
}
- public MutableLiveData> login(String username,String password , Context context){
- return new NetworkBoundStatusResource(){
+ public MutableLiveData> login(String username, String password, Context context) {
+ return new NetworkBoundStatusResource() {
@Override protected void createCall() {
- HashMap data = new HashMap<>();
- data.put(ApiConstants.USERNAME,username);
- data.put(ApiConstants.PASSWORD,password);
- clientApi.login(data).enqueue(new Callback() {
- @Override public void onResponse(final Call call, final Response response) {
- if(response.isSuccessful()) {
- String token = response.body().getToken();
- UserStore.getInstance(context).setToken(token);
- setMutableLiveData(StatusResource.success());
- }
- else {
- String errorMessage = createErrorMessage(call,response);
- setMutableLiveData(StatusResource.error(errorMessage));
- }
- }
-
- @Override public void onFailure(final Call call, final Throwable t) {
- if(t instanceof Exception){
+ HashMap data = new HashMap<>();
+ data.put(ApiConstants.USERNAME, username);
+ data.put(ApiConstants.PASSWORD, password);
+ Disposable disposableLogin = clientApi.login(data)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(user -> {
+ UserStore.getInstance(context).setToken(user.getToken());
+ if (!isDeviceLoggedIn(context)) {
+ addDevice(context);
+ }
+ else {
+ getAllDevices(context);
+ }
+ }, throwable -> {
+ setMutableLiveData(StatusResource.error(throwable.getMessage()));
+ });
+ compositeDisposable.add(disposableLogin);
+ }
+
+ private void getAllDevices(final Context context) {
+ final String token = UserStore.getInstance(context).getToken();
+ final HashMap header = new HashMap<>();
+ header.put(ApiConstants.AUTHORIZATION_HEADER,token);
+ Disposable disposableAllDevices = clientApi.getAllDevices(header)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(listDevices->{
+ boolean hasDevice = false;
+ for (Device item : listDevices) {
+ if (UserStore.getInstance(context).getDeviceID().equals(item.getUuid())) {
+ setMutableLiveData(StatusResource.success());
+ hasDevice = true;
+ break;
+ }
+ }
+ if(!hasDevice) {
+ addDevice(context);
+ }
+ },throwable -> {
+
+ });
+ compositeDisposable.add(disposableAllDevices);
+ }
+
+ private void addDevice(final Context context) {
+ final String brand = getBrand();
+ final String model = getDeviceModel();
+ final String imei = getDeviceID(context);
+ final String deviceName = brand + SPACE + model + SPACE + SEPARATOR + SPACE + imei;
+ final String token = UserStore.getInstance(context).getToken();
+ final HashMap header = new HashMap<>();
+ header.put(ApiConstants.AUTHORIZATION_HEADER, token);
+ final Disposable disposableAllDevices = clientApi.getAllDevices(header)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(listDevices -> {
+ String brandModel = brand + SPACE + model + SPACE;
+ final List list = listDevices;
+ final List arrayListDevicesName = new ArrayList<>();
+ boolean hasDevice = false;
+ for (final Device device : list) {
+ final String[] deviceNameItem = device.getName().split(SEPARATOR);
+ final String[] myDeviceName = deviceName.split(SEPARATOR);
+ if (deviceNameItem.length > 1) {
+ if (deviceNameItem[ANDROID_ID].equals(myDeviceName[ANDROID_ID])) {
+ UserStore.getInstance(context).setDeviceName(device.getName());
+ UserStore.getInstance(context).setDeviceID(device.getUuid());
+ hasDevice = true;
+ setMutableLiveData(StatusResource.success());
+ break;
+ } else {
+ final String[] itemDevice = device.getName().split(SEPARATOR);
+ if (itemDevice.length != 1) {
+ if (itemDevice[0].contains(brandModel)) {
+ arrayListDevicesName.add(itemDevice[0]);
+ }
+ }
+ }
+
+ }
+ }
+ if (!hasDevice) {
+ if (arrayListDevicesName.isEmpty()) {
+ brandModel = deviceName;
+ final HashMap body = new HashMap<>();
+ body.put(ApiConstants.DEVICE_NAME, brandModel);
+ body.put(ApiConstants.DEVICE_TYPE, "android");
+ final Disposable disposableAddDevice = clientApi.addDevice(header, body)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(device -> {
+ UserStore.getInstance(context).setDeviceName(device.getName());
+ UserStore.getInstance(context).setDeviceID(device.getUuid());
+ setMutableLiveData(StatusResource.success());
+ }, throwable -> {
+ setMutableLiveData(StatusResource.error(throwable.getMessage()));
+ });
+ compositeDisposable.add(disposableAddDevice);
+ }
+ else {
+ for (int i = (arrayListDevicesName.size() - 1); i >= arrayListDevicesName.size() - 1; i--) {
+ if (arrayListDevicesName.get(i).contains(brandModel)) {
+ final char[] arr = arrayListDevicesName.get(i).toCharArray();
+ if (arr[arr.length - 2] != ')') {
+ brandModel += "(2)" + SPACE + SEPARATOR + SPACE + imei;
+ } else {
+ String countDevice = "";
+ int indexfirst = 0;
+ int indexlast = 0;
+ for (int j = arr.length - 1; j >= 0; j--) {
+ if (arr[j] == '(') {
+ indexfirst = j;
+ }
+ if (arr[j] == ')') {
+ indexlast = j;
+ }
+ }
+ final String device = new String(arr);
+ countDevice += device.substring(indexfirst + 1, indexlast);
+ int count = Integer.parseInt(countDevice);
+ count++;
+ brandModel += "(" + count + ")" + SPACE + SEPARATOR + SPACE + imei;
+ }
+ }
+ final HashMap body = new HashMap<>();
+ body.put(ApiConstants.DEVICE_NAME, brandModel);
+ body.put(ApiConstants.DEVICE_TYPE, "android");
+ Disposable disposableAddDevice = clientApi.addDevice(header,body)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(device -> {
+ UserStore.getInstance(context).setDeviceName(device.getName());
+ UserStore.getInstance(context).setDeviceID(device.getUuid());
+ setMutableLiveData(StatusResource.success());
+ },throwable -> {
+ setMutableLiveData(StatusResource.error(throwable.getMessage()));
+ });
+ compositeDisposable.add(disposableAddDevice);
+ }
+ }
+ }
+ }, throwable -> {
setMutableLiveData(StatusResource.error(NO_INTERNET_CONNECTION));
- }
- }
- });
+ });
+ compositeDisposable.add(disposableAllDevices);
}
}.getMutableLiveData();
}
+ private boolean isDeviceLoggedIn(Context context) {
+ return !UserStore.DEVICE_DEFAULT_VALUE.equals(UserStore.getInstance(context).getDeviceName());
+ }
private String createErrorMessage(Call call, retrofit2.Response response) {
return "Error: User agent: " + System.getProperty("http.agent") + ", Request body: " + call.request().body() + ", URL: " +
@@ -78,9 +215,33 @@ public class DataRepository {
response.message();
}
- public boolean isUserLoggedIn(Context context)
- {
+ public boolean isUserLoggedIn(Context context) {
return !UserStore.USER_TOKEN_DEFAULT_VALUE.equals(UserStore.getInstance(context).getToken());
}
+ private String getBrand() {
+ final String brand = Build.MANUFACTURER;
+ return capitalize(brand);
+ }
+
+
+ private String capitalize(final String brand) {
+ if (brand == null || brand.isEmpty()) {
+ return "";
+ }
+ final char first = brand.charAt(0);
+ return Character.isUpperCase(first) ? brand : Character.toUpperCase(first) + brand.substring(1);
+ }
+
+ private String getDeviceID(final Context context) {
+ return Settings.Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
+ }
+
+ private String getDeviceModel() {
+ return Build.MODEL;
+ }
+
+ public void clearDisposable(){
+ compositeDisposable.clear();
+ }
}
diff --git a/ui/src/main/java/com/wireguard/android/util/UserStore.java b/ui/src/main/java/com/wireguard/android/util/UserStore.java
index 6c378a6..8537f31 100644
--- a/ui/src/main/java/com/wireguard/android/util/UserStore.java
+++ b/ui/src/main/java/com/wireguard/android/util/UserStore.java
@@ -9,7 +9,11 @@ public class UserStore {
private static final String USER_SHARED_PREF = "com.wireguard.android.util.bubbleUserSharedPref";
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";
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 UserStore getInstance(Context context) {
if (instance == null) {
@@ -35,4 +39,19 @@ public class UserStore {
return sharedPreferences.getString(USER_DATA_KEY, USER_TOKEN_DEFAULT_VALUE);
}
+ public void setDeviceName(String device){
+ sharedPreferences.edit().putString(DEVICE_DATA_KEY, device).apply();
+ }
+
+ public String getDeviceName(){
+ return sharedPreferences.getString(DEVICE_DATA_KEY, DEVICE_DEFAULT_VALUE);
+ }
+
+ public void setDeviceID(String id){
+ sharedPreferences.edit().putString(DEVICE_ID_KEY,id).apply();
+ }
+
+ public String getDeviceID(){
+ return sharedPreferences.getString(DEVICE_ID_KEY,DEVICE_ID_DEFAULT_VALUE);
+ }
}
diff --git a/ui/src/main/java/com/wireguard/android/viewmodel/LoginViewModel.java b/ui/src/main/java/com/wireguard/android/viewmodel/LoginViewModel.java
index 3669b85..b804581 100644
--- a/ui/src/main/java/com/wireguard/android/viewmodel/LoginViewModel.java
+++ b/ui/src/main/java/com/wireguard/android/viewmodel/LoginViewModel.java
@@ -1,13 +1,9 @@
package com.wireguard.android.viewmodel;
import android.content.Context;
-
import com.wireguard.android.model.User;
import com.wireguard.android.repository.DataRepository;
import com.wireguard.android.resource.StatusResource;
-
-import java.util.HashMap;
-
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
@@ -15,4 +11,9 @@ public class LoginViewModel extends ViewModel {
public LiveData> login(String username,String password, Context context){
return DataRepository.getRepositoryInstance().login(username,password,context);
}
+
+ @Override protected void onCleared() {
+ super.onCleared();
+ DataRepository.getRepositoryInstance().clearDisposable();
+ }
}
diff --git a/ui/src/main/res/layout/fragment_loading_dialog.xml b/ui/src/main/res/layout/fragment_loading_dialog.xml
new file mode 100644
index 0000000..c088797
--- /dev/null
+++ b/ui/src/main/res/layout/fragment_loading_dialog.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ui/src/main/res/values/colors.xml b/ui/src/main/res/values/colors.xml
index 989c6fc..38e173c 100644
--- a/ui/src/main/res/values/colors.xml
+++ b/ui/src/main/res/values/colors.xml
@@ -25,4 +25,6 @@
#aa0000
#00aa00
#aaaa00
+
+ #C04B26
diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml
index 5bdabba..997f8f4 100644
--- a/ui/src/main/res/values/strings.xml
+++ b/ui/src/main/res/values/strings.xml
@@ -241,4 +241,5 @@
Connect
Disable Apps
Please turn on internet connection
+ Loading