diff --git a/app/src/main/java/ru/myitschool/work/data/UserRepositoryImplementation.java b/app/src/main/java/ru/myitschool/work/data/UserRepositoryImplementation.java index bce082b..8bbd742 100644 --- a/app/src/main/java/ru/myitschool/work/data/UserRepositoryImplementation.java +++ b/app/src/main/java/ru/myitschool/work/data/UserRepositoryImplementation.java @@ -5,16 +5,19 @@ import androidx.annotation.NonNull; import java.util.function.Consumer; import ru.myitschool.work.data.network.RetrofitFactory; +import ru.myitschool.work.data.source.Credentials; import ru.myitschool.work.data.source.UserApi; import ru.myitschool.work.domain.entities.Status; import ru.myitschool.work.domain.entities.UserEntity; +import ru.myitschool.work.domain.login.LoginRepository; import ru.myitschool.work.domain.user.UserRepository; import ru.myitschool.work.utils.CallToConsumer; -public class UserRepositoryImplementation implements UserRepository { +public class UserRepositoryImplementation implements UserRepository, LoginRepository { private static UserRepositoryImplementation INSTANCE; private final UserApi userApi = RetrofitFactory.getInstance().getUserApi(); + private final Credentials credentials = Credentials.getInstance(); private UserRepositoryImplementation() {} @@ -25,16 +28,16 @@ public class UserRepositoryImplementation implements UserRepository { return INSTANCE; } @Override - public void getUserById(@NonNull String id, @NonNull Consumer> callback) { + public void getUserByLogin(@NonNull String login, @NonNull Consumer> callback) { - userApi.getById(id).enqueue(new CallToConsumer<>( + userApi.getByLogin(login).enqueue(new CallToConsumer<>( callback, userDto -> { - final String resultId = userDto.id; + final String resultLogin = userDto.login; final String name = userDto.name; - if (resultId != null && name != null) { + if (resultLogin != null && name != null) { return new UserEntity( - resultId, + resultLogin, name, userDto.lastVisit, userDto.photoUrl, @@ -46,4 +49,17 @@ public class UserRepositoryImplementation implements UserRepository { } )); } + + @Override + public void isUserExist(@NonNull String login, Consumer> callback) { + userApi.isExist(login).enqueue(new CallToConsumer<>( + callback, + dto -> null + )); + } + + @Override + public void logoutUser() { + credentials.setAuthData(null); + } } diff --git a/app/src/main/java/ru/myitschool/work/data/dto/UserDto.java b/app/src/main/java/ru/myitschool/work/data/dto/UserDto.java index 25d0d5b..5da13f5 100644 --- a/app/src/main/java/ru/myitschool/work/data/dto/UserDto.java +++ b/app/src/main/java/ru/myitschool/work/data/dto/UserDto.java @@ -10,16 +10,16 @@ public class UserDto { @Nullable @SerializedName("name") public String name; + @Nullable @SerializedName("lastVisit") - @Nullable public String lastVisit; + @Nullable @SerializedName("photoUrl") - @Nullable public String photoUrl; + @Nullable @SerializedName("position") - @Nullable public String position; - @SerializedName("id") @Nullable - public String id; + @SerializedName("login") + public String login; } diff --git a/app/src/main/java/ru/myitschool/work/data/network/RetrofitFactory.java b/app/src/main/java/ru/myitschool/work/data/network/RetrofitFactory.java index 0976cd2..9166a2a 100644 --- a/app/src/main/java/ru/myitschool/work/data/network/RetrofitFactory.java +++ b/app/src/main/java/ru/myitschool/work/data/network/RetrofitFactory.java @@ -1,5 +1,7 @@ package ru.myitschool.work.data.network; +import static ru.myitschool.work.core.Constants.SERVER_ADDRESS; + import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; import ru.myitschool.work.data.UserRepositoryImplementation; @@ -16,8 +18,8 @@ public class RetrofitFactory { return INSTANCE; } - private Retrofit retrofit = new Retrofit.Builder() - .baseUrl("http://10.0.2.2:8080/") + private final Retrofit retrofit = new Retrofit.Builder() + .baseUrl(SERVER_ADDRESS) .addConverterFactory(GsonConverterFactory.create()) .build(); diff --git a/app/src/main/java/ru/myitschool/work/data/source/Credentials.java b/app/src/main/java/ru/myitschool/work/data/source/Credentials.java new file mode 100644 index 0000000..69d9870 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/source/Credentials.java @@ -0,0 +1,30 @@ +package ru.myitschool.work.data.source; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class Credentials { + + private static Credentials INSTANCE; + + private Credentials() {} + + public static synchronized Credentials getInstance() { + if (INSTANCE == null) { + INSTANCE = new Credentials(); + } + return INSTANCE; + } + + @Nullable + private String authData = null; + + public void setAuthData(@Nullable String authData) { + this.authData = authData; + } + + @Nullable + public String getAuthData() { + return authData; + } +} diff --git a/app/src/main/java/ru/myitschool/work/data/source/UserApi.java b/app/src/main/java/ru/myitschool/work/data/source/UserApi.java index 275c1f1..04e3bf5 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/UserApi.java +++ b/app/src/main/java/ru/myitschool/work/data/source/UserApi.java @@ -7,6 +7,8 @@ import ru.myitschool.work.data.dto.UserDto; public interface UserApi { - @GET("user/{id}") - Call getById(@Path("id") String id); + @GET("user/{login}") + Call getByLogin(@Path("login") String login); + @GET("user/username/{username}") + Call isExist(@Path("username") String login); } diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.java b/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.java index 16c5da2..e1835ac 100644 --- a/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.java +++ b/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.java @@ -9,7 +9,7 @@ public class UserEntity { @NonNull private final String name; @NonNull - private final String id; + private final String login; @Nullable private final String last_visit; @Nullable @@ -19,7 +19,7 @@ public class UserEntity { @NonNull public String getId() { - return id; + return login; } @Nullable @@ -43,12 +43,12 @@ public class UserEntity { } public UserEntity( - @NonNull String id, + @NonNull String login, @NonNull String name, @Nullable String last_visit, @Nullable String photoUrl, @Nullable String position) { - this.id = id; + this.login = login; this.name = name; this.last_visit = last_visit; this.photoUrl = photoUrl; diff --git a/app/src/main/java/ru/myitschool/work/domain/login/CreateUserUseCase.java b/app/src/main/java/ru/myitschool/work/domain/login/CreateUserUseCase.java deleted file mode 100644 index 0f27b41..0000000 --- a/app/src/main/java/ru/myitschool/work/domain/login/CreateUserUseCase.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.myitschool.work.domain.login; - -public class CreateUserUseCase { -} diff --git a/app/src/main/java/ru/myitschool/work/domain/login/IsUserExistUseCase.java b/app/src/main/java/ru/myitschool/work/domain/login/IsUserExistUseCase.java index a7a586f..e9eb667 100644 --- a/app/src/main/java/ru/myitschool/work/domain/login/IsUserExistUseCase.java +++ b/app/src/main/java/ru/myitschool/work/domain/login/IsUserExistUseCase.java @@ -1,4 +1,29 @@ package ru.myitschool.work.domain.login; +import androidx.annotation.NonNull; + +import java.util.function.Consumer; + +import ru.myitschool.work.domain.entities.Status; + public class IsUserExistUseCase { + + private final LoginRepository repository; + + public IsUserExistUseCase(LoginRepository repository) { + this.repository = repository; + } + + public void execute(@NonNull String login, Consumer> callback) { + repository.isUserExist(login, status -> { + boolean isAvailable = status.getStatusCode() == 200 || status.getStatusCode() == 404; + callback.accept( + new Status<>( + status.getStatusCode(), + isAvailable ? status.getStatusCode() == 200 : null, + status.getErrors() + ) + ); + }); + } } diff --git a/app/src/main/java/ru/myitschool/work/domain/login/LoginRepository.java b/app/src/main/java/ru/myitschool/work/domain/login/LoginRepository.java index f534d0b..2cb86a7 100644 --- a/app/src/main/java/ru/myitschool/work/domain/login/LoginRepository.java +++ b/app/src/main/java/ru/myitschool/work/domain/login/LoginRepository.java @@ -1,4 +1,13 @@ package ru.myitschool.work.domain.login; +import androidx.annotation.NonNull; + +import java.util.function.Consumer; + +import ru.myitschool.work.domain.entities.Status; + public interface LoginRepository { + + void isUserExist(@NonNull String login, Consumer> callback); + void logoutUser(); } diff --git a/app/src/main/java/ru/myitschool/work/domain/login/LoginUserUseCase.java b/app/src/main/java/ru/myitschool/work/domain/login/LoginUserUseCase.java deleted file mode 100644 index a7d3c54..0000000 --- a/app/src/main/java/ru/myitschool/work/domain/login/LoginUserUseCase.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.myitschool.work.domain.login; - -public class LoginUserUseCase { -} diff --git a/app/src/main/java/ru/myitschool/work/domain/user/GetUserByIdUseCase.java b/app/src/main/java/ru/myitschool/work/domain/user/GetUserByLoginUseCase.java similarity index 56% rename from app/src/main/java/ru/myitschool/work/domain/user/GetUserByIdUseCase.java rename to app/src/main/java/ru/myitschool/work/domain/user/GetUserByLoginUseCase.java index 641aa43..4636485 100644 --- a/app/src/main/java/ru/myitschool/work/domain/user/GetUserByIdUseCase.java +++ b/app/src/main/java/ru/myitschool/work/domain/user/GetUserByLoginUseCase.java @@ -7,14 +7,14 @@ import java.util.function.Consumer; import ru.myitschool.work.domain.entities.Status; import ru.myitschool.work.domain.entities.UserEntity; -public class GetUserByIdUseCase { +public class GetUserByLoginUseCase { private final UserRepository repository; - public GetUserByIdUseCase(UserRepository repository) { + public GetUserByLoginUseCase(UserRepository repository) { this.repository = repository; } - public void execute(@NonNull String id, @NonNull Consumer> callback) { - repository.getUserById(id, callback); + public void execute(@NonNull String login, @NonNull Consumer> callback) { + repository.getUserByLogin(login, callback); } } diff --git a/app/src/main/java/ru/myitschool/work/domain/user/UserRepository.java b/app/src/main/java/ru/myitschool/work/domain/user/UserRepository.java index 2405f1b..c5ab832 100644 --- a/app/src/main/java/ru/myitschool/work/domain/user/UserRepository.java +++ b/app/src/main/java/ru/myitschool/work/domain/user/UserRepository.java @@ -10,5 +10,5 @@ import ru.myitschool.work.domain.entities.UserEntity; public interface UserRepository { - void getUserById(@NonNull String id, @NonNull Consumer> callback); + void getUserByLogin(@NonNull String login, @NonNull Consumer> callback); } diff --git a/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt b/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt index 8ebe7af..3ff6bc2 100644 --- a/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt +++ b/app/src/main/java/ru/myitschool/work/ui/RootActivity.kt @@ -1,19 +1,9 @@ package ru.myitschool.work.ui import android.os.Bundle -import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity -import androidx.navigation.createGraph -import androidx.navigation.findNavController -import androidx.navigation.fragment.NavHostFragment -import androidx.navigation.fragment.fragment import dagger.hilt.android.AndroidEntryPoint import ru.myitschool.work.R -import ru.myitschool.work.domain.user.GetUserByIdUseCase -import ru.myitschool.work.ui.login.LoginDestination -import ru.myitschool.work.ui.login.LoginFragment -import ru.myitschool.work.ui.qr.scan.QrScanDestination -import ru.myitschool.work.ui.qr.scan.QrScanFragment // НЕ ИЗМЕНЯЙТЕ НАЗВАНИЕ КЛАССА! @AndroidEntryPoint diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginDestination.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginDestination.kt deleted file mode 100644 index 50acfb0..0000000 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginDestination.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ru.myitschool.work.ui.login - -import kotlinx.serialization.Serializable - -@Serializable -data object LoginDestination \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.java b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.java new file mode 100644 index 0000000..0ef9ecd --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.java @@ -0,0 +1,63 @@ +package ru.myitschool.work.ui.login; + +import android.os.Bundle; +import android.text.Editable; +import android.view.View; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.navigation.Navigation; + +import ru.myitschool.work.R; +import ru.myitschool.work.databinding.FragmentLoginBinding; +import ru.myitschool.work.utils.OnChangeText; + +public class LoginFragment extends Fragment { + private FragmentLoginBinding binding; + private LoginViewModel viewModel; + + public LoginFragment() { + super(R.layout.fragment_login); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + binding = FragmentLoginBinding.bind(view); + viewModel = new ViewModelProvider(this).get(LoginViewModel.class); + binding.username.addTextChangedListener(new OnChangeText() { + @Override + public void afterTextChanged(Editable s) { + super.afterTextChanged(s); + viewModel.changeLogin(s.toString()); + } + }); + binding.login.setOnClickListener(v -> viewModel.confirm()); + subscribe(viewModel); + } + + private void subscribe(LoginViewModel viewModel) { + viewModel.errorLiveData.observe(getViewLifecycleOwner(), error -> { + Toast.makeText(getContext(), error, Toast.LENGTH_SHORT).show(); + }); + viewModel.stateLiveData.observe(getViewLifecycleOwner(), state -> { + binding.login.setClickable(state.isButtonActive()); + }); + viewModel.openProfileLiveData.observe(getViewLifecycleOwner(), (unused) -> { + View view = getView(); + if (view == null) return; + + Navigation.findNavController(getView()).navigate( + R.id.action_loginFragment_to_userFragment); + }); + } + + @Override + public void onDestroyView() { + binding = null; + super.onDestroyView(); + } +} diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt deleted file mode 100644 index 02842ce..0000000 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt +++ /dev/null @@ -1,36 +0,0 @@ -package ru.myitschool.work.ui.login - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import dagger.hilt.android.AndroidEntryPoint -import ru.myitschool.work.R -import ru.myitschool.work.databinding.FragmentLoginBinding -import ru.myitschool.work.utils.collectWhenStarted -import ru.myitschool.work.utils.visibleOrGone - -@AndroidEntryPoint -class LoginFragment : Fragment(R.layout.fragment_login) { - private var _binding: FragmentLoginBinding? = null - private val binding: FragmentLoginBinding get() = _binding!! - - private val viewModel: LoginViewModel by viewModels() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - _binding = FragmentLoginBinding.bind(view) - subscribe() - } - - private fun subscribe() { - viewModel.state.collectWhenStarted(this) { state -> - binding.loading.visibleOrGone(state) - } - } - - override fun onDestroyView() { - _binding = null - super.onDestroyView() - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.java b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.java new file mode 100644 index 0000000..b4bbf65 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.java @@ -0,0 +1,76 @@ +package ru.myitschool.work.ui.login; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import ru.myitschool.work.data.UserRepositoryImplementation; +import ru.myitschool.work.domain.login.IsUserExistUseCase; + +public class LoginViewModel extends ViewModel { + + private final State INIT_STATE = new State(false); + private final MutableLiveData mutableStateLiveData = new MutableLiveData( + INIT_STATE + ); + public final LiveData stateLiveData = mutableStateLiveData; + private final MutableLiveData mutableErrorLiveData = new MutableLiveData(); + public final LiveData errorLiveData = mutableErrorLiveData; + private final MutableLiveData mutableOpenProfileLiveData = new MutableLiveData(); + public final LiveData openProfileLiveData = mutableOpenProfileLiveData; + + private final IsUserExistUseCase isUserExistUseCase = new IsUserExistUseCase( + UserRepositoryImplementation.getInstance() + ); + + @Nullable + private String login = null; + private boolean userCheckCompleted = false; + public void changeLogin(@NonNull String login) { + this.login = login; + userCheckCompleted = !login.isBlank() && + login.length() >= 3 && + !Character.isDigit(login.charAt(0)) && + login.matches("^[a-zA-Z0-9]+$"); + + mutableStateLiveData.postValue(new State(userCheckCompleted)); + } + + public void confirm() { + checkUserExist(); + } + + private void checkUserExist() { + final String currentLogin = login; + if (currentLogin == null || currentLogin.isEmpty()) { + mutableErrorLiveData.postValue("Login cannot be null"); + return; + } + isUserExistUseCase.execute(currentLogin, status -> { + if (status.getValue() == null || status.getErrors() != null) { + mutableErrorLiveData.postValue("Something went wrong. Try again later"); + return; + } + if (status.getStatusCode() == 200) { + mutableOpenProfileLiveData.postValue(null); + } + }); + + } + + public class State { + private final boolean isButtonActive; + + public boolean isButtonActive() { + return isButtonActive; + } + + public State(boolean isButtonActive) { + this.isButtonActive = isButtonActive; + } + } + + +} diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt deleted file mode 100644 index 3a53d6c..0000000 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt +++ /dev/null @@ -1,17 +0,0 @@ -package ru.myitschool.work.ui.login - -import android.content.Context -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import javax.inject.Inject - -@HiltViewModel -class LoginViewModel @Inject constructor( - @ApplicationContext private val context: Context, -) : ViewModel() { - private val _state = MutableStateFlow(true) - val state = _state.asStateFlow() -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/profile/UserViewModel.java b/app/src/main/java/ru/myitschool/work/ui/profile/UserViewModel.java index 3c9d493..4e2ab7a 100644 --- a/app/src/main/java/ru/myitschool/work/ui/profile/UserViewModel.java +++ b/app/src/main/java/ru/myitschool/work/ui/profile/UserViewModel.java @@ -9,14 +9,14 @@ import androidx.lifecycle.ViewModel; import ru.myitschool.work.data.UserRepositoryImplementation; import ru.myitschool.work.domain.entities.Status; import ru.myitschool.work.domain.entities.UserEntity; -import ru.myitschool.work.domain.user.GetUserByIdUseCase; +import ru.myitschool.work.domain.user.GetUserByLoginUseCase; public class UserViewModel extends ViewModel { private final MutableLiveData mutableStateLiveData = new MutableLiveData(); public final LiveData stateLiveData = mutableStateLiveData; - public final GetUserByIdUseCase getUserByIdUseCase = new GetUserByIdUseCase( + public final GetUserByLoginUseCase getUserByLoginUseCase = new GetUserByLoginUseCase( UserRepositoryImplementation.getInstance() ); @@ -28,7 +28,7 @@ public class UserViewModel extends ViewModel { public void update(@NonNull String id) { mutableStateLiveData.setValue(new State(null, null, true)); - getUserByIdUseCase.execute(id, status -> { + getUserByLoginUseCase.execute(id, status -> { mutableStateLiveData.postValue(fromStatus(status)); }); } diff --git a/app/src/main/java/ru/myitschool/work/utils/OnChangeText.java b/app/src/main/java/ru/myitschool/work/utils/OnChangeText.java new file mode 100644 index 0000000..504d821 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/utils/OnChangeText.java @@ -0,0 +1,21 @@ +package ru.myitschool.work.utils; + +import android.text.Editable; +import android.text.TextWatcher; + +public class OnChangeText implements TextWatcher { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + + } +} diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index 7f3cd66..7e5a48a 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -1,16 +1,44 @@ - + + + android:text="@string/welcome_text" /> + + + +