diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 984bf1e..ce5ae38 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,17 +16,17 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher" + android:icon="@drawable/ic_launcher_main" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@drawable/ic_launcher_main" android:supportsRtl="true" android:usesCleartextTraffic="true" android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/Theme.StandAPP"> + @@ -34,6 +34,12 @@ + + (R.id.loading_layout)?.visibility = android.view.View.GONE + } catch (e: Exception) { + Log.e("UI", "隐藏加载布局失败", e) + } + } + } } diff --git a/app/src/main/java/com/stand/standapp/LoginActivity.kt b/app/src/main/java/com/stand/standapp/LoginActivity.kt index 8190840..0957fc3 100644 --- a/app/src/main/java/com/stand/standapp/LoginActivity.kt +++ b/app/src/main/java/com/stand/standapp/LoginActivity.kt @@ -2,12 +2,19 @@ package com.stand.standapp import android.content.Intent import android.os.Bundle +import android.util.Log import android.widget.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaType +import org.json.JSONObject +import java.io.IOException class LoginActivity : AppCompatActivity() { + private val client = OkHttpClient() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) @@ -42,27 +49,8 @@ class LoginActivity : AppCompatActivity() { return@setOnClickListener } - // 模拟登录校验 - if (username == "admin" && password == "admin") { - // 保存“记住登录”状态及凭据 - AppConfig.setRememberLogin(this, cbRemember.isChecked) - if (cbRemember.isChecked) { - AppConfig.setSavedUsername(this, username) - AppConfig.setSavedPassword(this, password) - } else { - AppConfig.setSavedUsername(this, "") - AppConfig.setSavedPassword(this, "") - } - - Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show() - - // 跳转到主界面 - val intent = Intent(this, MainActivity::class.java) - startActivity(intent) - finish() - } else { - Toast.makeText(this, "账号或密码错误", Toast.LENGTH_SHORT).show() - } + // 执行真实接口登录 + performLogin(username, password, cbRemember.isChecked) } // 4. PTT 示例按钮点击事件 @@ -76,6 +64,73 @@ class LoginActivity : AppCompatActivity() { } } + /** + * 执行真实接口登录 + */ + private fun performLogin(username: String, password: String, remember: Boolean) { + val loginUrl = "https://template.gyouzhe.com/api/auth/login" + val bearerToken = "eyJraWQiOiJzLXYxIiwidHlwIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJzdWIiOiJhZG1pbiIsInR5cCI6ImFjY2VzcyIsImV4cCI6MTc3NjQwMTU2MSwianRpIjoiMjM0NjdiZGMtNTY0Zi00NmY0LWJhZTgtNzJhZmQ1MzhmYmZhIiwiZmdwIjoiY2FhNzYzOTI4YWRhYWZiYjgzMjE3NjExYmVjYTc3ZjMiLCJraWQiOiJzLXYxIn0.4Zf1eiKcUjGNoFylEzmZ2no1kJY-44CQ8QWmMA_UxvY" + + val body = MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("username", username) + .addFormDataPart("password", password) + .build() + + val request = Request.Builder() + .url(loginUrl) + .post(body) + .addHeader("Authorization", "Bearer $bearerToken") + .build() + + val loadingDialog = AlertDialog.Builder(this) + .setMessage("正在登录...") + .setCancelable(false) + .create() + loadingDialog.show() + + client.newCall(request).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + runOnUiThread { + loadingDialog.dismiss() + Toast.makeText(this@LoginActivity, "网络异常: ${e.message}", Toast.LENGTH_SHORT).show() + } + } + + override fun onResponse(call: Call, response: Response) { + val responseBody = response.body?.string() + runOnUiThread { + loadingDialog.dismiss() + try { + val json = JSONObject(responseBody ?: "") + val code = json.optInt("code", -1) + if (code == 0) { + // 登录成功 + AppConfig.setRememberLogin(this@LoginActivity, remember) + if (remember) { + AppConfig.setSavedUsername(this@LoginActivity, username) + AppConfig.setSavedPassword(this@LoginActivity, password) + } else { + AppConfig.setSavedUsername(this@LoginActivity, "") + AppConfig.setSavedPassword(this@LoginActivity, "") + } + + Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show() + startActivity(Intent(this@LoginActivity, MainActivity::class.java)) + finish() + } else { + val msg = json.optString("msg", "登录失败") + Toast.makeText(this@LoginActivity, msg, Toast.LENGTH_SHORT).show() + } + } catch (e: Exception) { + Log.e("Login", "解析响应失败", e) + Toast.makeText(this@LoginActivity, "服务器响应异常", Toast.LENGTH_SHORT).show() + } + } + } + }) + } + /** * 弹出服务器地址配置对话框 */ diff --git a/app/src/main/java/com/stand/standapp/MainActivity.kt b/app/src/main/java/com/stand/standapp/MainActivity.kt index ab87b68..8b0512a 100644 --- a/app/src/main/java/com/stand/standapp/MainActivity.kt +++ b/app/src/main/java/com/stand/standapp/MainActivity.kt @@ -13,6 +13,12 @@ import com.stand.standapp.R class MainActivity : AppCompatActivity() { private var mAgentWeb: AgentWeb? = null + private val mHandler = android.os.Handler(android.os.Looper.getMainLooper()) + private val TIMEOUT_MS = 30000L + + private val timeoutRunnable = Runnable { + showErrorPage() + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -23,9 +29,15 @@ class MainActivity : AppCompatActivity() { val container = findViewById(R.id.container) if (container == null) return + findViewById(R.id.btn_retry)?.setOnClickListener { + hideErrorPage() + mAgentWeb?.webCreator?.webView?.reload() + startTimeoutTimer() + } + // 从配置中获取服务器地址 val serverUrl = AppConfig.getServerUrl(this) - val finalUrl = "$serverUrl/example/print_demo.html?t=\${System.currentTimeMillis()}" + val finalUrl = "$serverUrl/example/print_demo.html?t=${System.currentTimeMillis()}" Log.d("MainActivity", "Loading URL: $finalUrl") mAgentWeb = AgentWeb.with(this) @@ -36,7 +48,10 @@ class MainActivity : AppCompatActivity() { .ready() .go(finalUrl) + startTimeoutTimer() + // 配置 WebView 属性以支持 SSE 和跨域 + mAgentWeb?.let { agent -> try { val webView = agent.webCreator.webView @@ -104,6 +119,26 @@ class MainActivity : AppCompatActivity() { override fun onDestroy() { mAgentWeb?.webLifeCycle?.onDestroy() + cancelTimeoutTimer() super.onDestroy() } + + private fun startTimeoutTimer() { + cancelTimeoutTimer() + mHandler.postDelayed(timeoutRunnable, TIMEOUT_MS) + } + + fun cancelTimeoutTimer() { + mHandler.removeCallbacks(timeoutRunnable) + } + + private fun showErrorPage() { + findViewById(R.id.loading_layout)?.visibility = android.view.View.GONE + findViewById(R.id.error_layout)?.visibility = android.view.View.VISIBLE + } + + private fun hideErrorPage() { + findViewById(R.id.error_layout)?.visibility = android.view.View.GONE + findViewById(R.id.loading_layout)?.visibility = android.view.View.VISIBLE + } } diff --git a/app/src/main/java/com/stand/standapp/SplashActivity.kt b/app/src/main/java/com/stand/standapp/SplashActivity.kt new file mode 100644 index 0000000..c63b1c4 --- /dev/null +++ b/app/src/main/java/com/stand/standapp/SplashActivity.kt @@ -0,0 +1,20 @@ +package com.stand.standapp + +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import androidx.appcompat.app.AppCompatActivity + +class SplashActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_splash) + + // 延迟 2 秒进入登录界面 + Handler(Looper.getMainLooper()).postDelayed({ + startActivity(Intent(this, LoginActivity::class.java)) + finish() + }, 2000) + } +} diff --git a/app/src/main/res/drawable/ic_fire_logo.xml b/app/src/main/res/drawable/ic_fire_logo.xml new file mode 100644 index 0000000..63f2fbb --- /dev/null +++ b/app/src/main/res/drawable/ic_fire_logo.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 07d5da9..1f62c35 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -5,166 +5,6 @@ android:viewportWidth="108" android:viewportHeight="108"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 2b068d1..70bf28c 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,30 +1,46 @@ - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_main.xml b/app/src/main/res/drawable/ic_launcher_main.xml new file mode 100644 index 0000000..8d0a890 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_main.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 519febe..18bafe5 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -24,14 +24,20 @@ android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:gravity="center" - android:orientation="horizontal"> + android:orientation="vertical"> + + @@ -58,7 +64,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="15dp" - android:text="测试账号: admin 密码: admin" + android:text="测试账号: admin 密码: 123456" android:textColor="#CCFFFFFF" android:textSize="14sp" /> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 91db11a..9df1cb5 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,7 +1,89 @@ - - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + +