refactoring code
This commit is contained in:
parent
47cf540fc3
commit
97c4102c47
|
|
@ -1,6 +1,7 @@
|
|||
package eu.csc.vehown.data;
|
||||
|
||||
import eu.csc.vehown.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package eu.csc.vehown.data;
|
||||
|
||||
import eu.csc.vehown.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
|
||||
/**
|
||||
* Class that requests authentication and user information from the remote data source and
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
package eu.csc.vehown.data;
|
||||
|
||||
/**
|
||||
* A generic class that holds a result success w/ data or an error exception.
|
||||
*/
|
||||
public class Result<T> {
|
||||
// hide the private constructor to limit subclass types (Success, Error)
|
||||
private Result() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this instanceof Result.Success) {
|
||||
Result.Success success = (Result.Success) this;
|
||||
return "Success[data=" + success.getData().toString() + "]";
|
||||
} else if (this instanceof Result.Error) {
|
||||
Result.Error error = (Result.Error) this;
|
||||
return "Error[exception=" + error.getError().toString() + "]";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Success sub-class
|
||||
public final static class Success<T> extends Result {
|
||||
private T data;
|
||||
|
||||
public Success(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
// Error sub-class
|
||||
public final static class Error extends Result {
|
||||
private Exception error;
|
||||
|
||||
public Error(Exception error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public Exception getError() {
|
||||
return this.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import eu.csc.ODPAppVehOwnServer.models.UserRegistrationDto;
|
|||
import eu.csc.ODPAppVehOwnServer.models.auth.AuthenticationRequest;
|
||||
import eu.csc.ODPAppVehOwnServer.models.regist.CustomerVehicleDto;
|
||||
import eu.csc.vehown.data.model.ICustomer;
|
||||
import eu.csc.vehown.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.data.model.Vehicle;
|
||||
import eu.csc.vehown.services.rest.vehownserver.pub.OfflineDummyCall;
|
||||
import lombok.var;
|
||||
|
|
@ -19,6 +20,7 @@ import lombok.var;
|
|||
import retrofit2.Call;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class VehOwnApiClientFactory {
|
||||
|
||||
|
|
@ -67,7 +69,9 @@ public class VehOwnApiClientFactory {
|
|||
return s.registerCustomerVehicle(vehicle);
|
||||
}
|
||||
|
||||
public JWTTokenResponse getAccessToken(ICustomer customer) throws IOException {
|
||||
|
||||
|
||||
public static JWTTokenResponse getAccessToken(ICustomer customer) throws IOException {
|
||||
|
||||
|
||||
if (customer == null)
|
||||
|
|
@ -77,7 +81,7 @@ public class VehOwnApiClientFactory {
|
|||
}
|
||||
|
||||
|
||||
public JWTTokenResponse getAccessToken(String email, String password) throws IOException {
|
||||
public static JWTTokenResponse getAccessToken(String email, String password) throws IOException {
|
||||
|
||||
|
||||
if (dummyMode) {
|
||||
|
|
@ -129,4 +133,24 @@ public class VehOwnApiClientFactory {
|
|||
return ClientFactory.createService(getURL(), AuthenticationService.class)
|
||||
.registerCustomer(dto);
|
||||
}
|
||||
|
||||
public static LoggedInUser fetchUserProfil(String email, String password) throws IOException {
|
||||
LoggedInUser result = new LoggedInUser();
|
||||
if(dummyMode)
|
||||
{
|
||||
result.setLogin(email);
|
||||
result.setPassword(password);
|
||||
}else{
|
||||
var call = ClientFactory.createAuthenticationService(getURL(), email, password).fetchCustomerProfile();
|
||||
var response = call.execute();
|
||||
|
||||
if(response.isSuccessful())
|
||||
{
|
||||
result.setFirstname(response.body().getFirstname());
|
||||
result.setLastname(response.body().getLastname());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package eu.csc.vehown.ui.fragments.data;
|
||||
|
||||
import eu.csc.vehown.ui.fragments.data.model.LoggedInUser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import eu.csc.vehown.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
|
||||
/**
|
||||
* Class that handles authentication w/ login credentials and retrieves user information.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package eu.csc.vehown.ui.fragments.data;
|
||||
|
||||
import eu.csc.vehown.ui.fragments.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
import eu.csc.vehown.ui.registration.data.CustomerContentDataSource;
|
||||
|
||||
/**
|
||||
* Class that requests authentication and user information from the remote data source and
|
||||
|
|
@ -10,18 +12,18 @@ public class LoginRepository {
|
|||
|
||||
private static volatile LoginRepository instance;
|
||||
|
||||
private LoginDataSource dataSource;
|
||||
private CustomerContentDataSource dataSource;
|
||||
|
||||
// If user credentials will be cached in local storage, it is recommended it be encrypted
|
||||
// @see https://developer.android.com/training/articles/keystore
|
||||
private LoggedInUser user = null;
|
||||
|
||||
// private constructor : singleton access
|
||||
private LoginRepository(LoginDataSource dataSource) {
|
||||
private LoginRepository(CustomerContentDataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public static LoginRepository getInstance(LoginDataSource dataSource) {
|
||||
public static LoginRepository getInstance(CustomerContentDataSource dataSource) {
|
||||
if (instance == null) {
|
||||
instance = new LoginRepository(dataSource);
|
||||
}
|
||||
|
|
@ -47,7 +49,7 @@ public class LoginRepository {
|
|||
// handle login
|
||||
Result<LoggedInUser> result = dataSource.login(username, password);
|
||||
if (result instanceof Result.Success) {
|
||||
setLoggedInUser(((Result.Success<LoggedInUser>) result).getData());
|
||||
// setLoggedInUser(((Result.Success<LoggedInUser>) result).getData());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
package eu.csc.vehown.ui.fragments.data;
|
||||
|
||||
/**
|
||||
* A generic class that holds a result success w/ data or an error exception.
|
||||
*/
|
||||
public class Result<T> {
|
||||
// hide the private constructor to limit subclass types (Success, Error)
|
||||
private Result() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this instanceof Result.Success) {
|
||||
Result.Success success = (Result.Success) this;
|
||||
return "Success[data=" + success.getData().toString() + "]";
|
||||
} else if (this instanceof Result.Error) {
|
||||
Result.Error error = (Result.Error) this;
|
||||
return "Error[exception=" + error.getError().toString() + "]";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Success sub-class
|
||||
public final static class Success<T> extends Result {
|
||||
private T data;
|
||||
|
||||
public Success(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
// Error sub-class
|
||||
public final static class Error extends Result {
|
||||
private Exception error;
|
||||
|
||||
public Error(Exception error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public Exception getError() {
|
||||
return this.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package eu.csc.vehown.ui.fragments.data.model;
|
||||
|
||||
/**
|
||||
* Data class that captures user information for logged in users retrieved from LoginRepository
|
||||
*/
|
||||
public class LoggedInUser {
|
||||
|
||||
private String userId;
|
||||
private String displayName;
|
||||
|
||||
public LoggedInUser(String userId, String displayName) {
|
||||
this.userId = userId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,14 +23,20 @@ import android.widget.TextView;
|
|||
import android.widget.Toast;
|
||||
import eu.csc.vehown.R;
|
||||
import eu.csc.vehown.databinding.FragmentLoginBinding;
|
||||
import eu.csc.vehown.persist.sharedPreferences.LocalStorageClient;
|
||||
import eu.csc.vehown.persist.sharedPreferences.SharedPreferencesFactory;
|
||||
import eu.csc.vehown.ui.models.LoggedInUserView;
|
||||
import eu.csc.vehown.ui.models.LoginResult;
|
||||
import eu.csc.vehown.ui.registration.customer.FragmentCustomerRegistrationViewModel;
|
||||
import eu.csc.vehown.ui.viewmodels.AppViewModelFactory;
|
||||
|
||||
public class LoginFragment extends Fragment {
|
||||
|
||||
private LoginViewModel loginViewModel;
|
||||
private FragmentLoginBinding binding;
|
||||
|
||||
private LocalStorageClient localStorageClient;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
|
|
@ -38,6 +44,9 @@ public class LoginFragment extends Fragment {
|
|||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
binding = FragmentLoginBinding.inflate(inflater, container, false);
|
||||
|
||||
localStorageClient = SharedPreferencesFactory.getInstance(getContext());
|
||||
|
||||
return binding.getRoot();
|
||||
|
||||
}
|
||||
|
|
@ -45,7 +54,7 @@ public class LoginFragment extends Fragment {
|
|||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
loginViewModel = new ViewModelProvider(this, new LoginViewModelFactory())
|
||||
loginViewModel = new ViewModelProvider(this, new AppViewModelFactory(getContext()))
|
||||
.get(LoginViewModel.class);
|
||||
|
||||
final EditText usernameEditText = binding.edEmail;
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import androidx.lifecycle.ViewModel;
|
|||
import android.util.Patterns;
|
||||
|
||||
import eu.csc.vehown.R;
|
||||
import eu.csc.vehown.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.ui.fragments.data.LoginRepository;
|
||||
import eu.csc.vehown.ui.fragments.data.Result;
|
||||
import eu.csc.vehown.ui.fragments.data.model.LoggedInUser;
|
||||
import eu.csc.vehown.ui.models.LoggedInUserView;
|
||||
import eu.csc.vehown.ui.models.LoginResult;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
|
||||
public class LoginViewModel extends ViewModel {
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ public class LoginViewModel extends ViewModel {
|
|||
private MutableLiveData<LoginResult> loginResult = new MutableLiveData<>();
|
||||
private LoginRepository loginRepository;
|
||||
|
||||
LoginViewModel(LoginRepository loginRepository) {
|
||||
public LoginViewModel(LoginRepository loginRepository) {
|
||||
this.loginRepository = loginRepository;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
package eu.csc.vehown.ui.fragments.ui.login;
|
||||
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import eu.csc.vehown.ui.fragments.data.LoginDataSource;
|
||||
import eu.csc.vehown.ui.fragments.data.LoginRepository;
|
||||
|
||||
/**
|
||||
* ViewModel provider factory to instantiate LoginViewModel.
|
||||
* Required given LoginViewModel has a non-empty constructor
|
||||
*/
|
||||
public class LoginViewModelFactory implements ViewModelProvider.Factory {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
if (modelClass.isAssignableFrom(LoginViewModel.class)) {
|
||||
return (T) new LoginViewModel(LoginRepository.getInstance(new LoginDataSource()));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown ViewModel class");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,11 +32,12 @@ import eu.csc.vehown.ui.register.RegisterCustomerActivity;
|
|||
import eu.csc.vehown.ui.registration.customer.FragmentCustomerRegistration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @see FragmentCustomerRegistration
|
||||
* @see LoginFragment
|
||||
*/
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
private Button btnShowRegistration;
|
||||
private Button btnSwitchView;
|
||||
|
||||
|
||||
private boolean loginVisible;
|
||||
|
|
@ -47,9 +48,9 @@ public class LoginActivity extends AppCompatActivity {
|
|||
setContentView(R.layout.activity_login);
|
||||
|
||||
|
||||
btnShowRegistration = findViewById(R.id.btnShowRegistration);
|
||||
btnSwitchView = findViewById(R.id.btnSwitchView);
|
||||
|
||||
btnShowRegistration.setOnClickListener(new View.OnClickListener() {
|
||||
btnSwitchView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchView();
|
||||
|
|
@ -175,7 +176,7 @@ public class LoginActivity extends AppCompatActivity {
|
|||
fragmentTransaction.commit();
|
||||
|
||||
loginVisible = false;
|
||||
btnShowRegistration.setText("Login");
|
||||
btnSwitchView.setText("Login");
|
||||
}
|
||||
|
||||
private void showLogin() {
|
||||
|
|
@ -186,7 +187,7 @@ public class LoginActivity extends AppCompatActivity {
|
|||
fragmentTransaction.commit();
|
||||
|
||||
loginVisible = true;
|
||||
btnShowRegistration.setText("Register");
|
||||
btnSwitchView.setText("Register");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -39,6 +39,9 @@ import eu.csc.vehown.ui.settings.SettingsActivity;
|
|||
import eu.csc.vehown.ui.svi.DashboardActivity;
|
||||
import eu.csc.vehown.ui.svi.RegisterSVIActivity;
|
||||
|
||||
/**
|
||||
* @see LoginActivity
|
||||
*/
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
// private static final int PERMISSIONS_REQUESTED = 1;
|
||||
|
|
@ -125,7 +128,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
Intent intent = null;
|
||||
switch (item.getItemId()) {
|
||||
case R.id.nav_login:
|
||||
showLogin();
|
||||
intent = showLogin();
|
||||
|
||||
break;
|
||||
case R.id.nav_list_content:
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ package eu.csc.vehown.ui.register;
|
|||
import android.content.Context;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import eu.csc.vehown.data.Result;
|
||||
import eu.csc.vehown.data.model.*;
|
||||
import eu.csc.vehown.services.rest.vehownserver.VehOwnApiClientFactory;
|
||||
import eu.csc.vehown.ui.modal.Helper;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
import eu.csc.vehown.ui.registration.data.CustomerVehicleRegistrationRepository;
|
||||
import lombok.Getter;
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,24 @@ public class CustomerContentDataSource {
|
|||
|
||||
}
|
||||
|
||||
public Result<LoggedInUser> login(String email, String password) {
|
||||
try {
|
||||
|
||||
|
||||
var profile = VehOwnApiClientFactory.fetchUserProfil(email, password);
|
||||
LoggedInUser fakeUser =
|
||||
new LoggedInUser(
|
||||
profile.getEmail(),
|
||||
profile.getEmail());
|
||||
|
||||
return new Result.Success<>(fakeUser);
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
return new Result.Error(new Exception("Error logging in ", ioException));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public Result<LoggedInUser> login(ICustomer customer) {
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ import androidx.lifecycle.ViewModel;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import eu.csc.vehown.data.Result;
|
||||
import eu.csc.vehown.data.model.Language;
|
||||
import eu.csc.vehown.data.model.Vehicle;
|
||||
import eu.csc.vehown.data.model.VehicleBrand;
|
||||
import eu.csc.vehown.data.model.VehicleModel;
|
||||
|
|
@ -20,6 +15,7 @@ import eu.csc.vehown.data.model.VehiclePropulsionType;
|
|||
import eu.csc.vehown.persist.sharedPreferences.LocalStorageClient;
|
||||
import eu.csc.vehown.services.rest.vehownserver.VehOwnApiClientFactory;
|
||||
import eu.csc.vehown.ui.modal.Helper;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
import eu.csc.vehown.ui.registration.data.CustomerContentDataSource;
|
||||
import eu.csc.vehown.ui.registration.data.CustomerRegistrationRepository;
|
||||
import eu.csc.vehown.ui.registration.data.CustomerVehicleRegistrationRepository;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import androidx.lifecycle.Observer;
|
|||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import eu.csc.vehown.R;
|
||||
import eu.csc.vehown.data.Result;
|
||||
import eu.csc.vehown.data.model.SVIVehicleModel;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
import eu.csc.vehown.ui.svi.pairing.PairingViewModel;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ import java.util.logging.Level;
|
|||
import java.util.logging.Logger;
|
||||
|
||||
import eu.csc.vehown.R;
|
||||
import eu.csc.vehown.data.Result;
|
||||
import eu.csc.vehown.data.model.SVIVehicleModel;
|
||||
import eu.csc.vehown.ui.modal.Helper;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
import eu.csc.vehown.ui.reportEvent.ReportEventActivity;
|
||||
import eu.csc.vehown.ui.svi.pairing.PairingViewModel;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import android.text.Editable;
|
|||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import eu.csc.vehown.data.Result;
|
||||
import eu.csc.vehown.data.model.SVIVehicleModel;
|
||||
import eu.csc.vehown.ui.models.Result;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import androidx.annotation.NonNull;
|
|||
|
||||
import eu.csc.vehown.persist.sharedPreferences.LocalStorageClient;
|
||||
import eu.csc.vehown.persist.sharedPreferences.SharedPreferencesFactory;
|
||||
import eu.csc.vehown.ui.fragments.data.LoginRepository;
|
||||
import eu.csc.vehown.ui.fragments.ui.login.LoginViewModel;
|
||||
import eu.csc.vehown.ui.register.RegistrationViewModel;
|
||||
import eu.csc.vehown.ui.registration.customer.FragmentCustomerRegistrationViewModel;
|
||||
import eu.csc.vehown.ui.registration.data.CustomerContentDataSource;
|
||||
|
|
@ -37,6 +39,13 @@ public class AppViewModelFactory implements ViewModelProvider.Factory {
|
|||
@SuppressWarnings("unchecked")
|
||||
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
LocalStorageClient localStorage = SharedPreferencesFactory.getInstance(context);
|
||||
|
||||
|
||||
|
||||
if (modelClass.isAssignableFrom(LoginViewModel.class)) {
|
||||
return (T) new LoginViewModel(LoginRepository.getInstance(new CustomerContentDataSource()));
|
||||
}
|
||||
|
||||
if (modelClass.isAssignableFrom(CustomerRegistrationViewModel.class)) {
|
||||
return (T) new CustomerRegistrationViewModel(CustomerRegistrationRepository.getInstance(
|
||||
new CustomerContentDataSource(),
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@
|
|||
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnShowRegistration"
|
||||
android:id="@+id/btnSwitchView"
|
||||
android:enabled="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -114,7 +114,7 @@
|
|||
android:layout_marginStart="48dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:text="@string/action_register_customer"
|
||||
android:text="@string/btn_no_text"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@
|
|||
<string name="hint_choose_model">Model</string>
|
||||
<string name="title_propulsion_type">Propulsion Type</string>
|
||||
<string name="hint_choose_propulsion_type">Propulsion Type</string>
|
||||
<string name="btn_no_text">NO_TEXT</string>
|
||||
|
||||
<!--string-array name="languages">
|
||||
<item>english</item>
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue