Compare commits

..

9 Commits

108
.gitignore vendored

@ -1,108 +0,0 @@
# ---> Java
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
# ---> Gradle
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
# ---> IntelliJ IDEA
.idea/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
# ---> Eclipse
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
# ---> NetBeans
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
# ---> VS Code
.vscode/
# ---> Maven
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
# ---> Android
*.apk
*.ap_
*.dex
*.class
**/build/
**/local.properties
**/captures/
**/.externalNativeBuild/
**/.cxx/
*.keystore
!debug.keystore
# ---> SLMS Project Specific
# Temporary files
temp_check.txt
*.tmp
# Database files
*.db
*.sqlite
*.sqlite3
# Log files
logs/
*.log
# Test reports
surefire-reports/
TEST-*.xml
*.dumpstream
# Build output directories
backend/build/
android/build/
src/main/**/build/
src/test/**/build/

@ -1,3 +1,19 @@
# CHZU_CS231_SEB
https://gitlink.org.cn/czldl123/CHZU_CS231_SEB_lab.git
SLMS é¡¹ç®æž„建制å“<C3A5>
构建ç¼å<EFBFBD>·: #126
构建时间: 2025/11/26 ÖÜÈý 23:18:11.22
## 制å“<C3A5>列表
- slms-cli.jar - CLI 命令行应�
- slms-gui.jar - GUI 桌é<C592>¢åº”用 (JAR)
- slms-gui.exe - GUI 桌é<C592>¢åº”用 (EXE)
- slms-gui.zip - GUI 桌é<C592>¢åº”用 (ZIP - 如果 EXE ä¸<C3A4>å<EFBFBD>¯ç”?
- slms-gui-installer.msi - GUI 安装�(MSI)
- slms-web.war - Web 应用
- slms-debug.apk - Android 应用
- library.db - æ•°æ<C2B0>®åº“æ‡ä»?
## 使用说明
详细使用说明请å<EFBFBD>考å<EFBFBD>„应用çš?README 文件ã€?

60
SLMS/.gitignore vendored

@ -1,60 +0,0 @@
# 构建输出
build/
target/
*.class
*.jar
*.war
*.ear
# Gradle
.gradle/
gradle-app.setting
!gradle-wrapper.jar
# Maven
.mvn/
!.mvn/wrapper/maven-wrapper.jar
# Android
*.apk
*.ap_
*.dex
local.properties
.idea/
*.iml
.DS_Store
/captures
.externalNativeBuild
.cxx
# 备份文件
*.bak
*.tmp
*.swp
*~
# 日志文件
*.log
# 临时文件
test_report.txt
*.png
*.jpg
!docs/**/*.png
!docs/**/*.jpg
# IDE
.vscode/
.settings/
.classpath
.project
*.iws
*.ipr
# 操作系统
Thumbs.db
.DS_Store
# 数据库备份
*.db.backup
*.db-journal

826
SLMS/Jenkinsfile vendored

@ -1,826 +0,0 @@
pipeline {
agent any
environment {
JAVA_HOME = 'E:\\2025-2026\\GitAIOps\\jdk'
ANDROID_HOME = 'D:\\development\\Android'
WIX_HOME = 'C:\\Program Files (x86)\\WiX Toolset v3.11'
SONAR_HOST_URL = 'http://localhost:9000'
SONAR_PROJECT_KEY = 'slms:slms'
SONARQUBE_SCANNER_PARAMS = '-Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=300'
SONARQUBE_QUALITY_GATE = 'SLMS-Quality-Gate'
}
tools {
maven 'maven396'
jdk 'jdk21'
}
stages {
stage('1. 拉取Gitea代码') {
steps {
echo '========== 从 Gitea 拉取代码 =========='
// 清理可能被占用的文件
script {
try {
bat '''
@echo off
echo 清理工作空间中可能被占用的文件...
REM 只结束与当前工作空间相关的 Java 进程
for /f "tokens=2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /FO LIST ^| findstr /C:"PID:"') do (
wmic process where "ProcessId=%%i and CommandLine like '%%SLMS%%'" delete 2>nul
)
REM 等待进程结束
timeout /t 1 /nobreak >nul
echo ✓ 清理完成
'''
} catch (Exception e) {
echo "清理过程中出现警告: ${e.message}"
}
}
checkout scm
echo '✓ 代码拉取成功'
}
}
stage('2. Maven编译') {
steps {
echo '========== Maven 编译 SLMS 项目 =========='
dir('SLMS') {
bat '''
set JAVA_HOME=%JAVA_HOME%
REM 强制删除 target 目录(如果存在)
if exist target (
echo 清理旧的 target 目录...
rmdir /S /Q target 2>nul
if exist target (
echo 警告: 无法删除 target 目录,尝试使用 Maven clean...
) else (
echo ✓ target 目录已清理
)
)
REM Maven 编译
mvn clean compile test-compile -DskipTests
'''
}
echo '✓ 项目编译成功'
}
}
stage('3. 运行Mock测试') {
steps {
echo '========== 运行单元测试 =========='
dir('SLMS') {
script {
try {
bat '''
set JAVA_HOME=%JAVA_HOME%
mvn test
'''
echo '✓ 测试执行完成'
} catch (Exception e) {
echo '⚠️ 测试失败,但继续流水线'
echo "错误信息: ${e.message}"
currentBuild.result = 'UNSTABLE'
}
}
}
}
post {
always {
junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
}
}
}
stage('4. Sonar质检') {
when {
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
}
steps {
echo '========== 执行 SonarQube 代码质量检测 =========='
dir('SLMS') {
withSonarQubeEnv('SonarQube') {
bat '''
set JAVA_HOME=%JAVA_HOME%
mvn sonar:sonar -Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=300
'''
}
}
echo '✓ SonarQube 分析完成'
}
}
stage('5. 质量门禁') {
steps {
echo '========== 等待 SLMS-Quality-Gate 质量门禁结果 =========='
timeout(time: 10, unit: 'MINUTES') {
script {
withSonarQubeEnv('SonarQube') {
def qg = waitForQualityGate()
if (qg.status != 'OK') {
error "SLMS-Quality-Gate 质量门禁未通过: ${qg.status}"
} else {
echo "✓ SLMS-Quality-Gate 质量门禁检查通过: ${qg.status}"
}
}
}
}
}
}
stage('6. 准备打包') {
when {
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
}
steps {
echo '========== 复制 target 目录用于并行打包 =========='
dir('SLMS') {
bat '''
REM 为 CLI、GUI、Web、Android 创建独立的 target 副本
echo 复制 target 到 target-cli...
xcopy /E /I /Y /Q target target-cli >nul
echo 复制 target 到 target-gui...
xcopy /E /I /Y /Q target target-gui >nul
echo 复制 target 到 target-web...
xcopy /E /I /Y /Q target target-web >nul
echo 复制 target 到 target-android...
xcopy /E /I /Y /Q target target-android >nul
echo ✓ target 目录复制完成4份准备并行打包
'''
}
}
}
stage('7. 四端并行打包') {
when {
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
}
parallel {
stage('7.1 CLI 打包 (JAR)') {
steps {
echo '========== 打包 CLI 应用 (JAR) =========='
dir('SLMS') {
bat '''
set JAVA_HOME=%JAVA_HOME%
echo 使用 target-cli 打包 CLI 应用...
REM 临时设置 Maven 输出目录为 target-cli
mvn package -Pcli -DskipTests -Dmaven.compiler.skip=true -Dmaven.build.directory=target-cli
REM 复制生成的 shaded JAR 到 target
if exist target-cli\\smart-library-management-system-1.0-SNAPSHOT-cli-shaded.jar (
copy /Y target-cli\\smart-library-management-system-1.0-SNAPSHOT-cli-shaded.jar target\\slms-cli.jar
echo ✓ CLI JAR 打包完成: slms-cli.jar
) else (
echo ⚠️ 警告: 找不到 CLI shaded JAR
dir target-cli
)
REM 复制 library.db 到 target 目录
if exist library.db (
copy /Y library.db target\\
echo ✓ 已复制 library.db 到 target 目录
)
'''
}
echo '✓ CLI JAR 打包成功'
}
}
stage('7.2 GUI 打包 (JAR + EXE)') {
steps {
echo '========== 打包 GUI 应用 (JAR + EXE) =========='
script {
dir('SLMS') {
// 步骤1: Maven 打包 GUI JAR
bat '''
set JAVA_HOME=%JAVA_HOME%
echo 使用 target-gui 打包 GUI 应用...
REM 临时设置 Maven 输出目录为 target-gui
mvn package -Pgui -DskipTests -Dmaven.compiler.skip=true -Dmaven.build.directory=target-gui
REM 复制生成的 JAR 和 libs 到 target
if exist target-gui\\smart-library-management-system-1.0-SNAPSHOT.jar (
copy /Y target-gui\\smart-library-management-system-1.0-SNAPSHOT.jar target\\slms-gui.jar
echo ✓ GUI JAR 打包完成: slms-gui.jar
REM 复制 libs 目录
if exist target-gui\\libs (
xcopy /E /I /Y /Q target-gui\\libs target\\libs >nul
echo ✓ 已复制 libs 目录
)
) else (
echo ⚠️ 警告: 找不到 GUI JAR
dir target-gui
)
REM 复制 library.db 到 target 目录
if exist library.db (
copy /Y library.db target\\
echo ✓ 已复制 library.db 到 target 目录
)
'''
// 步骤2: 使用 jpackage 创建 EXE 和 MSI
bat '''
REM 使用 jpackage 创建 Windows EXE
echo.
echo ========== 使用 jpackage 创建 EXE ==========
REM 设置 WiX Toolset 路径
set "WIX=%WIX_HOME%"
set "PATH=%WIX_HOME%\\bin;%PATH%"
REM 检查 jpackage 是否可用
where jpackage >nul 2>&1
if errorlevel 1 (
echo ⚠️ 警告: jpackage 不可用,跳过 EXE 打包
echo 提示: jpackage 需要 JDK 14+
echo 当前 JAVA_HOME: %JAVA_HOME%
goto skip_exe
)
echo ✓ jpackage 可用
REM 检查 WiX Toolset 是否安装
where candle.exe >nul 2>&1
if errorlevel 1 (
echo ⚠️ 警告: WiX Toolset 未找到,跳过 EXE 打包
echo 提示: 请检查 WiX Toolset 安装路径
echo 当前 WIX_HOME: %WIX_HOME%
goto skip_exe
)
echo ✓ WiX Toolset 可用
REM 创建 jpackage 输入目录
if not exist target\\jpackage-input mkdir target\\jpackage-input
copy /Y target\\slms-gui.jar target\\jpackage-input\\
copy /Y target\\library.db target\\jpackage-input\\
if exist target-gui\\libs xcopy /E /I /Y /Q target-gui\\libs target\\jpackage-input\\libs >nul
REM 步骤1: 创建 app-image (包含 EXE)
echo 步骤1: 创建 Windows 应用镜像 (app-image)...
jpackage --type app-image ^
--input target\\jpackage-input ^
--name SLMS-GUI ^
--main-jar slms-gui.jar ^
--main-class com.smartlibrary.gui.GUIApplication ^
--dest target ^
--app-version 1.0 ^
--vendor "Smart Library" ^
--description "Smart Library Management System - GUI Application" ^
--win-console
if errorlevel 1 (
echo ⚠️ app-image 创建失败
goto skip_exe
)
REM 检查生成的 EXE
if exist target\\SLMS-GUI\\SLMS-GUI.exe (
echo ✓ EXE 创建成功: target\\SLMS-GUI\\SLMS-GUI.exe
REM 复制 EXE 到 target 根目录
copy /Y target\\SLMS-GUI\\SLMS-GUI.exe target\\slms-gui.exe
echo ✓ 已复制 slms-gui.exe 到 target 目录
REM 复制整个应用目录(包含运行时)
if exist target\\slms-gui-app rmdir /S /Q target\\slms-gui-app
xcopy /E /I /Y /Q target\\SLMS-GUI target\\slms-gui-app >nul
echo ✓ 已创建完整应用目录: target\\slms-gui-app
) else (
echo ⚠️ 未找到生成的 EXE 文件
goto skip_exe
)
REM 步骤2: 创建 MSI 安装包
echo.
echo 步骤2: 创建 Windows MSI 安装包...
jpackage --type msi ^
--input target\\jpackage-input ^
--name SLMS-GUI ^
--main-jar slms-gui.jar ^
--main-class com.smartlibrary.gui.GUIApplication ^
--dest target ^
--app-version 1.0 ^
--vendor "Smart Library" ^
--description "Smart Library Management System - GUI Application" ^
--win-console ^
--win-dir-chooser ^
--win-menu ^
--win-shortcut
if errorlevel 1 (
echo ⚠️ MSI 安装包创建失败
echo 提示: 这不影响 EXE 的使用
) else (
if exist target\\SLMS-GUI-1.0.msi (
echo ✓ MSI 安装包创建成功: target\\SLMS-GUI-1.0.msi
copy /Y target\\SLMS-GUI-1.0.msi target\\slms-gui-installer.msi
echo ✓ 已复制为 slms-gui-installer.msi
) else (
echo ⚠️ 未找到生成的 MSI 文件
)
)
:skip_exe
echo.
REM 创建 GUI 启动脚本Windows- 作为备用
echo @echo off > target\\run-gui.bat
echo echo Starting SLMS GUI Application... >> target\\run-gui.bat
echo if exist slms-gui.exe ( >> target\\run-gui.bat
echo echo Running EXE version... >> target\\run-gui.bat
echo start slms-gui.exe >> target\\run-gui.bat
echo ^) else ( >> target\\run-gui.bat
echo echo Running JAR version... >> target\\run-gui.bat
echo java -jar slms-gui.jar >> target\\run-gui.bat
echo if errorlevel 1 ( >> target\\run-gui.bat
echo echo. >> target\\run-gui.bat
echo echo JavaFX runtime not found! Trying with module path... >> target\\run-gui.bat
echo java --module-path libs --add-modules javafx.controls,javafx.fxml -jar slms-gui.jar >> target\\run-gui.bat
echo ^) >> target\\run-gui.bat
echo ^) >> target\\run-gui.bat
echo pause >> target\\run-gui.bat
echo ✓ 已创建 GUI 启动脚本: run-gui.bat
REM 创建 README 文件
echo SLMS GUI Application > target\\README-GUI.txt
echo. >> target\\README-GUI.txt
echo 运行方式1 (推荐): 双击 slms-gui.exe (如果存在) >> target\\README-GUI.txt
echo 运行方式2: 双击 run-gui.bat >> target\\README-GUI.txt
echo 运行方式3: java -jar slms-gui.jar >> target\\README-GUI.txt
echo. >> target\\README-GUI.txt
echo 注意: library.db 必须与应用文件在同一目录 >> target\\README-GUI.txt
echo ✓ 已创建 README-GUI.txt
'''
}
}
echo '✓ GUI JAR + EXE 打包成功'
}
}
stage('7.3 Web 打包 (WAR)') {
steps {
echo '========== 打包 Web 应用 (WAR) =========='
dir('SLMS') {
bat '''
set JAVA_HOME=%JAVA_HOME%
echo 使用 target-web 打包 Web 应用...
REM 临时设置 Maven 输出目录为 target-web
mvn package -Pweb -DskipTests -Dmaven.compiler.skip=true -Dmaven.build.directory=target-web
REM 复制生成的 WAR 到 target
if exist target-web\\smart-library-management-system-1.0-SNAPSHOT.war (
copy /Y target-web\\smart-library-management-system-1.0-SNAPSHOT.war target\\slms-web.war
echo ✓ Web WAR 打包完成: slms-web.war
) else if exist target-web\\smart-library-management-system-1.0-SNAPSHOT.jar (
copy /Y target-web\\smart-library-management-system-1.0-SNAPSHOT.jar target\\slms-web.jar
echo ✓ Web JAR 打包完成: slms-web.jar (备用)
) else (
echo ⚠️ 警告: 找不到 Web WAR/JAR
dir target-web
)
'''
}
echo '✓ Web WAR 打包成功'
}
}
stage('7.4 Android Gradle 打包 (APK)') {
steps {
echo '========== 打包 Android 应用 (APK) =========='
dir('SLMS') {
bat '''
@echo off
setlocal EnableDelayedExpansion
set JAVA_HOME=%JAVA_HOME%
set ANDROID_HOME=%ANDROID_HOME%
echo 打包 Android 应用...
REM 使用 Gradle 打包 Android APK不操作 target 目录)
call gradlew.bat :android:assembleDebug
REM 先列出 APK 目录看文件名
echo 列出 APK 目录:
dir android\\build\\outputs\\apk\\debug\\*.apk
REM 检查并复制 APK - 如果文件名相同就跳过
if exist android\\build\\outputs\\apk\\debug\\SLMS-debug.apk (
echo ✓ 找到 SLMS-debug.apk
if /I "SLMS-debug.apk" NEQ "slms-debug.apk" (
copy /Y android\\build\\outputs\\apk\\debug\\SLMS-debug.apk android\\build\\outputs\\apk\\debug\\slms-debug.apk >nul 2>&1
)
echo ✓ Android APK 打包完成: slms-debug.apk
exit /b 0
)
if exist android\\build\\outputs\\apk\\debug\\android-debug.apk (
echo ✓ 找到 android-debug.apk
copy /Y android\\build\\outputs\\apk\\debug\\android-debug.apk android\\build\\outputs\\apk\\debug\\slms-debug.apk >nul 2>&1
echo ✓ Android APK 打包完成: slms-debug.apk
exit /b 0
)
REM 尝试复制第一个找到的 APK
echo ⚠️ 警告: 找不到预期的 APK 文件,尝试复制第一个找到的 APK
set APK_FOUND=0
for %%f in (android\\build\\outputs\\apk\\debug\\*.apk) do (
if !APK_FOUND! EQU 0 (
echo 找到 APK: %%f
copy /Y "%%f" android\\build\\outputs\\apk\\debug\\slms-debug.apk >nul 2>&1
echo ✓ 已复制 %%f 为 slms-debug.apk
set APK_FOUND=1
)
)
if !APK_FOUND! EQU 0 (
echo ✗ 错误: 完全找不到 APK 文件
exit /b 1
)
exit /b 0
'''
}
echo '✓ Android APK 打包成功'
}
}
}
}
stage('8. 归档制品') {
when {
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
}
steps {
echo '========== 归档构建制品 =========='
script {
// 显示当前工作目录
bat '''
@echo off
echo 当前工作目录:
cd
echo.
echo 列出 SLMS/target 目录:
dir SLMS\\target\\*.jar 2>nul || echo 没有找到 JAR 文件
echo.
echo 列出 SLMS/android/build/outputs/apk/debug 目录:
dir SLMS\\android\\build\\outputs\\apk\\debug\\*.apk 2>nul || echo 没有找到 APK 文件
echo.
'''
// 检查文件是否存在
bat '''
@echo off
echo 检查制品文件...
if exist SLMS\\target\\slms-cli.jar (echo ✓ 找到 slms-cli.jar) else (echo ✗ 未找到 slms-cli.jar)
if exist SLMS\\target\\slms-gui.jar (echo ✓ 找到 slms-gui.jar) else (echo ✗ 未找到 slms-gui.jar)
if exist SLMS\\target\\slms-gui.exe (echo ✓ 找到 slms-gui.exe) else (echo ⚠️ 未找到 slms-gui.exe)
if exist SLMS\\target\\slms-gui-installer.msi (echo ✓ 找到 slms-gui-installer.msi) else (echo ⚠️ 未找到 MSI 安装包)
if exist SLMS\\target\\slms-web.war (echo ✓ 找到 slms-web.war) else if exist SLMS\\target\\slms-web.jar (echo ✓ 找到 slms-web.jar) else (echo ✗ 未找到 slms-web.war/jar)
if exist SLMS\\android\\build\\outputs\\apk\\debug\\slms-debug.apk (echo ✓ 找到 slms-debug.apk) else if exist SLMS\\android\\build\\outputs\\apk\\debug\\SLMS-debug.apk (echo ✓ 找到 SLMS-debug.apk) else (echo ✗ 未找到 APK)
exit /b 0
'''
// 归档制品 - 使用 try-catch 确保即使部分文件缺失也能继续
try {
echo '开始归档制品...'
archiveArtifacts artifacts: 'SLMS/target/slms-*.jar,SLMS/target/slms-*.war,SLMS/target/slms-*.exe,SLMS/target/slms-*.msi,SLMS/target/*.bat,SLMS/target/README-*.txt,SLMS/target/library.db,SLMS/android/build/outputs/apk/debug/slms-debug.apk',
fingerprint: true,
allowEmptyArchive: true,
onlyIfSuccessful: false
echo '✓ 制品归档完成'
} catch (Exception e) {
echo "⚠️ 制品归档时出现问题: ${e.message}"
echo "继续执行流水线..."
}
}
}
}
stage('9. 推送头歌') {
when {
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
}
parallel {
stage('9.1 推送源代码到 feature-ldl') {
steps {
echo '========== 推送源代码到头歌 feature-ldl 分支 =========='
script {
try {
withCredentials([usernamePassword(
credentialsId: 'educoder-credentials',
usernameVariable: 'EDUCODER_USER',
passwordVariable: 'EDUCODER_PASS'
)]) {
bat '''
@echo off
setlocal EnableExtensions EnableDelayedExpansion
REM URL编码用户名和密码
for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_USER))"') do set USER_ENC=%%i
for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_PASS))"') do set PASS_ENC=%%i
git config user.name "Jenkins CI"
git config user.email "ldl@chzu.edu.cn"
git remote add educoder https://bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git || git remote set-url educoder https://bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git
git push https://%USER_ENC%:%PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git HEAD:refs/heads/feature-ldl --force
'''
}
echo '✓ 源代码推送到 feature-ldl 成功'
} catch (Exception e) {
echo '⚠ 推送代码到 feature-ldl 失败'
echo "错误信息: ${e.message}"
currentBuild.result = 'UNSTABLE'
}
}
}
}
stage('9.2 推送制品到 release') {
steps {
echo '========== 推送构建制品到头歌 release 分支 =========='
script {
try {
withCredentials([usernamePassword(
credentialsId: 'educoder-credentials',
usernameVariable: 'EDUCODER_USER',
passwordVariable: 'EDUCODER_PASS'
)]) {
bat '''
@echo off
setlocal EnableExtensions EnableDelayedExpansion
REM URL编码用户名和密码
for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_USER))"') do set USER_ENC=%%i
for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_PASS))"') do set PASS_ENC=%%i
REM 显示当前目录
echo 当前工作目录:
cd
REM 进入 SLMS 目录
cd SLMS
REM 创建 artifacts 目录
if not exist artifacts mkdir artifacts
REM 复制制品
echo 复制制品到 artifacts 目录...
if exist target\\slms-cli.jar (
copy /Y target\\slms-cli.jar artifacts\\
echo ✓ 已复制 CLI JAR
) else (
echo ⚠️ 警告: 找不到 CLI JAR
)
if exist target\\slms-gui.jar (
copy /Y target\\slms-gui.jar artifacts\\
echo ✓ 已复制 GUI JAR
) else (
echo ⚠️ 警告: 找不到 GUI JAR
)
if exist target\\slms-web.war (
copy /Y target\\slms-web.war artifacts\\
echo ✓ 已复制 Web WAR
) else if exist target\\slms-web.jar (
copy /Y target\\slms-web.jar artifacts\\
echo ✓ 已复制 Web JAR (备用)
) else (
echo ⚠️ 警告: 找不到 Web WAR/JAR
)
if exist android\\build\\outputs\\apk\\debug\\slms-debug.apk (
copy /Y android\\build\\outputs\\apk\\debug\\slms-debug.apk artifacts\\
echo ✓ 已复制 Android APK
) else if exist android\\build\\outputs\\apk\\debug\\SLMS-debug.apk (
copy /Y android\\build\\outputs\\apk\\debug\\SLMS-debug.apk artifacts\\slms-debug.apk
echo ✓ 已复制 Android APK (SLMS-debug.apk)
) else (
echo ⚠️ 警告: 找不到 APK 文件
)
if exist target\\library.db (
copy /Y target\\library.db artifacts\\
echo ✓ 已复制 library.db
) else if exist library.db (
copy /Y library.db artifacts\\
echo ✓ 已复制 library.db (从根目录)
) else (
echo ⚠️ 警告: 找不到 library.db
)
if exist target\\slms-gui.exe (
copy /Y target\\slms-gui.exe artifacts\\
echo ✓ 已复制 slms-gui.exe
) else (
echo ⚠️ 警告: 找不到 slms-gui.exe
)
if exist target\\slms-gui-installer.msi (
copy /Y target\\slms-gui-installer.msi artifacts\\
echo ✓ 已复制 slms-gui-installer.msi
) else (
echo ⚠️ 警告: 找不到 slms-gui-installer.msi
)
if exist target\\run-gui.bat (
copy /Y target\\run-gui.bat artifacts\\
echo ✓ 已复制 run-gui.bat
)
if exist target\\README-GUI.txt (
copy /Y target\\README-GUI.txt artifacts\\
echo ✓ 已复制 README-GUI.txt
)
REM 不复制 libs 目录,减少仓库大小
REM if exist target\\libs (
REM xcopy /E /I /Y /Q target\\libs artifacts\\libs >nul 2>&1
REM echo ✓ 已复制 libs 目录
REM )
REM 显示复制的文件
echo.
echo 制品列表:
dir artifacts
REM 创建临时目录用于 release 分支
cd ..
set RELEASE_DIR=%TEMP%\\slms-release-%BUILD_NUMBER%
if exist "%RELEASE_DIR%" rmdir /S /Q "%RELEASE_DIR%"
mkdir "%RELEASE_DIR%"
REM 复制制品到临时目录
echo 准备 release 分支内容...
xcopy /E /I /Y /Q SLMS\\artifacts "%RELEASE_DIR%\\artifacts" >nul
REM 创建 README 文件
echo SLMS 项目构建制品 > "%RELEASE_DIR%\\README.md"
echo. >> "%RELEASE_DIR%\\README.md"
echo 构建编号: #%BUILD_NUMBER% >> "%RELEASE_DIR%\\README.md"
echo 构建时间: %DATE% %TIME% >> "%RELEASE_DIR%\\README.md"
echo. >> "%RELEASE_DIR%\\README.md"
echo ## 制品列表 >> "%RELEASE_DIR%\\README.md"
echo. >> "%RELEASE_DIR%\\README.md"
echo - slms-cli.jar - CLI 命令行应用 >> "%RELEASE_DIR%\\README.md"
echo - slms-gui.jar - GUI 桌面应用 (JAR) >> "%RELEASE_DIR%\\README.md"
echo - slms-gui.exe - GUI 桌面应用 (EXE) >> "%RELEASE_DIR%\\README.md"
echo - slms-gui-installer.msi - GUI 安装包 (MSI) >> "%RELEASE_DIR%\\README.md"
echo - slms-web.war - Web 应用 >> "%RELEASE_DIR%\\README.md"
echo - slms-debug.apk - Android 应用 >> "%RELEASE_DIR%\\README.md"
echo - library.db - 数据库文件 >> "%RELEASE_DIR%\\README.md"
echo. >> "%RELEASE_DIR%\\README.md"
echo ## 使用说明 >> "%RELEASE_DIR%\\README.md"
echo. >> "%RELEASE_DIR%\\README.md"
echo 详细使用说明请参考各应用的 README 文件。 >> "%RELEASE_DIR%\\README.md"
REM 进入临时目录
cd "%RELEASE_DIR%"
REM 初始化 Git 仓库
git init
git config user.name "Jenkins CI"
git config user.email "ldl@chzu.edu.cn"
REM 添加所有文件
git add .
git commit -m "release: 构建制品 Build #%BUILD_NUMBER%"
REM 推送到 release 分支(强制覆盖,因为只包含制品)
echo 推送制品到头歌 release 分支(仅制品,无源码)...
git push https://%USER_ENC%:%PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git HEAD:refs/heads/release --force
if errorlevel 1 (
echo ✗ 推送失败!
exit /b 1
)
echo ✓ 推送成功!
REM 清理临时目录
cd %WORKSPACE%
rmdir /S /Q "%RELEASE_DIR%" 2>nul
echo ✓ 已清理临时目录
'''
}
echo '✓ 制品推送到 release 分支成功'
} catch (Exception e) {
echo '⚠ 推送制品到 release 失败'
echo "错误信息: ${e.message}"
currentBuild.result = 'UNSTABLE'
}
}
}
}
}
}
}
post {
always {
script {
echo '========== 流水线执行完成 =========='
echo "当前构建结果: ${currentBuild.result}"
echo "当前构建状态: ${currentBuild.currentResult}"
// 清理残留的 Java 进程
echo '========== 清理残留进程 =========='
try {
bat '''
@echo off
echo 清理与 SLMS 相关的残留进程...
for /f "tokens=2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /FO LIST ^| findstr /C:"PID:"') do (
wmic process where "ProcessId=%%i and CommandLine like '%%SLMS%%'" delete 2>nul
)
echo ✓ 进程清理完成
'''
} catch (Exception e) {
echo "⚠️ 清理进程时出错: ${e.message}"
}
// 检查制品是否存在
echo '========== 检查制品文件 =========='
try {
bat '''
@echo off
if exist SLMS\\target\\slms-cli.jar (echo ✓ slms-cli.jar 存在) else (echo ✗ slms-cli.jar 不存在)
if exist SLMS\\target\\slms-gui.jar (echo ✓ slms-gui.jar 存在) else (echo ✗ slms-gui.jar 不存在)
if exist SLMS\\target\\slms-web.war (echo ✓ slms-web.war 存在) else if exist SLMS\\target\\slms-web.jar (echo ✓ slms-web.jar 存在) else (echo ✗ slms-web.war/jar 不存在)
if exist SLMS\\android\\build\\outputs\\apk\\debug\\slms-debug.apk (echo ✓ slms-debug.apk 存在) else (echo ✗ slms-debug.apk 不存在)
exit /b 0
'''
} catch (Exception e) {
echo "⚠️ 检查制品文件时出错: ${e.message}"
}
// 发送邮件 - 使用最简单的方式
echo '========== 发送邮件通知 =========='
def finalResult = currentBuild.result ?: 'SUCCESS'
def emailSubject = ""
def emailStatus = ""
if (finalResult == 'SUCCESS') {
emailSubject = "✅ SLMS 构建成功 - Build #${BUILD_NUMBER}"
emailStatus = "成功"
} else if (finalResult == 'FAILURE') {
emailSubject = "❌ SLMS 构建失败 - Build #${BUILD_NUMBER}"
emailStatus = "失败"
} else if (finalResult == 'UNSTABLE') {
emailSubject = "⚠️ SLMS 构建不稳定 - Build #${BUILD_NUMBER}"
emailStatus = "不稳定"
} else {
emailSubject = " SLMS 构建完成 - Build #${BUILD_NUMBER}"
emailStatus = "完成"
}
echo "准备发送邮件: ${emailSubject}"
echo "收件人: 602924803@qq.com"
try {
emailext (
subject: emailSubject,
to: '602924803@qq.com',
from: 'ldl@chzu.edu.cn',
replyTo: 'ldl@chzu.edu.cn',
body: """
<h2>SLMS 项目构建${emailStatus}</h2>
<p><strong>构建编号:</strong> #${BUILD_NUMBER}</p>
<p><strong>构建状态:</strong> ${emailStatus}</p>
<p><strong>构建时间:</strong> ${new Date(currentBuild.startTimeInMillis)}</p>
<p><strong>Git 提交:</strong> ${env.GIT_COMMIT}</p>
<hr>
<h3><3E> 相关链接</h3>
<ul>
<li><a href="${BUILD_URL}">📊 查看构建详情</a></li>
<li><a href="${BUILD_URL}console">📋 查看构建日志</a></li>
<li><a href="${BUILD_URL}artifact/">📦 Jenkins 制品列表</a></li>
<li><a href="http://localhost:9000/dashboard?id=slms:slms">📈 SonarQube 报告</a></li>
<li><a href="https://bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab/tree/release/SLMS/artifacts">🎁 头歌 Release 分支</a></li>
</ul>
<hr>
<p style="color: #666; font-size: 12px;">
<em>此邮件由 Jenkins CI/CD 自动发送 | Build #${BUILD_NUMBER}</em>
</p>
""",
mimeType: 'text/html'
)
echo "✓ 邮件已发送到: 602924803@qq.com"
echo "✓ 邮件主题: ${emailSubject}"
} catch (Exception e) {
echo "❌ 邮件发送失败!"
echo "错误信息: ${e.message}"
echo "错误类型: ${e.class.name}"
echo "错误详情:"
e.printStackTrace()
}
}
}
}
}

@ -1,109 +0,0 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.smartlibrary'
compileSdk 34
compileOptions {
coreLibraryDesugaringEnabled false
}
defaultConfig {
applicationId "com.smartlibrary"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
// APK
setProperty("archivesBaseName", "SLMS")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
debug {
debuggable true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
// Android 14+
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
kotlinOptions {
jvmTarget = '21'
}
buildFeatures {
compose true
viewBinding true
}
composeOptions {
kotlinCompilerExtensionVersion '1.5.8'
}
packaging {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'androidx.activity:activity-compose:1.8.2'
implementation platform('androidx.compose:compose-bom:2024.02.02')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
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.navigation:navigation-fragment-ktx:2.7.7'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation 'androidx.room:room-runtime:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1'
implementation 'androidx.work:work-runtime-ktx:2.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
implementation 'androidx.datastore:datastore-preferences:1.0.0'
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'androidx.camera:camera-core:1.3.1'
implementation 'androidx.camera:camera-camera2:1.3.1'
implementation 'androidx.camera:camera-lifecycle:1.3.1'
implementation 'androidx.camera:camera-view:1.3.1'
// implementation project(':backend') // backendSpring BootAndroid
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation platform('androidx.compose:compose-bom:2024.02.02')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
}

@ -1,81 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Android 14+ (API 34+) 权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 存储权限 - Android 13+ 使用分部权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- 相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 通知权限 - Android 13+ 需要 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- 闹钟权限 -->
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<!-- 生物识别权限 -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:label="SLMS"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light"
android:requestLegacyExternalStorage="false"
android:hardwareAccelerated="true"
android:largeHeap="true"
android:usesCleartextTraffic="false"
tools:targetApi="34">
<activity
android:name=".android.MainActivity"
android:exported="true"
android:theme="@style/Theme.AppCompat.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Android 14 兼容性配置 -->
<service
android:name=".android.service.LibraryService"
android:foregroundServiceType="dataSync"
android:exported="false" />
<receiver
android:name=".android.receiver.LibraryBroadcastReceiver"
android:exported="false">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

@ -1,233 +0,0 @@
package com.smartlibrary.android;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.smartlibrary.R;
import com.smartlibrary.android.data.DataManager;
import com.smartlibrary.android.model.Book;
import com.smartlibrary.android.model.Loan;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private DataManager dataManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dataManager = DataManager.getInstance(this);
initializeMockData();
ScrollView scrollView = new ScrollView(this);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(40, 40, 40, 40);
TextView titleView = new TextView(this);
titleView.setText("智能图书管理系统 (SLMS)");
titleView.setTextSize(24);
titleView.setTextColor(0xFF1976D2);
titleView.setPadding(0, 0, 0, 30);
layout.addView(titleView);
TextView welcomeView = new TextView(this);
welcomeView.setText("欢迎使用 Android 版本!\n");
welcomeView.setTextSize(16);
layout.addView(welcomeView);
TextView statsView = new TextView(this);
List<Book> books = dataManager.getAllBooks();
List<Loan> loans = dataManager.getLoans();
List<Loan> activeLoans = dataManager.getActiveLoans();
statsView.setText(String.format(
"📊 系统统计\n" +
"━━━━━━━━━━━━━━━━\n" +
"📚 图书总数: %d 本\n" +
"📖 借阅记录: %d 条\n" +
"🔄 当前借阅: %d 本\n" +
"✅ 可借图书: %d 本\n\n",
books.size(),
loans.size(),
activeLoans.size(),
countAvailableBooks(books)
));
statsView.setTextSize(14);
statsView.setLineSpacing(8, 1);
layout.addView(statsView);
TextView booksTitle = new TextView(this);
booksTitle.setText("📚 图书列表");
booksTitle.setTextSize(18);
booksTitle.setTextColor(0xFF1976D2);
booksTitle.setPadding(0, 20, 0, 10);
layout.addView(booksTitle);
for (int i = 0; i < Math.min(books.size(), 10); i++) {
Book book = books.get(i);
TextView bookView = new TextView(this);
bookView.setText(String.format(
"%d. %s\n 作者: %s\n 分类: %s | 状态: %s\n",
i + 1,
book.getTitle(),
book.getAuthor(),
book.getCategory(),
book.isAvailable() ? "✅ 可借" : "❌ 已借出"
));
bookView.setTextSize(13);
bookView.setPadding(10, 10, 10, 10);
bookView.setBackgroundColor(i % 2 == 0 ? 0xFFF5F5F5 : 0xFFFFFFFF);
layout.addView(bookView);
}
if (activeLoans.size() > 0) {
TextView loansTitle = new TextView(this);
loansTitle.setText("\n📖 当前借阅");
loansTitle.setTextSize(18);
loansTitle.setTextColor(0xFF1976D2);
loansTitle.setPadding(0, 20, 0, 10);
layout.addView(loansTitle);
for (int i = 0; i < Math.min(activeLoans.size(), 5); i++) {
Loan loan = activeLoans.get(i);
Book book = dataManager.getBookById(loan.getBookId());
TextView loanView = new TextView(this);
loanView.setText(String.format(
"%d. %s\n 借阅日期: %s\n 应还日期: %s\n",
i + 1,
book != null ? book.getTitle() : "未知图书",
formatDate(loan.getBorrowDate()),
formatDate(loan.getDueDate())
));
loanView.setTextSize(13);
loanView.setPadding(10, 10, 10, 10);
loanView.setBackgroundColor(i % 2 == 0 ? 0xFFFFF3E0 : 0xFFFFFFFF);
layout.addView(loanView);
}
}
TextView footerView = new TextView(this);
footerView.setText("\n✨ 应用已成功启动!\n数据已加载完成。");
footerView.setTextSize(14);
footerView.setTextColor(0xFF4CAF50);
footerView.setPadding(0, 30, 0, 0);
layout.addView(footerView);
scrollView.addView(layout);
setContentView(scrollView);
}
private void initializeMockData() {
if (dataManager.getAllBooks().isEmpty()) {
addMockBooks();
addMockLoans();
}
}
private void addMockBooks() {
Book[] mockBooks = {
new Book("B001", "Java编程思想", "Bruce Eckel", "9787111213826", "编程", "available"),
new Book("B002", "Effective Java", "Joshua Bloch", "9780134685991", "编程", "borrowed"),
new Book("B003", "设计模式", "Erich Gamma", "9787111075752", "软件工程", "available"),
new Book("B004", "代码大全", "Steve McConnell", "9787121022982", "软件工程", "available"),
new Book("B005", "重构", "Martin Fowler", "9787115508645", "软件工程", "borrowed"),
new Book("B006", "算法导论", "Thomas H. Cormen", "9787111407010", "算法", "available"),
new Book("B007", "深入理解计算机系统", "Randal E. Bryant", "9787111544937", "计算机系统", "available"),
new Book("B008", "操作系统概念", "Abraham Silberschatz", "9787111544968", "操作系统", "borrowed"),
new Book("B009", "计算机网络", "Andrew S. Tanenbaum", "9787111453833", "网络", "available"),
new Book("B010", "数据库系统概念", "Abraham Silberschatz", "9787111375296", "数据库", "available"),
new Book("B011", "人工智能", "Stuart Russell", "9787111617143", "AI", "available"),
new Book("B012", "机器学习", "Tom Mitchell", "9787111211396", "AI", "borrowed"),
new Book("B013", "深度学习", "Ian Goodfellow", "9787115461476", "AI", "available"),
new Book("B014", "Python编程", "Eric Matthes", "9787115428028", "编程", "available"),
new Book("B015", "JavaScript高级程序设计", "Nicholas C. Zakas", "9787115275790", "编程", "available")
};
for (Book book : mockBooks) {
dataManager.addBook(book);
}
}
private void addMockLoans() {
java.util.Calendar cal = java.util.Calendar.getInstance();
Loan loan1 = new Loan();
loan1.setId("L001");
loan1.setBookId("B002");
loan1.setUserId("U001");
cal.add(java.util.Calendar.DAY_OF_MONTH, -10);
loan1.setBorrowDate(cal.getTime());
cal.add(java.util.Calendar.DAY_OF_MONTH, 20);
loan1.setDueDate(cal.getTime());
dataManager.addLoan(loan1);
Loan loan2 = new Loan();
loan2.setId("L002");
loan2.setBookId("B005");
loan2.setUserId("U002");
cal = java.util.Calendar.getInstance();
cal.add(java.util.Calendar.DAY_OF_MONTH, -5);
loan2.setBorrowDate(cal.getTime());
cal.add(java.util.Calendar.DAY_OF_MONTH, 25);
loan2.setDueDate(cal.getTime());
dataManager.addLoan(loan2);
Loan loan3 = new Loan();
loan3.setId("L003");
loan3.setBookId("B008");
loan3.setUserId("U003");
cal = java.util.Calendar.getInstance();
cal.add(java.util.Calendar.DAY_OF_MONTH, -15);
loan3.setBorrowDate(cal.getTime());
cal.add(java.util.Calendar.DAY_OF_MONTH, 15);
loan3.setDueDate(cal.getTime());
dataManager.addLoan(loan3);
Loan loan4 = new Loan();
loan4.setId("L004");
loan4.setBookId("B012");
loan4.setUserId("U001");
cal = java.util.Calendar.getInstance();
cal.add(java.util.Calendar.DAY_OF_MONTH, -3);
loan4.setBorrowDate(cal.getTime());
cal.add(java.util.Calendar.DAY_OF_MONTH, 27);
loan4.setDueDate(cal.getTime());
dataManager.addLoan(loan4);
}
private int countAvailableBooks(List<Book> books) {
int count = 0;
for (Book book : books) {
if (book.isAvailable()) {
count++;
}
}
return count;
}
private String formatDate(java.util.Date date) {
if (date == null) return "未设置";
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
@Override
protected void onResume() {
super.onResume();
if (dataManager != null) {
dataManager.syncData();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (dataManager != null) {
dataManager.cleanup();
}
}
}

@ -1,11 +0,0 @@
package com.smartlibrary.android;
import android.app.Activity;
import android.os.Bundle;
public class SimpleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

@ -1,396 +0,0 @@
package com.smartlibrary.android.data;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.smartlibrary.android.model.Book;
import com.smartlibrary.android.model.Loan;
import com.smartlibrary.android.network.ApiService;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
/**
* -
*
*
*/
public class DataManager extends Observable {
private static final String TAG = "DataManager";
private static final String PREFS_NAME = "smart_library_prefs";
private static final String BOOKS_KEY = "books_key";
private static final String LOANS_KEY = "loans_key";
private static DataManager instance;
private Context context;
private SharedPreferences preferences;
private Gson gson;
private ApiService apiService;
private List<Book> books;
private List<Loan> loans;
private DataManager(Context context) {
this.context = context.getApplicationContext();
this.preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
this.gson = new Gson();
this.apiService = ApiService.getInstance();
// 从本地存储加载数据
loadDataFromLocal();
Log.d(TAG, "DataManager实例已创建");
}
/**
*
* @param context
* @return DataManager
*/
public static synchronized DataManager getInstance(Context context) {
if (instance == null) {
instance = new DataManager(context);
}
return instance;
}
/**
*
* @return DataManager
*/
public static synchronized DataManager getInstance() {
if (instance == null) {
throw new IllegalStateException("DataManager未初始化请先调用getInstance(Context)");
}
return instance;
}
/**
*
*/
private void loadDataFromLocal() {
// 加载图书数据
String booksJson = preferences.getString(BOOKS_KEY, "");
if (!booksJson.isEmpty()) {
Type bookListType = new TypeToken<List<Book>>() {}.getType();
books = gson.fromJson(booksJson, bookListType);
}
if (books == null) {
books = new ArrayList<>();
}
// 加载借阅数据
String loansJson = preferences.getString(LOANS_KEY, "");
if (!loansJson.isEmpty()) {
Type loanListType = new TypeToken<List<Loan>>() {}.getType();
loans = gson.fromJson(loansJson, loanListType);
}
if (loans == null) {
loans = new ArrayList<>();
}
Log.d(TAG, "从本地存储加载了 " + books.size() + " 本图书和 " + loans.size() + " 条借阅记录");
}
/**
*
*/
private void saveDataToLocal() {
SharedPreferences.Editor editor = preferences.edit();
// 保存图书数据
String booksJson = gson.toJson(books);
editor.putString(BOOKS_KEY, booksJson);
// 保存借阅数据
String loansJson = gson.toJson(loans);
editor.putString(LOANS_KEY, loansJson);
editor.apply();
Log.d(TAG, "数据已保存到本地存储");
}
/**
*
*/
public void syncDataFromServer() {
Log.d(TAG, "开始从服务器同步数据");
// 同步图书数据
apiService.getBooks(new ApiService.ApiCallback<List<Book>>() {
@Override
public void onSuccess(List<Book> result) {
books = result;
saveDataToLocal();
setChanged();
notifyObservers("books_updated");
Log.d(TAG, "图书数据同步成功");
}
@Override
public void onFailure(String errorMessage) {
Log.e(TAG, "图书数据同步失败: " + errorMessage);
}
});
// 同步借阅数据
apiService.getLoans(new ApiService.ApiCallback<List<Loan>>() {
@Override
public void onSuccess(List<Loan> result) {
loans = result;
saveDataToLocal();
setChanged();
notifyObservers("loans_updated");
Log.d(TAG, "借阅数据同步成功");
}
@Override
public void onFailure(String errorMessage) {
Log.e(TAG, "借阅数据同步失败: " + errorMessage);
}
});
}
/**
*
* @return
*/
public List<Book> getBooks() {
return new ArrayList<>(books);
}
/**
* ID
* @param bookId ID
* @return null
*/
public Book getBookById(String bookId) {
for (Book book : books) {
if (book.getId().equals(bookId)) {
return book;
}
}
return null;
}
/**
*
* @param book
*/
public void addBook(Book book) {
books.add(book);
saveDataToLocal();
setChanged();
notifyObservers("book_added");
Log.d(TAG, "添加图书: " + book.getTitle());
}
/**
*
* @param book
*/
public void updateBook(Book book) {
for (int i = 0; i < books.size(); i++) {
if (books.get(i).getId().equals(book.getId())) {
books.set(i, book);
saveDataToLocal();
setChanged();
notifyObservers("book_updated");
Log.d(TAG, "更新图书: " + book.getTitle());
return;
}
}
}
/**
*
* @param bookId ID
*/
public void deleteBook(String bookId) {
for (int i = 0; i < books.size(); i++) {
if (books.get(i).getId().equals(bookId)) {
Book book = books.remove(i);
saveDataToLocal();
setChanged();
notifyObservers("book_deleted");
Log.d(TAG, "删除图书: " + book.getTitle());
return;
}
}
}
/**
*
* @return
*/
public List<Loan> getLoans() {
return new ArrayList<>(loans);
}
/**
* ID
* @param loanId ID
* @return null
*/
public Loan getLoanById(String loanId) {
for (Loan loan : loans) {
if (loan.getId().equals(loanId)) {
return loan;
}
}
return null;
}
/**
*
* @param loan
*/
public void addLoan(Loan loan) {
loans.add(loan);
saveDataToLocal();
setChanged();
notifyObservers("loan_added");
Log.d(TAG, "添加借阅记录: " + loan.getId());
}
/**
*
* @param loan
*/
public void updateLoan(Loan loan) {
for (int i = 0; i < loans.size(); i++) {
if (loans.get(i).getId().equals(loan.getId())) {
loans.set(i, loan);
saveDataToLocal();
setChanged();
notifyObservers("loan_updated");
Log.d(TAG, "更新借阅记录: " + loan.getId());
return;
}
}
}
/**
*
* @param loanId ID
*/
public void deleteLoan(String loanId) {
for (int i = 0; i < loans.size(); i++) {
if (loans.get(i).getId().equals(loanId)) {
Loan loan = loans.remove(i);
saveDataToLocal();
setChanged();
notifyObservers("loan_deleted");
Log.d(TAG, "删除借阅记录: " + loan.getId());
return;
}
}
}
/**
*
*/
public void clearAllData() {
books.clear();
loans.clear();
saveDataToLocal();
setChanged();
notifyObservers("data_cleared");
Log.d(TAG, "所有数据已清空");
}
public void initializeData() {
if (books.isEmpty()) {
Log.d(TAG, "初始化示例数据");
}
}
public void syncData() {
syncDataFromServer();
}
public void cleanup() {
Log.d(TAG, "清理资源");
}
public List<Book> getAllBooks() {
return getBooks();
}
public void borrowBook(String bookId) {
Book book = getBookById(bookId);
if (book != null) {
book.setStatus("borrowed");
updateBook(book);
}
}
public void reserveBook(String bookId) {
Book book = getBookById(bookId);
if (book != null) {
book.setStatus("reserved");
updateBook(book);
}
}
public List<Loan> getActiveLoans() {
List<Loan> activeLoans = new ArrayList<>();
for (Loan loan : loans) {
if (loan.getReturnDate() == null) {
activeLoans.add(loan);
}
}
return activeLoans;
}
public List<Book> getRecentBooks() {
return books.size() > 5 ? books.subList(0, 5) : new ArrayList<>(books);
}
public List<Book> getPopularBooks() {
return books.size() > 5 ? books.subList(0, 5) : new ArrayList<>(books);
}
public List<Loan> getCurrentLoans() {
return getActiveLoans();
}
public List<Loan> getHistoryLoans() {
List<Loan> historyLoans = new ArrayList<>();
for (Loan loan : loans) {
if (loan.getReturnDate() != null) {
historyLoans.add(loan);
}
}
return historyLoans;
}
public void returnBook(String loanId) {
Loan loan = getLoanById(loanId);
if (loan != null) {
loan.setReturnDate(new java.util.Date());
updateLoan(loan);
}
}
public boolean renewBook(String loanId) {
Loan loan = getLoanById(loanId);
if (loan != null && loan.canRenew()) {
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.setTime(loan.getDueDate());
cal.add(java.util.Calendar.DAY_OF_MONTH, 14);
loan.setDueDate(cal.getTime());
updateLoan(loan);
return true;
}
return false;
}
}

@ -1,28 +0,0 @@
package com.smartlibrary.android.data.model;
public class User {
private String id;
private String username;
private String email;
private String phone;
public User() {}
public User(String id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}

@ -1,146 +0,0 @@
package com.smartlibrary.android.factory;
import com.smartlibrary.android.model.Book;
import com.smartlibrary.android.model.Loan;
import com.smartlibrary.android.model.User;
import java.util.Date;
import java.util.UUID;
/**
* -
*
*
*/
public class LibraryObjectFactory {
/**
*
* @param title
* @param author
* @param isbn ISBN
* @param category
* @return
*/
public static Book createBook(String title, String author, String isbn, String category) {
Book book = new Book();
book.setId(UUID.randomUUID().toString());
book.setTitle(title);
book.setAuthor(author);
book.setIsbn(isbn);
book.setCategory(category);
book.setStatus("available");
book.setCreatedAt(new Date());
book.setUpdatedAt(new Date());
return book;
}
/**
* ID
* @param id ID
* @param title
* @param author
* @param isbn ISBN
* @param category
* @param status
* @return
*/
public static Book createBook(String id, String title, String author, String isbn, String category, String status) {
Book book = new Book();
book.setId(id);
book.setTitle(title);
book.setAuthor(author);
book.setIsbn(isbn);
book.setCategory(category);
book.setStatus(status);
book.setCreatedAt(new Date());
book.setUpdatedAt(new Date());
return book;
}
/**
*
* @param userId ID
* @param bookId ID
* @param dueDate
* @return
*/
public static Loan createLoan(String userId, String bookId, Date dueDate) {
Loan loan = new Loan();
loan.setId(UUID.randomUUID().toString());
loan.setUserId(userId);
loan.setBookId(bookId);
loan.setLoanDate(new Date());
loan.setDueDate(dueDate);
loan.setStatus("active");
loan.setCreatedAt(new Date());
loan.setUpdatedAt(new Date());
return loan;
}
/**
* ID
* @param id ID
* @param userId ID
* @param bookId ID
* @param loanDate
* @param dueDate
* @param status
* @return
*/
public static Loan createLoan(String id, String userId, String bookId, Date loanDate, Date dueDate, String status) {
Loan loan = new Loan();
loan.setId(id);
loan.setUserId(userId);
loan.setBookId(bookId);
loan.setLoanDate(loanDate);
loan.setDueDate(dueDate);
loan.setStatus(status);
loan.setCreatedAt(new Date());
loan.setUpdatedAt(new Date());
return loan;
}
/**
*
* @param name
* @param email
* @param phone
* @return
*/
public static User createUser(String name, String email, String phone) {
User user = new User();
user.setId(UUID.randomUUID().toString());
user.setName(name);
user.setEmail(email);
user.setPhone(phone);
user.setCreatedAt(new Date());
user.setUpdatedAt(new Date());
return user;
}
/**
* ID
* @param id ID
* @param name
* @param email
* @param phone
* @return
*/
public static User createUser(String id, String name, String email, String phone) {
User user = new User();
user.setId(id);
user.setName(name);
user.setEmail(email);
user.setPhone(phone);
user.setCreatedAt(new Date());
user.setUpdatedAt(new Date());
return user;
}
}

@ -1,116 +0,0 @@
package com.smartlibrary.android.model;
import java.util.Date;
/**
*
*
*/
public class Book {
private String id;
private String title;
private String author;
private String isbn;
private String category;
private String status;
private Date createdAt;
private Date updatedAt;
public Book() {
}
public Book(String id, String title, String author, String isbn, String category, String status) {
this.id = id;
this.title = title;
this.author = author;
this.isbn = isbn;
this.category = category;
this.status = status;
this.createdAt = new Date();
this.updatedAt = new Date();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public boolean isAvailable() {
return "available".equalsIgnoreCase(status);
}
public String getDescription() {
return "A book by " + author;
}
@Override
public String toString() {
return "Book{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", author='" + author + '\'' +
", isbn='" + isbn + '\'' +
", category='" + category + '\'' +
", status='" + status + '\'' +
'}';
}
}

@ -1,142 +0,0 @@
package com.smartlibrary.android.model;
import java.util.Date;
/**
*
*
*/
public class Loan {
private String id;
private String userId;
private String bookId;
private Date loanDate;
private Date dueDate;
private Date returnDate;
private String status;
private Date createdAt;
private Date updatedAt;
public Loan() {
}
public Loan(String id, String userId, String bookId, Date loanDate, Date dueDate, String status) {
this.id = id;
this.userId = userId;
this.bookId = bookId;
this.loanDate = loanDate;
this.dueDate = dueDate;
this.status = status;
this.createdAt = new Date();
this.updatedAt = new Date();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getBookId() {
return bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public Date getLoanDate() {
return loanDate;
}
public void setLoanDate(Date loanDate) {
this.loanDate = loanDate;
}
public Date getBorrowDate() {
return loanDate;
}
public void setBorrowDate(Date borrowDate) {
this.loanDate = borrowDate;
}
public Date getDueDate() {
return dueDate;
}
public void setDueDate(Date dueDate) {
this.dueDate = dueDate;
}
public Date getReturnDate() {
return returnDate;
}
public void setReturnDate(Date returnDate) {
this.returnDate = returnDate;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public boolean isOverdue() {
if (returnDate != null) return false;
return dueDate != null && new Date().after(dueDate);
}
@Override
public String toString() {
return "Loan{" +
"id='" + id + '\'' +
", userId='" + userId + '\'' +
", bookId='" + bookId + '\'' +
", loanDate=" + loanDate +
", dueDate=" + dueDate +
", status='" + status + '\'' +
'}';
}
public String getBookTitle() {
return "Book Title";
}
public String getBookAuthor() {
return "Book Author";
}
public boolean canRenew() {
return returnDate == null && !isOverdue();
}
}

@ -1,86 +0,0 @@
package com.smartlibrary.android.model;
import java.util.Date;
/**
*
*
*/
public class User {
private String id;
private String name;
private String email;
private String phone;
private Date createdAt;
private Date updatedAt;
public User() {
}
public User(String id, String name, String email, String phone) {
this.id = id;
this.name = name;
this.email = email;
this.phone = phone;
this.createdAt = new Date();
this.updatedAt = new Date();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
'}';
}
}

@ -1,343 +0,0 @@
package com.smartlibrary.android.network;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.smartlibrary.android.model.Book;
import com.smartlibrary.android.model.Loan;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* API -
*
* CRUD
*/
public class ApiService {
private static final String TAG = "ApiService";
private static final String BASE_URL = "http://10.0.2.2:8080/SLMS/api/"; // 模拟器访问本机地址
private static ApiService instance;
private Retrofit retrofit;
private LibraryApi libraryApi;
private Gson gson;
private ApiService() {
// 配置Gson
gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
// 配置OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new HttpLoggingInterceptor())
.build();
// 配置Retrofit
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
libraryApi = retrofit.create(LibraryApi.class);
Log.d(TAG, "ApiService实例已创建");
}
/**
*
* @return ApiService
*/
public static synchronized ApiService getInstance() {
if (instance == null) {
instance = new ApiService();
}
return instance;
}
/**
*
* @param callback
*/
public void getBooks(ApiCallback<List<Book>> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.getBooks()
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(1000);
// 模拟返回数据
List<Book> books = getMockBooks();
// 在主线程回调
if (callback != null) {
callback.onSuccess(books);
}
} catch (Exception e) {
Log.e(TAG, "获取图书失败", e);
if (callback != null) {
callback.onFailure("获取图书失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @param callback
*/
public void getLoans(ApiCallback<List<Loan>> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.getLoans()
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(1000);
// 模拟返回数据
List<Loan> loans = getMockLoans();
// 在主线程回调
if (callback != null) {
callback.onSuccess(loans);
}
} catch (Exception e) {
Log.e(TAG, "获取借阅记录失败", e);
if (callback != null) {
callback.onFailure("获取借阅记录失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @param book
* @param callback
*/
public void addBook(Book book, ApiCallback<Book> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.addBook(book)
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(500);
// 在主线程回调
if (callback != null) {
callback.onSuccess(book);
}
} catch (Exception e) {
Log.e(TAG, "添加图书失败", e);
if (callback != null) {
callback.onFailure("添加图书失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @param book
* @param callback
*/
public void updateBook(Book book, ApiCallback<Book> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.updateBook(book)
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(500);
// 在主线程回调
if (callback != null) {
callback.onSuccess(book);
}
} catch (Exception e) {
Log.e(TAG, "更新图书失败", e);
if (callback != null) {
callback.onFailure("更新图书失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @param bookId ID
* @param callback
*/
public void deleteBook(String bookId, ApiCallback<Boolean> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.deleteBook(bookId)
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(500);
// 在主线程回调
if (callback != null) {
callback.onSuccess(true);
}
} catch (Exception e) {
Log.e(TAG, "删除图书失败", e);
if (callback != null) {
callback.onFailure("删除图书失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @param loan
* @param callback
*/
public void addLoan(Loan loan, ApiCallback<Loan> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.addLoan(loan)
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(500);
// 在主线程回调
if (callback != null) {
callback.onSuccess(loan);
}
} catch (Exception e) {
Log.e(TAG, "添加借阅记录失败", e);
if (callback != null) {
callback.onFailure("添加借阅记录失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @param loan
* @param callback
*/
public void updateLoan(Loan loan, ApiCallback<Loan> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.updateLoan(loan)
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(500);
// 在主线程回调
if (callback != null) {
callback.onSuccess(loan);
}
} catch (Exception e) {
Log.e(TAG, "更新借阅记录失败", e);
if (callback != null) {
callback.onFailure("更新借阅记录失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @param loanId ID
* @param callback
*/
public void deleteLoan(String loanId, ApiCallback<Boolean> callback) {
// 模拟网络请求实际项目中应该调用libraryApi.deleteLoan(loanId)
new Thread(() -> {
try {
// 模拟网络延迟
Thread.sleep(500);
// 在主线程回调
if (callback != null) {
callback.onSuccess(true);
}
} catch (Exception e) {
Log.e(TAG, "删除借阅记录失败", e);
if (callback != null) {
callback.onFailure("删除借阅记录失败: " + e.getMessage());
}
}
}).start();
}
/**
*
* @return
*/
private List<Book> getMockBooks() {
List<Book> books = new ArrayList<>();
// 添加一些模拟图书数据
books.add(new Book("1", "Java编程思想", "Bruce Eckel", "9787111213826", "计算机", "available"));
books.add(new Book("2", "设计模式", "Erich Gamma", "9787111075756", "计算机", "available"));
books.add(new Book("3", "重构", "Martin Fowler", "9787115508645", "计算机", "borrowed"));
books.add(new Book("4", "代码整洁之道", "Robert C. Martin", "9787115216878", "计算机", "available"));
books.add(new Book("5", "算法导论", "Thomas H. Cormen", "9787111407928", "计算机", "borrowed"));
return books;
}
/**
*
* @return
*/
private List<Loan> getMockLoans() {
List<Loan> loans = new ArrayList<>();
// 添加一些模拟借阅数据
java.util.Calendar calendar = java.util.Calendar.getInstance();
// 借阅记录1
calendar.add(java.util.Calendar.DAY_OF_MONTH, -7);
java.util.Date loanDate1 = calendar.getTime();
calendar.add(java.util.Calendar.DAY_OF_MONTH, 21);
java.util.Date dueDate1 = calendar.getTime();
Loan loan1 = new Loan("1", "user1", "3", loanDate1, dueDate1, "active");
loans.add(loan1);
// 借阅记录2
calendar = java.util.Calendar.getInstance();
calendar.add(java.util.Calendar.DAY_OF_MONTH, -14);
java.util.Date loanDate2 = calendar.getTime();
calendar.add(java.util.Calendar.DAY_OF_MONTH, 14);
java.util.Date dueDate2 = calendar.getTime();
Loan loan2 = new Loan("2", "user2", "5", loanDate2, dueDate2, "active");
loans.add(loan2);
return loans;
}
/**
* API
* @param <T>
*/
public interface ApiCallback<T> {
/**
*
* @param result
*/
void onSuccess(T result);
/**
*
* @param errorMessage
*/
void onFailure(String errorMessage);
}
}

@ -1,48 +0,0 @@
package com.smartlibrary.android.network;
import android.util.Log;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* HTTP
*
*/
public class HttpLoggingInterceptor implements Interceptor {
private static final String TAG = "HttpLogging";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.currentTimeMillis();
Log.d(TAG, "发送请求: " + request.method() + " " + request.url());
Response response;
try {
response = chain.proceed(request);
} catch (Exception e) {
Log.e(TAG, "请求失败: " + e.getMessage());
throw e;
}
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
MediaType contentType = response.body().contentType();
String responseBody = response.body().string();
Log.d(TAG, "收到响应: " + response.code() + " (" + duration + "ms)");
Log.d(TAG, "响应内容: " + responseBody);
// 重新创建响应因为body.string()只能调用一次
ResponseBody newResponseBody = ResponseBody.create(contentType, responseBody);
return response.newBuilder().body(newResponseBody).build();
}
}

@ -1,101 +0,0 @@
package com.smartlibrary.android.network;
import com.smartlibrary.android.model.Book;
import com.smartlibrary.android.model.Loan;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
/**
* API
* REST API
*/
public interface LibraryApi {
/**
*
* @return
*/
@GET("books")
Call<List<Book>> getBooks();
/**
* ID
* @param id ID
* @return
*/
@GET("books/{id}")
Call<Book> getBook(@Path("id") String id);
/**
*
* @param book
* @return
*/
@POST("books")
Call<Book> addBook(@Body Book book);
/**
*
* @param id ID
* @param book
* @return
*/
@PUT("books/{id}")
Call<Book> updateBook(@Path("id") String id, @Body Book book);
/**
*
* @param id ID
* @return
*/
@DELETE("books/{id}")
Call<Void> deleteBook(@Path("id") String id);
/**
*
* @return
*/
@GET("loans")
Call<List<Loan>> getLoans();
/**
* ID
* @param id ID
* @return
*/
@GET("loans/{id}")
Call<Loan> getLoan(@Path("id") String id);
/**
*
* @param loan
* @return
*/
@POST("loans")
Call<Loan> addLoan(@Body Loan loan);
/**
*
* @param id ID
* @param loan
* @return
*/
@PUT("loans/{id}")
Call<Loan> updateLoan(@Path("id") String id, @Body Loan loan);
/**
*
* @param id ID
* @return
*/
@DELETE("loans/{id}")
Call<Void> deleteLoan(@Path("id") String id);
}

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_on_primary">
<path
android:fillColor="@android:color/white"
android:pathData="M21,5c-1.11,-0.35 -2.33,-0.5 -3.5,-0.5 -1.95,0 -4.05,0.4 -5.5,1.5 -1.45,-1.1 -3.55,-1.5 -5.5,-1.5S2.45,4.9 1,6v14.65c0,0.25 0.25,0.5 0.5,0.5 0.1,0 0.15,-0.05 0.25,-0.05C3.1,20.45 5.05,20 6.5,20c1.95,0 4.05,0.4 5.5,1.5 1.35,-0.85 3.8,-1.5 5.5,-1.5 1.65,0 3.35,0.3 4.75,1.05 0.1,0.05 0.15,0.05 0.25,0.05 0.25,0 0.5,-0.25 0.5,-0.5V6c-0.6,-0.45 -1.25,-0.75 -2,-1zM21,18.5c-1.1,-0.35 -2.3,-0.5 -3.5,-0.5 -1.7,0 -4.15,0.65 -5.5,1.5V8c1.35,-0.85 3.8,-1.5 5.5,-1.5 1.2,0 2.4,0.15 3.5,0.5v11.5z"/>
</vector>

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/primary_light" />
</shape>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2l3.09,6.26L22,9.27l-5,4.87 1.18,6.88L12,17.77l-6.18,3.25L7,14.14 2,9.27l6.91,-1.01L12,2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M7,14l5,-5 5,5z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17H7v-2h7v2zM17,13H7v-2h10v2zM17,9H7V7h10v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17H7v-2h7v2zM17,13H7v-2h10v2zM17,9H7L7,7h10v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM10,17l-4,-4 1.41,-1.41L10,14.17l6.59,-6.59L18,9l-8,8z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zM13,17h-2L11,7h2v10zM17,17h-2v-4h2v4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M21,5c-1.11,-0.35 -2.33,-0.5 -3.5,-0.5 -1.95,0 -4.05,0.4 -5.5,1.5 -1.45,-1.1 -3.55,-1.5 -5.5,-1.5S2.45,4.9 1,6v14.65c0,0.25 0.25,0.5 0.5,0.5 0.1,0 0.15,-0.05 0.25,-0.05C3.1,20.45 5.05,20 6.5,20c1.95,0 4.05,0.4 5.5,1.5 1.35,-0.85 3.8,-1.5 5.5,-1.5 1.65,0 3.35,0.3 4.75,1.05 0.1,0.05 0.15,0.05 0.25,0.05 0.25,0 0.5,-0.25 0.5,-0.5V6c-0.6,-0.45 -1.25,-0.75 -2,-1zM21,18.5c-1.1,-0.35 -2.3,-0.5 -3.5,-0.5 -1.7,0 -4.15,0.65 -5.5,1.5V8c1.35,-0.85 3.8,-1.5 5.5,-1.5 1.2,0 2.4,0.15 3.5,0.5v11.5z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2l3.09,6.26L22,9.27l-5,4.87 1.18,6.88L12,17.77l-6.18,3.25L7,14.14 2,9.27l6.91,-1.01L12,2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_on_primary">
<path
android:fillColor="@android:color/white"
android:pathData="M21,5c-1.11,-0.35 -2.33,-0.5 -3.5,-0.5 -1.95,0 -4.05,0.4 -5.5,1.5 -1.45,-1.1 -3.55,-1.5 -5.5,-1.5S2.45,4.9 1,6v14.65c0,0.25 0.25,0.5 0.5,0.5 0.1,0 0.15,-0.05 0.25,-0.05C3.1,20.45 5.05,20 6.5,20c1.95,0 4.05,0.4 5.5,1.5 1.35,-0.85 3.8,-1.5 5.5,-1.5 1.65,0 3.35,0.3 4.75,1.05 0.1,0.05 0.15,0.05 0.25,0.05 0.25,0 0.5,-0.25 0.5,-0.5V6c-0.6,-0.45 -1.25,-0.75 -2,-1zM21,18.5c-1.1,-0.35 -2.3,-0.5 -3.5,-0.5 -1.7,0 -4.15,0.65 -5.5,1.5V8c1.35,-0.85 3.8,-1.5 5.5,-1.5 1.2,0 2.4,0.15 3.5,0.5v11.5z"/>
</vector>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/darker_gray"
android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M9,11H7v2h2v-2zM13,11h-2v2h2v-2zM17,11h-2v2h2v-2zM19,4h-1L18,2h-2v2L8,4L8,2L6,2v2L5,4c-1.11,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM19,20L5,20L5,9h14v11z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M20,6h-2.18c0.11,-0.31 0.18,-0.65 0.18,-1 0,-1.66 -1.34,-3 -3,-3 -1.05,0 -1.96,0.54 -2.5,1.35l-0.5,0.67 -0.5,-0.68C10.96,2.54 10.05,2 9,2 7.34,2 6,3.34 6,5c0,0.35 0.07,0.69 0.18,1L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM15,5c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM9,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM10,17L5,12l1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM19,19H5V5h14v14z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-2,15l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M18,2h-3.17C14.42,0.84 13.3,0 12,0S9.58,0.84 9.17,2L6,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM13,18L11,18v-2h2v2zM13,14L11,14v-2h2v2zM13,10L11,10L11,8h2v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17L7,17v-2h7v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18H8v-2h8v2zM16,14H8v-2h8v2zM13,9V3.5L18.5,9H13z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,7h2v6h-2zM11,15h2v2h-2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6h-2v6h2V6z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M9,11H7v2h2v-2zM13,11h-2v2h2v-2zM17,11h-2v2h2v-2zM19,4h-1V2h-2v2H8V2H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM19,20H5V9h14v11z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M9,11H7v2h2v-2zM13,11h-2v2h2v-2zM17,11h-2v2h2v-2zM19,4h-1V2h-2v2H8V2H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM19,20H5V9h14v11z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM19,19H5V8h14v11zM7,10h5v5H7z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zm-4.4,15.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M10,18h4v-2h-4v2zM3,6v2h18V6H3zm3,7h12v-2H6v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M10,4H4c-1.11,0 -2,0.89 -2,2v12c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2h-8l-2,-2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM5.49,9.99C5.49,7.01 7.51,5 10.49,5c1.48,0 2.75,0.54 3.76,1.44l-1.62,1.56C12.23,7.59 11.44,7.29 10.49,7.29c-1.48,0 -2.68,1.21 -2.68,2.7s1.2,2.7 2.68,2.7c0.86,0 1.54,-0.33 2.02,-0.78h-2.16v-2.05h4.61c0.08,0.4 0.12,0.82 0.12,1.26 0,3.46 -2.35,5.88 -5.59,5.88 -3.31,0 -6,-2.69 -6,-6.01z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_on_primary">
<path
android:fillColor="@android:color/white"
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,7h2v6h-2zM11,15h2v2h-2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2V7h2v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18H8v-2h8v2zM16,14H8v-2h8v2zM13,9V3.5L18.5,9H13z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.94,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_on_primary">
<path
android:fillColor="@android:color/white"
android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M3,18h18v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h18v-2L3,11v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M21,5c-1.11,-0.35 -2.33,-0.5 -3.5,-0.5 -1.95,0 -4.05,0.4 -5.5,1.5 -1.45,-1.1 -3.55,-1.5 -5.5,-1.5S2.45,4.9 1,6v14.65c0,0.25 0.25,0.5 0.5,0.5 0.1,0 0.15,-0.05 0.25,-0.05C3.1,20.45 5.05,20 6.5,20c1.95,0 4.05,0.4 5.5,1.5 1.35,-0.85 3.8,-1.5 5.5,-1.5 1.65,0 3.35,0.3 4.75,1.05 0.1,0.05 0.15,0.05 0.25,0.05 0.25,0 0.5,-0.25 0.5,-0.5L23,6c-0.6,-0.45 -1.25,-0.75 -2,-1zM21,18.5c-1.1,-0.35 -2.3,-0.5 -3.5,-0.5 -1.7,0 -4.15,0.65 -5.5,1.5L12,8c1.35,-0.85 3.8,-1.5 5.5,-1.5 1.2,0 2.4,0.15 3.5,0.5v11.5z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M3,18h18v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h18v-2L3,11v2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,17H9V7h2V17zM15,17h-2V7h2V17z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5V19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45V19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1H5.9V17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,13c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M8,5v14l11,-7z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
</vector>

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_on_primary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.94,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4z"/>
</vector>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/text_secondary">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm3.5,-9c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zm3.5,6.5c2.33,0 4.31,-1.46 5.11,-3.5H6.89c0.8,2.04 2.78,3.5 5.11,3.5z"/>
</vector>

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

Loading…
Cancel
Save