From 313c1ad8510711357827ce879b449dcb770bce9a Mon Sep 17 00:00:00 2001 From: Administrator <123> Date: Tue, 02 Nov 2021 09:40:19 +0000 Subject: [PATCH] titlebar base 登录页UI --- app/src/main/java/com/duqing/missions/base/BaseActivity.java | 70 +++ app/src/main/res/values/styles.xml | 12 app/src/main/res/values/themes.xml | 12 app/src/main/java/com/duqing/missions/ui/login/view/LoggedInUserView.java | 17 app/src/main/java/com/duqing/missions/ui/login/view/LoginFormState.java | 40 ++ app/src/main/res/mipmap-hdpi/icon_white_back.png | 0 app/src/main/res/values/colors.xml | 5 app/src/main/res/values-night/themes.xml | 10 app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java | 6 app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModel.java | 89 ++++ app/src/main/java/com/duqing/missions/MainActivity.java | 11 app/src/main/java/com/duqing/missions/ui/login/data/LoginDataSource.java | 40 ++ app/src/main/res/mipmap-xhdpi/icon_white_back.png | 0 app/src/main/res/mipmap-xxhdpi/icon_delete.png | 0 app/src/main/java/com/duqing/missions/base/BaseTitleBarActivity.java | 57 +++ app/src/main/java/com/duqing/missions/ui/login/view/LoginResult.java | 31 + app/src/main/java/com/duqing/missions/ui/login/data/LoginRepository.java | 53 ++ app/src/main/res/values/strings.xml | 8 app/src/main/res/mipmap-xxhdpi/icon_back_black.png | 0 app/src/main/res/mipmap-xxhdpi/icon_white_back.png | 0 app/src/main/AndroidManifest.xml | 3 app/src/main/res/mipmap-xhdpi/icon_back_black.png | 0 app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModelFactory.java | 27 + app/src/main/java/com/duqing/missions/ui/login/data/model/LoggedInUser.java | 23 + app/src/main/java/com/duqing/missions/base/BaseFragment.java | 8 app/src/main/res/mipmap-hdpi/icon_back_black.png | 0 app/src/main/java/com/duqing/missions/widgets/ClearEditText.java | 194 ++++++++++ app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java | 25 - app/src/main/res/mipmap-xxxhdpi/icon_back_black.png | 0 app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java | 153 ++++++++ app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java | 4 app/src/main/res/layout/activity_login.xml | 161 ++++++++ app/src/main/res/mipmap-xxxhdpi/icon_white_back.png | 0 app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java | 2 34 files changed, 1,006 insertions(+), 55 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 468dd29..f2b6215 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:ignore="ProtectedPermissions" package="com.duqing.missions" > <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> @@ -20,6 +22,7 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:name=".ui.login.view.LoginActivity" /> </application> </manifest> \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/MainActivity.java b/app/src/main/java/com/duqing/missions/MainActivity.java index 6c2f39a..d0ce711 100644 --- a/app/src/main/java/com/duqing/missions/MainActivity.java +++ b/app/src/main/java/com/duqing/missions/MainActivity.java @@ -1,10 +1,7 @@ package com.duqing.missions; -import android.os.Bundle; - import androidx.navigation.NavController; import androidx.navigation.Navigation; -import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import com.duqing.missions.base.BaseActivity; @@ -14,16 +11,10 @@ @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - + public void initViews() { // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. - AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( - R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications) - .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main); - NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupWithNavController(binding.navView, navController); } diff --git a/app/src/main/java/com/duqing/missions/base/BaseActivity.java b/app/src/main/java/com/duqing/missions/base/BaseActivity.java index ef14ad3..b756705 100644 --- a/app/src/main/java/com/duqing/missions/base/BaseActivity.java +++ b/app/src/main/java/com/duqing/missions/base/BaseActivity.java @@ -14,12 +14,16 @@ import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; import android.widget.Toast; import androidx.annotation.ColorRes; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.FileProvider; import androidx.viewbinding.ViewBinding; @@ -114,7 +118,10 @@ } catch (Exception e) { } TAG = this.getClass().getSimpleName(); + initViews(); } + + public abstract void initViews(); public void setStatusBarTransparent(boolean isBlack){ @@ -164,6 +171,62 @@ } + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { //把操作放在用户点击的时候 + View v = getCurrentFocus(); //得到当前页面的焦点,ps:有输入框的页面焦点一般会被输入框占据 + if (isShouldHideKeyboard(v, ev)) { //判断用户点击的是否是输入框以外的区域 + hideSoftKeyboard (); //收起键盘 + } + } + return super.dispatchTouchEvent(ev); + } + + /** + * 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘,因为当用户点击EditText时则不能隐藏 + * + * @param v + * @param event + * @return + */ + private boolean isShouldHideKeyboard(View v, MotionEvent event) { + if (v != null && (v instanceof EditText)) { //判断得到的焦点控件是否包含EditText + int[] l = {0, 0}; + v.getLocationInWindow(l); + int left = l[0], //得到输入框在屏幕中上下左右的位置 + top = l[1], + bottom = top + v.getHeight(), + right = left + v.getWidth(); + if (event.getX() > left && event.getX() < right + && event.getY() > top && event.getY() < bottom) { + // 点击位置如果是EditText的区域,忽略它,不收起键盘。 + return false; + } else { + return true; + } + } + // 如果焦点不是EditText则忽略 + return false; + } + /** + * 判断软键盘输入法是否弹出 + */ + public boolean isKeyboardVisbility(Context context, View v) { + InputMethodManager imm = (InputMethodManager) context.getSystemService(context.INPUT_METHOD_SERVICE); + if (imm.hideSoftInputFromWindow(v.getWindowToken(), 0)) { + imm.showSoftInput(v, 0); + return true;//键盘显示中 + } else { + return false;//键盘未显示 + } + } + protected void hideSoftKeyboard() { + if (getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) { + if (getCurrentFocus() != null) + ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), + InputMethodManager.HIDE_NOT_ALWAYS); + } + } /** * 状态栏高度 * @return @@ -193,6 +256,13 @@ //quitApp(); } } + public void showToast(String message){ + Toast.makeText(this,message,Toast.LENGTH_SHORT).show(); + } + + public void showToast(@StringRes int msg){ + showToast(getString(msg)); + } ApkUpGradeResult.AppInfo apkUpGrade; ProgressDialog progressDialog ; diff --git a/app/src/main/java/com/duqing/missions/base/BaseFragment.java b/app/src/main/java/com/duqing/missions/base/BaseFragment.java index c4eccf8..099381b 100644 --- a/app/src/main/java/com/duqing/missions/base/BaseFragment.java +++ b/app/src/main/java/com/duqing/missions/base/BaseFragment.java @@ -15,16 +15,16 @@ /** * Created by Administrator on 2021/10/28 0028. */ -public abstract class BaseFragment<A extends BaseActivity,B extends ViewBinding> extends Fragment { +public abstract class BaseFragment<B extends ViewBinding> extends Fragment { - protected A activity; + protected BaseActivity activity; protected B binding; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { // get genericity "B" - Class<B> entityClass = (Class<B>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]; + Class<B> entityClass = (Class<B>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; try { Method method = entityClass.getMethod("inflate", LayoutInflater.class,ViewGroup.class,boolean.class);//get method from name "inflate"; binding = (B) method.invoke(entityClass,inflater,container,false);//execute method to create a objct of viewbind; @@ -37,7 +37,7 @@ @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - activity = (A) getActivity(); + activity = (BaseActivity) getActivity(); initViews(); } diff --git a/app/src/main/java/com/duqing/missions/base/BaseTitleBarActivity.java b/app/src/main/java/com/duqing/missions/base/BaseTitleBarActivity.java new file mode 100644 index 0000000..8a56999 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/base/BaseTitleBarActivity.java @@ -0,0 +1,57 @@ +package com.duqing.missions.base; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + +import com.duqing.missions.widgets.TitleBarView; + +/** + * Created by Administrator on 2021/11/2 0002. + */ +public abstract class BaseTitleBarActivity<B extends ViewBinding> extends BaseActivity<B> { + TitleBarView titleBarView; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + titleBarView = (TitleBarView) binding.getClass().getDeclaredField("titleBar").get(binding); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } + + protected void setTitle(String text){ + titleBarView.setTitleText(text); + } + + protected void onTitleLeftClick(){ + onBackKeyDown(); + } + + protected void setTitleRight(String text){ + titleBarView.setRightText(text); + titleBarView.setRightDra(null); + } + + protected void setTitleRight(Drawable drawable){ + titleBarView.setRightText(null); + titleBarView.setRightDra(drawable); + } + + @Override + public void setStatusBarTransparent(boolean isBlack) { + super.setStatusBarTransparent(isBlack); + final ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) titleBarView.getLayoutParams(); + layoutParams.topMargin = layoutParams.topMargin+getStatusBarHeight(); + titleBarView.setLayoutParams(layoutParams); + + } +} diff --git a/app/src/main/java/com/duqing/missions/ui/login/data/LoginDataSource.java b/app/src/main/java/com/duqing/missions/ui/login/data/LoginDataSource.java new file mode 100644 index 0000000..1fb4877 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/data/LoginDataSource.java @@ -0,0 +1,40 @@ +package com.duqing.missions.ui.login.data; + + +import com.duqing.missions.ui.login.data.model.LoggedInUser; + +import io.reactivex.Observable; +import io.reactivex.ObservableEmitter; +import io.reactivex.ObservableOnSubscribe; + +/** + * Class that handles authentication w/ login credentials and retrieves user information. + */ +public class LoginDataSource { + + public Observable<LoggedInUser> login(String username, String password) { + final Observable<LoggedInUser> observable = Observable.create(new ObservableOnSubscribe<LoggedInUser>() { + @Override + public void subscribe(ObservableEmitter<LoggedInUser> e) throws Exception { + LoggedInUser fakeUser = new LoggedInUser( java.util.UUID.randomUUID().toString(), "Jane Doe"); + e.onNext(fakeUser); + } + }); + return observable; + } + + public Observable<LoggedInUser> loginByCode(String phone, String verifyCode){ + final Observable<LoggedInUser> observable = Observable.create(new ObservableOnSubscribe<LoggedInUser>() { + @Override + public void subscribe(ObservableEmitter<LoggedInUser> e) throws Exception { + LoggedInUser fakeUser = new LoggedInUser( java.util.UUID.randomUUID().toString(), "Jane Doe"); + e.onNext(fakeUser); + } + }); + return observable; + } + + public void logout() { + // TODO: revoke authentication + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/data/LoginRepository.java b/app/src/main/java/com/duqing/missions/ui/login/data/LoginRepository.java new file mode 100644 index 0000000..3767c5c --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/data/LoginRepository.java @@ -0,0 +1,53 @@ +package com.duqing.missions.ui.login.data; + + +import com.duqing.missions.ui.login.data.model.LoggedInUser; + +import io.reactivex.Observable; + +/** + * Class that requests authentication and user information from the remote data source and + * maintains an in-memory cache of login status and user credentials information. + */ +public class LoginRepository { + + private static volatile LoginRepository instance; + + private LoginDataSource 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) { + this.dataSource = dataSource; + } + + public static LoginRepository getInstance(LoginDataSource dataSource) { + if (instance == null) { + instance = new LoginRepository(dataSource); + } + return instance; + } + + public boolean isLoggedIn() { + return user != null; + } + + public void logout() { + user = null; + dataSource.logout(); + } + + private void setLoggedInUser(LoggedInUser user) { + this.user = user; + // If user credentials will be cached in local storage, it is recommended it be encrypted + // @see https://developer.android.com/training/articles/keystore + } + + public Observable<LoggedInUser> login(String username, String password) { + // handle login + return dataSource.login(username, password); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/data/model/LoggedInUser.java b/app/src/main/java/com/duqing/missions/ui/login/data/model/LoggedInUser.java new file mode 100644 index 0000000..35c7e49 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/data/model/LoggedInUser.java @@ -0,0 +1,23 @@ +package com.duqing.missions.ui.login.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; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/view/LoggedInUserView.java b/app/src/main/java/com/duqing/missions/ui/login/view/LoggedInUserView.java new file mode 100644 index 0000000..f0dea47 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/view/LoggedInUserView.java @@ -0,0 +1,17 @@ +package com.duqing.missions.ui.login.view; + +/** + * Class exposing authenticated user details to the UI. + */ +class LoggedInUserView { + private String displayName; + //... other data fields that may be accessible to the UI + + LoggedInUserView(String displayName) { + this.displayName = displayName; + } + + String getDisplayName() { + return displayName; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java b/app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java new file mode 100644 index 0000000..59eeae1 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java @@ -0,0 +1,153 @@ +package com.duqing.missions.ui.login.view; + +import android.app.Activity; +import android.graphics.Typeface; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.TypedValue; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + +import com.duqing.missions.R; +import com.duqing.missions.base.BaseTitleBarActivity; +import com.duqing.missions.databinding.ActivityLoginBinding; + + +public class LoginActivity extends BaseTitleBarActivity<ActivityLoginBinding> { + + private LoginViewModel loginViewModel; + + @Override + public void initViews() { + + loginViewModel = new ViewModelProvider(this, new LoginViewModelFactory()).get(LoginViewModel.class); + + final EditText phoneEdit = binding.editPhone; + final EditText passwordEditText = binding.editPassword; + final Button loginButton = binding.login; + + loginViewModel.getLoginFormState().observe(this, new Observer<LoginFormState>() { + @Override + public void onChanged(@Nullable LoginFormState loginFormState) { + if (loginFormState == null) { + return; + } + loginButton.setEnabled(loginFormState.isDataValid()); + if (loginFormState.getUsernameError() != null) { + phoneEdit.setError(getString(loginFormState.getUsernameError())); + } + if (loginFormState.getPasswordError() != null) { + passwordEditText.setError(getString(loginFormState.getPasswordError())); + } + } + }); + + loginViewModel.getLoginResult().observe(this, new Observer<LoginResult>() { + @Override + public void onChanged(@Nullable LoginResult loginResult) { + if (loginResult == null) { + return; + } + if (loginResult.getError() != null) { + showLoginFailed(loginResult.getError()); + } + if (loginResult.getSuccess() != null) { + updateUiWithUser(loginResult.getSuccess()); + } + setResult(Activity.RESULT_OK); + + //Complete and destroy login activity once successful + finish(); + } + }); + + binding.txtPasswordTitle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + binding.containerVerify.setVisibility(View.GONE); + binding.containerPassword.setVisibility(View.VISIBLE); + checkedStyle(binding.txtPasswordTitle); + unCheckStyle(binding.txtVerifyTitle); + } + }); + + binding.txtVerifyTitle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + binding.containerPassword.setVisibility(View.GONE); + binding.containerVerify.setVisibility(View.VISIBLE); + checkedStyle(binding.txtVerifyTitle); + unCheckStyle(binding.txtPasswordTitle); + } + }); + + TextWatcher afterTextChangedListener = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // ignore + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // ignore + } + + @Override + public void afterTextChanged(Editable s) { + loginViewModel.loginDataChanged(phoneEdit.getText().toString(), + passwordEditText.getText().toString()); + } + }; + phoneEdit.addTextChangedListener(afterTextChangedListener); + passwordEditText.addTextChangedListener(afterTextChangedListener); + passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + loginViewModel.login(phoneEdit.getText().toString(), + passwordEditText.getText().toString()); + } + return false; + } + }); + + loginButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + loginViewModel.login(phoneEdit.getText().toString(), + passwordEditText.getText().toString()); + } + }); + } + + public void checkedStyle(TextView textView){ + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP,17); + textView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + } + + public void unCheckStyle(TextView textView){ + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP,14); + textView.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL)); + } + + private void updateUiWithUser(LoggedInUserView model) { + String welcome = getString(R.string.welcome) + model.getDisplayName(); + // TODO : initiate successful logged in experience + Toast.makeText(getApplicationContext(), welcome, Toast.LENGTH_LONG).show(); + } + + private void showLoginFailed(@StringRes Integer errorString) { + Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/view/LoginFormState.java b/app/src/main/java/com/duqing/missions/ui/login/view/LoginFormState.java new file mode 100644 index 0000000..9c77e3f --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/view/LoginFormState.java @@ -0,0 +1,40 @@ +package com.duqing.missions.ui.login.view; + +import androidx.annotation.Nullable; + +/** + * Data validation state of the login form. + */ +class LoginFormState { + @Nullable + private Integer usernameError; + @Nullable + private Integer passwordError; + private boolean isDataValid; + + LoginFormState(@Nullable Integer usernameError, @Nullable Integer passwordError) { + this.usernameError = usernameError; + this.passwordError = passwordError; + this.isDataValid = false; + } + + LoginFormState(boolean isDataValid) { + this.usernameError = null; + this.passwordError = null; + this.isDataValid = isDataValid; + } + + @Nullable + Integer getUsernameError() { + return usernameError; + } + + @Nullable + Integer getPasswordError() { + return passwordError; + } + + boolean isDataValid() { + return isDataValid; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/view/LoginResult.java b/app/src/main/java/com/duqing/missions/ui/login/view/LoginResult.java new file mode 100644 index 0000000..64bd2f4 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/view/LoginResult.java @@ -0,0 +1,31 @@ +package com.duqing.missions.ui.login.view; + +import androidx.annotation.Nullable; + +/** + * Authentication result : success (user details) or error message. + */ +class LoginResult { + @Nullable + private LoggedInUserView success; + @Nullable + private Integer error; + + LoginResult(@Nullable Integer error) { + this.error = error; + } + + LoginResult(@Nullable LoggedInUserView success) { + this.success = success; + } + + @Nullable + LoggedInUserView getSuccess() { + return success; + } + + @Nullable + Integer getError() { + return error; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModel.java b/app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModel.java new file mode 100644 index 0000000..63f177a --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModel.java @@ -0,0 +1,89 @@ +package com.duqing.missions.ui.login.view; + +import android.util.Patterns; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import com.duqing.missions.R; +import com.duqing.missions.ui.login.data.LoginRepository; +import com.duqing.missions.ui.login.data.model.LoggedInUser; + +import io.reactivex.Observable; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; +import io.reactivex.observers.DisposableObserver; + +public class LoginViewModel extends ViewModel { + + private MutableLiveData<LoginFormState> loginFormState = new MutableLiveData<>(); + private MutableLiveData<LoginResult> loginResult = new MutableLiveData<>(); + private LoginRepository loginRepository; + + LoginViewModel(LoginRepository loginRepository) { + this.loginRepository = loginRepository; + } + + LiveData<LoginFormState> getLoginFormState() { + return loginFormState; + } + + LiveData<LoginResult> getLoginResult() { + return loginResult; + } + + public void login(String username, String password) { + // can be launched in a separate asynchronous job + Observable<LoggedInUser> result = loginRepository.login(username, password); + result.doOnSubscribe(new Consumer<Disposable>() { + @Override + public void accept(Disposable disposable) throws Exception { + + } + }).subscribe(new DisposableObserver<LoggedInUser>(){ + + @Override + public void onNext(LoggedInUser value) { + loginResult.setValue(new LoginResult((new LoggedInUserView(value.getDisplayName())))); + } + + @Override + public void onError(Throwable e) { + loginResult.setValue(new LoginResult(R.string.login_failed)); + } + + @Override + public void onComplete() { + + } + }); + } + + public void loginDataChanged(String username, String password) { + if (!isUserNameValid(username)) { + loginFormState.setValue(new LoginFormState(R.string.invalid_username, null)); + } else if (!isPasswordValid(password)) { + loginFormState.setValue(new LoginFormState(null, R.string.invalid_password)); + } else { + loginFormState.setValue(new LoginFormState(true)); + } + } + + // A placeholder username validation check + private boolean isUserNameValid(String username) { + if (username == null) { + return false; + } + if (username.contains("@")) { + return Patterns.EMAIL_ADDRESS.matcher(username).matches(); + } else { + return !username.trim().isEmpty(); + } + } + + // A placeholder password validation check + private boolean isPasswordValid(String password) { + return password != null && password.trim().length() > 5; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModelFactory.java b/app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModelFactory.java new file mode 100644 index 0000000..c7ce3ad --- /dev/null +++ b/app/src/main/java/com/duqing/missions/ui/login/view/LoginViewModelFactory.java @@ -0,0 +1,27 @@ +package com.duqing.missions.ui.login.view; + +import androidx.annotation.NonNull; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; + +import com.duqing.missions.ui.login.data.LoginDataSource; +import com.duqing.missions.ui.login.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"); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java b/app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java index d0da00b..0f03177 100644 --- a/app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java +++ b/app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java @@ -4,14 +4,12 @@ import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; -import com.duqing.missions.MainActivity; import com.duqing.missions.base.BaseFragment; import com.duqing.missions.databinding.FragmentDashboardBinding; -public class DashboardFragment extends BaseFragment<MainActivity,FragmentDashboardBinding> { +public class DashboardFragment extends BaseFragment<FragmentDashboardBinding> { private DashboardViewModel dashboardViewModel; - private FragmentDashboardBinding binding; @Override public void initViews() { diff --git a/app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java b/app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java index d1fc918..f4a1f9f 100644 --- a/app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java +++ b/app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java @@ -1,5 +1,6 @@ package com.duqing.missions.ui.main.home; +import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; @@ -16,9 +17,9 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.duqing.missions.MainActivity; import com.duqing.missions.base.BaseFragment; import com.duqing.missions.databinding.FragmentHomeBinding; +import com.duqing.missions.ui.login.view.LoginActivity; import com.duqing.missions.ui.main.home.adapter.MissionAdapter; import com.duqing.missions.ui.main.home.adapter.MissionTopAdapter; import com.duqing.missions.ui.main.home.model.MissionDesc; @@ -30,7 +31,7 @@ import java.util.List; -public class HomeFragment extends BaseFragment<MainActivity,FragmentHomeBinding> { +public class HomeFragment extends BaseFragment<FragmentHomeBinding> { private HomeViewModel homeViewModel; final String TAG = "HomeFragment"; @@ -39,6 +40,7 @@ @Override public void initViews() { homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class); + binding.imgSearch.setOnClickListener(v -> startActivity(new Intent(getContext(), LoginActivity.class))); final SmartRefreshLayout smartRefresh = binding.smartRefresh; smartRefresh.setRefreshHeader(new ClassicsHeader(getContext())); smartRefresh.setRefreshFooter(new ClassicsFooter(getContext())); diff --git a/app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java b/app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java index be17225..eb69ae9 100644 --- a/app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java +++ b/app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java @@ -47,7 +47,7 @@ } public void onLoadMore(){ - List<MissionDesc> list = recommendMissions.getValue(); + List<MissionDesc> list = recommendMissions.getValue() == null? new ArrayList<>():recommendMissions.getValue() ; list.add(new MissionDesc()); list.add(new MissionDesc()); list.add(new MissionDesc()); diff --git a/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java b/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java index e94c5af..38fa92a 100644 --- a/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java +++ b/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java @@ -1,39 +1,24 @@ package com.duqing.missions.ui.main.notifications; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; +import com.duqing.missions.base.BaseFragment; import com.duqing.missions.databinding.FragmentNotificationsBinding; -public class NotificationsFragment extends Fragment { +public class NotificationsFragment extends BaseFragment<FragmentNotificationsBinding> { private NotificationsViewModel notificationsViewModel; - private FragmentNotificationsBinding binding; - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + @Override + public void initViews() { notificationsViewModel = new ViewModelProvider(this).get(NotificationsViewModel.class); - binding = FragmentNotificationsBinding.inflate(inflater, container, false); - View root = binding.getRoot(); notificationsViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { @Override public void onChanged(@Nullable String s) { } }); - return root; } - - @Override - public void onDestroyView() { - super.onDestroyView(); - binding = null; - } - } \ No newline at end of file diff --git a/app/src/main/java/com/duqing/missions/widgets/ClearEditText.java b/app/src/main/java/com/duqing/missions/widgets/ClearEditText.java new file mode 100644 index 0000000..c0557f4 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/widgets/ClearEditText.java @@ -0,0 +1,194 @@ +package com.duqing.missions.widgets; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.CycleInterpolator; +import android.view.animation.TranslateAnimation; +import android.widget.EditText; + +import com.duqing.missions.R; + + +/** + * My father is Object, ites purpose of + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-2-25. + */ +@SuppressLint("AppCompatCustomView") +public class ClearEditText extends EditText implements View.OnFocusChangeListener, + TextWatcher { + /** + * 删除按钮的引用 + */ + private Drawable mClearDrawable; + /** + * 控件是否有焦点 + */ + private boolean hasFoucs; + + /** + * 是否可清除内容 + */ + private boolean isClearable = true; + + /** + * @param context + * @Description TODO + */ + public ClearEditText(Context context) { + this(context, null); + } + + /** + * @param context + * @param attrs + * @Description TODO + */ + public ClearEditText(Context context, AttributeSet attrs) { + // 这里构造方法也很重要,不加这个很多属性不能再XML里面定义 + this(context, attrs, android.R.attr.editTextStyle); + } + + /** + * @param context + * @param attrs + * @param defStyle + * @Description TODO + */ + public ClearEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + // 获取EditText的DrawableRight,假如没有设置我们就使用默认的图片 + mClearDrawable = getCompoundDrawables()[2]; + if (mClearDrawable == null) { + // throw new + // NullPointerException("You can add drawableRight attribute in XML"); + mClearDrawable = getResources().getDrawable(R.mipmap.icon_delete); + } + + mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), + mClearDrawable.getIntrinsicHeight()); + + // 默认设置隐藏图标 + setClearIconVisible(false); + // 设置焦点改变的监听 + setOnFocusChangeListener(this); + // 设置输入框里面内容发生改变的监听 + addTextChangedListener(this); + } + + /** + * 因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件 当我们按下的位置 在 EditText的宽度 - + * 图标到控件右边的间距 - 图标的宽度 和 EditText的宽度 - 图标到控件右边的间距之间我们就算点击了图标,竖直方向就没有考虑 + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + if (getCompoundDrawables()[2] != null) { + + boolean touchable = event.getX() > (getWidth() - getTotalPaddingRight()) + && (event.getX() < ((getWidth() - getPaddingRight()))); + + if (touchable && isClearable) { + this.setText(""); + } + } + } + + return super.onTouchEvent(event); + } + + /** + * 当ClearEditText焦点发生变化的时候,判断里面字符串长度设置清除图标的显示与隐藏 + */ + @Override + public void onFocusChange(View v, boolean hasFocus) { + this.hasFoucs = hasFocus; + if (hasFocus) { + setClearIconVisible(getText().toString().length() > 0); + } else { + setClearIconVisible(false); + } + } + + /** + * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去 + * + * @param visible + */ + public void setClearIconVisible(boolean visible) { + Drawable right = visible ? mClearDrawable : null; + setCompoundDrawables(getCompoundDrawables()[0], + getCompoundDrawables()[1], right, getCompoundDrawables()[3]); + } + + /** + * 当输入框里面内容发生变化的时候回调的方法 + */ + @Override + public void onTextChanged(CharSequence s, int start, int count, int after) { + if (hasFoucs) { + setClearIconVisible(s.toString().length() > 0); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + + } + + @Override + public void afterTextChanged(Editable s) { + + } + + /** + * 设置晃动动画 + */ + public void startShakeAnimation() { + this.startAnimation(shakeAnimation(5)); + } + + /** + * 晃动动画 + * + * @param counts 1秒钟晃动多少下 + * @return + */ + public static Animation shakeAnimation(int counts) { + Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0); + translateAnimation.setInterpolator(new CycleInterpolator(counts)); + translateAnimation.setDuration(1000); + return translateAnimation; + } + + /** + * 设置是否可清除 + * + * @param clearable + */ + public void setClearable(boolean clearable) { + isClearable = clearable; + } + + /** + * 获取是否可清除 + * + * @return + */ + public boolean getClearable() { + return isClearable; + } + +} diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..92e5134 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,161 @@ +<?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:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + tools:context=".ui.login.view.LoginActivity"> + + <com.duqing.missions.widgets.TitleBarView + android:id="@+id/titleBar" + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_marginBottom="150dp" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:leftDrawable="@mipmap/icon_back_black"/> + + + <TextView + android:id="@+id/txt_password_title" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:layout_marginTop="20dp" + android:gravity="center" + android:text="密码登录" + android:textSize="17sp" + android:textStyle="bold" + app:layout_constraintTop_toBottomOf="@+id/titleBar" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toLeftOf="@id/txt_verify_title"/> + + + <TextView + android:id="@+id/txt_verify_title" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:layout_marginTop="20dp" + android:text="短信登录" + android:gravity="center" + app:layout_constraintTop_toBottomOf="@+id/titleBar" + app:layout_constraintLeft_toRightOf="@id/txt_password_title" + app:layout_constraintRight_toRightOf="parent"/> + + <com.duqing.missions.widgets.ClearEditText + android:id="@+id/edit_phone" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="请输入手机号" + android:inputType="phone" + android:maxLength="11" + android:selectAllOnFocus="true" + android:layout_marginTop="26dp" + app:layout_constraintTop_toBottomOf="@id/txt_password_title" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" /> + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/container_password" + android:layout_width="match_parent" + android:layout_height="180dp" + app:layout_constraintTop_toBottomOf="@id/edit_phone" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent"> + + <com.duqing.missions.widgets.ClearEditText + android:id="@+id/edit_password" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:hint="请输入密码" + android:imeActionLabel="@string/action_sign_in_short" + android:imeOptions="actionDone" + android:inputType="textPassword" + android:selectAllOnFocus="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> + <TextView + android:id="@+id/text_register" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@id/edit_password" + app:layout_constraintRight_toLeftOf="@id/text_forgot" + android:paddingTop="@dimen/frame_margin_lr" + android:paddingLeft="3dp" + android:text="注册账号" /> + <TextView + android:id="@+id/text_forgot" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="忘记密码" + android:paddingTop="@dimen/frame_margin_lr" + android:paddingRight="3dp" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constraintTop_toBottomOf="@id/edit_password" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintLeft_toRightOf="@id/text_register" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/container_verify" + android:layout_width="match_parent" + android:layout_height="180dp" + android:visibility="gone" + app:layout_constraintTop_toBottomOf="@id/edit_phone" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent"> + + + <EditText + android:id="@+id/edit_verify" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:hint="请输入验证码" + android:imeActionLabel="@string/action_sign_in_short" + android:imeOptions="actionDone" + android:inputType="number" + android:selectAllOnFocus="true" + android:maxLength="4" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> + <TextView + android:id="@+id/text_verify" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="获取验证码" + android:textSize="16sp" + android:textColor="@color/deep_sky" + android:paddingRight="5dp" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="@id/edit_verify" + app:layout_constraintBottom_toBottomOf="@id/edit_verify" /> + </androidx.constraintlayout.widget.ConstraintLayout> + + <Button + android:id="@+id/login" + android:layout_width="200dp" + android:layout_height="wrap_content" + android:layout_gravity="start" + android:layout_marginTop="16dp" + android:layout_marginBottom="64dp" + android:enabled="false" + android:text="登录" + android:textColor="@color/white" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.501" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/container_password" + app:layout_constraintVertical_bias="0.0" + app:layout_goneMarginTop="180dp" /> + + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/icon_back_black.png b/app/src/main/res/mipmap-hdpi/icon_back_black.png new file mode 100644 index 0000000..4139667 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/icon_back_black.png Binary files differ diff --git a/app/src/main/res/mipmap-hdpi/icon_white_back.png b/app/src/main/res/mipmap-hdpi/icon_white_back.png new file mode 100644 index 0000000..c78eaf6 --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/icon_white_back.png Binary files differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_back_black.png b/app/src/main/res/mipmap-xhdpi/icon_back_black.png new file mode 100644 index 0000000..608b650 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/icon_back_black.png Binary files differ diff --git a/app/src/main/res/mipmap-xhdpi/icon_white_back.png b/app/src/main/res/mipmap-xhdpi/icon_white_back.png new file mode 100644 index 0000000..8f3faab --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/icon_white_back.png Binary files differ diff --git a/app/src/main/res/mipmap-xxhdpi/icon_back_black.png b/app/src/main/res/mipmap-xxhdpi/icon_back_black.png new file mode 100644 index 0000000..529b555 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/icon_back_black.png Binary files differ diff --git a/app/src/main/res/mipmap-xxhdpi/icon_delete.png b/app/src/main/res/mipmap-xxhdpi/icon_delete.png new file mode 100644 index 0000000..a2fdf07 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/icon_delete.png Binary files differ diff --git a/app/src/main/res/mipmap-xxhdpi/icon_white_back.png b/app/src/main/res/mipmap-xxhdpi/icon_white_back.png new file mode 100644 index 0000000..7d82e4c --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/icon_white_back.png Binary files differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_back_black.png b/app/src/main/res/mipmap-xxxhdpi/icon_back_black.png new file mode 100644 index 0000000..3e4c72b --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/icon_back_black.png Binary files differ diff --git a/app/src/main/res/mipmap-xxxhdpi/icon_white_back.png b/app/src/main/res/mipmap-xxxhdpi/icon_white_back.png new file mode 100644 index 0000000..f982292 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/icon_white_back.png Binary files differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 9d7a9e1..dea610f 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,13 +1,13 @@ <resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> - <style name="Theme.Missions" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> + <style name="Theme.Missions" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <!-- Primary brand color. --> - <item name="colorPrimary">@color/purple_200</item> - <item name="colorPrimaryVariant">@color/purple_700</item> + <item name="colorPrimary">@color/sky</item> + <item name="colorPrimaryVariant">@color/red</item> <item name="colorOnPrimary">@color/black</item> <!-- Secondary brand color. --> - <item name="colorSecondary">@color/teal_200</item> - <item name="colorSecondaryVariant">@color/teal_200</item> + <item name="colorSecondary">@color/black_4</item> + <item name="colorSecondaryVariant">@color/black_4</item> <item name="colorOnSecondary">@color/black</item> <!-- Status bar color. --> <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5707c0a..9d53e55 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,9 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="purple_200">#FFBB86FC</color> - <color name="purple_500">#FF6200EE</color> - <color name="purple_700">#FF3700B3</color> - <color name="teal_200">#FF03DAC5</color> <color name="teal_700">#FF018786</color> <color name="black">#FF000000</color> <color name="black_4">#373737</color> @@ -12,6 +8,7 @@ <color name="red">#FF1414</color> <color name="gray">#CDCDCD</color> <color name="enable">#ECECEC</color> + <color name="deep_sky">#4184D6</color> <color name="sky">#509CFA</color> <color name="gold">#FAD550</color> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0988b12..6bed2fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,4 +3,12 @@ <string name="title_home">Home</string> <string name="title_dashboard">Dashboard</string> <string name="title_notifications">Notifications</string> + <string name="prompt_email">Email</string> + <string name="prompt_password">Password</string> + <string name="action_sign_in">Sign in or register</string> + <string name="action_sign_in_short">Sign in</string> + <string name="welcome">"Welcome !"</string> + <string name="invalid_username">Not a valid username</string> + <string name="invalid_password">Password must be >5 characters</string> + <string name="login_failed">"Login failed"</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4ade4fe..4834c95 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -27,4 +27,16 @@ <item name="android:textColor">@color/black_4</item> <item name="android:layout_weight">1</item> </style> + + <declare-styleable name="TitleBarView"> + <attr name="leftDrawable" format="reference" /> + <attr name="rightDrawable" format="reference" /> + <attr name="rightDrawablePadding" format="dimension" /> + <attr name="titleText" format="string" /> + <attr name="titleTextSize" format="dimension" /> + <attr name="titleTextColor" format="color" /> + <attr name="rightText" format="string" /> + <attr name="rightTextSize" format="dimension" /> + <attr name="rightTextColor" format="color" /> + </declare-styleable> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 5f3f2d5..f4592f1 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,13 +1,13 @@ <resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> - <style name="Theme.Missions" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> + <style name="Theme.Missions" parent="Theme.MaterialComponents.Light.NoActionBar"> <!-- Primary brand color. --> - <item name="colorPrimary">@color/purple_500</item> - <item name="colorPrimaryVariant">@color/purple_700</item> - <item name="colorOnPrimary">@color/white</item> + <item name="colorPrimary">@color/sky</item> + <item name="colorPrimaryVariant">@color/red</item> + <item name="colorOnPrimary">@color/black</item> <!-- Secondary brand color. --> - <item name="colorSecondary">@color/teal_200</item> - <item name="colorSecondaryVariant">@color/teal_700</item> + <item name="colorSecondary">@color/black_4</item> + <item name="colorSecondaryVariant">@color/black_4</item> <item name="colorOnSecondary">@color/black</item> <!-- Status bar color. --> <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> -- Gitblit v1.9.1