This commit is contained in:
parent
6199c50c0a
commit
b4b45eeb14
|
@ -0,0 +1,15 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
|
@ -0,0 +1,3 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
|
@ -0,0 +1 @@
|
|||
My Application
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,9 @@
|
|||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,5 +1,6 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
id("com.chaquo.python")
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -12,7 +13,10 @@ android {
|
|||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
ndk {
|
||||
// On Apple silicon, you can omit x86_64.
|
||||
abiFilters += listOf("arm64-v8a", "x86_64")
|
||||
}
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
|
@ -51,8 +55,19 @@ android {
|
|||
viewBinding = true
|
||||
}
|
||||
|
||||
}
|
||||
chaquopy {
|
||||
defaultConfig {
|
||||
version = "3.11"
|
||||
buildPython("C:\\Users\\xuyvqi\\AppData\\Local\\Programs\\Python\\Python311\\python.exe")
|
||||
pip {
|
||||
install("typing_extensions")
|
||||
install("lxml==5.3.0") // python-docx 的依赖
|
||||
install("python-docx") // 使用兼容性较好的版本
|
||||
install("Pillow") // 可选,处理图片
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -68,6 +83,7 @@ dependencies {
|
|||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
implementation(libs.okhttp)
|
||||
|
||||
implementation("com.google.android.gms:play-services-location:21.0.1")
|
||||
implementation("androidx.exifinterface:exifinterface:1.3.3")
|
||||
implementation ("commons-io:commons-io:2.11.0")
|
||||
|
@ -79,5 +95,5 @@ dependencies {
|
|||
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
|
||||
implementation ("com.squareup.retrofit2:adapter-rxjava2:2.9.0")
|
||||
implementation ("com.squareup.okhttp3:logging-interceptor:4.9.3")
|
||||
|
||||
implementation ("com.google.code.gson:gson:2.10.1")
|
||||
}
|
||||
|
|
|
@ -83,4 +83,8 @@
|
|||
}
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Application
|
||||
-keep public class * extends android.app.Service
|
||||
-keep public class * extends android.app.Service
|
||||
# 保留数据库相关类
|
||||
-keep class com.example.myapplication.DataBase.** { *; }
|
||||
-keep class com.example.myapplication.Service.** { *; }
|
||||
-keep class com.example.myapplication.Tool.** { *; }
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -2,11 +2,11 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 4,
|
||||
"identityHash": "65748a86249bdef977f472ddeba83ee9",
|
||||
"identityHash": "ab7260384e2d58c0d78dc0dae5130632",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "images",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `path` TEXT, `time` INTEGER NOT NULL, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `altitude` REAL NOT NULL, `user` TEXT, `audioPath` TEXT, `blade` INTEGER NOT NULL, `unit` TEXT, `project` TEXT, `b` INTEGER NOT NULL)",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `path` TEXT, `time` INTEGER NOT NULL, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `altitude` REAL NOT NULL, `user` TEXT, `audioPath` TEXT, `blade` INTEGER NOT NULL, `unit` TEXT, `unitName` TEXT, `project` TEXT, `b` INTEGER NOT NULL, `temperature` TEXT, `humidity` TEXT, `weather` TEXT)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
@ -68,6 +68,12 @@
|
|||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "unitName",
|
||||
"columnName": "unitName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "project",
|
||||
"columnName": "project",
|
||||
|
@ -79,6 +85,24 @@
|
|||
"columnName": "b",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "temperature",
|
||||
"columnName": "temperature",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "humidity",
|
||||
"columnName": "humidity",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "weather",
|
||||
"columnName": "weather",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
|
@ -188,7 +212,7 @@
|
|||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '65748a86249bdef977f472ddeba83ee9')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ab7260384e2d58c0d78dc0dae5130632')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@
|
|||
<!-- 基础权限 -->
|
||||
<!-- Android 14+ 新增权限 -->
|
||||
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
@ -41,6 +44,7 @@
|
|||
android:requestLegacyExternalStorage="true"
|
||||
android:windowSoftInputMode="stateVisible|adjustResize"
|
||||
android:theme="@style/Theme.MyApplication"
|
||||
android:name="com.chaquo.python.android.PyApplication"
|
||||
tools:targetApi="31"
|
||||
tools:ignore="ForegroundServicePermission">
|
||||
<activity
|
||||
|
@ -70,7 +74,7 @@
|
|||
android:exported="false" />
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
package com.example.myapplication.DataBase;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.example.myapplication.model.ImageSourceItem;
|
||||
import com.example.myapplication.model.PartResponse;
|
||||
import com.example.myapplication.model.Project;
|
||||
import com.google.firebase.crashlytics.buildtools.reloc.com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String DATABASE_NAME = "UserDB";
|
||||
private static final int DATABASE_VERSION = 3;
|
||||
private static final String DATABASE_NAME = "AppDB";
|
||||
private static final int DATABASE_VERSION = 6;
|
||||
private static final String TABLE_USERS = "users";
|
||||
private static final String TABLE_PROJECTS = "projects";
|
||||
|
||||
|
@ -28,6 +35,26 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
public static final String COLUMN_PROJECT_ID = "project_id";
|
||||
public static final String COLUMN_PROJECT_NAME = "project_name";
|
||||
public static final String COLUMN_LAST_UPDATED = "last_updated";
|
||||
private static final String TABLE_PARTS = "parts";
|
||||
public static final String COLUMN_PART_ID = "part_id";
|
||||
public static final String COLUMN_PART_NAME = "part_name";
|
||||
public static final String COLUMN_PART_CODE = "part_code";
|
||||
public static final String COLUMN_PART_TYPE = "part_type";
|
||||
public static final String COLUMN_PART_TYPE_LABEL = "part_type_label";
|
||||
public static final String COLUMN_Part_PROJECT_ID = "project_id";
|
||||
public static final String COLUMN_Part_PROJECT_NAME = "project_name";
|
||||
public static final String COLUMN_TURBINE_ID = "turbine_id";
|
||||
public static final String COLUMN_TURBINE_NAME = "turbine_name";
|
||||
public static final String COLUMN_LAST_SYNC = "last_sync";
|
||||
public static final String TABLE_IMAGE_SOURCES = "image_sources";
|
||||
public static final String COLUMN_SOURCE_KEY = "source_key";
|
||||
public static final String COLUMN_SOURCE_VALUE = "source_value";
|
||||
private static final String TABLE_DYNAMIC_DATA = "dynamic_data";
|
||||
private static final String COLUMN_JSON_DATA = "json_data";
|
||||
private static final String COLUMN_DATA_TYPE = "data_type"; // 用于区分不同类型的数据
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
private static final Type MAP_LIST_TYPE = new TypeToken<List<Map<String, String>>>() {}.getType();
|
||||
|
||||
public DatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
|
@ -50,6 +77,30 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
+ COLUMN_PROJECT_NAME + " TEXT,"
|
||||
+ COLUMN_LAST_UPDATED + " INTEGER" + ")";
|
||||
db.execSQL(CREATE_PROJECTS_TABLE);
|
||||
String CREATE_PARTS_TABLE = "CREATE TABLE " + TABLE_PARTS + "("
|
||||
+ COLUMN_PART_ID + " TEXT PRIMARY KEY,"
|
||||
+ COLUMN_PART_NAME + " TEXT,"
|
||||
+ COLUMN_PART_CODE + " TEXT,"
|
||||
+ COLUMN_PART_TYPE + " TEXT,"
|
||||
+ COLUMN_PART_TYPE_LABEL + " TEXT,"
|
||||
+ COLUMN_Part_PROJECT_ID + " TEXT,"
|
||||
+ COLUMN_Part_PROJECT_NAME + " TEXT,"
|
||||
+ COLUMN_TURBINE_ID + " TEXT,"
|
||||
+ COLUMN_TURBINE_NAME + " TEXT,"
|
||||
+ COLUMN_LAST_SYNC + " INTEGER" + ")";
|
||||
db.execSQL(CREATE_PARTS_TABLE);
|
||||
String CREATE_IMAGE_SOURCES_TABLE = "CREATE TABLE " + TABLE_IMAGE_SOURCES + "("
|
||||
+ COLUMN_SOURCE_KEY + " TEXT PRIMARY KEY,"
|
||||
+ COLUMN_SOURCE_VALUE + " TEXT,"
|
||||
+ COLUMN_LAST_SYNC + " INTEGER" + ")";
|
||||
db.execSQL(CREATE_IMAGE_SOURCES_TABLE);
|
||||
// 新增动态数据表
|
||||
String CREATE_DYNAMIC_DATA_TABLE = "CREATE TABLE " + TABLE_DYNAMIC_DATA + "("
|
||||
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
+ COLUMN_DATA_TYPE + " TEXT NOT NULL,"
|
||||
+ COLUMN_JSON_DATA + " TEXT NOT NULL,"
|
||||
+ COLUMN_LAST_SYNC + " INTEGER DEFAULT 0" + ")";
|
||||
db.execSQL(CREATE_DYNAMIC_DATA_TABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,8 +125,260 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
// 版本2到版本3的迁移,添加项目名称列
|
||||
db.execSQL("ALTER TABLE " + TABLE_USERS + " ADD COLUMN " + COLUMN_USER_PROJECT_NAME + " TEXT");
|
||||
}
|
||||
if (oldVersion < 4) {
|
||||
// 版本3到版本4的迁移,添加部件表
|
||||
String CREATE_PARTS_TABLE = "CREATE TABLE " + TABLE_PARTS + "("
|
||||
+ COLUMN_PART_ID + " TEXT PRIMARY KEY,"
|
||||
+ COLUMN_PART_NAME + " TEXT,"
|
||||
+ COLUMN_PART_CODE + " TEXT,"
|
||||
+ COLUMN_PART_TYPE + " TEXT,"
|
||||
+ COLUMN_PART_TYPE_LABEL + " TEXT,"
|
||||
+ COLUMN_Part_PROJECT_ID + " TEXT,"
|
||||
+ COLUMN_Part_PROJECT_NAME + " TEXT,"
|
||||
+ COLUMN_TURBINE_ID + " TEXT,"
|
||||
+ COLUMN_TURBINE_NAME + " TEXT,"
|
||||
+ COLUMN_LAST_SYNC + " INTEGER" + ")";
|
||||
db.execSQL(CREATE_PARTS_TABLE);
|
||||
}
|
||||
if(oldVersion<5)
|
||||
{
|
||||
String CREATE_IMAGE_SOURCES_TABLE = "CREATE TABLE " + TABLE_IMAGE_SOURCES + "("
|
||||
+ COLUMN_SOURCE_KEY + " TEXT PRIMARY KEY,"
|
||||
+ COLUMN_SOURCE_VALUE + " TEXT,"
|
||||
+ COLUMN_LAST_SYNC + " INTEGER" + ")";
|
||||
db.execSQL(CREATE_IMAGE_SOURCES_TABLE);
|
||||
}
|
||||
if (oldVersion < 6) {
|
||||
// 版本5到版本6的迁移:添加动态数据表
|
||||
String CREATE_DYNAMIC_DATA_TABLE = "CREATE TABLE " + TABLE_DYNAMIC_DATA + "("
|
||||
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
+ COLUMN_DATA_TYPE + " TEXT NOT NULL,"
|
||||
+ COLUMN_JSON_DATA + " TEXT NOT NULL,"
|
||||
+ COLUMN_LAST_SYNC + " INTEGER DEFAULT 0" + ")";
|
||||
db.execSQL(CREATE_DYNAMIC_DATA_TABLE);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 保存动态数据列表
|
||||
* @param dataType 数据类型标识(如"work_types")
|
||||
* @param dataList 要保存的数据列表
|
||||
*/
|
||||
public void saveDynamicData(String dataType, List<Map<String, String>> dataList) {
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// 删除旧数据
|
||||
db.delete(TABLE_DYNAMIC_DATA,
|
||||
COLUMN_DATA_TYPE + " = ?",
|
||||
new String[]{dataType});
|
||||
|
||||
// 插入新数据
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_DATA_TYPE, dataType);
|
||||
values.put(COLUMN_JSON_DATA, gson.toJson(dataList));
|
||||
values.put(COLUMN_LAST_SYNC, System.currentTimeMillis());
|
||||
|
||||
db.insert(TABLE_DYNAMIC_DATA, null, values);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动态数据列表
|
||||
* @param dataType 数据类型标识
|
||||
* @return 对应的数据列表,如果没有则返回空列表
|
||||
*/
|
||||
public List<Map<String, String>> getDynamicData(String dataType) {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_DYNAMIC_DATA,
|
||||
new String[]{COLUMN_JSON_DATA},
|
||||
COLUMN_DATA_TYPE + " = ?",
|
||||
new String[]{dataType},
|
||||
null, null,
|
||||
COLUMN_LAST_SYNC + " DESC",
|
||||
"1"); // 只获取最新的一条
|
||||
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
String jsonData = cursor.getString(0);
|
||||
return gson.fromJson(jsonData, MAP_LIST_TYPE);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
return new ArrayList<>(); // 返回空列表而非null
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定数据类型的最后同步时间
|
||||
*/
|
||||
public long getDynamicDataLastSync(String dataType) {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_DYNAMIC_DATA,
|
||||
new String[]{"MAX(" + COLUMN_LAST_SYNC + ")"},
|
||||
COLUMN_DATA_TYPE + " = ?",
|
||||
new String[]{dataType},
|
||||
null, null, null);
|
||||
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getLong(0);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定类型的数据
|
||||
*/
|
||||
public void clearDynamicData(String dataType) {
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.delete(TABLE_DYNAMIC_DATA,
|
||||
COLUMN_DATA_TYPE + " = ?",
|
||||
new String[]{dataType});
|
||||
}
|
||||
public void saveImageSources(List<ImageSourceItem> sources) {
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
try {
|
||||
db.delete(TABLE_IMAGE_SOURCES, null, null);
|
||||
|
||||
for (ImageSourceItem source : sources) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_SOURCE_KEY, source.getKey());
|
||||
values.put(COLUMN_SOURCE_VALUE, source.getValue());
|
||||
values.put(COLUMN_LAST_SYNC, System.currentTimeMillis());
|
||||
db.insert(TABLE_IMAGE_SOURCES, null, values);
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ImageSourceItem> getImageSources() {
|
||||
List<ImageSourceItem> sources = new ArrayList<>();
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
|
||||
Cursor cursor = db.query(TABLE_IMAGE_SOURCES,
|
||||
new String[]{COLUMN_SOURCE_KEY, COLUMN_SOURCE_VALUE},
|
||||
null, null, null, null, null);
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
sources.add(new ImageSourceItem(
|
||||
cursor.getString(0),
|
||||
cursor.getString(1)
|
||||
));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
cursor.close();
|
||||
return sources;
|
||||
}
|
||||
/**
|
||||
* 保存部件列表到数据库
|
||||
*/
|
||||
public void saveParts(List<PartResponse> parts) {
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
try {
|
||||
// 先清空表
|
||||
db.delete(TABLE_PARTS, null, null);
|
||||
|
||||
// 插入新数据
|
||||
for (PartResponse part : parts) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_PART_ID, part.getPartId());
|
||||
values.put(COLUMN_PART_NAME, part.getPartName());
|
||||
values.put(COLUMN_PART_CODE, part.getPartCode());
|
||||
values.put(COLUMN_PART_TYPE, part.getPartType());
|
||||
values.put(COLUMN_PART_TYPE_LABEL, part.getPartTypeLabel());
|
||||
values.put(COLUMN_PROJECT_ID, part.getProjectId());
|
||||
values.put(COLUMN_PROJECT_NAME, part.getProjectName());
|
||||
values.put(COLUMN_TURBINE_ID, part.getTurbineId());
|
||||
values.put(COLUMN_TURBINE_NAME, part.getTurbineName());
|
||||
values.put(COLUMN_LAST_SYNC, System.currentTimeMillis());
|
||||
|
||||
db.insert(TABLE_PARTS, null, values);
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部件名称获取部件ID
|
||||
*/
|
||||
public String getPartIdByName(String partName) {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
String partId = null;
|
||||
|
||||
Cursor cursor = db.query(TABLE_PARTS,
|
||||
new String[]{COLUMN_PART_ID},
|
||||
COLUMN_PART_NAME + " = ?",
|
||||
new String[]{partName},
|
||||
null, null, null);
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
partId = cursor.getString(0);
|
||||
}
|
||||
cursor.close();
|
||||
return partId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有部件
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
public List<PartResponse> getAllParts() {
|
||||
List<PartResponse> parts = new ArrayList<>();
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
|
||||
Cursor cursor = db.query(TABLE_PARTS,
|
||||
null, null, null, null, null, null);
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
PartResponse part = new PartResponse();
|
||||
part.setPartId(cursor.getString(cursor.getColumnIndex(COLUMN_PART_ID)));
|
||||
part.setPartName(cursor.getString(cursor.getColumnIndex(COLUMN_PART_NAME)));
|
||||
part.setPartCode(cursor.getString(cursor.getColumnIndex(COLUMN_PART_CODE)));
|
||||
part.setPartType(cursor.getString(cursor.getColumnIndex(COLUMN_PART_TYPE)));
|
||||
part.setPartTypeLabel(cursor.getString(cursor.getColumnIndex(COLUMN_PART_TYPE_LABEL)));
|
||||
part.setProjectId(cursor.getString(cursor.getColumnIndex(COLUMN_Part_PROJECT_ID)));
|
||||
part.setProjectName(cursor.getString(cursor.getColumnIndex(COLUMN_Part_PROJECT_NAME)));
|
||||
part.setTurbineId(cursor.getString(cursor.getColumnIndex(COLUMN_TURBINE_ID)));
|
||||
part.setTurbineName(cursor.getString(cursor.getColumnIndex(COLUMN_TURBINE_NAME)));
|
||||
|
||||
parts.add(part);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
cursor.close();
|
||||
return parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后同步时间
|
||||
*/
|
||||
public long getLastSyncTime() {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
long lastSync = 0;
|
||||
|
||||
Cursor cursor = db.query(TABLE_PARTS,
|
||||
new String[]{"MAX(" + COLUMN_LAST_SYNC + ")"},
|
||||
null, null, null, null, null);
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
lastSync = cursor.getLong(0);
|
||||
}
|
||||
cursor.close();
|
||||
return lastSync;
|
||||
}
|
||||
// 用户操作方法
|
||||
public boolean addUser(String username, String password, String projectId, String projectName) {
|
||||
SQLiteDatabase db = this.getWritableDatabase();
|
||||
|
|
|
@ -40,4 +40,6 @@ public interface ImageDao {
|
|||
|
||||
@Query("DELETE FROM images WHERE time BETWEEN :startTime AND :endTime")
|
||||
int deleteByTimeRange(long startTime, long endTime);
|
||||
@Query("SELECT * FROM images WHERE time BETWEEN :startTime AND :endTime")
|
||||
List<ImageEntity> getImagesByTimeRange(long startTime, long endTime);
|
||||
}
|
||||
|
|
|
@ -11,11 +11,13 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
|
@ -37,9 +39,18 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.example.myapplication.DataBase.DatabaseHelper;
|
||||
import com.example.myapplication.Tool.AuthInterceptor;
|
||||
import com.example.myapplication.Tool.RetrofitClient;
|
||||
import com.example.myapplication.Tool.ShowError;
|
||||
import com.example.myapplication.api.AuthApi;
|
||||
import com.example.myapplication.api.ProjectApi;
|
||||
import com.example.myapplication.api.UserApi;
|
||||
import com.example.myapplication.model.ApiResponse;
|
||||
import com.example.myapplication.model.LoginEntity;
|
||||
import com.example.myapplication.model.LoginRequest;
|
||||
import com.example.myapplication.model.PageResult;
|
||||
import com.example.myapplication.model.Project;
|
||||
import com.example.myapplication.model.UserInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
|
@ -51,6 +62,8 @@ import java.lang.reflect.ParameterizedType;
|
|||
import java.lang.reflect.Type;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
@ -61,7 +74,13 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.ResponseBody;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
|
@ -69,15 +88,28 @@ import retrofit2.Response;
|
|||
import retrofit2.Retrofit;
|
||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.Headers;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.PUT;
|
||||
|
||||
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
private EditText etUsername, etPassword;
|
||||
private CheckBox cbRemember;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private final ExecutorService executor = Executors.newFixedThreadPool(7);
|
||||
|
||||
private Button btnClearProject;
|
||||
private Button btnLogin;
|
||||
|
||||
private DatabaseHelper dbHelper;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private final String AUTH_TOKEN_KEY = "auth_token";
|
||||
private static final String Name = "name";
|
||||
private static final String BASE_URL = "http://pms.dtyx.net:9158/";
|
||||
private String authToken = "";
|
||||
private static final String PREFS_NAME = "LoginPrefs";
|
||||
private static final String PREF_USERNAME = "username";
|
||||
private static final String PREF_PASSWORD = "password";
|
||||
|
@ -88,6 +120,9 @@ public class LoginActivity extends AppCompatActivity {
|
|||
private TextView tvSelectedProjectName;
|
||||
private TextView tvSelectedProjectId;
|
||||
private List<Project> projectList = new ArrayList<>();
|
||||
ShowError errorShower = new ShowError(this);
|
||||
|
||||
|
||||
|
||||
@SuppressLint("SuspiciousIndentation")
|
||||
@Override
|
||||
|
@ -132,7 +167,16 @@ public class LoginActivity extends AppCompatActivity {
|
|||
|
||||
btnLogin.setOnClickListener(v -> attemptLogin());
|
||||
}
|
||||
private boolean isNetworkAvailable() {
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (cm == null) return false;
|
||||
|
||||
NetworkCapabilities capabilities = cm.getNetworkCapabilities(cm.getActiveNetwork());
|
||||
return capabilities != null &&
|
||||
(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET));
|
||||
}
|
||||
private void loadSavedPreferences() {
|
||||
boolean remember = sharedPreferences.getBoolean(PREF_REMEMBER, false);
|
||||
if (remember) {
|
||||
|
@ -176,12 +220,246 @@ public class LoginActivity extends AppCompatActivity {
|
|||
return;
|
||||
}
|
||||
|
||||
// 在后台线程执行数据库操作
|
||||
// 先进行认证
|
||||
if(isNetworkAvailable())
|
||||
authenticateUser(username, password, projectInput);
|
||||
else continueLoginProcess(username,password,projectInput);
|
||||
}
|
||||
private void authenticateUser(String username, String password, String projectInput) {
|
||||
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
// 加密密码
|
||||
String encryptedPassword2 = encryptPassword(username, password);
|
||||
|
||||
|
||||
// 创建认证请求体
|
||||
LoginRequest loginRequest = new LoginRequest(username, encryptedPassword2);
|
||||
String token = sharedPreferences.getString(AUTH_TOKEN_KEY, "");
|
||||
// 初始化Retrofit
|
||||
Retrofit retrofit = RetrofitClient.getClient(token);
|
||||
|
||||
// 创建认证API服务
|
||||
AuthApi authApi = retrofit.create(AuthApi.class);
|
||||
|
||||
// 执行认证请求
|
||||
Call<ApiResponse<LoginEntity>> call = authApi.login(loginRequest);
|
||||
Response<ApiResponse<LoginEntity>> response = call.execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
|
||||
if (response.body().getCode() == 500200) {
|
||||
//修改密码操作
|
||||
runOnUiThread(() -> showChangePasswordDialog(username));
|
||||
} else if (response.body().getCode() == 200) {
|
||||
|
||||
authToken = response.body().getData().getTokenValue();
|
||||
Log.e("令牌:",authToken);
|
||||
sharedPreferences.edit().putString(AUTH_TOKEN_KEY, authToken).apply();
|
||||
Retrofit retrofit2 = RetrofitClient.getClient(authToken);
|
||||
// 创建UserApi服务
|
||||
UserApi userApi = retrofit2.create(UserApi.class);
|
||||
|
||||
// 执行查询请求,只根据account查询
|
||||
Call<PageResult<UserInfo>> userCall = userApi.getUserList(
|
||||
authToken, // 注意token前缀,根据你的API要求调整
|
||||
username, // account参数
|
||||
null, // deptId
|
||||
null, // mobile
|
||||
null, // name
|
||||
null, // userCode
|
||||
null, // userStatus
|
||||
null // userType
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
Response<PageResult<UserInfo>> userResponse = userCall.execute();
|
||||
if (userResponse.isSuccessful() && userResponse.body() != null
|
||||
&& userResponse.body().getCode() == 200
|
||||
&& !userResponse.body().getRows().isEmpty()) {
|
||||
// 获取第一个匹配用户的name
|
||||
String name = userResponse.body().getRows().get(0).getName();
|
||||
sharedPreferences.edit().putString(Name, name).apply();
|
||||
executor.execute(()-> {
|
||||
|
||||
continueLoginProcess(username, password, projectInput);
|
||||
});
|
||||
|
||||
} else {
|
||||
// 查询失败或没有结果,使用username作为name
|
||||
errorShower.showErrorDialog("查询姓名失败" , userResponse.body().getMsg());
|
||||
|
||||
sharedPreferences.edit().putString(Name, "").apply();
|
||||
executor.execute(()-> {
|
||||
|
||||
continueLoginProcess(username, password, projectInput);
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// 发生异常,使用username作为name
|
||||
errorShower.showErrorDialog("出现异常:", e.getMessage());
|
||||
executor.execute(()-> {
|
||||
|
||||
continueLoginProcess(username, password, projectInput);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
showToast(password+"密码出错"+response.body().toString());
|
||||
}
|
||||
} else {
|
||||
runOnUiThread(() -> {
|
||||
String errorMsg = "认证失败: ";
|
||||
if (response.errorBody() != null) {
|
||||
try {
|
||||
errorMsg += response.errorBody().string();
|
||||
} catch (IOException e) {
|
||||
errorMsg += "无法读取错误详情";
|
||||
}
|
||||
}
|
||||
showToast(errorMsg);
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
showToast("认证出错:"+e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 封装 Toast 显示
|
||||
private void showToast(String message) {
|
||||
mainHandler.post(() -> Toast.makeText(LoginActivity.this, message, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
private void showChangePasswordDialog(String username) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("请修改密码");
|
||||
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_change_password, null);
|
||||
EditText etOldPassword = view.findViewById(R.id.etOldPassword);
|
||||
EditText etNewPassword = view.findViewById(R.id.etNewPassword);
|
||||
EditText etConfirmPassword = view.findViewById(R.id.etConfirmPassword);
|
||||
|
||||
builder.setView(view);
|
||||
builder.setPositiveButton("确认", (dialog, which) -> {
|
||||
String oldPassword = etOldPassword.getText().toString().trim();
|
||||
String newPassword = etNewPassword.getText().toString().trim();
|
||||
String confirmPassword = etConfirmPassword.getText().toString().trim();
|
||||
|
||||
if (newPassword.equals(confirmPassword)) {
|
||||
changePassword(username, oldPassword, newPassword);
|
||||
} else {
|
||||
Toast.makeText(this, "新密码与确认密码不匹配", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton("取消", null);
|
||||
builder.setCancelable(false);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void changePassword(String username, String oldPassword, String newPassword) {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
// 加密旧密码和新密码
|
||||
String encryptedOldPassword = encryptPassword(username, oldPassword);
|
||||
String encryptedNewPassword = encryptPassword(username, newPassword);
|
||||
|
||||
// 创建修改密码请求体
|
||||
ChangePasswordRequest request = new ChangePasswordRequest(
|
||||
username,
|
||||
encryptedNewPassword,
|
||||
encryptedOldPassword
|
||||
);
|
||||
|
||||
// 获取token
|
||||
String token = sharedPreferences.getString(AUTH_TOKEN_KEY, "");
|
||||
|
||||
// 初始化Retrofit
|
||||
Retrofit retrofit = RetrofitClient.getClient(token);
|
||||
|
||||
// 创建API服务
|
||||
AuthApi authApi = retrofit.create(AuthApi.class);
|
||||
|
||||
// 执行修改密码请求
|
||||
Call<ApiResponse<Void>> call = authApi.modifyPassword(request);
|
||||
Response<ApiResponse<Void>> response = call.execute();
|
||||
|
||||
runOnUiThread(() -> {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
if (response.body().isSuccess()) {
|
||||
Toast.makeText(LoginActivity.this, "密码修改成功,请重新登录", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(LoginActivity.this, "密码修改失败: " + response.body().toString(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
String errorMsg = "修改密码失败: ";
|
||||
if (response.errorBody() != null) {
|
||||
try {
|
||||
errorMsg += response.errorBody().string();
|
||||
} catch (IOException e) {
|
||||
errorMsg += "无法读取错误详情";
|
||||
}
|
||||
}
|
||||
Toast.makeText(LoginActivity.this, errorMsg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
runOnUiThread(() ->
|
||||
Toast.makeText(LoginActivity.this, "修改密码出错: " + e.getMessage(), Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 新增修改密码请求体
|
||||
public static class ChangePasswordRequest {
|
||||
private String account;
|
||||
private String newPassword;
|
||||
private String oldPassword;
|
||||
|
||||
public ChangePasswordRequest(String account, String newPassword, String oldPassword) {
|
||||
this.account = account;
|
||||
this.newPassword = newPassword;
|
||||
this.oldPassword = oldPassword;
|
||||
}
|
||||
|
||||
// getters and setters
|
||||
public String getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(String account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public String getNewPassword() {
|
||||
return newPassword;
|
||||
}
|
||||
|
||||
public void setNewPassword(String newPassword) {
|
||||
this.newPassword = newPassword;
|
||||
}
|
||||
|
||||
public String getOldPassword() {
|
||||
return oldPassword;
|
||||
}
|
||||
|
||||
public void setOldPassword(String oldPassword) {
|
||||
this.oldPassword = oldPassword;
|
||||
}
|
||||
}
|
||||
private void continueLoginProcess(String username, String password, String projectInput) {
|
||||
// 在后台线程执行数据库操作
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
// 解析项目ID和名称
|
||||
|
||||
Pair<String, String> projectInfo = parseProjectInfo(projectInput);
|
||||
String projectId = projectInfo.first;
|
||||
String projectName = projectInfo.second;
|
||||
|
@ -196,14 +474,14 @@ public class LoginActivity extends AppCompatActivity {
|
|||
if (userExists) {
|
||||
// 用户存在,验证密码
|
||||
if (dbHelper.checkUser(username, password)) {
|
||||
handleLoginSuccess(username, projectId, projectName);
|
||||
handleLoginSuccess(username, projectId, projectName,password);
|
||||
} else {
|
||||
Toast.makeText(LoginActivity.this, "密码错误", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
// 用户不存在,自动注册
|
||||
if (dbHelper.addUser(username, password, projectId, projectName)) {
|
||||
handleLoginSuccess(username, projectId, projectName);
|
||||
handleLoginSuccess(username, projectId, projectName,password);
|
||||
} else {
|
||||
Toast.makeText(LoginActivity.this, "注册失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
@ -216,6 +494,47 @@ public class LoginActivity extends AppCompatActivity {
|
|||
});
|
||||
}
|
||||
|
||||
private String encryptPassword(String username, String password) {
|
||||
try {
|
||||
// 1. 对账号做MD5计算
|
||||
String md5Hash = md5(username);
|
||||
|
||||
// 2. 取8-24位作为密钥
|
||||
String key = md5Hash.substring(8, 24); // 8-24位(索引从0开始)
|
||||
Log.e("key:",key);
|
||||
// 3. AES加密
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // PKCS5Padding等同于PKCS7Padding在Java中
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
|
||||
byte[] encryptedBytes = cipher.doFinal(password.getBytes());
|
||||
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT).trim();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
showToast("加密密码错误:"+e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String md5(String input) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] messageDigest = md.digest(input.getBytes());
|
||||
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : messageDigest) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) hexString.append('0');
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Pair<String, String> parseProjectInfo(String projectInput) {
|
||||
String projectId = "";
|
||||
String projectName = "";
|
||||
|
@ -256,11 +575,12 @@ public class LoginActivity extends AppCompatActivity {
|
|||
editor.apply();
|
||||
}
|
||||
|
||||
private void handleLoginSuccess(String username, String projectId, String projectName) {
|
||||
private void handleLoginSuccess(String username, String projectId, String projectName,String password) {
|
||||
dbHelper.updateUserProject(username, projectId, projectName);
|
||||
Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
|
||||
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
|
||||
intent.putExtra("username", username);
|
||||
intent.putExtra("password",password);
|
||||
intent.putExtra("projectId", projectId);
|
||||
intent.putExtra("projectName", projectName);
|
||||
startActivity(intent);
|
||||
|
@ -344,23 +664,20 @@ public class LoginActivity extends AppCompatActivity {
|
|||
|
||||
|
||||
private void loadProjects() {
|
||||
String BASE_URL = "http://pms.dtyx.net:9158/";
|
||||
|
||||
|
||||
try {
|
||||
// 1. 验证接口定义
|
||||
if (!validateApiDefinition()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS);
|
||||
okHttpClientBuilder.addInterceptor(new AuthInterceptor(authToken));
|
||||
// 2. 初始化Retrofit
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(new OkHttpClient.Builder()
|
||||
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.build())
|
||||
.build();
|
||||
Retrofit retrofit = RetrofitClient.getClient(authToken);
|
||||
|
||||
// 3. 创建API服务
|
||||
ProjectApi projectApi = retrofit.create(ProjectApi.class);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,4 @@
|
|||
|
||||
package com.example.myapplication.Service;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
@ -66,7 +67,7 @@ public class FloatingWindowService extends Service {
|
|||
|
||||
private WindowManager windowManager;
|
||||
private View floatingView;
|
||||
|
||||
|
||||
private RadioGroup radioGroup;
|
||||
private static final String EXTRA_PROJECT_ID = "PROJECT_ID";
|
||||
private String lastUnit = "";
|
||||
|
@ -92,7 +93,7 @@ public class FloatingWindowService extends Service {
|
|||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && intent.hasExtra(EXTRA_PROJECT_ID)) {
|
||||
projectID = intent.getStringExtra(EXTRA_PROJECT_ID);
|
||||
// 你可以在这里使用projectID,或者在其他方法中使用这个成员变量
|
||||
|
||||
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
|
@ -160,13 +161,14 @@ public class FloatingWindowService extends Service {
|
|||
|
||||
}
|
||||
private void setupAutoCompleteBehavior() {
|
||||
actvUnit.setFocusableInTouchMode(true); // 允许触摸获取焦点
|
||||
actvUnit.setFocusable(true);
|
||||
actvUnit.setOnClickListener(v -> {
|
||||
if (turbineAdapter.getCount() > 0) {
|
||||
actvUnit.showDropDown();
|
||||
} else {
|
||||
actvUnit.requestFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(actvUnit, InputMethodManager.SHOW_IMPLICIT);
|
||||
showSoftInputDelayed(actvUnit);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -181,15 +183,28 @@ public class FloatingWindowService extends Service {
|
|||
if (turbineAdapter.getCount() > 0) {
|
||||
actvUnit.showDropDown();
|
||||
} else {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(actvUnit.getWindowToken(), 0);
|
||||
hideSoftInput(actvUnit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
private void showSoftInputDelayed(View view) {
|
||||
view.postDelayed(() -> {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
private void hideSoftInput(View view) {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
// 移除 setupInputMethod() 方法,因为功能已整合到上面的方法中
|
||||
|
||||
|
||||
|
@ -202,21 +217,35 @@ public class FloatingWindowService extends Service {
|
|||
public boolean onTouch(View v, MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// 检查是否点击在输入区域
|
||||
Rect rect = new Rect();
|
||||
actvUnit.getGlobalVisibleRect(rect);
|
||||
if (rect.contains((int)event.getRawX(), (int)event.getRawY())) {
|
||||
return false; // 让事件继续传递
|
||||
// 如果是点击输入区域,不处理拖动,让输入框获取焦点
|
||||
return false;
|
||||
}
|
||||
|
||||
// 记录初始位置
|
||||
initialX = params.x;
|
||||
initialY = params.y;
|
||||
initialTouchX = event.getRawX();
|
||||
initialTouchY = event.getRawY();
|
||||
|
||||
// 隐藏键盘和下拉菜单
|
||||
hideSoftInput(actvUnit);
|
||||
actvUnit.dismissDropDown();
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// 更新悬浮窗位置
|
||||
params.x = initialX + (int)(event.getRawX() - initialTouchX);
|
||||
params.y = initialY + (int)(event.getRawY() - initialTouchY);
|
||||
windowManager.updateViewLayout(floatingView, params);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
// 拖动结束,不处理焦点
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -495,12 +524,14 @@ public class FloatingWindowService extends Service {
|
|||
new Handler().postDelayed(() -> {
|
||||
actvUnit.setBackgroundColor(originalColor);
|
||||
radioGroup.setBackgroundColor(Color.TRANSPARENT);
|
||||
}, 300); // 0.5秒后恢复
|
||||
}, 300); // 0.3秒后恢复
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
hideSoftInput(actvUnit);
|
||||
actvUnit.dismissDropDown();
|
||||
if (floatingView != null && windowManager != null) {
|
||||
windowManager.removeView(floatingView);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.example.myapplication.Tool;
|
||||
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import java.io.IOException;
|
||||
|
||||
public class AuthInterceptor implements Interceptor {
|
||||
private String authToken;
|
||||
|
||||
public AuthInterceptor(String token) {
|
||||
this.authToken = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request originalRequest = chain.request();
|
||||
|
||||
// 添加 Authorization header
|
||||
Request newRequest = originalRequest.newBuilder()
|
||||
.header("Authorization", authToken)
|
||||
.build();
|
||||
|
||||
return chain.proceed(newRequest);
|
||||
}
|
||||
}
|
|
@ -15,12 +15,10 @@ public class BackgroundToast {
|
|||
}
|
||||
|
||||
handler.post(() -> {
|
||||
// 取消之前的Toast
|
||||
if (currentToast != null) {
|
||||
currentToast.cancel();
|
||||
}
|
||||
|
||||
// 创建新的Toast
|
||||
currentToast = Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_SHORT);
|
||||
currentToast.show();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
package com.example.myapplication.Tool;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.myapplication.DataBase.DatabaseHelper;
|
||||
import com.example.myapplication.api.CommonService;
|
||||
import com.example.myapplication.model.ApiResponse;
|
||||
import com.example.myapplication.model.ImageSourceItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
|
||||
public class CommonImageSourceFetcher {
|
||||
private String token;
|
||||
private final CommonService commonService;
|
||||
private final DatabaseHelper dbHelper;
|
||||
private final Context context;
|
||||
public CommonImageSourceFetcher(String token,Context context) {
|
||||
|
||||
this.token=token;
|
||||
this.dbHelper = new DatabaseHelper(context);
|
||||
this.context = context;
|
||||
Retrofit retrofit =RetrofitClient.getClient(token);
|
||||
|
||||
|
||||
this.commonService = retrofit.create(CommonService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步获取图像来源列表
|
||||
*/
|
||||
public List<ImageSourceItem> fetchImageSourceListSync() throws IOException {
|
||||
if (isNetworkAvailable()) {
|
||||
Call<ApiResponse<List<ImageSourceItem>>> call = commonService.getImageSourceList();
|
||||
|
||||
Response<ApiResponse<List<ImageSourceItem>>> response = call.execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isSuccess()) {
|
||||
List<ImageSourceItem> result = response.body().getData();
|
||||
// 保存到数据库
|
||||
dbHelper.saveImageSources(result);
|
||||
return result;
|
||||
} else {
|
||||
throw new IOException("请求失败,状态码: " + response.code());
|
||||
}
|
||||
} else {
|
||||
// 无网络时从数据库获取
|
||||
return dbHelper.getImageSources();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步获取图像来源列表
|
||||
*/
|
||||
public void fetchImageSourceListAsync(ImageSourceCallback callback) {
|
||||
List<ImageSourceItem> cachedSources = dbHelper.getImageSources();
|
||||
if (!cachedSources.isEmpty()) {
|
||||
callback.onSuccess(cachedSources);
|
||||
}
|
||||
|
||||
// 检查网络连接
|
||||
if (isNetworkAvailable()) {
|
||||
Call<ApiResponse<List<ImageSourceItem>>> call = commonService.getImageSourceList();
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<List<ImageSourceItem>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<List<ImageSourceItem>>> call,
|
||||
Response<ApiResponse<List<ImageSourceItem>>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isSuccess()) {
|
||||
List<ImageSourceItem> result = response.body().getData();
|
||||
|
||||
Log.e("data信息:", result != null ? result.toString() : "Result is null");
|
||||
// 保存到数据库
|
||||
dbHelper.saveImageSources(result);
|
||||
callback.onSuccess(result);
|
||||
} else if (cachedSources.isEmpty()) {
|
||||
callback.onFailure(new IOException("请求失败,状态码: " + response.code()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<List<ImageSourceItem>>> call, Throwable t) {
|
||||
if (cachedSources.isEmpty()) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (cachedSources.isEmpty()) {
|
||||
callback.onFailure(new IOException("无网络连接且无缓存数据"));
|
||||
}
|
||||
}
|
||||
private boolean isNetworkAvailable() {
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivityManager != null) {
|
||||
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public interface ImageSourceCallback {
|
||||
void onSuccess(List<ImageSourceItem> imageSources);
|
||||
void onFailure(Throwable t);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.example.myapp;
|
||||
package com.example.myapplication.Tool;
|
||||
|
||||
|
||||
|
||||
|
@ -11,7 +11,6 @@ import com.chaquo.python.Python;
|
|||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
package com.example.myapplication.Tool;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.myapplication.DataBase.DatabaseHelper;
|
||||
import com.example.myapplication.api.CommonService;
|
||||
import com.example.myapplication.model.ApiResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
public class DynamicDataFetcher {
|
||||
private final String token;
|
||||
private final CommonService commonService;
|
||||
private final DatabaseHelper dbHelper;
|
||||
private final Context context;
|
||||
private final String dataType; // 新增:数据类型标识
|
||||
|
||||
/**
|
||||
* @param token 用户令牌
|
||||
* @param context 上下文
|
||||
* @param dataType 数据类型标识(如"work_types"、"image_sources"等)
|
||||
*/
|
||||
public DynamicDataFetcher(String token, Context context, String dataType) {
|
||||
this.token = token;
|
||||
this.context = context;
|
||||
this.dataType = dataType;
|
||||
this.dbHelper = new DatabaseHelper(context);
|
||||
Retrofit retrofit = RetrofitClient.getClient(token);
|
||||
this.commonService = retrofit.create(CommonService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步获取动态数据列表
|
||||
*/
|
||||
public List<Map<String, String>> fetchDynamicDataSync() throws IOException {
|
||||
if (isNetworkAvailable()) {
|
||||
Call<ApiResponse<List<Map<String, String>>>> call = commonService.getDynamicDataList();
|
||||
|
||||
Response<ApiResponse<List<Map<String, String>>>> response = call.execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isSuccess()) {
|
||||
List<Map<String, String>> result = response.body().getData();
|
||||
// 保存到数据库(带数据类型标识)
|
||||
dbHelper.saveDynamicData(dataType, result);
|
||||
return result;
|
||||
} else {
|
||||
throw new IOException("请求失败,状态码: " + response.code());
|
||||
}
|
||||
} else {
|
||||
// 无网络时从数据库获取(带数据类型标识)
|
||||
return dbHelper.getDynamicData(dataType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步获取动态数据列表
|
||||
*/
|
||||
public void fetchDynamicDataAsync(DynamicDataCallback callback) {
|
||||
List<Map<String, String>> cachedData = dbHelper.getDynamicData(dataType);
|
||||
if (!cachedData.isEmpty()) {
|
||||
callback.onSuccess(cachedData);
|
||||
}
|
||||
|
||||
if (isNetworkAvailable()) {
|
||||
Call<ApiResponse<List<Map<String, String>>>> call = commonService.getDynamicDataList();
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<List<Map<String, String>>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<List<Map<String, String>>>> call,
|
||||
Response<ApiResponse<List<Map<String, String>>>> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isSuccess()) {
|
||||
Log.e("图片源列表:",response.body().toString());
|
||||
List<Map<String, String>> result = response.body().getData();
|
||||
Log.e("result",result.toString());
|
||||
// 保存到数据库(带数据类型标识)
|
||||
dbHelper.saveDynamicData(dataType, result);
|
||||
callback.onSuccess(result);
|
||||
} else if (cachedData.isEmpty()) {
|
||||
callback.onFailure(new IOException("请求失败,状态码: " + response.code()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<List<Map<String, String>>>> call, Throwable t) {
|
||||
if (cachedData.isEmpty()) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (cachedData.isEmpty()) {
|
||||
callback.onFailure(new IOException("无网络连接且无缓存数据"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查网络是否可用
|
||||
*/
|
||||
private boolean isNetworkAvailable() {
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivityManager != null) {
|
||||
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该数据类型的最后同步时间
|
||||
*/
|
||||
public long getLastSyncTime() {
|
||||
return dbHelper.getDynamicDataLastSync(dataType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前数据类型的缓存
|
||||
*/
|
||||
public void clearCache() {
|
||||
dbHelper.clearDynamicData(dataType);
|
||||
}
|
||||
|
||||
public interface DynamicDataCallback {
|
||||
void onSuccess(List<Map<String, String>> dataList);
|
||||
void onFailure(Throwable t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
package com.example.myapplication.Tool;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
|
||||
import com.example.myapplication.DataBase.DatabaseHelper;
|
||||
import com.example.myapplication.api.PartService;
|
||||
import com.example.myapplication.model.ApiResponse;
|
||||
import com.example.myapplication.model.PartResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
public class PartListFetcher {
|
||||
private final Context context;
|
||||
private final String token;
|
||||
private final String projectId;
|
||||
|
||||
private final PartService partService;
|
||||
private final DatabaseHelper dbHelper;
|
||||
|
||||
public PartListFetcher(Context context, String token, String projectId) {
|
||||
this.context = context;
|
||||
this.token = token;
|
||||
this.projectId = projectId;
|
||||
|
||||
this.dbHelper = new DatabaseHelper(context);
|
||||
|
||||
// 初始化Retrofit
|
||||
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
Retrofit retrofit = RetrofitClient.getClient(token);
|
||||
|
||||
this.partService = retrofit.create(PartService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步获取部件列表
|
||||
* @return 部件列表
|
||||
* @throws IOException 网络异常
|
||||
* @throws ApiException API异常
|
||||
*/
|
||||
public List<PartResponse> fetchPartListSync() throws IOException, ApiException {
|
||||
if (isNetworkAvailable()) {
|
||||
// 有网络时从服务器获取并更新数据库
|
||||
List<PartResponse> serverParts = fetchFromServer();
|
||||
dbHelper.saveParts(serverParts);
|
||||
return serverParts;
|
||||
} else {
|
||||
// 无网络时从数据库获取
|
||||
return dbHelper.getAllParts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步获取部件列表
|
||||
* @param callback 回调接口
|
||||
*/
|
||||
public void fetchPartListAsync(final PartListCallback callback) {
|
||||
// 先从数据库获取数据快速显示
|
||||
List<PartResponse> cachedParts = dbHelper.getAllParts();
|
||||
if (!cachedParts.isEmpty()) {
|
||||
callback.onSuccess(cachedParts);
|
||||
}
|
||||
|
||||
// 检查网络连接
|
||||
if (isNetworkAvailable()) {
|
||||
// 有网络时从服务器获取并更新数据库
|
||||
fetchFromServerAsync(new PartListCallback() {
|
||||
@Override
|
||||
public void onSuccess(List<PartResponse> partList) {
|
||||
dbHelper.saveParts(partList);
|
||||
callback.onSuccess(partList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
// 服务器获取失败,如果之前有缓存数据则不报错
|
||||
if (cachedParts.isEmpty()) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (cachedParts.isEmpty()) {
|
||||
// 无网络且无缓存数据
|
||||
callback.onFailure(new IOException("无网络连接且无缓存数据"));
|
||||
}
|
||||
}
|
||||
public String getPartIdByName(String partName) {
|
||||
// 先从数据库查询
|
||||
String partId = dbHelper.getPartIdByName(partName);
|
||||
if (partId != null) {
|
||||
return partId;
|
||||
}
|
||||
|
||||
// 数据库中没有则尝试从服务器获取
|
||||
if (isNetworkAvailable()) {
|
||||
try {
|
||||
List<PartResponse> parts = fetchFromServer();
|
||||
dbHelper.saveParts(parts);
|
||||
// 再次从数据库查询
|
||||
return dbHelper.getPartIdByName(partName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private List<PartResponse> fetchFromServer() throws IOException, ApiException {
|
||||
Call<ApiResponse<List<PartResponse>>> call = partService.getPartList(
|
||||
projectId,
|
||||
|
||||
null, null, null, null
|
||||
|
||||
);
|
||||
|
||||
Response<ApiResponse<List<PartResponse>>> response = call.execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<List<PartResponse>> apiResponse = response.body();
|
||||
if (apiResponse.isSuccess()) {
|
||||
return apiResponse.getData();
|
||||
} else {
|
||||
throw new ApiException(apiResponse.getCode(), apiResponse.getMsg());
|
||||
}
|
||||
} else {
|
||||
throw new IOException("请求失败,状态码: " + response.code());
|
||||
}
|
||||
}
|
||||
private void fetchFromServerAsync(final PartListCallback callback) {
|
||||
Call<ApiResponse<List<PartResponse>>> call = partService.getPartList(
|
||||
projectId,
|
||||
|
||||
null, null, null, null
|
||||
|
||||
);
|
||||
|
||||
call.enqueue(new retrofit2.Callback<ApiResponse<List<PartResponse>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<List<PartResponse>>> call,
|
||||
Response<ApiResponse<List<PartResponse>>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<List<PartResponse>> apiResponse = response.body();
|
||||
if (apiResponse.isSuccess()) {
|
||||
callback.onSuccess(apiResponse.getData());
|
||||
} else {
|
||||
callback.onFailure(new ApiException(apiResponse.getCode(), apiResponse.getMsg()));
|
||||
}
|
||||
} else {
|
||||
callback.onFailure(new IOException("请求失败,状态码: " + response.code()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<List<PartResponse>>> call, Throwable t) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isNetworkAvailable() {
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (connectivityManager != null) {
|
||||
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 带筛选条件的异步获取部件列表
|
||||
* @param keyword 关键字
|
||||
* @param manufacturer 厂商
|
||||
* @param model 型号
|
||||
* @param type 类型
|
||||
* @param callback 回调接口
|
||||
*/
|
||||
public void fetchPartListWithFilterAsync(String keyword, String manufacturer,
|
||||
String model, String type,
|
||||
PartListCallback callback) {
|
||||
Call<ApiResponse<List<PartResponse>>> call = partService.getPartList(
|
||||
projectId,
|
||||
|
||||
keyword,
|
||||
manufacturer,
|
||||
model,
|
||||
type
|
||||
);
|
||||
|
||||
call.enqueue(new retrofit2.Callback<ApiResponse<List<PartResponse>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<List<PartResponse>>> call,
|
||||
Response<ApiResponse<List<PartResponse>>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<List<PartResponse>> apiResponse = response.body();
|
||||
if (apiResponse.isSuccess()) {
|
||||
callback.onSuccess(apiResponse.getData());
|
||||
} else {
|
||||
callback.onFailure(new ApiException(apiResponse.getCode(), apiResponse.getMsg()));
|
||||
}
|
||||
} else {
|
||||
callback.onFailure(new IOException("请求失败,状态码: " + response.code()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<List<PartResponse>>> call, Throwable t) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface PartListCallback {
|
||||
void onSuccess(List<PartResponse> partList);
|
||||
void onFailure(Throwable t);
|
||||
}
|
||||
public String getPartIdByNameSync(String partName) throws IOException, ApiException {
|
||||
List<PartResponse> partList = fetchPartListSync();
|
||||
for (PartResponse part : partList) {
|
||||
if (partName.equals(part.getPartName())) {
|
||||
return part.getPartId();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步根据部件名称获取部件ID
|
||||
* @param partName 要查找的部件名称
|
||||
* @param callback 回调接口
|
||||
*/
|
||||
public void getPartIdByNameAsync(String partName, PartIdCallback callback) {
|
||||
fetchPartListAsync(new PartListCallback() {
|
||||
@Override
|
||||
public void onSuccess(List<PartResponse> partList) {
|
||||
for (PartResponse part : partList) {
|
||||
if (partName.equals(part.getPartName())) {
|
||||
callback.onSuccess(part.getPartId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback.onSuccess(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
callback.onFailure(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface PartIdCallback {
|
||||
void onSuccess(String partId);
|
||||
void onFailure(Throwable t);
|
||||
}
|
||||
public static class ApiException extends Exception {
|
||||
private final int code;
|
||||
|
||||
public ApiException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.example.myapp;
|
||||
package com.example.myapplication.Tool;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package com.example.myapp;
|
||||
package com.example.myapplication.Tool;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.chaquo.python.Python;
|
||||
import com.chaquo.python.android.AndroidPlatform;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class ReportGeneratorHelper {
|
||||
|
||||
private Context context;
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package com.example.myapplication.Tool;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RetrofitClient {
|
||||
private static final String BASE_URL = "http://pms.dtyx.net:9158/";
|
||||
private static Retrofit retrofit;
|
||||
|
||||
public static Retrofit getClient(String authToken) {
|
||||
if (retrofit == null) {
|
||||
// 创建 OkHttpClient 并添加拦截器
|
||||
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS);
|
||||
|
||||
// 添加日志拦截器(可选)
|
||||
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
|
||||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
|
||||
okHttpClientBuilder.addInterceptor(loggingInterceptor);
|
||||
|
||||
// 添加认证拦截器(自动添加 Authorization header)
|
||||
okHttpClientBuilder.addInterceptor(new AuthInterceptor(authToken));
|
||||
|
||||
// 构建 Retrofit
|
||||
retrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.client(okHttpClientBuilder.build())
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build();
|
||||
}
|
||||
return retrofit;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.example.myapplication.Tool;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ClipData;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class ShowError {
|
||||
private Context context;
|
||||
private final Handler mainHandler;
|
||||
public ShowError(Context context) {
|
||||
this.context = context;
|
||||
this.mainHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
public void showErrorDialog(String title, String message) {
|
||||
if (context instanceof AppCompatActivity) {
|
||||
AppCompatActivity activity = (AppCompatActivity) context;
|
||||
activity.runOnUiThread(() -> {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton("确定", null)
|
||||
.setNegativeButton("复制错误", (dialog, which) -> {
|
||||
// 将错误复制到剪贴板
|
||||
ClipboardManager clipboard =
|
||||
(ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip =
|
||||
ClipData.newPlainText("错误信息", message);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
showToast("已复制错误信息");
|
||||
})
|
||||
.show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showToast(String message) {
|
||||
mainHandler.post(() -> Toast.makeText(context, message, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.example.myapplication.api;
|
||||
|
||||
import com.example.myapplication.LoginActivity;
|
||||
import com.example.myapplication.model.ApiResponse;
|
||||
import com.example.myapplication.model.LoginEntity;
|
||||
import com.example.myapplication.model.LoginRequest;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.PUT;
|
||||
|
||||
public interface AuthApi {
|
||||
@POST("/auth/login")
|
||||
Call<ApiResponse<LoginEntity>> login(@Body LoginRequest loginRequest);
|
||||
@PUT("/auth/modify-password")
|
||||
|
||||
Call<ApiResponse<Void>> modifyPassword(
|
||||
@Body LoginActivity.ChangePasswordRequest request
|
||||
);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.example.myapplication.api;
|
||||
|
||||
import com.example.myapplication.model.ApiResponse;
|
||||
import com.example.myapplication.model.ImageSourceItem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
|
||||
public interface CommonService {
|
||||
@GET("/common/list/common-image-source")
|
||||
Call<ApiResponse<List<ImageSourceItem>>> getImageSourceList(
|
||||
|
||||
);
|
||||
@GET("/common/list/common-image-source")
|
||||
Call<ApiResponse<List<Map<String, String>>>> getDynamicDataList();
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.example.myapplication.api;
|
||||
|
||||
import com.example.myapplication.model.ApiResponse;
|
||||
import com.example.myapplication.model.PartResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface PartService {
|
||||
@GET("/part/list")
|
||||
Call<ApiResponse<List<PartResponse>>> getPartList(
|
||||
@Query("projectId") String projectId,
|
||||
@Query("keyword") String keyword,
|
||||
@Query("partManufacturer") String partManufacturer,
|
||||
@Query("partModel") String partModel,
|
||||
@Query("partType") String partType
|
||||
|
||||
);
|
||||
}
|
|
@ -7,7 +7,7 @@ import java.util.List;
|
|||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
|
||||
import retrofit2.http.Headers;
|
||||
|
||||
|
||||
// ApiService.java
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.example.myapplication.api;
|
||||
|
||||
import com.example.myapplication.model.PageResult;
|
||||
import com.example.myapplication.model.UserInfo;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface UserApi {
|
||||
@GET("/user/list")
|
||||
Call<PageResult<UserInfo>> getUserList(
|
||||
@Header("Authorization") String token,
|
||||
@Query("account") String account,
|
||||
@Query("deptId") String deptId,
|
||||
@Query("mobile") String mobile,
|
||||
@Query("name") String name,
|
||||
@Query("userCode") String userCode,
|
||||
@Query("userStatus") String userStatus,
|
||||
@Query("userType") String userType
|
||||
);
|
||||
}
|
|
@ -17,10 +17,15 @@ import androidx.room.PrimaryKey;
|
|||
public String audioPath;
|
||||
public int blade;
|
||||
public String unit;
|
||||
public String unitName;
|
||||
public String project;
|
||||
public boolean b;
|
||||
public String temperature="0"; // 温度
|
||||
public String humidity="0"; // 湿度
|
||||
public String weather="0"; // 天气状况
|
||||
public String imageSource="-1";
|
||||
|
||||
public ImageEntity(String path, long time, double latitude, double longitude, double altitude, String user, String audioPath, String project, String unit, int blade, boolean b) {
|
||||
public ImageEntity(String path, long time, double latitude, double longitude, double altitude, String user, String audioPath, String project, String unit, int blade, boolean b,String unitName,String temperature,String humidity,String weather,String imageSource) {
|
||||
|
||||
this.path = path;
|
||||
this.time = time;
|
||||
|
@ -33,6 +38,11 @@ import androidx.room.PrimaryKey;
|
|||
this.unit=unit;
|
||||
this.blade=blade;
|
||||
this.b=b;
|
||||
this.weather=weather;
|
||||
this.temperature=temperature;
|
||||
this.humidity=humidity;
|
||||
this.unitName=unitName;
|
||||
this.imageSource=imageSource;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package com.example.myapplication.model;
|
||||
|
||||
public class ImageSourceItem {
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
public ImageSourceItem(String key, String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// 用于JSON解析的无参构造
|
||||
public ImageSourceItem() {}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageSourceItem{" +
|
||||
"key='" + key + '\'' +
|
||||
", 类型='" + value + '\'' +
|
||||
'}';
|
||||
}
|
||||
// Getters and Setters
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package com.example.myapplication.model;
|
||||
|
||||
public class LoginEntity {
|
||||
private boolean isLogin;
|
||||
private String loginDeviceType;
|
||||
private Object loginId; // Using Object as specified
|
||||
private String loginType;
|
||||
private long sessionTimeout; // Changed to long for int64
|
||||
private String tag;
|
||||
private long tokenActiveTimeout; // Changed to long for int64
|
||||
private String tokenName;
|
||||
private long tokenSessionTimeout; // Changed to long for int64
|
||||
private long tokenTimeout; // Changed to long for int64
|
||||
private String tokenValue;
|
||||
|
||||
// Default constructor
|
||||
public LoginEntity() {
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public boolean isLogin() {
|
||||
return isLogin;
|
||||
}
|
||||
|
||||
public void setLogin(boolean login) {
|
||||
isLogin = login;
|
||||
}
|
||||
|
||||
public String getLoginDeviceType() {
|
||||
return loginDeviceType;
|
||||
}
|
||||
|
||||
public void setLoginDeviceType(String loginDeviceType) {
|
||||
this.loginDeviceType = loginDeviceType;
|
||||
}
|
||||
|
||||
public Object getLoginId() {
|
||||
return loginId;
|
||||
}
|
||||
|
||||
public void setLoginId(Object loginId) {
|
||||
this.loginId = loginId;
|
||||
}
|
||||
|
||||
public String getLoginType() {
|
||||
return loginType;
|
||||
}
|
||||
|
||||
public void setLoginType(String loginType) {
|
||||
this.loginType = loginType;
|
||||
}
|
||||
|
||||
public long getSessionTimeout() {
|
||||
return sessionTimeout;
|
||||
}
|
||||
|
||||
public void setSessionTimeout(long sessionTimeout) {
|
||||
this.sessionTimeout = sessionTimeout;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public long getTokenActiveTimeout() {
|
||||
return tokenActiveTimeout;
|
||||
}
|
||||
|
||||
public void setTokenActiveTimeout(long tokenActiveTimeout) {
|
||||
this.tokenActiveTimeout = tokenActiveTimeout;
|
||||
}
|
||||
|
||||
public String getTokenName() {
|
||||
return tokenName;
|
||||
}
|
||||
|
||||
public void setTokenName(String tokenName) {
|
||||
this.tokenName = tokenName;
|
||||
}
|
||||
|
||||
public long getTokenSessionTimeout() {
|
||||
return tokenSessionTimeout;
|
||||
}
|
||||
|
||||
public void setTokenSessionTimeout(long tokenSessionTimeout) {
|
||||
this.tokenSessionTimeout = tokenSessionTimeout;
|
||||
}
|
||||
|
||||
public long getTokenTimeout() {
|
||||
return tokenTimeout;
|
||||
}
|
||||
|
||||
public void setTokenTimeout(long tokenTimeout) {
|
||||
this.tokenTimeout = tokenTimeout;
|
||||
}
|
||||
|
||||
public String getTokenValue() {
|
||||
return tokenValue;
|
||||
}
|
||||
|
||||
public void setTokenValue(String tokenValue) {
|
||||
this.tokenValue = tokenValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Login{" +
|
||||
"isLogin=" + isLogin +
|
||||
", loginDeviceType='" + loginDeviceType + '\'' +
|
||||
", loginId=" + loginId +
|
||||
", loginType='" + loginType + '\'' +
|
||||
", sessionTimeout=" + sessionTimeout +
|
||||
", tag='" + tag + '\'' +
|
||||
", tokenActiveTimeout=" + tokenActiveTimeout +
|
||||
", tokenName='" + tokenName + '\'' +
|
||||
", tokenSessionTimeout=" + tokenSessionTimeout +
|
||||
", tokenTimeout=" + tokenTimeout +
|
||||
", tokenValue='" + tokenValue + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.example.myapplication.model;
|
||||
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class LoginRequest {
|
||||
@SerializedName("account")
|
||||
private String account;
|
||||
@SerializedName("password")
|
||||
private String password;
|
||||
|
||||
public LoginRequest(String account, String password) {
|
||||
this.account = account;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.example.myapplication.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PageResult<T> {
|
||||
private int code;
|
||||
private String msg;
|
||||
private List<T> rows;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public PageResult() {
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public List<T> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void setRows(List<T> rows) {
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public PageResult(int code, String msg, List<T> rows, long total) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.rows = rows;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
private long total;
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package com.example.myapplication.model;
|
||||
|
||||
public class PartResponse {
|
||||
private String partCode;
|
||||
private String partId;
|
||||
private String partName;
|
||||
private String partType;
|
||||
private String partTypeLabel;
|
||||
private String projectId;
|
||||
private String projectName;
|
||||
private String turbineId;
|
||||
private String turbineName;
|
||||
|
||||
public String getPartCode() {
|
||||
return partCode;
|
||||
}
|
||||
|
||||
public void setPartCode(String partCode) {
|
||||
this.partCode = partCode;
|
||||
}
|
||||
|
||||
public String getPartId() {
|
||||
return partId;
|
||||
}
|
||||
|
||||
public void setPartId(String partId) {
|
||||
this.partId = partId;
|
||||
}
|
||||
|
||||
public String getPartTypeLabel() {
|
||||
return partTypeLabel;
|
||||
}
|
||||
|
||||
public void setPartTypeLabel(String partTypeLabel) {
|
||||
this.partTypeLabel = partTypeLabel;
|
||||
}
|
||||
|
||||
public String getPartType() {
|
||||
return partType;
|
||||
}
|
||||
|
||||
public void setPartType(String partType) {
|
||||
this.partType = partType;
|
||||
}
|
||||
|
||||
public void setPartName(String partName) {
|
||||
this.partName = partName;
|
||||
}
|
||||
|
||||
public String getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(String projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getTurbineId() {
|
||||
return turbineId;
|
||||
}
|
||||
|
||||
public void setTurbineId(String turbineId) {
|
||||
this.turbineId = turbineId;
|
||||
}
|
||||
|
||||
public String getTurbineName() {
|
||||
return turbineName;
|
||||
}
|
||||
|
||||
public void setTurbineName(String turbineName) {
|
||||
this.turbineName = turbineName;
|
||||
}
|
||||
|
||||
public String getPartName() {
|
||||
return partName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.example.myapplication.model;
|
||||
|
||||
public class UserInfo {
|
||||
private String account;
|
||||
private String name;
|
||||
private String deptName;
|
||||
|
||||
public String getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(String account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public UserInfo() {
|
||||
}
|
||||
|
||||
public UserInfo(String account, String name, String deptName, String mobile, String userCode, String userId) {
|
||||
this.account = account;
|
||||
this.name = name;
|
||||
this.deptName = deptName;
|
||||
this.mobile = mobile;
|
||||
this.userCode = userCode;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getDeptName() {
|
||||
return deptName;
|
||||
}
|
||||
|
||||
public void setDeptName(String deptName) {
|
||||
this.deptName = deptName;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public void setMobile(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
|
||||
public String getUserCode() {
|
||||
return userCode;
|
||||
}
|
||||
|
||||
public void setUserCode(String userCode) {
|
||||
this.userCode = userCode;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
private String mobile;
|
||||
private String userCode;
|
||||
private String userId;
|
||||
// 其他字段根据需要添加
|
||||
|
||||
// getters and setters
|
||||
}
|
|
@ -126,4 +126,5 @@
|
|||
android:text="登录"/>
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
|
@ -236,6 +236,27 @@
|
|||
android:text="当前状态:未监听"
|
||||
android:textColor="#212121"
|
||||
android:textSize="14sp" />
|
||||
<TextView
|
||||
android:id="@+id/tem_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="当前温度:为获取"
|
||||
android:textColor="#212121"
|
||||
android:textSize="14sp" />
|
||||
<TextView
|
||||
android:id="@+id/hum_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="当前湿度:未获取"
|
||||
android:textColor="#212121"
|
||||
android:textSize="14sp" />
|
||||
<TextView
|
||||
android:id="@+id/weather_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="当前天气:为获取"
|
||||
android:textColor="#212121"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
@ -467,6 +488,33 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="查看失败录音"
|
||||
android:layout_margin="8dp"/>
|
||||
<Button
|
||||
android:id="@+id/btn_success_audios"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="查看成功上传录音"
|
||||
android:layout_margin="8dp"/>
|
||||
<Button
|
||||
android:id="@+id/btn_convert"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="根据日期生成日报" />
|
||||
<Button
|
||||
android:id="@+id/btn_readdocx"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="查看已经生成日报" />
|
||||
<Button
|
||||
android:id="@+id/changePassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="修改密码"/>
|
||||
<TextView
|
||||
android:id="@+id/tv_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="准备就绪" />
|
||||
<!-- 定时上传图片设置(移动到最下方) -->
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
|
@ -567,7 +615,18 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="选择图像来源:"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_image_source"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:prompt="@string/image_source_prompt"/>
|
||||
<Button
|
||||
android:id="@+id/btnShowFloating"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="旧密码:"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etOldPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="新密码:"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etNewPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="确认密码:"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etConfirmPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</LinearLayout>
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="选择报告"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="400dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/report_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:choiceMode="multipleChoice"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="16dp"
|
||||
android:weightSum="4">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_delete_selected"
|
||||
android:layout_width="5dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="删除选中" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_delete_all"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="删除全部" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_share"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="分享"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="返回"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="12dp">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:focusable="false"
|
||||
android:clickable="false"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/file_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"/>
|
||||
</LinearLayout>
|
|
@ -3,6 +3,7 @@
|
|||
<!-- 应用名称 -->
|
||||
<string name="app_name">My Application</string>
|
||||
|
||||
<string name="image_source_prompt">请选择图像来源</string>
|
||||
<!-- 解决错误所需的字符串 -->
|
||||
<string name="next">Next</string>
|
||||
<string name="previous">Previous</string>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.chaquo.python") version "16.1.0" apply false
|
||||
alias(libs.plugins.android.application) apply false
|
||||
}
|
|
@ -6,9 +6,11 @@ pluginManagement {
|
|||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
maven { url = uri("https://chaquo.com/maven") }
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
|
@ -16,6 +18,7 @@ dependencyResolutionManagement {
|
|||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url = uri("https://chaquo.com/maven") }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue