x1ongzhu 2 éve
szülő
commit
6efe7a2195

+ 3 - 1
app/build.gradle

@@ -98,7 +98,9 @@ dependencies {
     implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
     debugImplementation 'androidx.compose.ui:ui-tooling'
     debugImplementation 'androidx.compose.ui:ui-test-manifest'
-    implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.20'
+    implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.10'
     implementation 'androidx.core:core-ktx:1.10.1'
     implementation 'com.louiscad.splitties:splitties-fun-pack-android-base:3.0.0'
+    implementation 'com.louiscad.splitties:splitties-fun-pack-android-material-components:3.0.0'
+    implementation 'com.github.dmytrodanylyk:circular-progress-button:1.3'
 }

+ 49 - 1
app/src/main/java/com/dar/nbook/BLoginActivity.kt

@@ -1,11 +1,22 @@
 package com.dar.nbook
 
-import android.content.Intent
 import android.os.Bundle
 import android.view.MenuItem
+import androidx.core.content.ContextCompat
+import com.dar.nbook.api.HttpCallback
+import com.dar.nbook.api.HttpClient
+import com.dar.nbook.api.HttpError
+import com.dar.nbook.api.components.NUser
+import com.dar.nbook.api.request.UserLogin
+import com.dar.nbook.api.request.UserRegister
+import com.dar.nbook.api.response.LoginResponse
 import com.dar.nbook.components.activities.GeneralActivity
 import com.dar.nbook.databinding.ActivityBloginBinding
+import com.dar.nbook.settings.Global
+import com.dar.nbook.utility.snackError
+import com.dar.nbook.utility.snackSuccess
 import splitties.activities.start
+import splitties.snackbar.snack
 
 class BLoginActivity : GeneralActivity() {
 
@@ -26,6 +37,20 @@ class BLoginActivity : GeneralActivity() {
         binding.register.setOnClickListener() {
             start<RegisterActivity>()
         }
+
+        binding.login.setOnClickListener() {
+            val username = binding.etUsername.text.toString()
+            val password = binding.etPassword.text.toString()
+            if (username.isEmpty()) {
+                binding.username.error = getString(R.string.fill_empty_field)
+                return@setOnClickListener
+            }
+            if (password.isEmpty()) {
+                binding.password.error = getString(R.string.fill_empty_field)
+                return@setOnClickListener
+            }
+            login(username, password)
+        }
     }
 
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -35,4 +60,27 @@ class BLoginActivity : GeneralActivity() {
         }
         return super.onOptionsItemSelected(item)
     }
+
+    fun login(username: String, password: String) {
+        binding.login.visibility = android.view.View.GONE
+        binding.progress.visibility = android.view.View.VISIBLE
+
+        HttpClient.request(
+            HttpClient.getApiService().login(UserLogin(username, password)),
+            object : HttpCallback<LoginResponse> {
+                override fun onSuccess(data: LoginResponse) {
+                    binding.login.visibility = android.view.View.VISIBLE
+                    binding.progress.visibility = android.view.View.GONE
+                    binding.root.snackSuccess(R.string.login_success)
+                    finish()
+                }
+
+                override fun onFailure(error: HttpError<LoginResponse>) {
+                    binding.login.visibility = android.view.View.VISIBLE
+                    binding.progress.visibility = android.view.View.GONE
+                    binding.root.snackError(error.message)
+                }
+            }
+        )
+    }
 }

+ 75 - 3
app/src/main/java/com/dar/nbook/RegisterActivity.kt

@@ -1,11 +1,22 @@
 package com.dar.nbook
 
-import androidx.appcompat.app.AppCompatActivity
 import android.os.Bundle
 import android.view.MenuItem
+import android.view.View
+import androidx.core.content.ContextCompat
+import com.dar.nbook.api.HttpCallback
+import com.dar.nbook.api.HttpClient
+import com.dar.nbook.api.HttpError
+import com.dar.nbook.api.components.NUser
+import com.dar.nbook.api.request.UserRegister
 import com.dar.nbook.components.activities.GeneralActivity
-import com.dar.nbook.databinding.ActivityBloginBinding
 import com.dar.nbook.databinding.ActivityRegisterBinding
+import com.dar.nbook.settings.Global
+import com.dar.nbook.utility.snackError
+import com.dar.nbook.utility.snackSuccess
+import com.google.android.material.snackbar.Snackbar
+import splitties.snackbar.snack
+import splitties.toast.toast
 
 class RegisterActivity : GeneralActivity() {
 
@@ -19,9 +30,64 @@ class RegisterActivity : GeneralActivity() {
 
         val toolbar = binding.toolbar
         setSupportActionBar(toolbar)
-        supportActionBar!!.setTitle(R.string.login)
+        supportActionBar!!.setTitle(R.string.register)
         supportActionBar!!.setDisplayHomeAsUpEnabled(true)
         supportActionBar!!.setDisplayShowTitleEnabled(true)
+
+        binding.register.setOnClickListener {
+            binding.username.error = null
+            binding.password.error = null
+            binding.password1.error = null
+            val username = binding.etUsername.text.toString()
+            val password = binding.etPassword.text.toString()
+            val password1 = binding.etPassword1.text.toString()
+            if (username.isEmpty()) {
+                binding.username.error = getString(R.string.fill_empty_field)
+                return@setOnClickListener
+            }
+            if (password.isEmpty()) {
+                binding.password.error = getString(R.string.fill_empty_field)
+                return@setOnClickListener
+            }
+            if (password1.isEmpty()) {
+                binding.password1.error = getString(R.string.fill_empty_field)
+                return@setOnClickListener
+            }
+            if (password != password1) {
+                binding.password.error = getString(R.string.passwords_dont_match)
+                binding.password1.error = getString(R.string.passwords_dont_match)
+                return@setOnClickListener
+            }
+            var invitor: Int? = null
+            try {
+                invitor = binding.etInvitor.text.toString().toInt()
+            } catch (_: Exception) {
+            }
+            register(username, password, invitor)
+        }
+
+    }
+
+    private fun register(username: String, password: String, invitor: Int? = null) {
+        binding.register.visibility = View.GONE
+        binding.progress.visibility = View.VISIBLE
+        HttpClient.request(
+            HttpClient.getApiService().register(UserRegister(username, password, invitor)),
+            object : HttpCallback<NUser> {
+                override fun onSuccess(data: NUser) {
+                    binding.progress.visibility = View.GONE
+                    binding.register.visibility = View.VISIBLE
+                    binding.root.snackSuccess(R.string.register_success)
+                    finish()
+                }
+
+                override fun onFailure(error: HttpError<NUser>) {
+                    binding.progress.visibility = View.GONE
+                    binding.register.visibility = View.VISIBLE
+                    binding.root.snackError(error.message)
+                }
+            }
+        )
     }
 
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -32,3 +98,9 @@ class RegisterActivity : GeneralActivity() {
         return super.onOptionsItemSelected(item)
     }
 }
+
+inline fun View.snack(
+    msg: CharSequence,
+    duration: Int = Snackbar.LENGTH_SHORT,
+    actionSetup: Snackbar.() -> Unit = {}
+) = Snackbar.make(this, msg, duration).apply(actionSetup).also { it.show() }

+ 14 - 2
app/src/main/java/com/dar/nbook/components/activities/GeneralActivity.java

@@ -1,10 +1,13 @@
 package com.dar.nbook.components.activities;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.Toast;
 
 import androidx.annotation.Nullable;
@@ -34,8 +37,8 @@ public abstract class GeneralActivity extends AppCompatActivity {
     private void inflateWebView() {
         if (tokenView == null) {
             Toast.makeText(this, R.string.fetching_cloudflare_token, Toast.LENGTH_SHORT).show();
-            ViewGroup rootView= (ViewGroup) findViewById(android.R.id.content).getRootView();
-            ViewGroup v= (ViewGroup) LayoutInflater.from(this).inflate(R.layout.cftoken_layout,rootView,false);
+            ViewGroup rootView = (ViewGroup) findViewById(android.R.id.content).getRootView();
+            ViewGroup v = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.cftoken_layout, rootView, false);
             tokenView = new CFTokenView(v);
             ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
             tokenView.setVisibility(View.GONE);
@@ -66,4 +69,13 @@ public abstract class GeneralActivity extends AppCompatActivity {
             Global.applyFastScroller(findViewById(R.id.recycler));
         }
     }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (getCurrentFocus() != null) {
+            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+            imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
+        }
+        return super.dispatchTouchEvent(ev);
+    }
 }

+ 13 - 2
app/src/main/java/com/dar/nbook/settings/Global.java

@@ -32,6 +32,7 @@ import androidx.recyclerview.widget.RecyclerView;
 import com.dar.nbook.CopyToClipboardActivity;
 import com.dar.nbook.R;
 import com.dar.nbook.api.components.GenericGallery;
+import com.dar.nbook.api.components.NUser;
 import com.dar.nbook.api.enums.Language;
 import com.dar.nbook.api.enums.SortType;
 import com.dar.nbook.api.enums.TitleType;
@@ -93,7 +94,17 @@ public class Global {
     private static boolean infiniteScrollMain, infiniteScrollFavorite, exactTagMatch;
     private static int defaultZoom, offscreenLimit;
     private static Point screenSize;
-    private static String userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0";
+    private static String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:110.0) Gecko/20100101 Firefox/110.0";
+
+    private static NUser NUser;
+
+    public static com.dar.nbook.api.components.NUser getNUser() {
+        return NUser;
+    }
+
+    public static void setNUser(com.dar.nbook.api.components.NUser NUser) {
+        Global.NUser = NUser;
+    }
 
     public static long recursiveSize(File path) {
         if (path.isFile()) return path.length();
@@ -318,7 +329,7 @@ public class Global {
         colPortStat = shared.getInt(context.getString(R.string.key_column_port_stat), 2);
         colLandStat = shared.getInt(context.getString(R.string.key_column_land_stat), 4);
         zoomOneColumn = shared.getBoolean(context.getString(R.string.key_zoom_one_column), false);
-        userAgent = shared.getString(context.getString(R.string.key_user_agent),context.getString(R.string.default_user_agent));
+        userAgent = shared.getString(context.getString(R.string.key_user_agent), context.getString(R.string.default_user_agent));
         int x = Math.max(0, shared.getInt(context.getString(R.string.key_only_language), Language.ALL.ordinal()));
         sortType = SortType.values()[shared.getInt(context.getString(R.string.key_by_popular), SortType.RECENT_ALL_TIME.ordinal())];
         usageMobile = DataUsageType.values()[shared.getInt(context.getString(R.string.key_mobile_usage), DataUsageType.FULL.ordinal())];

+ 63 - 0
app/src/main/java/com/dar/nbook/utility/Snack.kt

@@ -0,0 +1,63 @@
+package com.dar.nbook.utility
+
+import android.view.View
+import androidx.annotation.StringRes
+import androidx.core.content.ContextCompat
+import com.dar.nbook.R
+import com.dar.nbook.settings.Global
+import com.google.android.material.snackbar.Snackbar
+import splitties.resources.txt
+import splitties.snackbar.snack
+
+fun View.snackError(
+    msg: CharSequence,
+    duration: Int = Snackbar.LENGTH_SHORT
+) = snack(msg, duration, actionSetup = {
+    setBackgroundTint(
+        ContextCompat.getColor(
+            this.context,
+            when (Global.getTheme()) {
+                Global.ThemeScheme.DARK -> R.color.red_700
+                else -> R.color.red_400
+            }
+        )
+    )
+    this.setTextColor(
+        ContextCompat.getColor(
+            this.context,
+            android.R.color.white
+        )
+    )
+})
+
+fun View.snackError(
+    @StringRes msgResId: Int,
+    duration: Int = Snackbar.LENGTH_SHORT
+) = snackError(txt(msgResId), duration)
+
+
+fun View.snackSuccess(
+    msg: CharSequence,
+    duration: Int = Snackbar.LENGTH_SHORT
+) = snack(msg, duration, actionSetup = {
+    setBackgroundTint(
+        ContextCompat.getColor(
+            this.context,
+            when (Global.getTheme()) {
+                Global.ThemeScheme.DARK -> R.color.green_400
+                else -> R.color.green_700
+            }
+        )
+    )
+    this.setTextColor(
+        ContextCompat.getColor(
+            this.context,
+            android.R.color.white
+        )
+    )
+})
+
+fun View.snackSuccess(
+    @StringRes msgResId: Int,
+    duration: Int = Snackbar.LENGTH_SHORT
+) = snackSuccess(txt(msgResId), duration)

+ 24 - 5
app/src/main/res/layout/activity_blogin.xml

@@ -29,9 +29,12 @@
         app:layout_constraintTop_toBottomOf="@id/toolbar">
 
         <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/et_username"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="@string/username" />
+            android:hint="@string/username"
+            android:inputType="textPersonName"
+            android:maxLength="30" />
     </com.google.android.material.textfield.TextInputLayout>
 
     <com.google.android.material.textfield.TextInputLayout
@@ -46,12 +49,14 @@
         app:layout_constraintTop_toBottomOf="@id/username">
 
         <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/et_password"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="@string/password" />
+            android:hint="@string/password"
+            android:inputType="textPassword" />
     </com.google.android.material.textfield.TextInputLayout>
 
-    <Button
+    <com.google.android.material.button.MaterialButton
         android:id="@+id/login"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -64,7 +69,19 @@
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/password" />
 
-    <Button
+    <com.google.android.material.progressindicator.CircularProgressIndicator
+        android:id="@+id/progress"
+        style="@style/Widget.Material3.CircularProgressIndicator.Small"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:indeterminate="true"
+        android:visibility="gone"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/login" />
+
+    <com.google.android.material.button.MaterialButton
         android:id="@+id/register"
         style="@style/Widget.MaterialComponents.Button.TextButton"
         android:layout_width="match_parent"
@@ -76,5 +93,7 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="0.5"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/login" />
+        app:layout_constraintTop_toBottomOf="@id/progress" />
+
+
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 59 - 5
app/src/main/res/layout/activity_register.xml

@@ -29,9 +29,12 @@
         app:layout_constraintTop_toBottomOf="@id/toolbar">
 
         <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/et_username"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="@string/username" />
+            android:hint="@string/username"
+            android:inputType="textPersonName"
+            android:maxLength="30" />
     </com.google.android.material.textfield.TextInputLayout>
 
     <com.google.android.material.textfield.TextInputLayout
@@ -46,22 +49,73 @@
         app:layout_constraintTop_toBottomOf="@id/username">
 
         <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/et_password"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="@string/password" />
+            android:hint="@string/password"
+            android:inputType="textPassword" />
     </com.google.android.material.textfield.TextInputLayout>
 
-    <Button
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/password1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="16dp"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/password">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/et_password1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/confirm_password"
+            android:inputType="textPassword" />
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/invitor"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="16dp"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/password1">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/et_invitor"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/invitation_code_optional"
+            android:inputType="numberDecimal" />
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <com.google.android.material.button.MaterialButton
         android:id="@+id/register"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginStart="16dp"
         android:layout_marginTop="16dp"
         android:layout_marginEnd="16dp"
-        android:text="@string/login"
+        android:text="@string/register"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="0.5"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/password" />
+        app:layout_constraintTop_toBottomOf="@id/invitor" />
 
+    <com.google.android.material.progressindicator.CircularProgressIndicator
+        android:id="@+id/progress"
+        style="@style/Widget.Material3.CircularProgressIndicator.Small"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:indeterminate="true"
+        android:visibility="gone"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/register" />
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 4 - 0
app/src/main/res/values-zh-rCN/strings.xml

@@ -317,4 +317,8 @@
         <item>日文</item>
         <item>简约</item>
     </string-array>
+    <string name="register">注册</string>
+    <string name="username">用户名</string>
+    <string name="password">密码</string>
+    <string name="confirm_password">确认密码</string>
 </resources>

+ 6 - 0
app/src/main/res/values/strings.xml

@@ -402,5 +402,11 @@
     <string name="register">Register</string>
     <string name="username">Username</string>
     <string name="password">Password</string>
+    <string name="confirm_password">Confirm password</string>
+    <string name="passwords_dont_match">Password do not match</string>
+    <string name="fill_empty_field">Fill empty field</string>
+    <string name="invitation_code_optional">Invitation Code (Optional)</string>
+    <string name="login_success">Login successfully!</string>
+    <string name="register_success">Registered successfully!</string>
 
 </resources>

+ 2 - 2
build.gradle

@@ -8,13 +8,13 @@ buildscript {
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:7.4.0'
-        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20'
+        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
     }
     ext {
-        kotlin_version = '1.8.20'
+        kotlin_version = '1.8.10'
     }
 }