aaa
Some checks failed
Flask 提示词大师 - CI/CD 流水线 / 代码质量检查 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 单元测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 集成测试 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 构建Docker镜像 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到测试环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署到生产环境 (push) Has been cancelled
Flask 提示词大师 - CI/CD 流水线 / 部署监控系统 (push) Has been cancelled

This commit is contained in:
2026-03-01 23:56:28 +08:00
parent 90f3103de3
commit 8e750f5bee
1095 changed files with 99308 additions and 6 deletions

View File

@@ -0,0 +1,155 @@
# Android 项目编译 Debug 和 Release 版本 APK 的方法
## 项目信息
- **项目名称**: com.xunpaisoft.social
- **项目路径**: D:\zhini\android-im
- **JDK 要求**: Java 11 或更高版本
- **推荐 JDK**: Android Studio 内置 JDK 21
## 环境准备
### 1. 检查 Java 版本
```bash
java -version
```
**注意**: 如果系统只有 Java 8需要使用 Android Studio 的 JDK 21
### 2. 设置环境变量
```powershell
# 设置 Android Studio JDK 路径
$env:JAVA_HOME="C:\Program Files\Android\Android Studio1\jbr"
# 设置 Android SDK 路径
$env:ANDROID_HOME="C:\Users\$env:USERNAME\AppData\Local\Android\Sdk"
```
## 编译方法
### 方法一:使用 Gradle Wrapper推荐
#### 1. Debug 版本编译
```bash
# 设置环境变量并编译 Debug 版本
$env:JAVA_HOME="C:\Program Files\Android\Android Studio1\jbr"; $env:ANDROID_HOME="C:\Users\$env:USERNAME\AppData\Local\Android\Sdk"; .\gradlew.bat assembleDebug
```
**编译结果**:
- ✅ 编译状态: BUILD SUCCESSFUL
- ⏱️ 编译时间: 约 34 秒
- 📁 APK 位置: `app\build\outputs\apk\debug\app-debug.apk`
#### 2. Release 版本编译
```bash
# 设置环境变量并编译 Release 版本
$env:JAVA_HOME="C:\Program Files\Android\Android Studio1\jbr"; $env:ANDROID_HOME="C:\Users\$env:USERNAME\AppData\Local\Android\Sdk"; .\gradlew.bat assembleRelease
```
**编译结果**:
- ✅ 编译状态: BUILD SUCCESSFUL
- ⏱️ 编译时间: 约 3分48秒
- 📁 APK 位置: `app\build\outputs\apk\release\app-release.apk`
### 方法二:使用 Android Studio最简单
1. **打开 Android Studio**
2. **导入项目**: 选择 `D:\zhini\android-im` 目录
3. **等待 Gradle 同步完成**
4. **编译项目**:
- **Debug 版本**: `Build` → `Build Bundle(s) / APK(s)` → `Build APK(s)`
- **Release 版本**: `Build` → `Generate Signed Bundle / APK` → `APK`
## 安装 APK 到设备
### 安装 Debug 版本
```bash
adb install -r app\build\outputs\apk\debug\app-debug.apk
```
### 安装 Release 版本
```bash
adb install -r app\build\outputs\apk\release\app-release.apk
```
## 版本对比
| 特性 | Debug 版本 | Release 版本 |
|------|------------|--------------|
| **编译时间** | 约 34 秒 | 约 3分48秒 |
| **APK 大小** | 较大 | 较小(优化后) |
| **代码混淆** | 否 | 是ProGuard |
| **调试信息** | 包含 | 移除 |
| **性能** | 较慢 | 较快 |
| **用途** | 开发测试 | 正式发布 |
## 常见问题解决
### 问题1: Java 版本不兼容
**错误信息**: `Dependency requires at least JVM runtime version 11. This build uses a Java 8 JVM.`
**解决方案**:
```bash
# 停止现有 Gradle daemon
.\gradlew.bat --stop
# 使用 Android Studio JDK 编译
$env:JAVA_HOME="C:\Program Files\Android\Android Studio1\jbr"; .\gradlew.bat assembleDebug
```
### 问题2: Gradle Wrapper 不存在
**解决方案**: 项目已包含 `gradlew.bat` 文件,如果缺失可以重新创建。
### 问题3: 权限重复警告
**现象**: 编译时出现权限重复警告
**影响**: 不影响编译,但建议清理重复权限声明
## 编译命令总结
### 一键编译脚本
```powershell
# Debug 版本一键编译
$env:JAVA_HOME="C:\Program Files\Android\Android Studio1\jbr"; $env:ANDROID_HOME="C:\Users\$env:USERNAME\AppData\Local\Android\Sdk"; .\gradlew.bat assembleDebug
# Release 版本一键编译
$env:JAVA_HOME="C:\Program Files\Android\Android Studio1\jbr"; $env:ANDROID_HOME="C:\Users\$env:USERNAME\AppData\Local\Android\Sdk"; .\gradlew.bat assembleRelease
```
### 清理和重新编译
```bash
# 清理项目
.\gradlew.bat clean
# 重新编译 Debug 版本
.\gradlew.bat assembleDebug
# 重新编译 Release 版本
.\gradlew.bat assembleRelease
```
## 注意事项
1. **JDK 版本**: 必须使用 Java 11 或更高版本
2. **环境变量**: 每次编译前需要设置正确的 JAVA_HOME
3. **签名配置**: Release 版本需要配置签名文件
4. **混淆规则**: Release 版本会应用 ProGuard 混淆规则
5. **资源优化**: Release 版本会进行资源压缩和优化
## 文件位置
- **Debug APK**: `app\build\outputs\apk\debug\app-debug.apk`
- **Release APK**: `app\build\outputs\apk\release\app-release.apk`
- **Gradle Wrapper**: `gradlew.bat`
- **构建配置**: `app\build.gradle`
## 成功标志
编译成功后会显示:
```
BUILD SUCCESSFUL in [时间]
[数量] actionable tasks: [执行数量] executed, [跳过数量] up-to-date
```
---
**最后更新**: 2025年10月29日
**适用版本**: Android Gradle Plugin 8.7.3
**JDK 版本**: Android Studio JDK 21

35
example/.gitignore vendored Normal file
View File

@@ -0,0 +1,35 @@
# ---> Android
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof

2
example/README.md Normal file
View File

@@ -0,0 +1,2 @@
# peizhen

1
example/app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

60
example/app/build.gradle Normal file
View File

@@ -0,0 +1,60 @@
apply plugin: 'com.android.application'
android {
namespace 'com.ruilaizi.example'
compileSdk 34
defaultConfig {
applicationId "com.ruilaizi.example"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "API_BASE_URL", "\"https://api.deepseek.com/v1/\""
buildConfigField "String", "DEFAULT_API_KEY", "\"sk-fdf7cc1c73504e628ec0119b7e11b8cc\""
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures {
viewBinding true
buildConfig true
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.core:core:1.12.0'
// ViewModel & LiveData
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata:2.7.0'
implementation 'androidx.lifecycle:lifecycle-runtime:2.7.0'
// Room
implementation 'androidx.room:room-runtime:2.6.1'
annotationProcessor 'androidx.room:room-compiler:2.6.1'
// Network
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// Security: 可选,用于加密存储 API Key
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
implementation 'com.readystatesoftware.systembartint:systembartint:1.0.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

21
example/app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.ruilaizi.service",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 101,
"versionName": "1.0.1",
"outputFile": "app-release.apk"
}
]
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name=".BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.ruilaizi.example.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths_public" />
</provider>
</application>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
{
"data": [
{
"address": "",
"budget": "",
"customer_id": "49fc031a8b374e5c69b1640c2f77b5f4",
"decoration": "1",
"headimg": "http://192.168.1.3/upload/2020090409422893432.jpg",
"name": "狗子",
"phone": "13255558866",
"source": "1",
"source_name": "添加客户",
"style_id": "",
"style_name": "",
"type": "new",
"wechat": "— —"
},
{
"address": "住址",
"budget": "15万-20万",
"customer_id": "4db0ac2253cb643bfb0ddeb6375a27b3",
"decoration": "1",
"headimg": "",
"name": "阿爸。",
"phone": "13458966692",
"source": "1",
"source_name": "添加客户",
"style_id": "5",
"style_name": "现代简约",
"type": "old",
"wechat": "call"
},
{
"address": "一次是真的",
"budget": "5万-10万",
"customer_id": "5619a67e02a85290ad485d2e2b48b788",
"decoration": "2",
"headimg": "http://192.168.1.3/upload/2020090315102014899.jpg",
"name": "在乎",
"phone": "13278787787",
"source": "1",
"source_name": "添加客户",
"style_id": "7",
"style_name": "地中海",
"type": "old",
"wechat": "一次"
},
{
"address": "",
"budget": "",
"customer_id": "638de5977746a9337b08d9caaee11393",
"decoration": "2",
"headimg": "http://192.168.1.3/upload/2020090409345081526.jpg",
"name": "1212121",
"phone": "21212121qwq",
"source": "1",
"source_name": "添加客户",
"style_id": "",
"style_name": "",
"type": "new",
"wechat": ""
},
{
"address": "等,你,你",
"budget": "20万以上",
"customer_id": "7b4df32da7c8c08b55903117bb7cc8d9",
"decoration": "1",
"headimg": "http://192.168.1.3/upload/2020090409233368106.jpg",
"name": "你在,他",
"phone": "13826942573",
"source": "1",
"source_name": "添加客户",
"style_id": "6",
"style_name": "东南亚",
"type": "new",
"wechat": ""
},
{
"address": "",
"budget": "",
"customer_id": "af8b2b183071a36cf216211c48890ce9",
"decoration": "1",
"headimg": "http://192.168.1.3/upload/2020090409364521615.jpg",
"name": "二哈",
"phone": "13245725369",
"source": "0",
"source_name": "",
"style_id": "",
"style_name": "",
"type": "new",
"wechat": "微信"
}
],
"msg": "获取成功",
"success": 0,
"total": 4
}

View File

@@ -0,0 +1,804 @@
{
"v": "5.7.1",
"fr": 120,
"ip": 0,
"op": 230,
"w": 500,
"h": 500,
"nm": "加载动画",
"ddd": 0,
"assets": [
{
"id": "comp_0",
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "形状图层 3",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
248,
274,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"o": [
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"v": [
[
-90,
-28
],
[
-21,
38
],
[
101,
-105
]
],
"c": false
},
"ix": 2
},
"nm": "路径 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
0.149019613862,
0.501960813999,
0.921568632126,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 33,
"ix": 5
},
"lc": 2,
"lj": 2,
"bm": 0,
"nm": "描边 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
72,
72
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}
],
"nm": "形状 1",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 1
},
"e": {
"a": 1,
"k": [
{
"i": {
"x": [
0
],
"y": [
0.982
]
},
"o": {
"x": [
0.333
],
"y": [
0
]
},
"t": 191,
"s": [
0
]
},
{
"t": 230,
"s": [
100
]
}
],
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 3
},
"m": 1,
"ix": 2,
"nm": "修剪路径 1",
"mn": "ADBE Vector Filter - Trim",
"hd": false
}
],
"ip": 191,
"op": 231,
"st": -69,
"bm": 0
},
{
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "形状图层 2",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
251.5,
256.5,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": {
"a": 0,
"k": [
321,
321
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 3
},
"nm": "椭圆路径 1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
0.149019613862,
0.501960813999,
0.921568632126,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 20,
"ix": 5
},
"lc": 2,
"lj": 1,
"ml": 4,
"bm": 0,
"nm": "描边 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-1.5,
-6.5
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}
],
"nm": "椭圆 1",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 1
},
"e": {
"a": 1,
"k": [
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 0,
"s": [
15
]
},
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 46,
"s": [
25
]
},
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 96,
"s": [
15
]
},
{
"t": 191,
"s": [
100
]
}
],
"ix": 2
},
"o": {
"a": 1,
"k": [
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 0,
"s": [
0
]
},
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 96,
"s": [
360
]
},
{
"t": 191,
"s": [
720
]
}
],
"ix": 3
},
"m": 1,
"ix": 2,
"nm": "修剪路径 1",
"mn": "ADBE Vector Filter - Trim",
"hd": false
}
],
"ip": 0,
"op": 360,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 3,
"ty": 4,
"nm": "形状图层 1",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
251.5,
256.5,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": {
"a": 0,
"k": [
321,
321
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 3
},
"nm": "椭圆路径 1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
1,
1,
1,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 21,
"ix": 5
},
"lc": 1,
"lj": 1,
"ml": 4,
"bm": 0,
"nm": "描边 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-1.5,
-6.5
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}
],
"nm": "椭圆 1",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 360,
"st": 0,
"bm": 0
}
]
}
],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 0,
"nm": "1",
"refId": "comp_0",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
250,
250,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
250,
250,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"w": 500,
"h": 500,
"ip": 0,
"op": 97,
"st": 0,
"bm": 0
}
],
"markers": []
}

View File

@@ -0,0 +1 @@
{"v":"5.7.1","fr":120,"ip":0,"op":96,"w":500,"h":500,"nm":"合成 1","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"形状图层 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[248,274,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-90,-28],[-21,38],[101,-105]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.149019607843,0.501960784314,0.921568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":33,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[72,72],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"形状 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0],"y":[0.982]},"o":{"x":[0.333],"y":[0]},"t":191,"s":[0]},{"t":230,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":191,"op":231,"st":-69,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"形状图层 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[251.5,256.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[321,321],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.149019607843,0.501960784314,0.921568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-1.5,-6.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[15]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":46,"s":[25]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96,"s":[15]},{"t":191,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":96,"s":[360]},{"t":191,"s":[720]}],"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"形状图层 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[251.5,256.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[321,321],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.686274509804,0.686274509804,0.686274509804,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-1.5,-6.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"完整动画","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":96,"st":0,"bm":0}],"markers":[]}

View File

@@ -0,0 +1,21 @@
{
"data": [
{
"customer_id": "ae0c5199fdf564f90796aec09482b85d",
"fp_num": "2",
"headimg": "localhost/upload/2020081115530962805.jpg",
"industry": [
{
"customer_id": "ae0c5199fdf564f90796aec09482b85d",
"industry": "家具",
"type": "0"
}
],
"name": "demo",
"phone": "18831913290",
"wechat": "wangyuxin283425757"
}
],
"msg": "获取成功",
"success": 0
}

View File

@@ -0,0 +1,34 @@
{
"data": [
{
"budget": "",
"decoration": "1",
"enter_state": "0",
"fp_time": "1970-01-01 08:00",
"id": "1",
"industry": "家具",
"order": [
{
"fh_id": "1",
"order_name": "qqq",
"recudesum": "1.00",
"time": "2020-09-08 10:14"
}
],
"order_state": "0",
"rob_time": "1970-01-01 08:00",
"travel": "1",
"type": "0",
"type_time": "1970-01-01 08:00",
"ygd_brand_name": "",
"ygd_brand_type": "",
"ygd_enter": "0",
"ygd_order": "0",
"ygd_rob_name": "",
"ygd_state": "0",
"ygd_store_name": ""
}
],
"msg": "成功",
"success": 0
}

View File

@@ -0,0 +1,373 @@
{
"v": "5.4.4",
"fr": 25,
"ip": 0,
"op": 24,
"w": 100,
"h": 50,
"nm": "合成 1",
"ddd": 0,
"assets": [],
"layers": [{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "形状图层 3",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [19.5, 25, 0],
"ix": 2
},
"a": {
"a": 0,
"k": [0, 0, 0],
"ix": 1
},
"s": {
"a": 0,
"k": [100, 100, 100],
"ix": 6
}
},
"ao": 0,
"shapes": [{
"ty": "gr",
"it": [{
"d": 1,
"ty": "el",
"s": {
"a": 0,
"k": [20, 20],
"ix": 2
},
"p": {
"a": 0,
"k": [0, 0],
"ix": 3
},
"nm": "椭圆路径 1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
}, {
"ty": "fl",
"c": {
"a": 0,
"k": [0.117647058824, 0.117647058824, 0.117647058824, 1],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"bm": 0,
"nm": "填充 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
}, {
"ty": "tr",
"p": {
"a": 0,
"k": [0, 0],
"ix": 2
},
"a": {
"a": 0,
"k": [0, 0],
"ix": 1
},
"s": {
"a": 0,
"k": [100, 100],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}],
"nm": "椭圆 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}],
"ip": 6,
"op": 256,
"st": 6,
"bm": 0
}, {
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "形状图层 1",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [50, 25, 0],
"ix": 2
},
"a": {
"a": 0,
"k": [0, 0, 0],
"ix": 1
},
"s": {
"a": 0,
"k": [100, 100, 100],
"ix": 6
}
},
"ao": 0,
"shapes": [{
"ty": "gr",
"it": [{
"d": 1,
"ty": "el",
"s": {
"a": 0,
"k": [20, 20],
"ix": 2
},
"p": {
"a": 0,
"k": [0, 0],
"ix": 3
},
"nm": "椭圆路径 1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
}, {
"ty": "fl",
"c": {
"a": 0,
"k": [0.117647058824, 0.117647058824, 0.117647058824, 1],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"bm": 0,
"nm": "填充 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
}, {
"ty": "tr",
"p": {
"a": 0,
"k": [0, 0],
"ix": 2
},
"a": {
"a": 0,
"k": [0, 0],
"ix": 1
},
"s": {
"a": 0,
"k": [100, 100],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}],
"nm": "椭圆 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}],
"ip": 12,
"op": 262,
"st": 12,
"bm": 0
}, {
"ddd": 0,
"ind": 3,
"ty": 4,
"nm": "形状图层 2",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [80.5, 25, 0],
"ix": 2
},
"a": {
"a": 0,
"k": [0, 0, 0],
"ix": 1
},
"s": {
"a": 0,
"k": [100, 100, 100],
"ix": 6
}
},
"ao": 0,
"shapes": [{
"ty": "gr",
"it": [{
"d": 1,
"ty": "el",
"s": {
"a": 0,
"k": [20, 20],
"ix": 2
},
"p": {
"a": 0,
"k": [0, 0],
"ix": 3
},
"nm": "椭圆路径 1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
}, {
"ty": "fl",
"c": {
"a": 0,
"k": [0.117647058824, 0.117647058824, 0.117647058824, 1],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"bm": 0,
"nm": "填充 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
}, {
"ty": "tr",
"p": {
"a": 0,
"k": [0, 0],
"ix": 2
},
"a": {
"a": 0,
"k": [0, 0],
"ix": 1
},
"s": {
"a": 0,
"k": [100, 100],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}],
"nm": "椭圆 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}],
"ip": 18,
"op": 268,
"st": 18,
"bm": 0
}],
"markers": []
}

View File

@@ -0,0 +1,16 @@
{
"data":
{
"industry_name": "家具",
"phone_number": "14729066305",
"status": "0",
"store_id": "1",
"user_head": "",
"user_name": "admin",
"user_type": "2",
"uuid": "3"
}
,
"msg": "登陆成功",
"success": 0
}

View File

@@ -0,0 +1,14 @@
{
"data": [
{
"customer_id": "ae0c5199fdf564f90796aec09482b85d",
"headimg": "localhost/upload/2020081115530962805.jpg",
"name": "demo",
"order_num": "1",
"phone": "18831913290",
"wechat": "wangyuxin283425757"
}
],
"msg": "获取成功",
"success": 0
}

View File

@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script src="http://www.fenghoo.com.cn/JS/fenghuSta/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="http://www.fenghoo.com.cn/JS/fenghuSta/yidongduan.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
html,body,ul,li,ol{padding: 0;margin: 0;}
body{
width: 94%;
margin-left: 3%;
}
li{list-style: none;text-indent: 2em;}
li{
font-size: .28rem;
}
</style>
</head>
<body>
<ul>
<li>瑞来健康尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,瑞来健康会按照本隐私权政策的规定使用和披露您的个人信息。但瑞来健康将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,瑞来健康不会将这些信息对外披露或向第三方提供。瑞来健康会不时更新本隐私权政策。 您在同意瑞来健康服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于瑞来健康服务使用协议不可分割的一部分。 </li>
<li>
<h5>1. 适用范围</h5>
<ol>
<li>(a) 在您注册瑞来健康帐号时,您根据瑞来健康要求提供的个人注册信息;</li>
<li>(b) 在您使用瑞来健康网络服务或访问瑞来健康平台网页时瑞来健康自动接收并记录的您的浏览器和计算机上的信息包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据</li>
<li>(c) 瑞来健康通过合法途径从商业伙伴处取得的用户个人数据。 </li>
<li>您了解并同意,以下信息不适用本隐私权政策:</li>
<li>(a) 您在使用瑞来健康平台提供的搜索服务时输入的关键字信息;</li>
<li>(b) 瑞来健康收集到的您在瑞来健康发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情;</li>
<li>(c) 违反法律规定或违反瑞来健康规则行为及瑞来健康已对您采取的措施。</li>
</ol>
</li>
<li>
<h5>2. 信息使用</h5>
<ol>
<li>(a)瑞来健康不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和瑞来健康(含瑞来健康关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。</li>
<li>(b) 瑞来健康亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何瑞来健康平台用户如从事上述活动,一经发现,瑞来健康有权立即终止与该用户的服务协议。</li>
<li>(c) 为服务用户的目的,瑞来健康可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与瑞来健康合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)。</li>
</ol>
</li>
<li>
<h5>3. 信息披露</h5>
<ol>
<li>在如下情况下,瑞来健康将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息:</li>
<li>(a) 经您事先同意,向第三方披露;</li>
<li>(b)为提供您所要求的产品和服务,而必须和第三方分享您的个人信息;</li>
<li>(c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;</li>
<li>(d) 如您出现违反中国有关法律、法规或者瑞来健康服务协议或相关规则的情况,需要向第三方披露;</li>
<li>(e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷;</li>
<li>(f) 在瑞来健康平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,瑞来健康有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。</li>
<li>(g) 其它瑞来健康根据法律、法规或者网站政策认为合适的披露。</li>
</ol>
</li>
<li>
<h5>4. 信息存储和交换</h5>
<ol>
<li>瑞来健康收集的有关您的信息和资料将保存在瑞来健康及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或瑞来健康收集信息和资料所在地的境外并在境外被访问、存储和展示。</li>
</ol>
</li>
<li>
<h5>5. Cookie的使用</h5>
<ol>
<li>(a) 在您未拒绝接受cookies的情况下瑞来健康会在您的计算机上设定或取用cookies以便您能登录或使用依赖于cookies的瑞来健康平台服务或功能。瑞来健康使用cookies可为您提供更加周到的个性化服务包括推广服务。</li>
<li>(b) 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies则您可能无法登录或使用依赖于cookies的瑞来健康网络服务或功能。</li>
<li>(c) 通过瑞来健康所设cookies所取得的有关信息将适用本政策。</li>
</ol>
</li>
<li>
<h5>6. 信息安全</h5>
<ol>
<li>(a) 瑞来健康帐号均有安全保护功能,请妥善保管您的用户名及密码信息。瑞来健康将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。</li>
<li>(b) 在使用瑞来健康网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是瑞来健康用户名及密码发生泄露,请您立即联络瑞来健康客服,以便瑞来健康采取相应措施。</li>
</ol>
</li>
<li>
<h5>7.本隐私政策的更改</h5>
<ol>
<li>(a)如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。</li>
<li>(b)本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是本应用用户名及密码发生泄露,请您立即联络本应用客服,以便本应用采取相应措施。</li>
</ol>
</li>
</ul>
</body>
</html>

View File

@@ -0,0 +1,15 @@
{
"data": [
{
"connect_time": "10:48",
"content": "",
"nick_img": "",
"nick_name": "私信昵称",
"private_id": "1",
"read_num": "0",
"short_id": "私信id"
}
],
"msg": "成功",
"success": 0
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,804 @@
{
"v": "5.7.1",
"fr": 120,
"ip": 0,
"op": 96,
"w": 500,
"h": 500,
"nm": "合成 1",
"ddd": 0,
"assets": [
{
"id": "comp_0",
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "形状图层 3",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
248,
274,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"o": [
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"v": [
[
-90,
-28
],
[
-21,
38
],
[
101,
-105
]
],
"c": false
},
"ix": 2
},
"nm": "路径 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
0.149019607843,
0.501960784314,
0.921568627451,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 33,
"ix": 5
},
"lc": 2,
"lj": 2,
"bm": 0,
"nm": "描边 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
72,
72
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}
],
"nm": "形状 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 1
},
"e": {
"a": 1,
"k": [
{
"i": {
"x": [
0
],
"y": [
0.982
]
},
"o": {
"x": [
0.333
],
"y": [
0
]
},
"t": 191,
"s": [
0
]
},
{
"t": 230,
"s": [
100
]
}
],
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 3
},
"m": 1,
"ix": 2,
"nm": "修剪路径 1",
"mn": "ADBE Vector Filter - Trim",
"hd": false
}
],
"ip": 191,
"op": 231,
"st": -69,
"bm": 0
},
{
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "形状图层 2",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
251.5,
256.5,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": {
"a": 0,
"k": [
321,
321
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 3
},
"nm": "椭圆路径 1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
0.149019607843,
0.501960784314,
0.921568627451,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 15,
"ix": 5
},
"lc": 2,
"lj": 1,
"ml": 4,
"bm": 0,
"nm": "描边 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-1.5,
-6.5
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}
],
"nm": "椭圆 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 1
},
"e": {
"a": 1,
"k": [
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 0,
"s": [
15
]
},
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 46,
"s": [
25
]
},
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 96,
"s": [
15
]
},
{
"t": 191,
"s": [
100
]
}
],
"ix": 2
},
"o": {
"a": 1,
"k": [
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 0,
"s": [
0
]
},
{
"i": {
"x": [
0.833
],
"y": [
0.833
]
},
"o": {
"x": [
0.167
],
"y": [
0.167
]
},
"t": 96,
"s": [
360
]
},
{
"t": 191,
"s": [
720
]
}
],
"ix": 3
},
"m": 1,
"ix": 2,
"nm": "修剪路径 1",
"mn": "ADBE Vector Filter - Trim",
"hd": false
}
],
"ip": 0,
"op": 360,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 3,
"ty": 4,
"nm": "形状图层 1",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
251.5,
256.5,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": {
"a": 0,
"k": [
321,
321
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 3
},
"nm": "椭圆路径 1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
0.686274509804,
0.686274509804,
0.686274509804,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 15,
"ix": 5
},
"lc": 1,
"lj": 1,
"ml": 4,
"bm": 0,
"nm": "描边 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-1.5,
-6.5
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 6
},
"o": {
"a": 0,
"k": 100,
"ix": 7
},
"sk": {
"a": 0,
"k": 0,
"ix": 4
},
"sa": {
"a": 0,
"k": 0,
"ix": 5
},
"nm": "变换"
}
],
"nm": "椭圆 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 360,
"st": 0,
"bm": 0
}
]
}
],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 0,
"nm": "完整动画",
"refId": "comp_0",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
250,
250,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
250,
250,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6
}
},
"ao": 0,
"w": 500,
"h": 500,
"ip": 0,
"op": 96,
"st": 0,
"bm": 0
}
],
"markers": []
}

View File

@@ -0,0 +1,27 @@
{
"result": {
"data": {
"image": [
{
"order_img": "http://localhost/file.php?img=order__2020__09__23__2020092317123855002746__2020092317534044856.jpg",
"order_img_cloud": ""
}
],
"order": {
"address": "陕西省西安市雁塔区科技二路清华科技园",
"contractnum": "",
"custorname": "demo",
"custorphone": "13575542727",
"ordernum": "2020092317123855002746",
"ordertype": "4",
"recudesum": "100000.00",
"remark": "出彩出差错错错",
"systemtime": "2020-09-23 17:53:40",
"user_name": "王总"
}
},
"msg": "获取成功",
"success": 0
},
"status": 0
}

View File

@@ -0,0 +1,28 @@
package com.ruilaizi.example;
import android.app.Application;
import android.content.Context;
/**
* 模板项目 Application可在此做全局初始化。
*/
public class BaseApplication extends Application {
private static Context sContext;
private static BaseApplication sInstance;
public static BaseApplication getInstance() {
return sInstance;
}
public static Context getContext() {
return sContext;
}
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
sContext = getApplicationContext();
}
}

View File

@@ -0,0 +1,198 @@
package com.ruilaizi.example;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.chip.Chip;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.slider.Slider;
import com.google.android.material.textfield.TextInputEditText;
import com.ruilaizi.example.databinding.ActivityMainBinding;
import com.ruilaizi.example.ui.MainViewModel;
/**
* 文思泉涌 - AI 文本生成助手主界面。
* Material 3 风格,预设提示、输入、参数滑块、生成、流式结果与复制/重新生成/续写。
*/
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private MainViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MainViewModel.class);
setupPresets();
setupInput();
setupSliders();
setupGenerateButton();
setupResultAndActions();
if (binding.btnSettings != null) {
binding.btnSettings.setOnClickListener(v -> showApiKeyDialog());
}
observeViewModel();
showApiKeyHintIfNeeded();
}
private void setupPresets() {
setPresetChip(binding.presetEmail, getString(R.string.preset_email));
setPresetChip(binding.presetProduct, getString(R.string.preset_product));
setPresetChip(binding.presetStory, getString(R.string.preset_story));
setPresetChip(binding.presetSummary, getString(R.string.preset_summary));
setPresetChip(binding.presetPoem, getString(R.string.preset_poem));
}
private void setPresetChip(Chip chip, String text) {
if (chip == null) return;
chip.setOnClickListener(v -> {
TextInputEditText input = binding.inputPrompt;
if (input != null) {
Editable ed = input.getText();
String current = ed != null ? ed.toString() : "";
input.setText(current.isEmpty() ? text : current + "\n" + text);
viewModel.setPrompt(input.getText() != null ? input.getText().toString() : "");
}
});
}
private void setupInput() {
TextInputEditText input = binding.inputPrompt;
if (input != null) {
input.addTextChangedListener(new 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) {
viewModel.setPrompt(s != null ? s.toString() : "");
}
@Override
public void afterTextChanged(Editable s) {}
});
}
}
private void setupSliders() {
Slider lengthSlider = binding.sliderLength;
if (lengthSlider != null) {
lengthSlider.setValue(1f);
lengthSlider.addOnChangeListener((slider, value, fromUser) -> {
if (fromUser) viewModel.setLengthLevel((int) value);
});
}
Slider tempSlider = binding.sliderTemperature;
if (tempSlider != null) {
tempSlider.setValue(1f);
tempSlider.addOnChangeListener((slider, value, fromUser) -> {
if (fromUser) viewModel.setTemperatureLevel((int) value);
});
}
}
private void setupGenerateButton() {
MaterialButton btn = binding.btnGenerate;
if (btn != null) {
btn.setOnClickListener(v -> viewModel.generate());
}
}
private void setupResultAndActions() {
MaterialButton copyBtn = binding.btnCopy;
if (copyBtn != null) copyBtn.setOnClickListener(v -> viewModel.copyResult(this));
MaterialButton regenBtn = binding.btnRegenerate;
if (regenBtn != null) regenBtn.setOnClickListener(v -> viewModel.regenerate());
MaterialButton continueBtn = binding.btnContinue;
if (continueBtn != null) continueBtn.setOnClickListener(v -> viewModel.continueEdit());
}
private void observeViewModel() {
viewModel.getResultLiveData().observe(this, text -> {
TextView resultText = binding.resultText;
if (resultText != null) {
if (text == null || text.isEmpty()) {
resultText.setText(R.string.result_placeholder);
resultText.setTextColor(getResources().getColor(R.color.colorText, getTheme()));
} else {
resultText.setText(text);
resultText.setTextColor(getResources().getColor(R.color.colorTextDark, getTheme()));
}
}
});
viewModel.getLoadingLiveData().observe(this, loading -> {
ProgressBar progress = binding.progressBar;
MaterialButton btn = binding.btnGenerate;
if (progress != null) progress.setVisibility(loading != null && loading ? View.VISIBLE : View.GONE);
if (btn != null) btn.setEnabled(loading == null || !loading);
});
viewModel.getErrorLiveData().observe(this, error -> {
if (error != null && !error.isEmpty()) {
new AlertDialog.Builder(this)
.setMessage(error)
.setPositiveButton(android.R.string.ok, (d, w) -> viewModel.clearError())
.show();
}
});
viewModel.getSnackbarLiveData().observe(this, msg -> {
if (msg != null && !msg.isEmpty()) {
Snackbar.make(binding.getRoot(), msg, Snackbar.LENGTH_SHORT).show();
viewModel.clearSnackbar();
}
});
viewModel.getLengthLevelLiveData().observe(this, level -> {
Slider s = binding.sliderLength;
if (s != null && level != null && s.getValue() != level) s.setValue(level.floatValue());
});
viewModel.getTemperatureLevelLiveData().observe(this, level -> {
Slider s = binding.sliderTemperature;
if (s != null && level != null && s.getValue() != level) s.setValue(level.floatValue());
});
viewModel.getPromptLiveData().observe(this, prompt -> {
TextInputEditText input = binding.inputPrompt;
if (input != null && prompt != null) {
String current = input.getText() != null ? input.getText().toString() : "";
if (!current.equals(prompt)) input.setText(prompt);
}
});
}
private void showApiKeyHintIfNeeded() {
if (viewModel.getApiKey() == null || viewModel.getApiKey().isEmpty()) {
Snackbar.make(binding.getRoot(), "请在设置中配置 API Key 后使用生成功能", Snackbar.LENGTH_LONG)
.setAction("设置", v -> showApiKeyDialog())
.show();
}
}
private void showApiKeyDialog() {
android.widget.EditText edit = new android.widget.EditText(this);
edit.setHint("API Key建议使用后端网关不在此填写");
edit.setText(viewModel.getApiKey());
edit.setMinEms(20);
new AlertDialog.Builder(this)
.setTitle("API Key")
.setView(edit)
.setPositiveButton(android.R.string.ok, (d, w) -> {
viewModel.setApiKey(edit.getText() != null ? edit.getText().toString().trim() : "");
Snackbar.make(binding.getRoot(), "已保存", Snackbar.LENGTH_SHORT).show();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
}

View File

@@ -0,0 +1,126 @@
package com.ruilaizi.example.base;
import android.Manifest;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.ColorRes;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import com.ruilaizi.example.dialog.LoadingDialog;
import com.ruilaizi.example.utils.StatusBarUtil;
import com.ruilaizi.example.utils.ToastUtils;
/**
* 模板项目 Activity 基类提供通用能力状态栏、权限、Toast、Loading 等)。
*/
public abstract class BaseActivity extends FragmentActivity {
private LoadingDialog mProgressDialog;
public static final int REQUEST_CALL_PERMISSION = 10111;
protected final String TAG = this.getClass().getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.transparencyBar(this);
StatusBarUtil.StatusBarLightMode(this);
initFontScale();
}
protected void initFontScale() {
Configuration configuration = getResources().getConfiguration();
configuration.fontScale = 1f;
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity = configuration.fontScale * metrics.density;
getBaseContext().getResources().updateConfiguration(configuration, metrics);
}
public boolean checkReadPermission(String permission, int requestCode) {
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
return true;
}
ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);
return false;
}
public void requestPermission(int code, String... permissions) {
ActivityCompat.requestPermissions(this, permissions, code);
}
public void showProgressDialog(String message, Context context) {
if (mProgressDialog == null) {
mProgressDialog = new LoadingDialog(context);
mProgressDialog.setCanceledOnTouchOutside(false);
}
mProgressDialog.show();
}
public void dismissProgressDialog() {
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
}
public boolean hasPermission(String... permissions) {
for (String p : permissions) {
if (ContextCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
public void hiddenStatusBar() {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
public void startActivity(Class<?> clz) {
startActivity(new Intent(this, clz));
}
public void changeStatusBarColor(@ColorRes int color) {
}
/** 简单提示对话框 */
public void messageDialog(String msg) {
new AlertDialog.Builder(this)
.setMessage(msg)
.setPositiveButton(android.R.string.ok, null)
.show();
}
public void toast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
public boolean isNetworkConnected(Context context, String tipWhenNoNetwork) {
if (!com.ruilaizi.example.utils.NetUtils.isConnected(context)) {
ToastUtils.showToast(context, tipWhenNoNetwork);
return false;
}
return true;
}
/** 子类在布局中提供 id 为 iv_left_btn 的 View 时,可调用此方法绑定返回。 */
public void initNormalBack() {
int id = getResources().getIdentifier("iv_left_btn", "id", getPackageName());
if (id != 0) {
View back = findViewById(id);
if (back != null) back.setOnClickListener(v -> finish());
}
}
}

View File

@@ -0,0 +1,88 @@
package com.ruilaizi.example.common;
/**
* Created by Administrator on 2016/1/10.
* 作者xudiwei
* <p>
* 描述:常量类
*/
public class Constants {
/**
* 模拟网络请求成功
*/
public static final int SUCCESS = 1;
/**
* 模拟网络请求失败
*/
public static final int FAILE = 0;
/**
* 模拟数据网络耗时
*/
public static final int LOADING_TIME = 1500;
/**
* 模拟数据每页长度
*/
public static final int LIMIT = 20;
/**
* 讯飞语音appid
*/
public static final String IFLYTEK_APPID = "58a176c2";
/**
* 当前App创建在Sd卡里的根目录其它的目录都要创建在App_root_dir里面。
*/
public static final String APP_ROOT_DIR = "LifeTree";
/**
* 存图片的dir
*/
public static final String IMG_DIR = APP_ROOT_DIR + "/img";
/**
* 图片预览里保存图片的目录
*/
public static final String SAVE_IMG_DIR = APP_ROOT_DIR + "/save";
/**
* apk下载存放目录
*/
public static final String APK_DIR = APP_ROOT_DIR + "/apk";
/**
* 缓存目录
*/
public static final String CACHE_DIR = APP_ROOT_DIR + "/cache";
/**
* SharedPreferences 文件名
*/
public static final String SP_CONFIG_FILE_NAME = "config";
private static final String TAG = "Constants";
/**
* 当前用户的user_id(token)
*/
private static String userId = "";
/**
* 当前用户的角色 1=普通用户 2=老师 3=义工
*/
// private static int role = -1;
/**
* 当前用户所在的班级
*/
private static String classId = "";
/**
* 当前用户的IMidentify
*/
private static String identify = "";
/**
* 消息内存中的用户相关的信息。
*/
public static void clearAccountInfo() {
userId = "";
//role = -1;
classId = "";
identify = "";
}
}

View File

@@ -0,0 +1,19 @@
package com.ruilaizi.example.data.api;
/**
* AI 流式生成接口。实现类可对接 OpenAI / Gemini / 国内合规 API。
*/
public interface AIService {
/**
* 请求流式生成。在后台线程执行,通过 callback 在主线程或调用线程回调。
*
* @param prompt 用户提示词
* @param maxTokens 最大 token 数(短/中/长 可映射为具体数值)
* @param temperature 温度 0~2
* @param apiKey API 密钥(由调用方从 ApiKeyProvider 获取)
* @param callback 流式回调
*/
void requestStream(String prompt, int maxTokens, float temperature,
String apiKey, StreamCallback callback);
}

View File

@@ -0,0 +1,55 @@
package com.ruilaizi.example.data.api;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;
import com.ruilaizi.example.BuildConfig;
import java.io.IOException;
import java.security.GeneralSecurityException;
/**
* 提供 API Key优先使用用户设置EncryptedSharedPreferences
* 未设置时使用 BuildConfig.DEFAULT_API_KEY打包进 APK 的默认 DeepSeek Key
*/
public class ApiKeyProvider {
private static final String PREFS_NAME = "api_key_prefs";
private static final String KEY_API_KEY = "api_key";
private final SharedPreferences prefs;
public ApiKeyProvider(Context context) {
SharedPreferences fallback = context.getApplicationContext()
.getSharedPreferences(PREFS_NAME + "_plain", Context.MODE_PRIVATE);
SharedPreferences encrypted = fallback;
try {
MasterKey masterKey = new MasterKey.Builder(context.getApplicationContext())
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();
encrypted = EncryptedSharedPreferences.create(
context.getApplicationContext(),
PREFS_NAME,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
} catch (GeneralSecurityException | IOException ignored) {
}
prefs = encrypted;
}
public String getApiKey() {
String saved = prefs.getString(KEY_API_KEY, "");
if (!TextUtils.isEmpty(saved)) return saved;
return BuildConfig.DEFAULT_API_KEY != null ? BuildConfig.DEFAULT_API_KEY : "";
}
public void setApiKey(String apiKey) {
prefs.edit().putString(KEY_API_KEY, apiKey != null ? apiKey : "").apply();
}
}

View File

@@ -0,0 +1,140 @@
package com.ruilaizi.example.data.api;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.ruilaizi.example.BuildConfig;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用 HttpURLConnection 实现 OpenAI 兼容的 Chat Completions 流式接口SSE
* 当前默认对接 DeepSeek APIhttps://api.deepseek.com/v1模型 deepseek-chat。
*/
public class OpenAIStreamService implements AIService {
private static final String TAG = "OpenAIStreamService";
private static final String CHAT_URL = "chat/completions";
private static final String MODEL_DEEPSEEK = "deepseek-chat";
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final String baseUrl;
public OpenAIStreamService(String baseUrl) {
this.baseUrl = baseUrl == null || baseUrl.isEmpty()
? BuildConfig.API_BASE_URL
: baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
}
@Override
public void requestStream(String prompt, int maxTokens, float temperature,
String apiKey, StreamCallback callback) {
if (callback == null) return;
executor.execute(() -> {
try {
doRequestStream(prompt, maxTokens, temperature, apiKey, callback);
} catch (Throwable t) {
Log.e(TAG, "requestStream error", t);
postError(callback, t);
}
});
}
private void doRequestStream(String prompt, int maxTokens, float temperature,
String apiKey, StreamCallback callback) throws Exception {
String urlStr = baseUrl + CHAT_URL;
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setConnectTimeout(30_000);
conn.setReadTimeout(60_000);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Authorization", "Bearer " + apiKey);
conn.setRequestProperty("Accept", "text/event-stream");
JSONObject body = new JSONObject();
body.put("model", MODEL_DEEPSEEK);
body.put("stream", true);
body.put("max_tokens", maxTokens);
body.put("temperature", temperature);
JSONArray messages = new JSONArray();
JSONObject user = new JSONObject();
user.put("role", "user");
user.put("content", prompt);
messages.put(user);
body.put("messages", messages);
try (OutputStream os = conn.getOutputStream()) {
os.write(body.toString().getBytes(StandardCharsets.UTF_8));
}
int code = conn.getResponseCode();
if (code != 200) {
String err = readAll(conn.getErrorStream());
throw new RuntimeException("HTTP " + code + ": " + err);
}
BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String line;
StringBuilder fullContent = new StringBuilder();
while ((line = reader.readLine()) != null) {
if (line.startsWith("data: ")) {
String data = line.substring(6).trim();
if ("[DONE]".equals(data)) break;
try {
JSONObject obj = new JSONObject(data);
JSONArray choices = obj.optJSONArray("choices");
if (choices != null && choices.length() > 0) {
JSONObject choice = choices.getJSONObject(0);
JSONObject delta = choice.optJSONObject("delta");
if (delta != null && delta.has("content")) {
String content = delta.getString("content");
fullContent.append(content);
postChunk(callback, content);
}
}
} catch (Exception ignore) {
}
}
}
postComplete(callback);
}
private static String readAll(java.io.InputStream is) {
if (is == null) return "";
try (BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = r.readLine()) != null) sb.append(line).append("\n");
return sb.toString();
} catch (Exception e) {
return e.getMessage();
}
}
private void postChunk(StreamCallback callback, String chunk) {
mainHandler.post(() -> callback.onChunk(chunk));
}
private void postComplete(StreamCallback callback) {
mainHandler.post(callback::onComplete);
}
private void postError(StreamCallback callback, Throwable t) {
mainHandler.post(() -> callback.onError(t));
}
}

View File

@@ -0,0 +1,13 @@
package com.ruilaizi.example.data.api;
/**
* 流式生成时的回调:每收到一段文本就回调一次,结束时 onComplete出错时 onError。
*/
public interface StreamCallback {
void onChunk(String textChunk);
void onComplete();
void onError(Throwable t);
}

View File

@@ -0,0 +1,30 @@
package com.ruilaizi.example.data.db;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
@Database(entities = {GenerationRecord.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static volatile AppDatabase INSTANCE;
public static AppDatabase getInstance(Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
context.getApplicationContext(),
AppDatabase.class,
"wensiquanyong_db"
).build();
}
}
}
return INSTANCE;
}
public abstract GenerationRecordDao generationRecordDao();
}

View File

@@ -0,0 +1,37 @@
package com.ruilaizi.example.data.db;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
/**
* 单条生成记录,用于 Room 缓存最近生成内容。
*/
@Entity(tableName = "generation_records")
public class GenerationRecord {
@PrimaryKey(autoGenerate = true)
public long id;
/** 用户输入的提示词 */
public String prompt;
/** AI 生成的结果全文 */
public String result;
/** 生成长度0 短 1 中 2 长 */
public int maxTokensLevel;
/** 创意程度0 保守 1 平衡 2 创意 */
public int temperatureLevel;
/** 创建时间戳 */
public long createdAt;
public GenerationRecord() {}
@Ignore
public GenerationRecord(String prompt, String result, int maxTokensLevel, int temperatureLevel) {
this.prompt = prompt;
this.result = result;
this.maxTokensLevel = maxTokensLevel;
this.temperatureLevel = temperatureLevel;
this.createdAt = System.currentTimeMillis();
}
}

View File

@@ -0,0 +1,21 @@
package com.ruilaizi.example.data.db;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
@Dao
public interface GenerationRecordDao {
@Insert
long insert(GenerationRecord record);
@Query("SELECT * FROM generation_records ORDER BY createdAt DESC LIMIT 10")
LiveData<List<GenerationRecord>> getRecentRecords();
@Query("DELETE FROM generation_records WHERE id NOT IN (SELECT id FROM generation_records ORDER BY createdAt DESC LIMIT 10)")
void keepOnlyRecent();
}

View File

@@ -0,0 +1,79 @@
package com.ruilaizi.example.data.repository;
import android.content.Context;
import androidx.lifecycle.LiveData;
import com.ruilaizi.example.data.api.AIService;
import com.ruilaizi.example.data.api.ApiKeyProvider;
import com.ruilaizi.example.data.api.StreamCallback;
import com.ruilaizi.example.data.db.AppDatabase;
import com.ruilaizi.example.data.db.GenerationRecord;
import com.ruilaizi.example.data.db.GenerationRecordDao;
/**
* 生成业务仓库:调用 AI 流式接口,并缓存记录到 Room。
*/
public class GenerationRepository {
private final AIService aiService;
private final ApiKeyProvider apiKeyProvider;
private final GenerationRecordDao dao;
public GenerationRepository(Context context) {
apiKeyProvider = new ApiKeyProvider(context);
aiService = new com.ruilaizi.example.data.api.OpenAIStreamService(null);
dao = AppDatabase.getInstance(context).generationRecordDao();
}
public String getApiKey() {
return apiKeyProvider.getApiKey();
}
public void setApiKey(String key) {
apiKeyProvider.setApiKey(key);
}
/**
* 将长度档位转为 max_tokens0 短 1 中 2 长
*/
public static int levelToMaxTokens(int level) {
switch (level) {
case 0: return 256;
case 1: return 512;
case 2: return 1024;
default: return 512;
}
}
/**
* 将创意档位转为 temperature0 保守 1 平衡 2 创意
*/
public static float levelToTemperature(int level) {
switch (level) {
case 0: return 0.3f;
case 1: return 0.7f;
case 2: return 1.2f;
default: return 0.7f;
}
}
public void requestStream(String prompt, int maxTokensLevel, int temperatureLevel,
StreamCallback callback) {
int maxTokens = levelToMaxTokens(maxTokensLevel);
float temperature = levelToTemperature(temperatureLevel);
String apiKey = apiKeyProvider.getApiKey();
aiService.requestStream(prompt, maxTokens, temperature, apiKey, callback);
}
public void saveRecord(GenerationRecord record) {
new Thread(() -> {
dao.insert(record);
dao.keepOnlyRecent();
}).start();
}
public LiveData<java.util.List<GenerationRecord>> getRecentRecords() {
return dao.getRecentRecords();
}
}

View File

@@ -0,0 +1,65 @@
package com.ruilaizi.example.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
/**
* 日期2017.01.11
* <p>
* 作者xudiwei
* <p>
* 描述base对话框
*/
public class BaseDialog extends Dialog implements View.OnClickListener {
protected Context mContext;
private View view;
private boolean isDismiss = true;
public BaseDialog(Context context) {
super(context);
}
public BaseDialog(Context context, int themeResId) {
super(context, themeResId);
}
public BaseDialog(Context context, View view, int themeResId) {
super(context, themeResId);
this.mContext = context;
this.view = view;
this.view.setOnClickListener(this);
setContentView(view);
}
@Override
public void show() {
super.show();
WindowManager windowManager = ((Activity) mContext).getWindowManager();
Display display = windowManager.getDefaultDisplay();
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.width = display.getWidth(); //设置宽度
getWindow().setAttributes(lp);
}
@Override
public void onClick(View v) {
if (isDismiss) {
dismiss();
}
}
/**
* 当点击对话框外面时是否关闭对话框,默认是要关闭对话框
*
* @param b
*/
public void clickOutsideDismiss(boolean b) {
this.isDismiss = b;
}
}

View File

@@ -0,0 +1,46 @@
package com.ruilaizi.example.dialog;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import com.ruilaizi.example.R;
/**
* Created by: xudiwei
* <p>
* on: 2017/5/16.
* <p>
* 描述:加载中禁止用户手机关闭的对话框
*/
public class LoadingDialog extends BaseDialog {
private boolean mClose = true;
public LoadingDialog(Context context) {
super(context, View.inflate(context, R.layout.dialog_loading, null), R.style.loadingDialog);
setCanceledOnTouchOutside(false);
}
@Override
public void onClick(View v) {
// super.onClick(v);
}
@Override
public void onBackPressed() {
super.onBackPressed();
if (mClose) {
((Activity) mContext).finish();
}
}
/**
* 当用户按下返回键时是否关闭Activity
* @param close
*/
public void setPressBackCloseActivity(boolean close) {
this.mClose = close;
}
}

View File

@@ -0,0 +1,170 @@
package com.ruilaizi.example.ui;
import android.app.Application;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.ruilaizi.example.data.api.StreamCallback;
import com.ruilaizi.example.data.db.GenerationRecord;
import com.ruilaizi.example.data.repository.GenerationRepository;
/**
* 主界面 MVVM ViewModel提示词、参数、生成结果、加载状态及复制/重新生成/续写。
*/
public class MainViewModel extends AndroidViewModel {
private final GenerationRepository repository;
private final MutableLiveData<String> promptLiveData = new MutableLiveData<>("");
private final MutableLiveData<String> resultLiveData = new MutableLiveData<>("");
private final MutableLiveData<Boolean> loadingLiveData = new MutableLiveData<>(false);
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
private final MutableLiveData<String> snackbarLiveData = new MutableLiveData<>();
/** 生成长度0 短 1 中 2 长 */
private final MutableLiveData<Integer> lengthLevelLiveData = new MutableLiveData<>(1);
/** 创意程度0 保守 1 平衡 2 创意 */
private final MutableLiveData<Integer> temperatureLevelLiveData = new MutableLiveData<>(1);
/** 最近一次生成用的 prompt用于“重新生成” */
private String lastPrompt;
private int lastLengthLevel = 1;
private int lastTemperatureLevel = 1;
private final StringBuilder streamingBuffer = new StringBuilder();
public MainViewModel(@NonNull Application application) {
super(application);
repository = new GenerationRepository(application);
}
public LiveData<String> getPromptLiveData() { return promptLiveData; }
public LiveData<String> getResultLiveData() { return resultLiveData; }
public LiveData<Boolean> getLoadingLiveData() { return loadingLiveData; }
public LiveData<String> getErrorLiveData() { return errorLiveData; }
public LiveData<String> getSnackbarLiveData() { return snackbarLiveData; }
public LiveData<Integer> getLengthLevelLiveData() { return lengthLevelLiveData; }
public LiveData<Integer> getTemperatureLevelLiveData() { return temperatureLevelLiveData; }
public void setPrompt(String prompt) {
promptLiveData.setValue(prompt != null ? prompt : "");
}
public void setLengthLevel(int level) {
if (level < 0) level = 0;
if (level > 2) level = 2;
lengthLevelLiveData.setValue(level);
}
public void setTemperatureLevel(int level) {
if (level < 0) level = 0;
if (level > 2) level = 2;
temperatureLevelLiveData.setValue(level);
}
/** 生成:使用当前 prompt 与参数 */
public void generate() {
String prompt = promptLiveData.getValue();
if (prompt == null) prompt = "";
prompt = prompt.trim();
if (prompt.isEmpty()) {
snackbarLiveData.setValue("请输入提示词");
return;
}
if (repository.getApiKey() == null || repository.getApiKey().isEmpty()) {
errorLiveData.setValue("请先在设置中配置 API Key或使用后端网关");
return;
}
lastPrompt = prompt;
lastLengthLevel = lengthLevelLiveData.getValue() != null ? lengthLevelLiveData.getValue() : 1;
lastTemperatureLevel = temperatureLevelLiveData.getValue() != null ? temperatureLevelLiveData.getValue() : 1;
streamingBuffer.setLength(0);
resultLiveData.setValue("");
loadingLiveData.setValue(true);
errorLiveData.setValue(null);
repository.requestStream(prompt, lastLengthLevel, lastTemperatureLevel, new StreamCallback() {
@Override
public void onChunk(String textChunk) {
streamingBuffer.append(textChunk);
resultLiveData.postValue(streamingBuffer.toString());
}
@Override
public void onComplete() {
loadingLiveData.postValue(false);
String full = streamingBuffer.toString();
if (full.length() > 0) {
repository.saveRecord(new GenerationRecord(
lastPrompt, full, lastLengthLevel, lastTemperatureLevel));
}
snackbarLiveData.postValue("生成完成");
}
@Override
public void onError(Throwable t) {
loadingLiveData.postValue(false);
errorLiveData.postValue(t != null ? t.getMessage() : "生成失败");
}
});
}
/** 重新生成:用上次的 prompt 和参数再调一次 */
public void regenerate() {
if (lastPrompt != null && !lastPrompt.isEmpty()) {
setPrompt(lastPrompt);
setLengthLevel(lastLengthLevel);
setTemperatureLevel(lastTemperatureLevel);
generate();
} else {
snackbarLiveData.setValue("请先完成一次生成");
}
}
/** 续写:把当前结果作为新 prompt清空结果区可编辑后生成 */
public void continueEdit() {
String result = resultLiveData.getValue();
if (result != null && !result.isEmpty()) {
setPrompt(result);
resultLiveData.setValue("");
snackbarLiveData.setValue("已填入输入框,可修改后点击生成");
} else {
snackbarLiveData.setValue("暂无生成结果可续写");
}
}
/** 复制结果到剪贴板 */
public void copyResult(Context context) {
String result = resultLiveData.getValue();
if (result == null || result.isEmpty()) {
snackbarLiveData.setValue("暂无内容可复制");
return;
}
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (cm != null) {
cm.setPrimaryClip(ClipData.newPlainText("AI生成", result));
snackbarLiveData.setValue("已复制到剪贴板");
}
}
public String getApiKey() {
return repository.getApiKey();
}
public void setApiKey(String key) {
repository.setApiKey(key);
}
public void clearError() {
errorLiveData.setValue(null);
}
public void clearSnackbar() {
snackbarLiveData.setValue(null);
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright (C) 2016 android@19code.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ruilaizi.example.utils;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
/**
* Blog : http://blog.csdn.net/u011240877
* 网络工具
*
*/
public class NetUtils {
public static final String NETWORK_TYPE_WIFI = "wifi";
public static final String NETWORK_TYPE_3G = "3g";
public static final String NETWORK_TYPE_2G = "2g";
public static final String NETWORK_TYPE_WAP = "wap";
public static final String NETWORK_TYPE_UNKNOWN = "unknown";
public static final String NETWORK_TYPE_DISCONNECT = "disconnect";
public static int getNetworkType(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager == null ? null : connectivityManager.getActiveNetworkInfo();
return networkInfo == null ? -1 : networkInfo.getType();
}
public static String getNetworkTypeName(Context context) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo;
String type = NETWORK_TYPE_DISCONNECT;
if (manager == null || (networkInfo = manager.getActiveNetworkInfo()) == null) {
return type;
}
if (networkInfo.isConnected()) {
String typeName = networkInfo.getTypeName();
if ("WIFI".equalsIgnoreCase(typeName)) {
type = NETWORK_TYPE_WIFI;
} else if ("MOBILE".equalsIgnoreCase(typeName)) {
//String proxyHost = android.net.Proxy.getDefaultHost();//deprecated
String proxyHost = System.getProperty("http.proxyHost");
type = TextUtils.isEmpty(proxyHost) ? (isFastMobileNetwork(context) ? NETWORK_TYPE_3G : NETWORK_TYPE_2G) : NETWORK_TYPE_WAP;
} else {
type = NETWORK_TYPE_UNKNOWN;
}
}
return type;
}
public static boolean isConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
return false;
}
public static boolean isNetworkAvailable(Context context) {
if (context == null) {
return false;
}
try {
ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
NetworkInfo info = connectivity.getActiveNetworkInfo();
return info.isAvailable();
}
} catch (Exception e) {
return false;
}
return false;
}
public static boolean isWiFi(Context cxt) {
ConnectivityManager cm = (ConnectivityManager) cxt.getSystemService(Context.CONNECTIVITY_SERVICE);
// wifi的状态ConnectivityManager.TYPE_WIFI
// 3G的状态ConnectivityManager.TYPE_MOBILE
return cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
}
//unchecked
public static void openNetSetting(Activity act) {
Intent intent = new Intent();
ComponentName cm = new ComponentName("com.android.settings", "com.android.settings.WirelessSettings");
intent.setComponent(cm);
intent.setAction("android.intent.action.VIEW");
act.startActivityForResult(intent, 0);
}
/**
* Whether is fast mobile network
*/
private static boolean isFastMobileNetwork(Context context) {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager == null) {
return false;
}
switch (telephonyManager.getNetworkType()) {
case TelephonyManager.NETWORK_TYPE_1xRTT:
return false;
case TelephonyManager.NETWORK_TYPE_CDMA:
return false;
case TelephonyManager.NETWORK_TYPE_EDGE:
return false;
case TelephonyManager.NETWORK_TYPE_EVDO_0:
return true;
case TelephonyManager.NETWORK_TYPE_EVDO_A:
return true;
case TelephonyManager.NETWORK_TYPE_GPRS:
return false;
case TelephonyManager.NETWORK_TYPE_HSDPA:
return true;
case TelephonyManager.NETWORK_TYPE_HSPA:
return true;
case TelephonyManager.NETWORK_TYPE_HSUPA:
return true;
case TelephonyManager.NETWORK_TYPE_UMTS:
return true;
case TelephonyManager.NETWORK_TYPE_EHRPD:
return true;
case TelephonyManager.NETWORK_TYPE_EVDO_B:
return true;
case TelephonyManager.NETWORK_TYPE_HSPAP:
return true;
case TelephonyManager.NETWORK_TYPE_IDEN:
return false;
case TelephonyManager.NETWORK_TYPE_LTE:
return true;
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
return false;
default:
return false;
}
}
public static void setWifiEnabled(Context context, boolean enabled) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(enabled);
}
public static void setDataEnabled(Context context, boolean enabled) {
ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
Class<?> conMgrClass = null;
Field iConMgrField = null;
Object iConMgr = null;
Class<?> iConMgrClass = null;
Method setMobileDataEnabledMethod = null;
try {
conMgrClass = Class.forName(conMgr.getClass().getName());
iConMgrField = conMgrClass.getDeclaredField("mService");
iConMgrField.setAccessible(true);
iConMgr = iConMgrField.get(conMgr);
iConMgrClass = Class.forName(iConMgr.getClass().getName());
setMobileDataEnabledMethod = iConMgrClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE);
setMobileDataEnabledMethod.setAccessible(true);
setMobileDataEnabledMethod.invoke(iConMgr, enabled);
} catch (Exception e) {
e.printStackTrace();
}
}
public static List<ScanResult> getWifiScanResults(Context context) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
return wifiManager.startScan() ? wifiManager.getScanResults() : null;
}
public static ScanResult getScanResultsByBSSID(Context context, String bssid) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
ScanResult scanResult = null;
boolean f = wifiManager.startScan();
if (!f) {
getScanResultsByBSSID(context, bssid);
}
List<ScanResult> list = wifiManager.getScanResults();
if (list != null) {
for (int i = 0; i < list.size(); i++) {
scanResult = list.get(i);
if (scanResult.BSSID.equals(bssid)) {
break;
}
}
}
return scanResult;
}
public static WifiInfo getWifiConnectionInfo(Context context) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
return wifiManager.getConnectionInfo();
}
}

View File

@@ -0,0 +1,205 @@
package com.ruilaizi.example.utils;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import com.readystatesoftware.systembartint.SystemBarTintManager;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by XiaoQiang on 2017/6/21.
*/
public class StatusBarUtil {
/**
* 修改状态栏为全透明
* @param activity
*/
@TargetApi(19)
public static void transparencyBar(Activity activity){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.WHITE);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window =activity.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/**
* 修改状态栏颜色支持4.4以上版本
* @param activity
* @param colorId
*/
public static void setStatusBarColor(Activity activity,int colorId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(activity.getResources().getColor(colorId));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//使用SystemBarTint库使4.4版本状态栏变色,需要先将状态栏设置为透明
transparencyBar(activity);
SystemBarTintManager tintManager = new SystemBarTintManager(activity);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(colorId);
}
}
/**
*状态栏亮色模式,设置状态栏黑色文字、图标,
* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
* @param activity
* @return 1:MIUUI 2:Flyme 3:android6.0
*/
public static int StatusBarLightMode(Activity activity){
int result=0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if(MIUISetStatusBarLightMode(activity, true)){
result=1;
}else if(FlymeSetStatusBarLightMode(activity.getWindow(), true)){
result=2;
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
result=3;
}else{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.BLACK);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window =activity.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
return result;
}
/**
* 已知系统类型时,设置状态栏黑色文字、图标。
* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
* @param activity
* @param type 1:MIUUI 2:Flyme 3:android6.0
*/
public static void StatusBarLightMode(Activity activity,int type){
if(type==1){
MIUISetStatusBarLightMode(activity, true);
}else if(type==2){
FlymeSetStatusBarLightMode(activity.getWindow(), true);
}else if(type==3){
activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
/**
* 状态栏暗色模式清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标
*/
public static void StatusBarDarkMode(Activity activity,int type){
if(type==1){
MIUISetStatusBarLightMode(activity, false);
}else if(type==2){
FlymeSetStatusBarLightMode(activity.getWindow(), false);
}else if(type==3){
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
/**
* 设置状态栏图标为深色和魅族特定的文字风格
* 可以用来判断是否为Flyme用户
* @param window 需要设置的窗口
* @param dark 是否把状态栏文字及图标颜色设置为深色
* @return boolean 成功执行返回true
*
*/
public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
boolean result = false;
if (window != null) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class
.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class
.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
result = true;
} catch (Exception e) {
}
}
return result;
}
/**
* 需要MIUIV6以上
* @param activity
* @param dark 是否把状态栏文字及图标颜色设置为深色
* @return boolean 成功执行返回true
*
*/
public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
boolean result = false;
Window window=activity.getWindow();
if (window != null) {
Class clazz = window.getClass();
try {
int darkModeFlag = 0;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if(dark){
extraFlagField.invoke(window,darkModeFlag,darkModeFlag);//状态栏透明且黑色字体
}else{
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
}
result=true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//开发版 7.7.13 及以后版本采用了系统API旧方法无效但不会报错所以两个方式都要加上
if(dark){
activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
}catch (Exception e){
}
}
return result;
}
}

View File

@@ -0,0 +1,18 @@
package com.ruilaizi.example.utils;
import android.content.Context;
import android.widget.Toast;
/**
* 模板项目 Toast 工具类。
*/
public class ToastUtils {
public static void showToast(Context context, String message) {
Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
public static void showToast(Context context, int stringResId) {
Toast.makeText(context.getApplicationContext(), stringResId, Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:toYDelta="100%p"
android:duration="500" />
</set>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--
有四种动画alpha (透明变化) translate(位置移动) scale(缩放) rotate(旋转),
android:duration: 动画运行时间定义在多次时间ms内完成动画
android:startOffset: 延迟一定时间后运行动画
fromXDelta: X轴方向开始位置可以是%,也可以是具体的像素 具体见图
toXDelta: X轴方向结束位置可以是%,也可以是具体的像素
fromYDelta: Y轴方向开始位置可以是%,也可以是具体的像素
toYDelta: Y轴方向结束位置可以是%,也可以是具体的像素
-->
<translate
android:duration="500"
android:fromXDelta="0"
android:fromYDelta="100%p"
android:toXDelta="0"
android:toYDelta="0%p" />
</set>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="0"
android:fromYDelta="0%p"
android:toXDelta="0"
android:toYDelta="100%p" />
</set>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="100%p"
android:duration="500" />
</set>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!-- 上下滑入式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="200"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
</set>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!-- 上下滑入式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="200"
android:fromYDelta="0"
android:toYDelta="100%p" />
</set>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromAlpha="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="1.0" />

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="0.0" />

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_decelerate"
>
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0" />
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="0.95"
android:toAlpha="1" />
</set>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="0"
android:toYDelta="100%p" />
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1"
android:toAlpha="0.95" />
</set>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorAccent" android:state_selected="true"/>
<item android:color="@color/colorTextDark" android:state_selected="false"/>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorAccent" android:state_enabled="true"/>
<item android:color="@color/colorText" android:state_enabled="false"/>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/colorPrimary" android:state_checked="true" />
<item android:color="@color/colorAccent" />
</selector><!-- From: file:/D:/Android/work/MerchantEdition/app/src/main/res/drawable/main_menu_selector.xml -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ff848484" />
<corners android:topLeftRadius="13dp" android:topRightRadius="13dp" android:bottomLeftRadius="13dp" android:bottomRightRadius="13dp" />
</shape>

View File

@@ -0,0 +1,288 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorSurface"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- 标题栏:应用名 + 设置 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/app_name"
android:textColor="@color/colorPrimary"
android:textSize="24sp"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_settings"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设置" />
</LinearLayout>
<!-- 预设提示区 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="常用提示"
android:textColor="@color/colorTextSecondary"
android:textSize="12sp"
android:layout_marginBottom="8dp" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/preset_chip_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="false"
app:selectionRequired="false"
android:layout_marginBottom="16dp">
<com.google.android.material.chip.Chip
android:id="@+id/preset_email"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/preset_email" />
<com.google.android.material.chip.Chip
android:id="@+id/preset_product"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/preset_product" />
<com.google.android.material.chip.Chip
android:id="@+id/preset_story"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/preset_story" />
<com.google.android.material.chip.Chip
android:id="@+id/preset_summary"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/preset_summary" />
<com.google.android.material.chip.Chip
android:id="@+id/preset_poem"
style="@style/Widget.MaterialComponents.Chip.Choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/preset_poem" />
</com.google.android.material.chip.ChipGroup>
<!-- 输入区 -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/prompt_hint"
app:boxBackgroundColor="@color/colorCard"
app:boxStrokeColor="@color/colorPrimary"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_marginBottom="12dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input_prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minLines="4"
android:gravity="top"
android:inputType="textMultiLine|textCapSentences" />
</com.google.android.material.textfield.TextInputLayout>
<!-- 生成长度 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="生成长度"
android:textColor="@color/colorTextSecondary"
android:textSize="12sp"
android:layout_marginBottom="4dp" />
<com.google.android.material.slider.Slider
android:id="@+id/slider_length"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="0"
android:valueTo="2"
android:stepSize="1"
android:value="1"
app:labelBehavior="gone"
android:layout_marginBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="12dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/length_short"
android:textSize="12sp"
android:textColor="@color/colorTextSecondary" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/length_medium"
android:textSize="12sp"
android:gravity="center"
android:textColor="@color/colorTextSecondary" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/length_long"
android:textSize="12sp"
android:gravity="end"
android:textColor="@color/colorTextSecondary" />
</LinearLayout>
<!-- 创意程度 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="创意程度"
android:textColor="@color/colorTextSecondary"
android:textSize="12sp"
android:layout_marginBottom="4dp" />
<com.google.android.material.slider.Slider
android:id="@+id/slider_temperature"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="0"
android:valueTo="2"
android:stepSize="1"
android:value="1"
app:labelBehavior="gone"
android:layout_marginBottom="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/temp_conservative"
android:textSize="12sp"
android:textColor="@color/colorTextSecondary" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/temp_balanced"
android:textSize="12sp"
android:gravity="center"
android:textColor="@color/colorTextSecondary" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/temp_creative"
android:textSize="12sp"
android:gravity="end"
android:textColor="@color/colorTextSecondary" />
</LinearLayout>
<!-- 生成按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_generate"
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="@string/btn_generate"
app:backgroundTint="@color/colorPrimary"
android:textColor="@color/colorWhite"
android:layout_marginBottom="20dp" />
<!-- 加载指示 -->
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
android:layout_marginBottom="16dp" />
<!-- 结果展示区 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="生成结果"
android:textColor="@color/colorTextSecondary"
android:textSize="12sp"
android:layout_marginBottom="8dp" />
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/colorCard"
app:cardElevation="2dp"
app:cardCornerRadius="12dp"
android:minHeight="120dp"
android:layout_marginBottom="12dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:padding="16dp">
<TextView
android:id="@+id/result_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/result_placeholder"
android:textColor="@color/colorText"
android:textSize="15sp"
android:lineSpacingMultiplier="1.2"
tools:text="这里是流式显示的结果文本…" />
</ScrollView>
</com.google.android.material.card.MaterialCardView>
<!-- 操作按钮栏 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:paddingTop="8dp"
android:paddingBottom="24dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_copy"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_copy" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_regenerate"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_regenerate" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_continue"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_continue" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_dialog"
android:gravity="center"
android:orientation="vertical"
android:padding="24dp">
<ProgressBar
android:layout_width="48dp"
android:layout_height="48dp"
android:indeterminateTint="@color/colorPrimary" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/loading"
android:textColor="@color/colorTextDark"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Some files were not shown because too many files have changed in this diff Show More