初始化项目
This commit is contained in:
+18
@@ -0,0 +1,18 @@
|
||||
*.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
|
||||
.idea/
|
||||
app/release/
|
||||
app/debug/
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,198 @@
|
||||
import com.android.build.api.dsl.Packaging
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
id("com.google.devtools.ksp")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.bbitcn.f8.pad"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.bbitcn.f8.pad"
|
||||
minSdk = 30
|
||||
targetSdk = 32
|
||||
versionCode = 44
|
||||
versionName = "1.0.44"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
ndk {
|
||||
abiFilters.addAll(listOf("armeabi", "armeabi-v7a", "x86", "arm64-v8a"))
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
// create("config") {
|
||||
// keyAlias = "key0"
|
||||
// keyPassword = "123456"
|
||||
// storeFile = file("../key/key.jks")
|
||||
// storePassword = "123456"
|
||||
// }
|
||||
getByName("debug") {
|
||||
keyAlias = "key0"
|
||||
keyPassword = "123456"
|
||||
storeFile = file("../key/key.jks")
|
||||
storeType = "jks"
|
||||
storePassword = "123456"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
// signingConfig = signingConfigs.getByName("config")
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// composeCompiler {
|
||||
// 强力跳过模式
|
||||
// enableStrongSkippingMode = true
|
||||
// }
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "21"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
getByName("main") {
|
||||
jniLibs.srcDirs("libs")
|
||||
}
|
||||
}
|
||||
fun Packaging.() {
|
||||
resources {
|
||||
excludes += setOf("META-INF/DEPENDENCIES", "common.properties")
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
// 打印机驱动CPCL_SDK_V1
|
||||
// 身份证驱动zkandroidcore
|
||||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
|
||||
// NFC需要
|
||||
// implementation("com.google.guava:guava:14.0")
|
||||
implementation("com.google.guava:guava:30.1-jre")
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
implementation(libs.androidx.core.splashscreen)
|
||||
//lifecycle-viewmodel-compose
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.material.icons.extended)
|
||||
//MMKV
|
||||
implementation(libs.mmkv)
|
||||
//util
|
||||
implementation(libs.com.blankj.utilcodex2)
|
||||
//GSON
|
||||
implementation(libs.com.google.code.gson.gson2)
|
||||
//权限库
|
||||
implementation(libs.com.github.getactivity.xxpermissions)
|
||||
//日志库
|
||||
implementation(libs.timber)
|
||||
implementation(libs.androidx.legacy.legacy.support.v43)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
//日期下拉框
|
||||
implementation(libs.android.pickerview)
|
||||
implementation("io.github.ltttttttttttt:ComposeViews:1.6.0.1")
|
||||
implementation("com.inuker.bluetooth:library:1.4.0")
|
||||
//RxJava
|
||||
// implementation("io.reactivex.rxjava2:rxjava:2.2.20")
|
||||
// implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
|
||||
// implementation("com.jakewharton.rxbinding2:rxbinding:2.2.0")
|
||||
// implementation("com.github.xuexiangjys:RxUtil2:1.2.1")
|
||||
// MD3
|
||||
implementation("androidx.compose.material3:material3:1.4.0-alpha12")
|
||||
//
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.5")
|
||||
//gson
|
||||
implementation("com.google.code.gson:gson:2.9.0")
|
||||
implementation("com.squareup.retrofit2:retrofit:2.11.0")
|
||||
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
|
||||
implementation("com.google.dagger:hilt-android:2.52")
|
||||
kapt("com.google.dagger:hilt-compiler:2.52")
|
||||
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
|
||||
implementation("com.github.devnied.emvnfccard:library:3.0.1")
|
||||
// 日历
|
||||
implementation("com.xhinliang:LunarCalendar:4.0.7")
|
||||
// 网络图片加载
|
||||
implementation("io.coil-kt.coil3:coil-compose:3.0.4")
|
||||
implementation("io.coil-kt.coil3:coil-network-okhttp:3.0.4")
|
||||
// Paging3
|
||||
val paging_version = "3.3.2"
|
||||
implementation("androidx.paging:paging-runtime:$paging_version")
|
||||
// optional - Jetpack Compose integration
|
||||
implementation("androidx.paging:paging-compose:$paging_version")
|
||||
// 阿里云接口
|
||||
implementation("com.aliyun:ocr_api20210707:3.1.2")
|
||||
|
||||
// CameraX core library using the camera2 implementation
|
||||
val camerax_version = "1.5.0-alpha04"
|
||||
// The following line is optional, as the core library is included indirectly by camera-camera2
|
||||
implementation("androidx.camera:camera-core:${camerax_version}")
|
||||
implementation("androidx.camera:camera-compose:${camerax_version}")
|
||||
implementation("androidx.camera:camera-camera2:${camerax_version}")
|
||||
// If you want to additionally use the CameraX Lifecycle library
|
||||
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
|
||||
// If you want to additionally use the CameraX VideoCapture library
|
||||
implementation("androidx.camera:camera-video:${camerax_version}")
|
||||
// If you want to additionally use the CameraX View class
|
||||
implementation("androidx.camera:camera-view:${camerax_version}")
|
||||
// If you want to additionally add CameraX ML Kit Vision Integration
|
||||
implementation("androidx.camera:camera-mlkit-vision:${camerax_version}")
|
||||
// If you want to additionally use the CameraX Extensions library
|
||||
implementation("androidx.camera:camera-extensions:${camerax_version}")
|
||||
// implementation("androidx.camera.viewfinder:viewfinder-compose:${camerax_version}")
|
||||
// implementation("androidx.camera:camera-viewfinder-compose:1.0.0-alpha02")
|
||||
|
||||
// 串口-超高频读卡器
|
||||
api("com.licheedev:android-serialport:2.1.3")
|
||||
// 条形码
|
||||
implementation("com.google.zxing:core:3.5.3")
|
||||
// 人脸识别 7M
|
||||
implementation("com.google.mlkit:face-detection:16.1.7")
|
||||
// 阿里云OSS
|
||||
// implementation("com.aliyun.oss:aliyun-sdk-oss:3.18.1")
|
||||
implementation("com.aliyun.dpa:oss-android-sdk:2.9.21")
|
||||
// USB驱动
|
||||
implementation("com.github.mik3y:usb-serial-for-android:3.8.1")
|
||||
// 日期时间选择器
|
||||
implementation("com.github.commandiron:WheelPickerCompose:1.1.11")
|
||||
// 图表组件
|
||||
implementation("io.github.ehsannarmani:compose-charts:0.1.2")
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.bbitcn.f8.pad
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.bbitcn.f8.pad", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- NFC -->
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.nfc"
|
||||
android:required="false" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<!-- 安装应用-->
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<!-- 电话状态-->
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<!-- 蓝牙-->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<!-- 网络-->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- 存储权限-->
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<!--配置权限,用来记录应用配置信息 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<!--手机定位信息,用来为语义等功能提供定位,提供更精准的服务-->
|
||||
<!--定位信息是敏感信息,可通过Setting.setLocationEnable(false)关闭定位请求 -->
|
||||
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>-->
|
||||
<!--如需使用人脸识别,还要添加:摄像头权限,拍照需要用到 -->
|
||||
<!-- <uses-permission android:name="android.permission.CAMERA" />-->
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth_le"
|
||||
android:required="true" />
|
||||
|
||||
<!-- 摄像头权限-->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera.any" />
|
||||
|
||||
<!-- 超高频读卡器 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<!-- 开机自启-->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<!-- USB-->
|
||||
<uses-permission android:name="android.permission.USB_PERMISSION" />
|
||||
<uses-feature android:name="android.hardware.usb.host" />
|
||||
|
||||
|
||||
<application
|
||||
android:name=".MyApp"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@drawable/logo"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:resizeableActivity="true"
|
||||
android:roundIcon="@drawable/logo"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.F8"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/Theme.F8">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.TECH_DISCOVERED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.nfc.action.TECH_DISCOVERED"
|
||||
android:resource="@xml/nfc_tech_filter" />
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||
android:resource="@xml/device_filter" />
|
||||
</activity>
|
||||
|
||||
<service android:name="com.inuker.bluetooth.library.BluetoothService" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="com.bbitcn.f8.pad.fileProvider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name=".receiver.BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receiver.FrpStartReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="receiver_start_frp" />
|
||||
<action android:name="receiver_frp_version" />
|
||||
<action android:name="receiver_frp_info" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="ScopedStorage"
|
||||
android:value="true" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
@@ -0,0 +1,30 @@
|
||||
package android.zyapi;
|
||||
|
||||
|
||||
public class CommonApi {
|
||||
|
||||
private static CommonApi mMe = null;
|
||||
|
||||
public CommonApi() {
|
||||
}
|
||||
|
||||
// gpio
|
||||
public native int setGpioMode(int pin, int mode);
|
||||
public native int setGpioDir(int pin, int dir);
|
||||
public native int setGpioPullEnable(int pin, int enable);
|
||||
public native int setGpioPullSelect(int pin, int select);
|
||||
public native int setGpioOut(int pin, int out);
|
||||
public native int getGpioIn(int pin);
|
||||
//serialport
|
||||
public native int openCom(String port, int baudrate, int bits, char event, int stop);
|
||||
public native int openComEx(String port, int baudrate, int bits, char event, int stop, int flags);
|
||||
public native int writeCom(int fd, byte[] buf, int sizes);
|
||||
public native int readCom(int fd, byte[] buf, int sizes);
|
||||
public native int readComEx(int fd, byte[] buf, int sizes, int sec, int usec);
|
||||
public native void closeCom(int fd);
|
||||
|
||||
static {
|
||||
System.loadLibrary("zyapi_common");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android_serialport_api.NFC;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
|
||||
/**
|
||||
* Utility class for creating {@link ParsedNdefMessage}s.
|
||||
*/
|
||||
public class NdefMessageParser {
|
||||
|
||||
// Utility class
|
||||
private NdefMessageParser() {
|
||||
|
||||
}
|
||||
|
||||
/** Parse an NdefMessage */
|
||||
public static List<ParsedNdefRecord> parse(NdefMessage message) {
|
||||
return getRecords(message.getRecords());
|
||||
}
|
||||
|
||||
public static List<ParsedNdefRecord> getRecords(NdefRecord[] records) {
|
||||
List<ParsedNdefRecord> elements = new ArrayList<ParsedNdefRecord>();
|
||||
for (final NdefRecord record : records) {
|
||||
if (UriRecord.isUri(record)) {
|
||||
elements.add(UriRecord.parse(record));
|
||||
} else if (TextRecord.isText(record)) {
|
||||
elements.add(TextRecord.parse(record));
|
||||
} else if (SmartPoster.isPoster(record)) {
|
||||
elements.add(SmartPoster.parse(record));
|
||||
} else {
|
||||
// String temp ="";
|
||||
// temp = new String(record.getPayload());
|
||||
// temp = bytesToHexString(record.getPayload(),true);
|
||||
elements.add(new ParsedNdefRecord() {
|
||||
@Override
|
||||
public String getViewText() {
|
||||
String temp = new String(record.getPayload()) + "\n";
|
||||
return temp;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
private static String hexString="0123456789ABCDEF";
|
||||
public static String decode(String bytes) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(
|
||||
bytes.length() / 2);
|
||||
for (int i = 0; i < bytes.length(); i += 2)
|
||||
baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString
|
||||
.indexOf(bytes.charAt(i + 1))));
|
||||
return new String(baos.toByteArray());
|
||||
}
|
||||
|
||||
|
||||
private static String bytesToHexString(byte[] src, boolean isPrefix) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (isPrefix == true) {
|
||||
stringBuilder.append("0x");
|
||||
}
|
||||
if (src == null || src.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
char[] buffer = new char[2];
|
||||
for (int i = 0; i < src.length; i++) {
|
||||
buffer[0] = Character.toUpperCase(Character.forDigit(
|
||||
(src[i] >>> 4) & 0x0F, 16));
|
||||
buffer[1] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,
|
||||
16));
|
||||
System.out.println(buffer);
|
||||
stringBuilder.append(buffer);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android_serialport_api.NFC;
|
||||
|
||||
public interface ParsedNdefRecord {
|
||||
|
||||
/**
|
||||
* Returns a view to display this record.
|
||||
*/
|
||||
public String getViewText();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android_serialport_api.NFC;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import android.nfc.FormatException;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* A representation of an NFC Forum "Smart Poster".
|
||||
*/
|
||||
public class SmartPoster implements ParsedNdefRecord {
|
||||
|
||||
/**
|
||||
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
|
||||
*
|
||||
* "The Title record for the service (there can be many of these in
|
||||
* different languages, but a language MUST NOT be repeated). This record is
|
||||
* optional."
|
||||
*/
|
||||
private final TextRecord mTitleRecord;
|
||||
|
||||
/**
|
||||
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
|
||||
*
|
||||
* "The URI record. This is the core of the Smart Poster, and all other
|
||||
* records are just metadata about this record. There MUST be one URI record
|
||||
* and there MUST NOT be more than one."
|
||||
*/
|
||||
private final UriRecord mUriRecord;
|
||||
|
||||
/**
|
||||
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
|
||||
*
|
||||
* "The Action record. This record describes how the service should be
|
||||
* treated. For example, the action may indicate that the device should save
|
||||
* the URI as a bookmark or open a browser. The Action record is optional.
|
||||
* If it does not exist, the device may decide what to do with the service.
|
||||
* If the action record exists, it should be treated as a strong suggestion;
|
||||
* the UI designer may ignore it, but doing so will induce a different user
|
||||
* experience from device to device."
|
||||
*/
|
||||
private final RecommendedAction mAction;
|
||||
|
||||
/**
|
||||
* NFC Forum Smart Poster Record Type Definition section 3.2.1.
|
||||
*
|
||||
* "The Type record. If the URI references an external entity (e.g., via a
|
||||
* URL), the Type record may be used to declare the MIME type of the entity.
|
||||
* This can be used to tell the mobile device what kind of an object it can
|
||||
* expect before it opens the connection. The Type record is optional."
|
||||
*/
|
||||
private final String mType;
|
||||
|
||||
private SmartPoster(UriRecord uri, TextRecord title, RecommendedAction action, String type) {
|
||||
mUriRecord = Preconditions.checkNotNull(uri);
|
||||
mTitleRecord = title;
|
||||
mAction = Preconditions.checkNotNull(action);
|
||||
mType = type;
|
||||
}
|
||||
|
||||
public UriRecord getUriRecord() {
|
||||
return mUriRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the smart poster. This may be {@code null}.
|
||||
*/
|
||||
public TextRecord getTitle() {
|
||||
return mTitleRecord;
|
||||
}
|
||||
|
||||
public static SmartPoster parse(NdefRecord record) {
|
||||
Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
|
||||
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER));
|
||||
try {
|
||||
NdefMessage subRecords = new NdefMessage(record.getPayload());
|
||||
return parse(subRecords.getRecords());
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static SmartPoster parse(NdefRecord[] recordsRaw) {
|
||||
try {
|
||||
Iterable<ParsedNdefRecord> records = NdefMessageParser.getRecords(recordsRaw);
|
||||
UriRecord uri = Iterables.getOnlyElement(Iterables.filter(records, UriRecord.class));
|
||||
TextRecord title = getFirstIfExists(records, TextRecord.class);
|
||||
RecommendedAction action = parseRecommendedAction(recordsRaw);
|
||||
String type = parseType(recordsRaw);
|
||||
return new SmartPoster(uri, title, action, type);
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPoster(NdefRecord record) {
|
||||
try {
|
||||
parse(record);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first element of {@code elements} which is an instance of
|
||||
* {@code type}, or {@code null} if no such element exists.
|
||||
*/
|
||||
private static <T> T getFirstIfExists(Iterable<?> elements, Class<T> type) {
|
||||
Iterable<T> filtered = Iterables.filter(elements, type);
|
||||
T instance = null;
|
||||
if (!Iterables.isEmpty(filtered)) {
|
||||
instance = Iterables.get(filtered, 0);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private enum RecommendedAction {
|
||||
UNKNOWN((byte) -1), DO_ACTION((byte) 0), SAVE_FOR_LATER((byte) 1), OPEN_FOR_EDITING(
|
||||
(byte) 2);
|
||||
|
||||
private static final ImmutableMap<Byte, RecommendedAction> LOOKUP;
|
||||
static {
|
||||
ImmutableMap.Builder<Byte, RecommendedAction> builder = ImmutableMap.builder();
|
||||
for (RecommendedAction action : RecommendedAction.values()) {
|
||||
builder.put(action.getByte(), action);
|
||||
}
|
||||
LOOKUP = builder.build();
|
||||
}
|
||||
|
||||
private final byte mAction;
|
||||
|
||||
private RecommendedAction(byte val) {
|
||||
this.mAction = val;
|
||||
}
|
||||
|
||||
private byte getByte() {
|
||||
return mAction;
|
||||
}
|
||||
}
|
||||
|
||||
private static NdefRecord getByType(byte[] type, NdefRecord[] records) {
|
||||
for (NdefRecord record : records) {
|
||||
if (Arrays.equals(type, record.getType())) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final byte[] ACTION_RECORD_TYPE = new byte[] {'a', 'c', 't'};
|
||||
|
||||
private static RecommendedAction parseRecommendedAction(NdefRecord[] records) {
|
||||
NdefRecord record = getByType(ACTION_RECORD_TYPE, records);
|
||||
if (record == null) {
|
||||
return RecommendedAction.UNKNOWN;
|
||||
}
|
||||
byte action = record.getPayload()[0];
|
||||
if (RecommendedAction.LOOKUP.containsKey(action)) {
|
||||
return RecommendedAction.LOOKUP.get(action);
|
||||
}
|
||||
return RecommendedAction.UNKNOWN;
|
||||
}
|
||||
|
||||
private static final byte[] TYPE_TYPE = new byte[] {'t'};
|
||||
|
||||
private static String parseType(NdefRecord[] records) {
|
||||
NdefRecord type = getByType(TYPE_TYPE, records);
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(type.getPayload(), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getViewText() {
|
||||
if (mTitleRecord != null) {
|
||||
return mTitleRecord.getText();
|
||||
}
|
||||
return mTitleRecord.getText();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android_serialport_api.NFC;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* An NFC Text Record
|
||||
*/
|
||||
public class TextRecord implements ParsedNdefRecord{
|
||||
|
||||
/** ISO/IANA language code */
|
||||
private final String mLanguageCode;
|
||||
|
||||
private final String mText;
|
||||
|
||||
private TextRecord(String languageCode, String text) {
|
||||
mLanguageCode = Preconditions.checkNotNull(languageCode);
|
||||
mText = Preconditions.checkNotNull(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getViewText() {
|
||||
return mText;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return mText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ISO/IANA language code associated with this text element.
|
||||
*/
|
||||
public String getLanguageCode() {
|
||||
return mLanguageCode;
|
||||
}
|
||||
|
||||
// TODO: deal with text fields which span multiple NdefRecords
|
||||
public static TextRecord parse(NdefRecord record) {
|
||||
Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN);
|
||||
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT));
|
||||
try {
|
||||
byte[] payload = record.getPayload();
|
||||
/*
|
||||
* payload[0] contains the "Status Byte Encodings" field, per the
|
||||
* NFC Forum "Text Record Type Definition" section 3.2.1.
|
||||
*
|
||||
* bit7 is the Text Encoding Field.
|
||||
*
|
||||
* if (Bit_7 == 0): The text is encoded in UTF-8 if (Bit_7 == 1):
|
||||
* The text is encoded in UTF16
|
||||
*
|
||||
* Bit_6 is reserved for future use and must be set to zero.
|
||||
*
|
||||
* Bits 5 to 0 are the length of the IANA language code.
|
||||
*/
|
||||
|
||||
if (payload.length <= 0) {
|
||||
payload = record.getType();
|
||||
return null;
|
||||
}
|
||||
String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
|
||||
int languageCodeLength = payload[0] & 0077;
|
||||
String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
|
||||
String text =
|
||||
new String(payload, languageCodeLength + 1,
|
||||
payload.length - languageCodeLength - 1, textEncoding);
|
||||
return new TextRecord(languageCode, text);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// should never happen unless we get a malformed tag.
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isText(NdefRecord record) {
|
||||
try {
|
||||
parse(record);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android_serialport_api.NFC;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import android.net.Uri;
|
||||
import android.nfc.NdefRecord;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
/**
|
||||
* A parsed record containing a Uri.
|
||||
*/
|
||||
public class UriRecord implements ParsedNdefRecord {
|
||||
|
||||
private static final String TAG = "UriRecord";
|
||||
|
||||
public static final String RECORD_TYPE = "UriRecord";
|
||||
|
||||
/**
|
||||
* NFC Forum "URI Record Type Definition"
|
||||
*
|
||||
* This is a mapping of "URI Identifier Codes" to URI string prefixes,
|
||||
* per section 3.2.2 of the NFC Forum URI Record Type Definition document.
|
||||
*/
|
||||
private static final BiMap<Byte, String> URI_PREFIX_MAP = ImmutableBiMap.<Byte, String>builder()
|
||||
.put((byte) 0x00, "")
|
||||
.put((byte) 0x01, "http://www.")
|
||||
.put((byte) 0x02, "https://www.")
|
||||
.put((byte) 0x03, "http://")
|
||||
.put((byte) 0x04, "https://")
|
||||
.put((byte) 0x05, "tel:")
|
||||
.put((byte) 0x06, "mailto:")
|
||||
.put((byte) 0x07, "ftp://anonymous:anonymous@")
|
||||
.put((byte) 0x08, "ftp://ftp.")
|
||||
.put((byte) 0x09, "ftps://")
|
||||
.put((byte) 0x0A, "sftp://")
|
||||
.put((byte) 0x0B, "smb://")
|
||||
.put((byte) 0x0C, "nfs://")
|
||||
.put((byte) 0x0D, "ftp://")
|
||||
.put((byte) 0x0E, "dav://")
|
||||
.put((byte) 0x0F, "news:")
|
||||
.put((byte) 0x10, "telnet://")
|
||||
.put((byte) 0x11, "imap:")
|
||||
.put((byte) 0x12, "rtsp://")
|
||||
.put((byte) 0x13, "urn:")
|
||||
.put((byte) 0x14, "pop:")
|
||||
.put((byte) 0x15, "sip:")
|
||||
.put((byte) 0x16, "sips:")
|
||||
.put((byte) 0x17, "tftp:")
|
||||
.put((byte) 0x18, "btspp://")
|
||||
.put((byte) 0x19, "btl2cap://")
|
||||
.put((byte) 0x1A, "btgoep://")
|
||||
.put((byte) 0x1B, "tcpobex://")
|
||||
.put((byte) 0x1C, "irdaobex://")
|
||||
.put((byte) 0x1D, "file://")
|
||||
.put((byte) 0x1E, "urn:epc:id:")
|
||||
.put((byte) 0x1F, "urn:epc:tag:")
|
||||
.put((byte) 0x20, "urn:epc:pat:")
|
||||
.put((byte) 0x21, "urn:epc:raw:")
|
||||
.put((byte) 0x22, "urn:epc:")
|
||||
.put((byte) 0x23, "urn:nfc:")
|
||||
.build();
|
||||
|
||||
private final Uri mUri;
|
||||
|
||||
private UriRecord(Uri uri) {
|
||||
this.mUri = Preconditions.checkNotNull(uri);
|
||||
}
|
||||
|
||||
public String getViewText(){
|
||||
return mUri.toString();
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert {@link android.nfc.NdefRecord} into a {@link android.net.Uri}.
|
||||
* This will handle both TNF_WELL_KNOWN / RTD_URI and TNF_ABSOLUTE_URI.
|
||||
*
|
||||
* @throws IllegalArgumentException if the NdefRecord is not a record
|
||||
* containing a URI.
|
||||
*/
|
||||
public static UriRecord parse(NdefRecord record) {
|
||||
short tnf = record.getTnf();
|
||||
if (tnf == NdefRecord.TNF_WELL_KNOWN) {
|
||||
return parseWellKnown(record);
|
||||
} else if (tnf == NdefRecord.TNF_ABSOLUTE_URI) {
|
||||
return parseAbsolute(record);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown TNF " + tnf);
|
||||
}
|
||||
|
||||
/** Parse and absolute URI record */
|
||||
private static UriRecord parseAbsolute(NdefRecord record) {
|
||||
byte[] payload = record.getPayload();
|
||||
Uri uri = Uri.parse(new String(payload, Charset.forName("UTF-8")));
|
||||
return new UriRecord(uri);
|
||||
}
|
||||
|
||||
/** Parse an well known URI record */
|
||||
private static UriRecord parseWellKnown(NdefRecord record) {
|
||||
Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord.RTD_URI));
|
||||
byte[] payload = record.getPayload();
|
||||
/*
|
||||
* payload[0] contains the URI Identifier Code, per the
|
||||
* NFC Forum "URI Record Type Definition" section 3.2.2.
|
||||
*
|
||||
* payload[1]...payload[payload.length - 1] contains the rest of
|
||||
* the URI.
|
||||
*/
|
||||
String prefix = URI_PREFIX_MAP.get(payload[0]);
|
||||
byte[] fullUri =
|
||||
Bytes.concat(prefix.getBytes(Charset.forName("UTF-8")), Arrays.copyOfRange(payload, 1,
|
||||
payload.length));
|
||||
Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8")));
|
||||
return new UriRecord(uri);
|
||||
}
|
||||
|
||||
public static boolean isUri(NdefRecord record) {
|
||||
try {
|
||||
parse(record);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] EMPTY = new byte[0];
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.bbitcn.f8.pad
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.provider.Settings
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bbitcn.f8.pad.utils.MMKVUtil
|
||||
import com.bbitcn.f8.pad.utils.TTSManager
|
||||
import com.bbitcn.f8.pad.utils.global.Global
|
||||
import com.bbitcn.f8.pad.utils.log.CrashHandlerUtil
|
||||
import com.bbitcn.f8.pad.utils.log.MyLog
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
import com.iflytek.cloud.SpeechConstant
|
||||
import com.iflytek.cloud.SpeechUtility
|
||||
|
||||
import org.xutils.x
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
/**
|
||||
* @Description APPLICATION类
|
||||
* @Author DuanKaiji
|
||||
* @CreateTime 2024年03月27日 13:43
|
||||
*/
|
||||
val M = Modifier
|
||||
.animateContentSize(
|
||||
animationSpec = spring(
|
||||
dampingRatio = Spring.DampingRatioLowBouncy,
|
||||
stiffness = Spring.StiffnessLow
|
||||
)
|
||||
)
|
||||
val MD = Modifier
|
||||
|
||||
val IS_DEBUG_DRYCOCOON = false // 是否是调试状态
|
||||
|
||||
|
||||
class MyApp : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
// 初始化MMKV
|
||||
MMKVUtil.init(applicationContext)
|
||||
// 初始化崩溃捕捉
|
||||
CrashHandlerUtil.init()
|
||||
// 初始化日志库
|
||||
Timber.plant(MyLog())
|
||||
// 初始化网络请求库
|
||||
x.Ext.init(this)
|
||||
// 初始化全局变量
|
||||
MMKVUtil.put(Global.DEVICE_ID, Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID))
|
||||
// 初始化讯飞语音
|
||||
SpeechUtility.createUtility(applicationContext, SpeechConstant.APPID +"=5d0fed03")
|
||||
// 初始化文本转语音
|
||||
TTSManager.init(applicationContext)
|
||||
|
||||
MyLog.test("设备唯一码:${Global.getDeviceId()}")
|
||||
|
||||
// 在开发阶段启用 StrictMode
|
||||
// if (BuildConfig.DEBUG) {
|
||||
// val policy = StrictMode.ThreadPolicy.Builder()
|
||||
// .detectDiskReads()
|
||||
// .detectDiskWrites()
|
||||
// .detectNetwork()
|
||||
// .penaltyLog() // 记录到 Logcat
|
||||
// .build()
|
||||
//
|
||||
// StrictMode.setThreadPolicy(policy)
|
||||
// }
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val appContext: Context
|
||||
get() = ActivityUtils.getTopActivity()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.bbitcn.f8.pad.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import android.view.WindowInsetsController
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.max
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.bbitcn.f8.pad.M
|
||||
import com.bbitcn.f8.pad.MyApp
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
|
||||
@Preview(showBackground = true, widthDp = 1280, heightDp = 800)
|
||||
@Composable
|
||||
fun BaseDialogPreview() {
|
||||
MyDialog(
|
||||
title = "关于我们",
|
||||
showDialog = true,
|
||||
onDismissRequest = {},
|
||||
content = {
|
||||
Column {
|
||||
Text(
|
||||
text = "奥立集团旗下四川主干信息技术有限公司,是一家专业从事搭建互联网云平台," +
|
||||
"服务传统行业的互联网企业,主营业务包括软件产品设计与研发、系统集成与维护" +
|
||||
"、互联网数据挖掘及应用。公司秉承“行业互助,资源共享、助小众企业实现大众信息化" +
|
||||
"、助大众企业实现全球化”的企业宗旨,以“专业、创新、服务、共赢”为核心价值观," +
|
||||
"为客户提供全方位的信息化解决方案。",
|
||||
color = Color.Black
|
||||
)
|
||||
}
|
||||
}, onClickOK = {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyDialog(
|
||||
title: String = "",
|
||||
showDialog: Boolean,
|
||||
onDismissRequest: () -> Unit,
|
||||
clickOKStr: String = "",
|
||||
onClickOK: () -> Unit = {},
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
MyAnimatedVisibility(showDialog) {
|
||||
Box(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
.background(Color(0x99000000))
|
||||
.noVisualFeedbackClickable {
|
||||
// onDismissRequest()
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
BaseDialogFrame(title, {
|
||||
content()
|
||||
}) {
|
||||
Row(modifier = M.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
BigButton(
|
||||
"返回", modifier = M
|
||||
.weight(1f)
|
||||
.widthIn(max = 100.dp)
|
||||
) {
|
||||
onDismissRequest()
|
||||
}
|
||||
// 判断是否有点击事件
|
||||
if (clickOKStr != "") {
|
||||
Spacer(modifier = M.width(30.dp))
|
||||
BigButton(clickOKStr, modifier = M.weight(1f), true) {
|
||||
onClickOK()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BaseDialogFrame(
|
||||
title: String = "",
|
||||
content: @Composable () -> Unit = {},
|
||||
bottomContent: @Composable () -> Unit = {}
|
||||
) {
|
||||
MyCard(
|
||||
modifier = M
|
||||
.padding(top = 10.dp, bottom = 50.dp, start = 100.dp, end = 100.dp)
|
||||
.noVisualFeedbackClickable { }
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 30.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = M
|
||||
.padding(top = 20.dp, bottom = 5.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 一个绿色的竖线
|
||||
Box(
|
||||
modifier = M
|
||||
.width(3.dp)
|
||||
.height(40.dp)
|
||||
.padding(end = 10.dp)
|
||||
.background(Color(0xFF209344))
|
||||
)
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = MaterialTheme.typography.titleLarge.fontSize,
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = M
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
content()
|
||||
}
|
||||
Column(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.padding(top = 10.dp, bottom = 20.dp), // 调整底部间距
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
bottomContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,751 @@
|
||||
package com.bbitcn.f8.pad.base
|
||||
|
||||
import android.R.attr.onClick
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.itemKey
|
||||
import com.bbitcn.f8.pad.M
|
||||
import com.bbitcn.f8.pad.MD
|
||||
import com.bbitcn.f8.pad.R
|
||||
import com.bbitcn.f8.pad.ui.theme.MyColors
|
||||
import com.cyberecho.ui.screen.JumpToBottomThreshold
|
||||
import com.cyberecho.ui.view.JumpToBottom
|
||||
import com.cyberecho.ui.view.JumpToTop
|
||||
import com.google.common.collect.Multimaps.index
|
||||
import kotlinx.coroutines.launch
|
||||
import org.slf4j.helpers.Reporter.info
|
||||
import kotlin.collections.map
|
||||
|
||||
@Composable
|
||||
fun TableHeadLine(modifier: Modifier, list: List<Pair<String, Int>>) {
|
||||
MyCard(elevation = 0.dp, colors = MyColors.BlueGreen, radius = 5.dp) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
list.forEach {
|
||||
if (it.first == "|") {
|
||||
VerticalDivider(
|
||||
color = MyColors.Gray,
|
||||
thickness = 1.dp,
|
||||
modifier = M
|
||||
.height(25.dp)
|
||||
.padding(horizontal = 5.dp)
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = it.first,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = M
|
||||
.weight(it.second.toFloat())
|
||||
.padding(vertical = 7.dp),
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MyColors.White,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TableHeadLineCard(modifier: Modifier, list: List<Pair<String, Int>>, isSelect: Boolean) {
|
||||
MyCard(
|
||||
elevation = 0.dp,
|
||||
radius = 0.dp,
|
||||
colors = if (isSelect) MyColors.BlueGreen else MyColors.White
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
list.forEach {
|
||||
Text(
|
||||
text = it.first,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = M
|
||||
.weight(it.second.toFloat())
|
||||
.padding(vertical = 5.dp),
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = if (isSelect) MyColors.White else MyColors.Black,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TableContent(
|
||||
modifier: Modifier,
|
||||
backgroundDeepColor: Boolean,
|
||||
list: List<Pair<String, Int>>,
|
||||
verticalPadding: Dp = 5.dp,
|
||||
onLongClick: () -> Unit = {},
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
onLongClick()
|
||||
},
|
||||
onTap = {
|
||||
onClick()
|
||||
}
|
||||
)
|
||||
},
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (backgroundDeepColor) Color(0xFFF3F7FD) else Color.White
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = M.padding(vertical = verticalPadding),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
list.forEach {
|
||||
if (it.first == "|") {
|
||||
VerticalDivider(
|
||||
color = MyColors.Gray,
|
||||
thickness = 1.dp,
|
||||
modifier = M
|
||||
.height(10.dp)
|
||||
.padding(horizontal = 5.dp)
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = it.first,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = M.weight(it.second.toFloat()),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyTable(
|
||||
modifier: Modifier,
|
||||
headerStrings: List<String>,
|
||||
ratio: List<Float>,
|
||||
items: List<List<Any>>,
|
||||
verticalPadding: Dp = 5.dp,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
TableHeadLine(
|
||||
modifier = M.fillMaxWidth(),
|
||||
list = headerStrings.zip(ratio.map { it.toInt() })
|
||||
)
|
||||
LazyColumn {
|
||||
items(count = items.size) { itIndex ->
|
||||
TableContent(
|
||||
verticalPadding = verticalPadding,
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.animateItem(),
|
||||
backgroundDeepColor = itIndex % 2 == 0,
|
||||
list = items[itIndex].map { it.toString() }.zip(ratio.map { it.toInt() }),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun <T : Any> MyRefreshTableForCard(
|
||||
modifier: Modifier,
|
||||
isRefreshing: Boolean,
|
||||
info: LazyPagingItems<T>,
|
||||
key: (T) -> Any,
|
||||
onFinishRefresh: () -> Unit,
|
||||
|
||||
columns: Int,// 列数
|
||||
item: @Composable (T, index: Int) -> Unit,
|
||||
) {
|
||||
val state = rememberPullToRefreshState()
|
||||
val pagerSate = info.loadState.append
|
||||
LaunchedEffect(pagerSate) {
|
||||
if (pagerSate is LoadState.NotLoading) {
|
||||
onFinishRefresh()
|
||||
}
|
||||
}
|
||||
Column(modifier = modifier) {
|
||||
PullToRefreshBox(
|
||||
modifier = M.fillMaxSize(),
|
||||
state = state,
|
||||
isRefreshing = isRefreshing,
|
||||
onRefresh = {
|
||||
info.refresh()
|
||||
},
|
||||
indicator = {
|
||||
PullToRefreshDefaults.IndicatorBox(
|
||||
state = state,
|
||||
isRefreshing = isRefreshing,
|
||||
modifier = M.align(Alignment.TopCenter),
|
||||
elevation = 0.dp
|
||||
) {
|
||||
if (isRefreshing) {
|
||||
CircularProgressIndicator(modifier = M.size(30.dp))
|
||||
} else {
|
||||
CircularProgressIndicator(
|
||||
modifier = M.size(30.dp),
|
||||
progress = { state.distanceFraction },
|
||||
trackColor = ProgressIndicatorDefaults.circularIndeterminateTrackColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
LazyVerticalGrid(
|
||||
modifier = M.fillMaxSize(),
|
||||
columns = GridCells.Fixed(columns),
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
items(count = info.itemCount, key = info.itemKey { key(it) }) { index ->
|
||||
val lineItem = info[index]!!
|
||||
Box(modifier = M.animateItem()) {
|
||||
item(lineItem, index)
|
||||
}
|
||||
}
|
||||
item {
|
||||
when (pagerSate) {
|
||||
is LoadState.NotLoading -> if (pagerSate.endOfPaginationReached)
|
||||
ListNoMore()
|
||||
|
||||
is LoadState.Loading -> ListLoading()
|
||||
is LoadState.Error -> ListError(message = pagerSate.error.message.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun <T : Any> MyAnyTable(
|
||||
modifier: Modifier,
|
||||
|
||||
// 每行
|
||||
info: List<T>,
|
||||
onLongClick: (T) -> Unit = {},
|
||||
onClick: (T) -> Unit = {},
|
||||
|
||||
// 每列
|
||||
items: List<MyTableData<T>>,
|
||||
verticalPadding: Dp = 15.dp,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
TableHeadLine(
|
||||
modifier = M.fillMaxWidth(), list = items.map { it.header to it.weight }
|
||||
)
|
||||
var selectIndex by rememberSaveable { mutableStateOf(-1) }
|
||||
LazyColumn {
|
||||
items(count = info.size) { index ->
|
||||
// 每行
|
||||
val lineItem = info[index]
|
||||
Card(
|
||||
modifier = M
|
||||
.animateItem()
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
onLongClick(lineItem)
|
||||
},
|
||||
onTap = {
|
||||
onClick(lineItem)
|
||||
selectIndex = if (selectIndex == index) -1 else index
|
||||
}
|
||||
)
|
||||
},
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (selectIndex == index
|
||||
|| (index % 2 == 0)
|
||||
) {
|
||||
Color(0xFFF3F7FD)
|
||||
} else {
|
||||
Color.White
|
||||
}
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
.padding()
|
||||
.padding(vertical = verticalPadding),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
items.forEach { item ->
|
||||
val content = item.content(lineItem)
|
||||
if (content == "|") {
|
||||
VerticalDivider(
|
||||
color = MyColors.Gray,
|
||||
thickness = 1.dp,
|
||||
modifier = M
|
||||
.height(10.dp)
|
||||
.padding(horizontal = 5.dp)
|
||||
)
|
||||
} else if (item.isButton) {
|
||||
MyButton(
|
||||
text = content,
|
||||
modifier = M
|
||||
.weight(item.weight.toFloat())
|
||||
.padding(horizontal = 2.5.dp),
|
||||
contentPadding = PaddingValues(5.dp, 2.5.dp)
|
||||
) {
|
||||
item.onClick(lineItem)
|
||||
}
|
||||
} else if (item.isIndex) {
|
||||
Text(
|
||||
text = (index + 1).toString(),
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = M.weight(item.weight.toFloat()),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = content,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = M.weight(item.weight.toFloat()),
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1, // 限制为单行
|
||||
overflow = TextOverflow.Ellipsis // 超出部分显示省略号
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T : Any> MyTable2(
|
||||
modifier: Modifier = Modifier,
|
||||
infos: List<T>,
|
||||
items: List<MyTableData<T>>,
|
||||
onLongClick: (T) -> Unit = {},
|
||||
onClick: (T) -> Unit = {},
|
||||
onExpend: @Composable (T) -> Unit = {},
|
||||
verticalPadding: Dp = 5.dp,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
// 表头
|
||||
TableHeadLine(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
list = items.map { it.header to it.weight }
|
||||
)
|
||||
|
||||
var selectIndex by rememberSaveable { mutableStateOf(-1) }
|
||||
|
||||
LazyColumn {
|
||||
items(count = infos.size) { index ->
|
||||
val lineItem = infos[index]
|
||||
|
||||
Card(
|
||||
modifier = MD
|
||||
.animateItem()
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
onLongClick(lineItem)
|
||||
},
|
||||
onTap = {
|
||||
selectIndex = if (selectIndex == index) -1 else index
|
||||
onClick(lineItem)
|
||||
}
|
||||
)
|
||||
},
|
||||
// 直角
|
||||
shape = MaterialTheme.shapes.small,
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (selectIndex == index) {
|
||||
MyColors.LightLightBlueGreen
|
||||
} else if (index % 2 == 0) {
|
||||
Color(0xFFF3F7FD)
|
||||
} else {
|
||||
Color.White
|
||||
}
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Row(
|
||||
modifier = M
|
||||
.padding(vertical = verticalPadding),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
items.forEach { item ->
|
||||
Text(
|
||||
text = item.content(lineItem),
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = Modifier.weight(item.weight.toFloat()),
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
if (selectIndex == index) {
|
||||
Box(modifier = M.padding(2.5.dp)){
|
||||
Column {
|
||||
onExpend(lineItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun <T : Any> MyRefreshTable(
|
||||
modifier: Modifier,
|
||||
isRefreshing: Boolean,
|
||||
info: LazyPagingItems<T>,
|
||||
key: (T) -> Any,
|
||||
onFinishRefresh: () -> Unit,
|
||||
|
||||
// 每行
|
||||
onLongClick: (T) -> Unit = {},
|
||||
onClick: (T) -> Unit = {},
|
||||
|
||||
onExpend: @Composable (T) -> Unit = {},
|
||||
|
||||
// 每列
|
||||
items: List<MyTableData<T>>,
|
||||
verticalPadding: Dp = 15.dp,
|
||||
scrollToTopOnRefresh: Boolean = false,
|
||||
) {
|
||||
val listState: LazyListState = rememberLazyListState()
|
||||
val state = rememberPullToRefreshState()
|
||||
val pagerSate = info.loadState.append
|
||||
LaunchedEffect(pagerSate) {
|
||||
if (pagerSate is LoadState.NotLoading) {
|
||||
onFinishRefresh()
|
||||
}
|
||||
}
|
||||
LaunchedEffect(info.loadState.refresh) {
|
||||
if (info.loadState.refresh is LoadState.NotLoading && scrollToTopOnRefresh) {
|
||||
listState.animateScrollToItem(0)
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
TableHeadLine(
|
||||
modifier = M.fillMaxWidth(), list = items.map { it.header to it.weight }
|
||||
)
|
||||
PullToRefreshBox(
|
||||
modifier = M.fillMaxSize(),
|
||||
state = state,
|
||||
isRefreshing = isRefreshing,
|
||||
onRefresh = {
|
||||
info.refresh()
|
||||
},
|
||||
indicator = {
|
||||
PullToRefreshDefaults.IndicatorBox(
|
||||
state = state,
|
||||
isRefreshing = isRefreshing,
|
||||
modifier = M.align(Alignment.TopCenter),
|
||||
elevation = 0.dp
|
||||
) {
|
||||
if (isRefreshing) {
|
||||
CircularProgressIndicator(modifier = M.size(30.dp))
|
||||
} else {
|
||||
CircularProgressIndicator(
|
||||
modifier = M.size(30.dp),
|
||||
progress = { state.distanceFraction },
|
||||
trackColor = ProgressIndicatorDefaults.circularIndeterminateTrackColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
var selectIndex by rememberSaveable { mutableStateOf(-1) }
|
||||
LazyColumn(state = listState) {
|
||||
items(count = info.itemCount, key = info.itemKey { key(it) }) { index ->
|
||||
// // 每行
|
||||
val lineItem = info[index]!!
|
||||
Card(
|
||||
modifier = MD
|
||||
.animateItem()
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
onLongClick(lineItem)
|
||||
},
|
||||
onTap = {
|
||||
onClick(lineItem)
|
||||
selectIndex = if (selectIndex == index) -1 else index
|
||||
}
|
||||
)
|
||||
},
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (selectIndex == index) {
|
||||
MyColors.LightLightBlueGreen
|
||||
} else if (index % 2 == 0) {
|
||||
Color(0xFFF3F7FD)
|
||||
} else {
|
||||
Color.White
|
||||
}
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
.padding(vertical = verticalPadding),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
items.forEach { item ->
|
||||
val content = item.content(lineItem)
|
||||
if (content == "|") {
|
||||
VerticalDivider(
|
||||
color = MyColors.Gray,
|
||||
thickness = 1.dp,
|
||||
modifier = M
|
||||
.height(10.dp)
|
||||
.padding(horizontal = 5.dp)
|
||||
)
|
||||
} else if (item.isButton) {
|
||||
MyButton(
|
||||
text = content,
|
||||
modifier = M
|
||||
.weight(item.weight.toFloat())
|
||||
.padding(horizontal = 2.5.dp),
|
||||
contentPadding = PaddingValues(5.dp, 2.dp)
|
||||
) {
|
||||
item.onClick(lineItem)
|
||||
}
|
||||
} else if (item.isIndex) {
|
||||
Text(
|
||||
text = (index + 1).toString(),
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = M.weight(item.weight.toFloat()),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = content,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
modifier = M.weight(item.weight.toFloat()),
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1, // 限制为单行
|
||||
overflow = TextOverflow.Ellipsis // 超出部分显示省略号
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectIndex == index) {
|
||||
onExpend(lineItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
when (pagerSate) {
|
||||
is LoadState.NotLoading -> if (pagerSate.endOfPaginationReached)
|
||||
ListNoMore()
|
||||
|
||||
is LoadState.Loading -> ListLoading()
|
||||
is LoadState.Error -> ListError(message = pagerSate.error.message.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
data class MyTableData<T>(
|
||||
val header: String,
|
||||
val weight: Int,
|
||||
val content: (T) -> String,
|
||||
val isButton: Boolean = false,
|
||||
val isIndex: Boolean = false,
|
||||
val onClick: (T) -> Unit = {},
|
||||
) {
|
||||
constructor(weight: Int, isIndex: Boolean = false) : this(
|
||||
header = "序号",
|
||||
content = { "" },
|
||||
weight = weight,
|
||||
isIndex = isIndex
|
||||
)
|
||||
|
||||
constructor(
|
||||
header: String, weight: Int, content: (T) -> String
|
||||
) : this(
|
||||
header = header,
|
||||
content = content,
|
||||
weight = weight,
|
||||
isButton = false,
|
||||
isIndex = false,
|
||||
onClick = { }
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun ListNoMore() {
|
||||
Row(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
color = Color.Black,
|
||||
text = "没有更多数据了",
|
||||
fontSize = 16.sp,
|
||||
modifier = M
|
||||
.padding(start = 12.dp)
|
||||
.align(CenterVertically)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ListLoading() {
|
||||
Row(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
CircularProgressIndicator(modifier = M.size(24.dp))
|
||||
Text(
|
||||
color = Color.Black,
|
||||
text = "正在加载中...",
|
||||
fontSize = 16.sp,
|
||||
modifier = M
|
||||
.padding(start = 12.dp)
|
||||
.align(CenterVertically)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ListError(message: String) {
|
||||
Card(
|
||||
modifier = M
|
||||
.padding(6.dp)
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
) {
|
||||
Row(
|
||||
modifier = M
|
||||
.fillMaxWidth()
|
||||
.background(Color.Red)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Image(
|
||||
modifier = M
|
||||
.clip(CircleShape)
|
||||
.width(42.dp)
|
||||
.height(42.dp),
|
||||
painter = painterResource(id = R.drawable.error),
|
||||
contentDescription = "",
|
||||
colorFilter = ColorFilter.tint(Color.White)
|
||||
)
|
||||
Text(
|
||||
color = Color.White,
|
||||
text = message,
|
||||
fontSize = 16.sp,
|
||||
modifier = M
|
||||
.padding(start = 12.dp)
|
||||
.align(CenterVertically)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.bbitcn.sericulture.base
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MMKVUtil
|
||||
import com.bbitcn.f8.pad.utils.log.MyLog
|
||||
import com.google.gson.Gson
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description 列表临时存储库
|
||||
* @Author DuanKaiji
|
||||
* @CreateTime 2024年08月06日 11:39:31
|
||||
*/
|
||||
abstract class BaseListTempDataBase<T> {
|
||||
protected val gson: Gson = Gson()
|
||||
|
||||
abstract fun getKey(): String
|
||||
|
||||
abstract fun getType(): Type
|
||||
|
||||
fun getAll(): MutableList<T> {
|
||||
val json = MMKVUtil.get(getKey(), "[]")
|
||||
return Gson().fromJson(json, getType())
|
||||
}
|
||||
|
||||
suspend fun init(data: List<T>) {
|
||||
MMKVUtil.put(getKey(), gson.toJson(data))
|
||||
}
|
||||
|
||||
suspend fun insert(t: T, afterInsert: (T) -> Unit = {}): Boolean {
|
||||
// val type: Type = ParameterizedTypeImpl(T::class.java)
|
||||
val temp: MutableList<T> = getAll()
|
||||
if (temp.contains(t)) {
|
||||
return false
|
||||
}
|
||||
temp.add(t)
|
||||
MyLog.test("insert: ${gson.toJson(temp)}")
|
||||
MMKVUtil.put(getKey(), gson.toJson(temp))
|
||||
afterInsert(t)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
*/
|
||||
fun update(predicate: (T) -> Boolean, afterUpdate: (T) -> Unit = {}): Boolean {
|
||||
val temp: MutableList<T> = getAll()
|
||||
for (mode in temp) {
|
||||
if (predicate(mode)) {
|
||||
afterUpdate(mode)
|
||||
MMKVUtil.put(getKey(), gson.toJson(temp))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun delete(predicate: (T) -> Boolean,afterDel: (T) -> Boolean = { true }): Boolean {
|
||||
val temp: MutableList<T> = getAll()
|
||||
val iterator = temp.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val mode = iterator.next()
|
||||
if (predicate(mode)) {
|
||||
iterator.remove()
|
||||
afterDel(mode)
|
||||
MMKVUtil.put(getKey(), gson.toJson(temp))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
fun clear(): Boolean {
|
||||
MMKVUtil.put(getKey(), "[]")
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.bbitcn.f8.pad.base
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.bbitcn.f8.pad.model.net.request.PageInfo
|
||||
import com.bbitcn.f8.pad.utils.network.RetrofitClient
|
||||
|
||||
abstract class BasePagingSource<T : Any, R : Any>(
|
||||
private val requestData: R
|
||||
) : PagingSource<Int, T>() {
|
||||
|
||||
val apiService = RetrofitClient.apiInterface()
|
||||
|
||||
abstract suspend fun fetchData(pageInfoJsonStr: String, requestData: R): List<T> // 返回 T 类型的列表
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
|
||||
return try {
|
||||
val page = params.key ?: 1
|
||||
val pageSize = params.loadSize
|
||||
val response = fetchData(PageInfo(page, pageSize).toJson(), requestData)
|
||||
// 计算上一页和下一页的 key
|
||||
val prevKey = if (page > 1) page - 1 else null
|
||||
val nextKey = if (response.size < pageSize) null else page + 1
|
||||
|
||||
LoadResult.Page(
|
||||
data = response, // 直接返回数据列表
|
||||
prevKey = prevKey,
|
||||
nextKey = nextKey
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
LoadResult.Error(exception)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 这里可以根据当前的分页位置来返回刷新key
|
||||
*/
|
||||
override fun getRefreshKey(state: PagingState<Int, T>): Int {
|
||||
return 1 // 默认返回1,具体实现可根据需求调整
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.bbitcn.sericulture.base
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MMKVUtil
|
||||
import com.blankj.utilcode.util.GsonUtils
|
||||
import com.google.gson.Gson
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
*
|
||||
* @Description 临时数据库(单实体,列表请用@BaseListTempDataBase)
|
||||
* @Author DuanKaiji
|
||||
* @CreateTime 2024年08月06日 11:39:31
|
||||
*/
|
||||
abstract class BaseTempDataBase<T> {
|
||||
|
||||
abstract fun getKey(): String
|
||||
abstract fun defaultData(): T
|
||||
abstract fun getType(): Type
|
||||
|
||||
fun init(value :T): Boolean {
|
||||
MMKVUtil.put(getKey(), Gson().toJson(value))
|
||||
return true
|
||||
}
|
||||
|
||||
fun getData(): T {
|
||||
val json = MMKVUtil.get(getKey(), GsonUtils.toJson(defaultData()))
|
||||
return Gson().fromJson(json, getType())
|
||||
}
|
||||
|
||||
fun clear(): Boolean {
|
||||
MMKVUtil.put(getKey(), "")
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.bbitcn.f8.pad.base
|
||||
|
||||
import android.media.MediaPlayer
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bbitcn.f8.pad.MyApp
|
||||
import com.bbitcn.f8.pad.R
|
||||
import com.bbitcn.f8.pad.ui.screen.dialog.LoadingDialogData
|
||||
import com.bbitcn.f8.pad.ui.screen.dialog.TipsDialogData
|
||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty.hideLoadingDialog
|
||||
import com.bbitcn.f8.pad.utils.PollingTask
|
||||
import com.bbitcn.f8.pad.utils.log.MyLog
|
||||
import com.bbitcn.f8.pad.utils.network.RetrofitClient
|
||||
import com.bbitcn.f8.pad.utils.network.RetrofitClientAI
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.SocketException
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
open class BaseViewModel : ViewModel() {
|
||||
|
||||
val apiService = RetrofitClient.apiInterface()
|
||||
val aiApiService = RetrofitClientAI.aiApiInterface()
|
||||
|
||||
protected var pollingTask: PollingTask =
|
||||
PollingTask.getInstance(javaClass.simpleName) // 使用类名作为ID
|
||||
|
||||
protected fun doInUIThread(task: () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.Main) {
|
||||
task()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun <T> doInIoThreadThenUI(
|
||||
loadingTips: String = "正在加载中",
|
||||
showDialog: Boolean = true,
|
||||
onError: (Throwable) -> Unit = { },
|
||||
onIO: suspend () -> T,
|
||||
onFinish: () -> Unit = { },
|
||||
onUI: (T) -> Unit,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val result = kotlin.runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (showDialog) {
|
||||
Toasty.showLoadingDialog(loadingTips)
|
||||
}
|
||||
onIO()
|
||||
}
|
||||
}
|
||||
|
||||
if (showDialog) {
|
||||
hideLoadingDialog()
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
result.onSuccess { data ->
|
||||
onUI(data)
|
||||
}.onFailure { exception ->
|
||||
// ✅ 如果是协程取消,不处理,只记录日志
|
||||
if (exception is CancellationException || exception.cause is SocketException && exception.cause?.message?.contains(
|
||||
"Socket closed"
|
||||
) == true
|
||||
) {
|
||||
MyLog.test("协程被取消:${exception.javaClass.simpleName},message=${exception.message}")
|
||||
return@onFailure
|
||||
}
|
||||
// 其他异常继续处理
|
||||
exception.printStackTrace()
|
||||
onError(exception)
|
||||
exception.message?.let {
|
||||
Toasty.error(it)
|
||||
}
|
||||
}.also {
|
||||
// ✅ 最终执行的操作
|
||||
onFinish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <T> doInIoThread(
|
||||
loadingTips: String = "正在加载中",
|
||||
showDialog: Boolean = true,
|
||||
onError: (Throwable) -> Unit = { },
|
||||
onFinish: () -> Unit = { },
|
||||
doInIO: suspend () -> T,
|
||||
) {
|
||||
doInIoThreadThenUI(loadingTips, showDialog, onError, doInIO, onFinish) { }
|
||||
}
|
||||
|
||||
fun <T> doInIoThreadNoDialog(
|
||||
onError: (Throwable) -> Unit = { },
|
||||
task: suspend () -> T,
|
||||
) {
|
||||
doInIoThread(showDialog = false, doInIO = task, onError = onError)
|
||||
}
|
||||
|
||||
/**
|
||||
* 在IO线程中执行任务,可选择是否显示加载对话框
|
||||
*/
|
||||
fun doInIoThreadWith(showLoading: Boolean, loadingTips: String, function: suspend () -> Unit) {
|
||||
if (showLoading) {
|
||||
doInIoThread(loadingTips) { function() }
|
||||
} else {
|
||||
doInIoThreadNoDialog { function() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动一个无限轮询任务
|
||||
*
|
||||
* @param pollingInterval 轮询间隔时间(单位:秒)
|
||||
* @param pollingTask 轮询任务的挂起函数
|
||||
*/
|
||||
fun polling(intervalSeconds: Long, task: suspend () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
MyLog.test("开始轮询任务,间隔:$intervalSeconds 秒")
|
||||
while (true) {
|
||||
task()
|
||||
delay(intervalSeconds * 1000L) // 转换秒为毫秒
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟开始轮询任务
|
||||
*/
|
||||
suspend fun delayPolling(delaySeconds: Long, intervalSeconds: Long, task: suspend () -> Unit) {
|
||||
delay(delaySeconds * 1000L)
|
||||
polling(intervalSeconds, task)
|
||||
}
|
||||
|
||||
private val taskMap = mutableMapOf<String, Job>()
|
||||
|
||||
/**
|
||||
* 放弃旧任务,执行新任务
|
||||
*
|
||||
* @param key 任务的唯一标识符
|
||||
* @param block 任务的挂起函数,必须使用协程,不能开启新的协程,否则无法取消任务
|
||||
*/
|
||||
fun launchTaskNewFirst(
|
||||
key: String,
|
||||
block: suspend () -> Unit
|
||||
) {
|
||||
taskMap[key]?.cancel() // 取消旧任务
|
||||
taskMap[key] = viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
block()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 有新任务时,取消。优先执行旧任务直到完成
|
||||
*
|
||||
* @param key 任务的唯一标识符
|
||||
* @param block 任务函数,可以自由新建协程,但一定要在任务完成时调用 onFinished 回调,否则会导致后续任务永远无法执行
|
||||
*/
|
||||
fun launchTaskOldFirst(
|
||||
key: String,
|
||||
block: (onFinished: () -> Unit) -> Unit
|
||||
) {
|
||||
val existing = taskMap[key]
|
||||
if (existing?.isActive == true) {
|
||||
MyLog.test("已有任务在运行,取消新任务")
|
||||
return
|
||||
}
|
||||
val job = viewModelScope.launch {
|
||||
try {
|
||||
suspendCancellableCoroutine<Unit> { continuation ->
|
||||
block {
|
||||
if (continuation.isActive) {
|
||||
continuation.resume(Unit) { cause, _, _ ->
|
||||
println("resume 后任务被取消: $cause")
|
||||
}
|
||||
}
|
||||
taskMap.remove(key)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
taskMap.remove(key)
|
||||
}
|
||||
}
|
||||
taskMap[key] = job
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
taskMap.values.forEach { it.cancel() }
|
||||
taskMap.clear()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,933 @@
|
||||
package com.bbitcn.f8.pad.base
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.AnimatedVisibilityScope
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CalendarMonth
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.AssistChip
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CheckboxDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.InputChipDefaults
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.ScrollableTabRow
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.request.ImageRequest
|
||||
import com.bbitcn.f8.pad.M
|
||||
import com.bbitcn.f8.pad.R
|
||||
import com.bbitcn.f8.pad.ui.screen.view.Toasty
|
||||
import com.bbitcn.f8.pad.ui.screen.view.drawer.IconInfo
|
||||
import com.bbitcn.f8.pad.ui.theme.MyColors
|
||||
import com.lt.compose_views.text_field.BackgroundComposeWithTextField
|
||||
import com.lt.compose_views.text_field.GoodTextField
|
||||
import com.lt.compose_views.text_field.HintComposeWithTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 监听某个键的变化,附带防抖功能
|
||||
*
|
||||
* @param key 要监听的键
|
||||
* @param debounceMillis 防抖延迟时间,默认100毫秒
|
||||
* @param effect 当键变化时执行的副作用函数
|
||||
*/
|
||||
@Composable
|
||||
fun <T> DebouncedEffect(
|
||||
key: T,
|
||||
debounceMillis: Long = 100L,
|
||||
effect: suspend () -> Unit
|
||||
) {
|
||||
val stableKey by produceState(initialValue = key, key) {
|
||||
delay(debounceMillis)
|
||||
value = key
|
||||
}
|
||||
|
||||
LaunchedEffect(stableKey) {
|
||||
effect()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @Author DuanKaiji
|
||||
* @CreateTime 2024年05月13日 10:09:12
|
||||
*/
|
||||
@Composable
|
||||
fun WhiteText(
|
||||
text: String,
|
||||
modifier: Modifier = M,
|
||||
fontSize: TextUnit = 16.sp,
|
||||
fontWeight: FontWeight? = null,
|
||||
color: Color = Color.White,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = modifier,
|
||||
fontSize = fontSize,
|
||||
fontWeight = fontWeight,
|
||||
color = color,
|
||||
style = TextStyle.Default.copy(color = color),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyButton(
|
||||
modifier: Modifier = M,
|
||||
text: String = "",
|
||||
enabled: Boolean = true,
|
||||
shape: Shape = RoundedCornerShape(4.dp), // 将圆角设为 4.dp
|
||||
border: BorderStroke? = null,
|
||||
colors: Color = MyColors.BlueGreen,
|
||||
contentPadding: PaddingValues = PaddingValues(
|
||||
start = 24.dp, top = 2.dp,
|
||||
end = 24.dp, bottom = 2.dp
|
||||
),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
interactionSource = interactionSource,
|
||||
shape = shape,
|
||||
border = border,
|
||||
colors = ButtonDefaults.buttonColors(containerColor = colors),
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BigButton(text: String, modifier: Modifier = M, isLight: Boolean = false, onClick: () -> Unit) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
shape = RoundedCornerShape(30.dp),
|
||||
modifier = modifier
|
||||
.height(50.dp)
|
||||
.fillMaxWidth(),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = if (text.contains("确") || isLight) MyColors.BlueGreen else MyColors.Gray)
|
||||
) {
|
||||
WhiteText(text, fontSize = MaterialTheme.typography.headlineMedium.fontSize)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyCard(
|
||||
modifier: Modifier = M,
|
||||
colors: Color = if (isSystemInDarkTheme()) MyColors.Black else Color.White,
|
||||
radius: Dp = 4.dp,
|
||||
border: BorderStroke? = null,
|
||||
elevation: Dp = 6.dp,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(radius),
|
||||
colors = CardDefaults.cardColors(containerColor = colors),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = elevation),
|
||||
border = border,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyInfoCard(
|
||||
modifier: Modifier,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
MyCard(
|
||||
modifier = modifier,
|
||||
elevation = 0.dp
|
||||
) {
|
||||
Box(modifier = M.fillMaxSize()) {
|
||||
this@MyCard.content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainFuncFrame(
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
.background(Color.White)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(R.drawable.bg)
|
||||
.build(),
|
||||
contentDescription = "Background",
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
|
||||
// 设置前景内容,透明背景方便显示背景图片
|
||||
Column(
|
||||
modifier = M
|
||||
.fillMaxSize()
|
||||
.padding(15.dp)
|
||||
.background(Color.Transparent) // 确保内容背景透明
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MyTextFieldPreview() {
|
||||
MyTextField(
|
||||
value = "Hello",
|
||||
onValueChange = {})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyTextField(
|
||||
modifier: Modifier = M,
|
||||
value: String,
|
||||
hint: String? = null,
|
||||
maxLines: Int = 1,
|
||||
fontSize: TextUnit = 16.sp,
|
||||
fontColor: Color = Color(0xff333333),
|
||||
maxLength: Int = Int.MAX_VALUE,
|
||||
contentAlignment: Alignment.Vertical = Alignment.CenterVertically,
|
||||
leading: (@Composable RowScope.() -> Unit)? = null,
|
||||
trailing: (@Composable RowScope.() -> Unit)? = null,
|
||||
background: BackgroundComposeWithTextField? = BackgroundComposeWithTextField.DEFAULT,
|
||||
horizontalPadding: Dp = 10.dp,
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
isNumberInputType: Boolean = false,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
onTextLayout: (TextLayoutResult) -> Unit = {},
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
cursorBrush: Brush = SolidColor(Color.Black),
|
||||
dateSelectTitle: String = "选择日期",
|
||||
isSelectDate: Boolean = false,
|
||||
onValueChange: (String) -> Unit = {},
|
||||
) {
|
||||
GoodTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier
|
||||
.height(35.dp)
|
||||
.background(color = MyColors.White)
|
||||
.noVisualFeedbackClickable {
|
||||
if (isSelectDate) {
|
||||
Toasty.openDatePickerDrawer(dateSelectTitle) {
|
||||
onValueChange(it.toString().replace("T", " "))
|
||||
}
|
||||
}
|
||||
},
|
||||
hint = hint?.let { HintComposeWithTextField.createTextHintCompose(it) },
|
||||
maxLines = maxLines,
|
||||
fontSize = fontSize,
|
||||
fontColor = if (readOnly) MyColors.Gray else fontColor,
|
||||
maxLength = maxLength,
|
||||
contentAlignment = contentAlignment,
|
||||
leading = leading,
|
||||
trailing = trailing,
|
||||
background = background,
|
||||
horizontalPadding = horizontalPadding,
|
||||
enabled = if (isSelectDate) false else enabled,
|
||||
readOnly = readOnly,
|
||||
textStyle = textStyle,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = if (isNumberInputType) KeyboardType.Number else KeyboardType.Text),
|
||||
keyboardActions = keyboardActions,
|
||||
visualTransformation = visualTransformation,
|
||||
onTextLayout = onTextLayout,
|
||||
interactionSource = interactionSource,
|
||||
cursorBrush = cursorBrush
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun VipBadgePreview() {
|
||||
VipBadge {
|
||||
MyButton(text = "Hello") {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VipBadge(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
|
||||
Box(modifier = modifier) {
|
||||
// 内容显示区域
|
||||
content()
|
||||
|
||||
// 显示 Badge
|
||||
Box(
|
||||
modifier = M
|
||||
.align(Alignment.TopEnd) // 控制Badge的位置
|
||||
.padding(2.5.dp) // 适当的内边距,避免和内容重叠
|
||||
) {
|
||||
// Badge(containerColor = MyColors.Transparent) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.vip),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RedPointBadge(content: @Composable () -> Unit) {
|
||||
Box {
|
||||
// 内容显示区域
|
||||
content()
|
||||
Box(
|
||||
modifier = M
|
||||
.align(Alignment.TopEnd) // 控制Badge的位置
|
||||
.padding(5.dp) // 适当的内边距,避免和内容重叠
|
||||
) {
|
||||
Box(
|
||||
modifier = M
|
||||
.size(6.dp) // 调整位置到右上角
|
||||
.background(MyColors.Red, shape = MaterialTheme.shapes.small)
|
||||
.align(Alignment.TopEnd),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyAnimatedVisibility(
|
||||
visible: Boolean,
|
||||
modifier: Modifier = M,
|
||||
enter: EnterTransition = fadeIn(),
|
||||
exit: ExitTransition = fadeOut(),
|
||||
label: String = "AnimatedVisibility",
|
||||
content: @Composable AnimatedVisibilityScope.() -> Unit
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
modifier = modifier,
|
||||
enter = enter,
|
||||
exit = exit,
|
||||
label = label,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyAnimatedVisibilityFromDownToUp(
|
||||
visible: Boolean,
|
||||
modifier: Modifier = M,
|
||||
enter: EnterTransition = expandVertically(expandFrom = Alignment.Bottom),
|
||||
exit: ExitTransition = fadeOut(),
|
||||
label: String = "AnimatedVisibility",
|
||||
content: @Composable AnimatedVisibilityScope.() -> Unit
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
modifier = modifier,
|
||||
enter = enter,
|
||||
exit = exit,
|
||||
label = label,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyOutlineButton(
|
||||
modifier: Modifier = M,
|
||||
text: String,
|
||||
isImportance: Boolean = false,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
border = BorderStroke(1.dp, if (isImportance) MyColors.Red else MyColors.BlueGreen),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
modifier = modifier.height(20.dp)
|
||||
) {
|
||||
Text(
|
||||
modifier = M.padding(horizontal = 5.dp),
|
||||
text = text,
|
||||
color = if (isImportance) MyColors.Red else MyColors.BlueGreen
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun QueryTextField(
|
||||
modifier: Modifier = M,
|
||||
text: String,
|
||||
hint: String = "搜索信息",
|
||||
onValueChange: (String) -> Unit
|
||||
) {
|
||||
MyTextField(
|
||||
modifier = modifier.height(35.dp),
|
||||
value = text,
|
||||
hint = hint,
|
||||
horizontalPadding = 5.dp,
|
||||
leading = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = "Search Icon",
|
||||
tint = Color.Gray
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
if (text.isNotEmpty()) {
|
||||
IconButton(onClick = {
|
||||
// 清空输入框
|
||||
onValueChange("")
|
||||
}) {
|
||||
Image(
|
||||
// painter = painterResource(id = R.drawable.check_more),
|
||||
Icons.Filled.Clear,
|
||||
contentDescription = "Check More",
|
||||
modifier = M.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}, onValueChange = onValueChange
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DateRangePickTextFiled(
|
||||
modifier: Modifier = M,
|
||||
dateRange: Pair<Date, Date>,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
GoodTextField(
|
||||
modifier = modifier
|
||||
.background(color = MyColors.White)
|
||||
.clickable(onClick = {
|
||||
onClick()
|
||||
})
|
||||
.width(200.dp)
|
||||
.height(35.dp),
|
||||
value = SimpleDateFormat("yyyy-M-d", Locale.getDefault()).format(
|
||||
dateRange.first
|
||||
) + " ~ " + SimpleDateFormat("yyyy-M-d", Locale.getDefault()).format(
|
||||
dateRange.second
|
||||
),
|
||||
onValueChange = {},
|
||||
horizontalPadding = 5.dp,
|
||||
enabled = false,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||
leading = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CalendarMonth,
|
||||
contentDescription = "Calendar Icon",
|
||||
tint = Color.Gray
|
||||
)
|
||||
}
|
||||
)
|
||||
// DateRangePickerModal(
|
||||
// showDialog = datePickerDialog,
|
||||
// onDateRangeSelected = { dateRange ->
|
||||
// if (dateRange.first != null && dateRange.second != null) {
|
||||
// queryDateRange = Pair(Date(dateRange.first!!), Date(dateRange.second!!))
|
||||
// onDateChanged(queryDateRange)
|
||||
// }
|
||||
// },
|
||||
// onDismiss = { datePickerDialog = false }
|
||||
// )
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun InfoText(title: String, content: String, modifier: Modifier = M, forceShow: Boolean = false) {
|
||||
if (content.isNotEmpty() || forceShow) {
|
||||
Row(
|
||||
modifier = modifier.padding(vertical = 3.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
color = MyColors.Gray,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
Text(
|
||||
modifier = M
|
||||
.weight(1f)
|
||||
.padding(horizontal = 5.dp),
|
||||
text = content,
|
||||
maxLines = 10,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VerticalTabs(
|
||||
modifier: Modifier = M,
|
||||
textModifier: Modifier = M,
|
||||
tabs: List<String>,
|
||||
selectedTab: Int,
|
||||
onTabSelected: (Int) -> Unit
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
tabs.forEachIndexed() { index, tab ->
|
||||
Row(
|
||||
modifier = M
|
||||
.animateItem()
|
||||
.clickable {
|
||||
onTabSelected(index)
|
||||
}
|
||||
.background(color = if (index == selectedTab) MyColors.LightGray else MyColors.Transparent),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
VerticalDivider(
|
||||
modifier = M
|
||||
.width(10.dp)
|
||||
.height(30.dp),
|
||||
color = if (index == selectedTab) MyColors.BlueGreen else MyColors.Transparent
|
||||
)
|
||||
Text(
|
||||
modifier = textModifier
|
||||
.padding(
|
||||
start = 15.dp, top = 15.dp, bottom = 15.dp
|
||||
)
|
||||
.width(100.dp),
|
||||
text = tab,
|
||||
fontWeight = if (index == selectedTab) FontWeight.Bold else FontWeight.Normal,
|
||||
color = if (index == selectedTab) MyColors.BlueGreen else MyColors.Black,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VerticalTabPages(
|
||||
modifier: Modifier = M,
|
||||
textModifier: Modifier = M,
|
||||
tabs: List<String>,
|
||||
onTabSelected: @Composable (Int) -> Unit
|
||||
) {
|
||||
var tabSelected by rememberSaveable { mutableStateOf(0) }
|
||||
Row {
|
||||
VerticalTabs(
|
||||
modifier = modifier,
|
||||
textModifier = textModifier,
|
||||
tabs = tabs,
|
||||
selectedTab = tabSelected,
|
||||
onTabSelected = {
|
||||
tabSelected = it
|
||||
}
|
||||
)
|
||||
VerticalDivider()
|
||||
Box(
|
||||
modifier = M
|
||||
.padding(horizontal = 10.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
onTabSelected(tabSelected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserBaseInfo(
|
||||
name: String,
|
||||
phone: String,
|
||||
address: String,
|
||||
idCard: String,
|
||||
payCard: String,
|
||||
payCardAddress: String = "",
|
||||
modifier: Modifier = M,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.icon_user),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier = M
|
||||
.size(35.dp)
|
||||
.padding(5.dp)
|
||||
)
|
||||
Text(
|
||||
text = name,
|
||||
fontSize = MaterialTheme.typography.bodyLarge.fontSize,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = M.weight(1f))
|
||||
Image(
|
||||
painter = painterResource(R.drawable.icon_tel),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier = M.size(20.dp)
|
||||
)
|
||||
Text(
|
||||
text = phone,
|
||||
fontSize = MaterialTheme.typography.bodyLarge.fontSize,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MyColors.BlueGreen
|
||||
)
|
||||
}
|
||||
IconInfo(R.drawable.icon_address, address)
|
||||
IconInfo(R.drawable.icon_idcard, idCard)
|
||||
IconInfo(R.drawable.icon_bankcard, payCard)
|
||||
if (payCardAddress != "") {
|
||||
InfoText("所属银行", payCardAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EasySelect(
|
||||
modifier: Modifier,
|
||||
items: List<String>,
|
||||
onlyNumber: Boolean = false,
|
||||
canInput: Boolean = true,
|
||||
onValueChanged: (String) -> Unit
|
||||
) {
|
||||
var value by rememberSaveable { mutableStateOf("") }
|
||||
LazyRow(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
item {
|
||||
if (items.isEmpty()) {
|
||||
Text(
|
||||
text = "暂无数据",
|
||||
color = MyColors.Black,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
items(items) {
|
||||
Box(
|
||||
modifier = M
|
||||
.clickable {
|
||||
value = it
|
||||
onValueChanged(it)
|
||||
}
|
||||
.background(color = if (value == it) MyColors.BlueGreen else MyColors.White)
|
||||
.border(1.dp, MyColors.Gray)
|
||||
) {
|
||||
Text(
|
||||
text = it,
|
||||
modifier = M
|
||||
.padding(horizontal = 10.dp, vertical = 5.dp),
|
||||
color = if (value == it) MyColors.White else MyColors.Black,
|
||||
fontSize = MaterialTheme.typography.bodyMedium.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
if (canInput) {
|
||||
MyTextField(
|
||||
modifier = M
|
||||
.width(100.dp)
|
||||
.padding(horizontal = 10.dp),
|
||||
value = value,
|
||||
isNumberInputType = true,
|
||||
onValueChange = {
|
||||
value = it
|
||||
onValueChanged(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyCheckBox(
|
||||
title: String,
|
||||
value: Boolean = true,
|
||||
modifier: Modifier = M,
|
||||
onValueChange: (Boolean) -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier
|
||||
.wrapContentWidth()
|
||||
.toggleable(
|
||||
value = value,
|
||||
onValueChange = {
|
||||
onValueChange(!value)
|
||||
},
|
||||
role = Role.Checkbox
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Checkbox(
|
||||
colors = CheckboxDefaults.colors(
|
||||
checkedColor = MyColors.BlueGreen
|
||||
),
|
||||
checked = value,
|
||||
onCheckedChange = {
|
||||
onValueChange(!value)
|
||||
}
|
||||
)
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = M.padding(start = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun isLandscape(): Boolean {
|
||||
val configuration = LocalConfiguration.current
|
||||
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
}
|
||||
|
||||
fun isBluetoothEnabled(): Boolean {
|
||||
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
|
||||
return bluetoothAdapter?.isEnabled == true
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义点击事件
|
||||
* 不使用clickable的点击效果
|
||||
*/
|
||||
fun Modifier.noVisualFeedbackClickable(
|
||||
onClick: () -> Unit
|
||||
): Modifier {
|
||||
return this.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onTap = {
|
||||
onClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyTabRowHorizontal(
|
||||
tabs: List<String>,
|
||||
onTabSelected: @Composable (Int) -> Unit
|
||||
) {
|
||||
var curPage by rememberSaveable { mutableStateOf(0) }
|
||||
Column {
|
||||
TabRow(
|
||||
selectedTabIndex = curPage,
|
||||
indicator = { tabPositions ->
|
||||
TabRowDefaults.Indicator(
|
||||
M.tabIndicatorOffset(tabPositions[curPage]),
|
||||
color = MyColors.BlueGreen
|
||||
)
|
||||
}
|
||||
) {
|
||||
tabs.forEachIndexed { index, title ->
|
||||
Tab(
|
||||
text = {
|
||||
Text(
|
||||
title,
|
||||
color = if (curPage == index) MyColors.BlueGreen else MyColors.Black,
|
||||
fontWeight = if (curPage == index) FontWeight.Bold else FontWeight.Normal,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
},
|
||||
selected = curPage == index,
|
||||
onClick = {
|
||||
curPage = index
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
// HorizontalPager(
|
||||
// state = pagerState,
|
||||
// beyondViewportPageCount = tabs.size,
|
||||
// modifier = M
|
||||
// .fillMaxSize()
|
||||
// .weight(1f)
|
||||
// ) { page ->
|
||||
// Column(
|
||||
// modifier = M.fillMaxWidth(),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally
|
||||
// ) {
|
||||
// onTabSelected(page)
|
||||
// }
|
||||
// }
|
||||
|
||||
Column(
|
||||
modifier = M.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
onTabSelected(curPage)
|
||||
}
|
||||
// HorizontalPager(
|
||||
// state = pagerState,
|
||||
// beyondViewportPageCount = tabs.size,
|
||||
// modifier = Modifier
|
||||
// .fillMaxSize()
|
||||
// .weight(1f)
|
||||
// ) { page ->
|
||||
// Column(
|
||||
// modifier = M.fillMaxWidth(),
|
||||
// horizontalAlignment = Alignment.CenterHorizontally
|
||||
// ) {
|
||||
// onTabSelected(page)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyScrollableTabRow(
|
||||
moidifer: Modifier = Modifier,
|
||||
position: Int,
|
||||
tabs: List<String>,
|
||||
onValueChange: (Int) -> Unit = {}
|
||||
) {
|
||||
ScrollableTabRow(
|
||||
selectedTabIndex = position,
|
||||
containerColor = MyColors.Transparent,
|
||||
edgePadding = 5.dp,
|
||||
modifier = moidifer,
|
||||
indicator = { tabPositions ->
|
||||
TabRowDefaults.SecondaryIndicator(
|
||||
M.tabIndicatorOffset(tabPositions[position]),
|
||||
color = MyColors.BlueGreen,
|
||||
)
|
||||
}
|
||||
) {
|
||||
tabs.forEachIndexed { index, title ->
|
||||
Tab(
|
||||
text = {
|
||||
Text(
|
||||
title,
|
||||
color = if (position == index) MyColors.BlueGreen else MyColors.Black,
|
||||
fontWeight = if (position == index) FontWeight.Bold else FontWeight.Normal,
|
||||
fontSize = MaterialTheme.typography.bodyLarge.fontSize
|
||||
)
|
||||
},
|
||||
selected = position == index,
|
||||
onClick = {
|
||||
onValueChange(index)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AssistChipFilter(
|
||||
title: String,
|
||||
content: String,
|
||||
onClick: (String) -> Unit = {},
|
||||
deleteEnable: Boolean = true,
|
||||
) {
|
||||
if (content.isNotEmpty()) {
|
||||
AssistChip(
|
||||
modifier = M.padding(horizontal = 10.dp),
|
||||
onClick = { onClick("") },
|
||||
label = { Text(title + content) },
|
||||
trailingIcon = {
|
||||
if (deleteEnable) {
|
||||
Icon(
|
||||
Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
M.size(InputChipDefaults.AvatarSize)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bbitcn.f8.pad.model
|
||||
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class ParameterizedTypeImpl(private var clazz: Class<*>) : ParameterizedType {
|
||||
override fun getActualTypeArguments(): Array<Type> {
|
||||
return arrayOf(clazz)
|
||||
}
|
||||
|
||||
override fun getRawType(): Type {
|
||||
return MutableList::class.java
|
||||
}
|
||||
|
||||
override fun getOwnerType(): Type? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.bbitcn.f8.pad.utils.TimeUtils
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AddDryAirRequest(
|
||||
@SerializedName("cjsysid")
|
||||
var cjsysid: String = "",
|
||||
@SerializedName("gjckcode")
|
||||
var gjckcode: String = "",
|
||||
@SerializedName("gjcksysid")
|
||||
var gjcksysid: String = "",
|
||||
@SerializedName("jiantype")
|
||||
var jiantype: String = "",
|
||||
@SerializedName("jiantypesysid")
|
||||
var jiantypesysid: String = "",
|
||||
@SerializedName("plantime")
|
||||
var plantime: String = TimeUtils.getStringTime(),
|
||||
@SerializedName("tanliangren")
|
||||
var tanliangren: String = "",
|
||||
@SerializedName("canpinzhong")
|
||||
var canpinzhong: String = "",
|
||||
@SerializedName("xiangzhen")
|
||||
var xiangzhen: String = "",
|
||||
)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import com.bbitcn.f8.pad.utils.TimeUtils
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AddDryInRequest(
|
||||
@SerializedName("bagtype")
|
||||
var bagtype: String = "",
|
||||
@SerializedName("bagzhongliang")
|
||||
var bagzhongliang: Double = 0.0,
|
||||
@SerializedName("canpinzhong")
|
||||
var canpinzhong: String = "",
|
||||
@SerializedName("cjsysid")
|
||||
var cjsysid: String = "",
|
||||
@SerializedName("codegeneralmodel")
|
||||
var codegeneralmodel: Int = 0,
|
||||
@SerializedName("gjckcode")
|
||||
var gjckcode: String = "",
|
||||
@SerializedName("gjcksysid")
|
||||
var gjcksysid: String = "",
|
||||
@SerializedName("hongjianren")
|
||||
var hongjianren: String = "",
|
||||
@SerializedName("jiantype")
|
||||
var jiantype: String = "",
|
||||
@SerializedName("jiantypesysid")
|
||||
var jiantypesysid: String = "",
|
||||
@SerializedName("rkdatetime")
|
||||
var rkdatetime: String = TimeUtils.getStringTime(),
|
||||
@SerializedName("rukuren")
|
||||
var rukuren: String = "",
|
||||
@SerializedName("xiangzhen")
|
||||
var xiangzhen: String = "",
|
||||
@SerializedName("rukutype")
|
||||
var rukutype: Int = -1, //入库类型 0:烘茧入库 1:翻包摊晾 2:出库盈余
|
||||
@SerializedName("standardtype")
|
||||
var standardtype: Int = 0
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import com.bbitcn.f8.pad.utils.TimeUtils
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AddDryOutRequest(
|
||||
@SerializedName("bagtype")
|
||||
var bagtype: String = "",
|
||||
@SerializedName("bagzhongliang")
|
||||
var bagzhongliang: Double = 0.0,
|
||||
@SerializedName("canpinzhong")
|
||||
var canpinzhong: String = "",
|
||||
@SerializedName("carpaihao")
|
||||
var carpaihao: String = "",
|
||||
@SerializedName("chukuren")
|
||||
var chukuren: String = "",
|
||||
@SerializedName("cjsysid")
|
||||
var cjsysid: String = "",
|
||||
@SerializedName("ckdatetime")
|
||||
var ckdatetime: String = TimeUtils.getStringTime(),
|
||||
@SerializedName("codegeneratemodel")
|
||||
var codegeneratemodel: Int = 0,
|
||||
@SerializedName("gjckcode")
|
||||
var gjckcode: String = "",
|
||||
@SerializedName("gjcksysid")
|
||||
var gjcksysid: String = "",
|
||||
@SerializedName("jiantype")
|
||||
var jiantype: String = "",
|
||||
@SerializedName("jiantypesysid")
|
||||
var jiantypesysid: String = "",
|
||||
@SerializedName("memo")
|
||||
var memo: String = "",
|
||||
@SerializedName("tihuoren")
|
||||
var tihuoren: String = "",
|
||||
@SerializedName("xiangzhen")
|
||||
var xiangzhen: String = "",
|
||||
@SerializedName("wldwsysid")
|
||||
var wldwsysid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AddFarmerRequest(
|
||||
@SerializedName("BankCode")
|
||||
val bankCode: String = "",
|
||||
@SerializedName("BankCode2")
|
||||
val bankCode2: String = "",
|
||||
@SerializedName("BankName")
|
||||
val bankName: String = "",
|
||||
@SerializedName("BankName2")
|
||||
val bankName2: String = "",
|
||||
@SerializedName("BankShortName")
|
||||
val bankShortName: String = "",
|
||||
@SerializedName("BankShortName2")
|
||||
val bankShortName2: String = "",
|
||||
@SerializedName("Cun")
|
||||
val cun: String = "",
|
||||
@SerializedName("DepartmentSysid")
|
||||
val departmentSysid: String = "",
|
||||
@SerializedName("IcCardCode")
|
||||
val icCardCode: Long = 0,
|
||||
@SerializedName("IdCard")
|
||||
val idCard: String = "",
|
||||
@SerializedName("IdCardAddress")
|
||||
val idCardAddress: String = "",
|
||||
@SerializedName("NhName")
|
||||
val nhName: String = "",
|
||||
@SerializedName("NhTips")
|
||||
val nhTips: String = "",
|
||||
@SerializedName("Phone")
|
||||
val phone: String = "",
|
||||
@SerializedName("PropertyName")
|
||||
val propertyName: String = "",
|
||||
@SerializedName("PropertySysid")
|
||||
val propertySysid: String = "",
|
||||
@SerializedName("RecBankCode")
|
||||
val recBankCode: String = "",
|
||||
@SerializedName("RecBankCode2")
|
||||
val recBankCode2: String = "",
|
||||
@SerializedName("Sex")
|
||||
val sex: String = "",
|
||||
@SerializedName("Sysid")
|
||||
val sysid: String = "",
|
||||
@SerializedName("Xian")
|
||||
val xian: String = "",
|
||||
@SerializedName("Xiang")
|
||||
val xiang: String = "",
|
||||
@SerializedName("Zu")
|
||||
val zu: String = ""
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AddUserRequest(
|
||||
@SerializedName("DepartmentSysid")
|
||||
val departmentSysid: String = "",
|
||||
@SerializedName("ICCardId")
|
||||
val iCCardId: Int = 0,
|
||||
@SerializedName("UserNew")
|
||||
val userNew: UserNew = UserNew(),
|
||||
@SerializedName("UserRole")
|
||||
val userRole: List<String> = listOf()
|
||||
) {
|
||||
data class UserNew(
|
||||
@SerializedName("Cun")
|
||||
val cun: String = "",
|
||||
@SerializedName("ICCardId")
|
||||
val iCCardId: Int = 0,
|
||||
@SerializedName("IdCard")
|
||||
val idCard: String = "",
|
||||
@SerializedName("LoginName")
|
||||
val loginName: String = "",
|
||||
@SerializedName("Memo")
|
||||
val memo: String = "",
|
||||
@SerializedName("Name")
|
||||
val name: String = "",
|
||||
@SerializedName("Sex")
|
||||
val sex: Boolean = false,
|
||||
@SerializedName("Sort")
|
||||
val sort: Int = 0,
|
||||
@SerializedName("Tel")
|
||||
val tel: String = "",
|
||||
@SerializedName("Xiang")
|
||||
val xiang: String = ""
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class AuthDevice(
|
||||
@SerializedName("Applicant")
|
||||
val applicant: String = "",
|
||||
@SerializedName("HardwareId")
|
||||
val hardwareId: String = "",
|
||||
@SerializedName("Job")
|
||||
val job: String = "",
|
||||
@SerializedName("Memo")
|
||||
val memo: String = "",
|
||||
@SerializedName("Phone")
|
||||
val phone: String = "",
|
||||
@SerializedName("Pwd")
|
||||
val pwd: String = "",
|
||||
@SerializedName("TenantCode")
|
||||
val tenantCode: String = ""
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ChatMessageRequest(
|
||||
@SerializedName("question")
|
||||
val question: String = "",
|
||||
@SerializedName("session_id")
|
||||
val sessionId: String = "",
|
||||
@SerializedName("stream")
|
||||
val stream: Boolean = false
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
data class CocoonTypeTranslateRequest(
|
||||
@SerializedName("czsysid")
|
||||
val czsysid: String = "",
|
||||
@SerializedName("newname")
|
||||
val newname: String = "",
|
||||
@SerializedName("newsysid")
|
||||
val newsysid: String = "",
|
||||
@SerializedName("oldname")
|
||||
val oldname: String = "",
|
||||
@SerializedName("oldsysid")
|
||||
val oldsysid: String = ""
|
||||
)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DateRangeRequest(
|
||||
@SerializedName("startdate")
|
||||
val startdate: String = "",
|
||||
@SerializedName("endate")
|
||||
val endate: String = "",
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class DeleteUserRequest(
|
||||
@SerializedName("Id")
|
||||
val id: Long = 0
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonAirDetailListRequest(
|
||||
@SerializedName("tlsysid")
|
||||
val tlsysid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonAirListRequest(
|
||||
@SerializedName("cjsysid")
|
||||
val cjsysid: String = "",
|
||||
@SerializedName("endtime")
|
||||
val endtime: String = "",
|
||||
@SerializedName("gjcksysid")
|
||||
val gjcksysid: String = "",
|
||||
@SerializedName("like")
|
||||
val like: String = "",
|
||||
@SerializedName("starttime")
|
||||
val starttime: String = ""
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonOutListRequest(
|
||||
@SerializedName("cjsysid")
|
||||
val cjsysid: String = "",
|
||||
@SerializedName("endtime")
|
||||
val endtime: String = "",
|
||||
@SerializedName("gjcksysid")
|
||||
val gjcksysid: String = "",
|
||||
@SerializedName("like")
|
||||
val like: String = "",
|
||||
@SerializedName("starttime")
|
||||
val starttime: String = ""
|
||||
)
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import com.bbitcn.f8.pad.utils.TimeUtils
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonPackageForOutLossRequest(
|
||||
@SerializedName("cjsysid")
|
||||
val cjsysid: String = "",
|
||||
@SerializedName("ckdsysid")
|
||||
val ckdsysid: String = "",
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = "",
|
||||
@SerializedName("time")
|
||||
val time: String = ""
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonPackageLossRequest(
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = "",
|
||||
@SerializedName("time")
|
||||
val time: String = "",
|
||||
@SerializedName("tlsysid")
|
||||
val tlsysid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonQueryListRequest(
|
||||
@SerializedName("cjsysid")
|
||||
val cjsysid: String = "",
|
||||
@SerializedName("endtime")
|
||||
val endtime: String = "",
|
||||
@SerializedName("gjcksysid")
|
||||
val gjcksysid: String = "",
|
||||
@SerializedName("like")
|
||||
val like: String = "",
|
||||
@SerializedName("starttime")
|
||||
val starttime: String = ""
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import com.bbitcn.f8.pad.utils.TimeUtils
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonRefreshStartRequest(
|
||||
@SerializedName("code")
|
||||
val code: String = "",
|
||||
@SerializedName("kcmaozhong")
|
||||
val kcmaozhong: Double = 0.0,
|
||||
@SerializedName("maozhong")
|
||||
val maozhong: String = "",
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = "",
|
||||
@SerializedName("rkitemsysid")
|
||||
val rkitemsysid: String = "",
|
||||
@SerializedName("time")
|
||||
val time: String = TimeUtils.getStringTime(),
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MyUtil
|
||||
import com.bbitcn.f8.pad.utils.TimeUtils
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonRefreshStopRequest(
|
||||
@SerializedName("maozhong")
|
||||
val maozhong: String = "",
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = "",
|
||||
@SerializedName("time")
|
||||
val time: String = TimeUtils.getStringTime(),
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonSaveInDetailRequest(
|
||||
@SerializedName("baoshu")
|
||||
val baoshu: Int = 0,
|
||||
@SerializedName("cjsysid")
|
||||
val cjsysid: String = "",
|
||||
@SerializedName("codegeneratemodel")
|
||||
val codegeneratemodel: Int = 0,
|
||||
@SerializedName("jingzhong")
|
||||
val jingzhong: Double = 0.0,
|
||||
@SerializedName("maozhong")
|
||||
val maozhong: Double = 0.0,
|
||||
@SerializedName("pizhong")
|
||||
val pizhong: Double = 0.0,
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = "",
|
||||
@SerializedName("rksysid")
|
||||
val rksysid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonSaveNewOutDetail(
|
||||
@SerializedName("baoshu")
|
||||
val baoshu: Int = 1,
|
||||
@SerializedName("cksysid")
|
||||
val cksysid: String = "",
|
||||
@SerializedName("jingzhong")
|
||||
val jingzhong: Double = 0.0,
|
||||
@SerializedName("maozhong")
|
||||
val maozhong: Double = 0.0,
|
||||
@SerializedName("pizhong")
|
||||
val pizhong: Double = 0.0,
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonSaveOutDetail(
|
||||
@SerializedName("baoshu")
|
||||
val baoshu: Int = 1,
|
||||
@SerializedName("cksysid")
|
||||
val cksysid: String = "",
|
||||
@SerializedName("code")
|
||||
val code: String = "",
|
||||
@SerializedName("jingzhong")
|
||||
val jingzhong: Double = 0.0,
|
||||
@SerializedName("maozhong")
|
||||
val maozhong: Double = 0.0,
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = "",
|
||||
@SerializedName("pizhong")
|
||||
val pizhong: Double = 0.0
|
||||
)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class DryCocoonStoreDetailListRequest(
|
||||
@SerializedName("tlsysid")
|
||||
val tlsysid: String = "",
|
||||
@SerializedName("tlsysid")
|
||||
val like: String = ""
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class EditPasswordRequest(
|
||||
@SerializedName("newpwd")
|
||||
val newpwd: String = "",
|
||||
@SerializedName("phone")
|
||||
val phone: String = "",
|
||||
@SerializedName("smsbucket")
|
||||
val smsbucket: String = "",
|
||||
@SerializedName("smscode")
|
||||
val smscode: String = ""
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FaceRecognizeRequest(
|
||||
@SerializedName("group_id_list")
|
||||
val group_id_list: String = "",
|
||||
@SerializedName("image")
|
||||
val image: String = "",
|
||||
@SerializedName("image_type")
|
||||
val image_type: String = "BASE64",
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FaceRegisterF8Request(
|
||||
@SerializedName("baidu_face_token")
|
||||
val baiduFaceToken: String = "",
|
||||
@SerializedName("oss_bucketname")
|
||||
val ossBucketname: String = "",
|
||||
@SerializedName("oss_objectname")
|
||||
val ossObjectname: String = "",
|
||||
@SerializedName("userid")
|
||||
val userid: String = "",
|
||||
@SerializedName("usertype")
|
||||
val usertype: Int = 0
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FaceRegisterRequest(
|
||||
@SerializedName("group_id")
|
||||
val group_id: String = "",
|
||||
@SerializedName("image")
|
||||
val image: String = "",
|
||||
@SerializedName("image_type")
|
||||
val image_type: String = "BASE64",
|
||||
@SerializedName("user_id")
|
||||
val user_id: String = "",
|
||||
@SerializedName("quality_control")
|
||||
val quality_control: String = "NORMAL",
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ForgetPasswordRequest(
|
||||
@SerializedName("hardwareid")
|
||||
val hardwareid: String = "",
|
||||
@SerializedName("newpwd")
|
||||
val newpwd: String = "",
|
||||
@SerializedName("phone")
|
||||
val phone: String = "",
|
||||
@SerializedName("smsbucket")
|
||||
val smsbucket: String = "",
|
||||
@SerializedName("smscode")
|
||||
val smscode: String = "",
|
||||
@SerializedName("tenantcode")
|
||||
val tenantcode: String = ""
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FundsRequest(
|
||||
@SerializedName("BegDate")
|
||||
val begDate: String = "",
|
||||
@SerializedName("DepSysid")
|
||||
val depSysid: String = "",
|
||||
@SerializedName("EndData")
|
||||
val endData: String = "",
|
||||
@SerializedName("Like")
|
||||
val like: String = "",
|
||||
@SerializedName("SearchType")
|
||||
val searchType: String = ""
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class FundsTotalListRequest(
|
||||
@SerializedName("BegDate")
|
||||
val begDate: String = "",
|
||||
@SerializedName("DepSysid")
|
||||
val depSysid: String = "",
|
||||
@SerializedName("EndData")
|
||||
val endData: String = "",
|
||||
@SerializedName("Like")
|
||||
val like: String = "",
|
||||
@SerializedName("SearchType")
|
||||
val searchType: String = "0"
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LoginByFaceRequest(
|
||||
@SerializedName("hardwareid")
|
||||
val hardwareid: String = "",
|
||||
@SerializedName("tenantcode")
|
||||
val tenantcode: String = "",
|
||||
@SerializedName("facetoken")
|
||||
val facetoken: String = "",
|
||||
@SerializedName("userid")
|
||||
val userid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LoginPhoneRequest(
|
||||
@SerializedName("hardwareid")
|
||||
val hardwareid: String = "",
|
||||
@SerializedName("phone")
|
||||
val phone: String = "",
|
||||
@SerializedName("smsbucket")
|
||||
val smsbucket: String = "",
|
||||
@SerializedName("smscode")
|
||||
val smscode: String = "",
|
||||
@SerializedName("tenantcode")
|
||||
val tenantcode: String = ""
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LoginRequest(
|
||||
@SerializedName("account")
|
||||
val account: String = "",
|
||||
@SerializedName("hardwareid")
|
||||
val hardwareid: String = "",
|
||||
@SerializedName("password")
|
||||
val password: String = "",
|
||||
@SerializedName("tenantcode")
|
||||
val tenantcode: String = ""
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
data class PageInfo(
|
||||
val page: Int = 1,
|
||||
val limit: Int = 30,
|
||||
val orderby: String = ""
|
||||
) {
|
||||
fun toJson(): String {
|
||||
return "{\"page\":$page,\"limit\":$limit,\"orderby\":\"$orderby\"}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.bbitcn.f8.pad.utils.MMKVUtil
|
||||
import com.bbitcn.f8.pad.utils.global.Global
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class PurchaseDataRequest(
|
||||
@SerializedName("BegDate")
|
||||
val begDate: String = "",
|
||||
@SerializedName("DepSysid")
|
||||
val depSysid: String = MMKVUtil.get(Global.DEP_SYS_ID),
|
||||
@SerializedName("EndData")
|
||||
val endData: String = "",
|
||||
@SerializedName("Like")
|
||||
val like: String = "",
|
||||
@SerializedName("sgSearchState")
|
||||
val sgSearchState: String = ""
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class RefreshTokenRequest(
|
||||
@SerializedName("hardwareid")
|
||||
val hardwareid: String = "",
|
||||
@SerializedName("ref_token")
|
||||
val refToken: String = "",
|
||||
@SerializedName("token")
|
||||
val token: String = ""
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
class SaveExtendInfoRequest(items: List<SaveExtendInfoRequestItem>) :
|
||||
ArrayList<SaveExtendInfoRequest.SaveExtendInfoRequestItem>(items) {
|
||||
|
||||
data class SaveExtendInfoRequestItem(
|
||||
@SerializedName("colname")
|
||||
val colname: String = "",
|
||||
@SerializedName("colsysid")
|
||||
val colsysid: String = "",
|
||||
@SerializedName("coltitle")
|
||||
val coltitle: String = "",
|
||||
@SerializedName("colvalue")
|
||||
val colvalue: String = "",
|
||||
@SerializedName("extendsysid")
|
||||
val extendsysid: String = "",
|
||||
@SerializedName("nhsysid")
|
||||
val nhsysid: String = ""
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SaveWeightDetailRequest(
|
||||
@SerializedName("BoxCount")
|
||||
val boxCount: Int = 0,
|
||||
@SerializedName("BoxName")
|
||||
val boxName: String = "",
|
||||
@SerializedName("BoxSysid")
|
||||
val boxSysid: String = "",
|
||||
@SerializedName("CarCode")
|
||||
val carCode: Int = 0,
|
||||
@SerializedName("CarWeight")
|
||||
val carWeight: Double = 0.0,
|
||||
@SerializedName("ChengIndex")
|
||||
val chengIndex: Int = 0,
|
||||
@SerializedName("CzItemSysid")
|
||||
val czItemSysid: String = "",
|
||||
@SerializedName("CzSysid")
|
||||
val czSysid: String = "",
|
||||
@SerializedName("HsRatio")
|
||||
val hsRatio: Int = 0,
|
||||
@SerializedName("HsValue")
|
||||
val hsValue: Int = 0,
|
||||
@SerializedName("ItemMoney")
|
||||
val itemMoney: Int = 0,
|
||||
@SerializedName("Jweight")
|
||||
val jweight: Double = 0.0,
|
||||
@SerializedName("Kweight")
|
||||
val kweight: Double = 0.0,
|
||||
@SerializedName("Mweight")
|
||||
val mweight: Double = 0.0,
|
||||
@SerializedName("NhSysid")
|
||||
val nhSysid: String = "",
|
||||
@SerializedName("Price")
|
||||
val price: Double = 0.0,
|
||||
@SerializedName("Pweight")
|
||||
val pweight: Double = 0.0,
|
||||
@SerializedName("SgTypeId")
|
||||
val sgTypeId: Int = 0,
|
||||
@SerializedName("SgTypeName")
|
||||
val sgTypeName: String = "",
|
||||
@SerializedName("SgTypeSysid")
|
||||
val sgTypeSysid: String = "",
|
||||
@SerializedName("WeiShuValue")
|
||||
val weiShuValue: Int = 0
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SaveWeightTicketRequest(
|
||||
@SerializedName("BatchCjSysid")
|
||||
val batchCjSysid: String = "",
|
||||
@SerializedName("CzSysid")
|
||||
val czSysid: String = "",
|
||||
@SerializedName("DepCode")
|
||||
val depCode: String = "",
|
||||
@SerializedName("DepSysid")
|
||||
val depSysid: String = "",
|
||||
@SerializedName("Extention")
|
||||
val extention: Extention = Extention(),
|
||||
@SerializedName("Gsysid")
|
||||
val gsysid: String = "",
|
||||
@SerializedName("GyhSysid")
|
||||
val gyhSysid: String = "",
|
||||
@SerializedName("InoputDataList")
|
||||
val inoputDataList: List<InoputData> = listOf(),
|
||||
@SerializedName("NhSysid")
|
||||
val nhSysid: String = "",
|
||||
@SerializedName("YpState")
|
||||
val ypState: String = ""
|
||||
) {
|
||||
data class Extention(
|
||||
@SerializedName("czsysid")
|
||||
val czsysid: String = "",
|
||||
@SerializedName("validhanshuicishu")
|
||||
val validhanshuicishu: Int = 0,
|
||||
@SerializedName("validhanshuilv")
|
||||
val validhanshuilv: String = ""
|
||||
)
|
||||
|
||||
data class InoputData(
|
||||
@SerializedName("InputName")
|
||||
val inputName: String = "",
|
||||
@SerializedName("InputSysid")
|
||||
val inputSysid: String = "",
|
||||
@SerializedName("InputValue")
|
||||
val inputValue: Int = 0 // 这里虽然理论上是Any 但实际上各个值都可以用int表示 例如:【是否方格簇-1表示没有选择,1是,0否】,【色泽1上、0中、-1下】 【含水率 也是数字】
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SearchOutDetailByRFIDRequest(
|
||||
@SerializedName("cjsysid")
|
||||
val cjsysid: String = "",
|
||||
@SerializedName("code")
|
||||
val code: String = "",
|
||||
@SerializedName("gjcksysid")
|
||||
val gjcksysid: String = "",
|
||||
@SerializedName("jiantypesysid")
|
||||
val jiantypesysid: String = "",
|
||||
@SerializedName("rfid")
|
||||
val rfid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SeedInfoRequest(
|
||||
@SerializedName("CjSysid")
|
||||
val cjSysid: String = "",
|
||||
@SerializedName("NhSysid")
|
||||
val nhSysid: String = ""
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SendCodeRequest(
|
||||
@SerializedName("expired")
|
||||
val expired: Int = 10,
|
||||
@SerializedName("smsbucket")
|
||||
val smsbucket: String = "",
|
||||
@SerializedName("tel")
|
||||
val tel: String = ""
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
|
||||
data class SetUserListRequest(
|
||||
@SerializedName("DepSysid")
|
||||
val depSysid: String = "",
|
||||
@SerializedName("Like")
|
||||
val like: String = "",
|
||||
/**
|
||||
* 查询用户类型
|
||||
* 全部用户=0,
|
||||
* 有效用户=1,
|
||||
* 冻结用户=2,
|
||||
*/
|
||||
@SerializedName("SearchUserType")
|
||||
val searchUserType: String = "0"//
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class StatisticsRequest(
|
||||
@SerializedName("endate")
|
||||
val endate: String = "",
|
||||
@SerializedName("like")
|
||||
val like: String = "",
|
||||
@SerializedName("sgtypesysid")
|
||||
val sgtypesysid: String = "",
|
||||
@SerializedName("startdate")
|
||||
val startdate: String = ""
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class TareRequest(
|
||||
@SerializedName("CzSysid")
|
||||
val czSysid: String = "",
|
||||
@SerializedName("UpdateItemList")
|
||||
val updateItemList: List<UpdateItem> = listOf()
|
||||
) {
|
||||
data class UpdateItem(
|
||||
@SerializedName("Jweight")
|
||||
var jweight: Double = 0.0,
|
||||
@SerializedName("Kweight")
|
||||
var kweight: Double = 0.0,
|
||||
@SerializedName("Pweight")
|
||||
var pweight: Double = 0.0,
|
||||
@SerializedName("SgTypName")
|
||||
val sgTypName: String = "",
|
||||
@SerializedName("Sysid")
|
||||
val sysid: String = ""
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class UpdateTicketPriceRequest(
|
||||
@SerializedName("CzSysid")
|
||||
val czSysid: String = "",
|
||||
@SerializedName("UpdateList")
|
||||
val updateList: List<Update> = listOf()
|
||||
) {
|
||||
data class Update(
|
||||
@SerializedName("NewJweight")
|
||||
val newJweight: Double = 0.0,
|
||||
@SerializedName("NewKweight")
|
||||
val newKweight: Double = 0.0,
|
||||
@SerializedName("NewMoney")
|
||||
var newMoney: Double = 0.0,
|
||||
@SerializedName("NewPrice")
|
||||
var newPrice: Double = 0.0,
|
||||
@SerializedName("NewPweight")
|
||||
val newPweight: Double = 0.0,
|
||||
@SerializedName("Sysid")
|
||||
val sysid: String = ""
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.bbitcn.f8.pad.model.net.request
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class UpdateUserRequest(
|
||||
@SerializedName("DepartmentSysid")
|
||||
val departmentSysid: String = "",
|
||||
@SerializedName("UserNew")
|
||||
val userNew: UserNew = UserNew(),
|
||||
@SerializedName("UserRole")
|
||||
val userRole: List<String> = listOf()
|
||||
) {
|
||||
data class UserNew(
|
||||
@SerializedName("Cun")
|
||||
val cun: String = "",
|
||||
@SerializedName("ICCardId")
|
||||
val iCCardId: Int = 0,
|
||||
@SerializedName("Id")
|
||||
val id: Long = 0,
|
||||
@SerializedName("IdCard")
|
||||
val idCard: String = "",
|
||||
@SerializedName("LoginName")
|
||||
val loginName: String = "",
|
||||
@SerializedName("Memo")
|
||||
val memo: String = "",
|
||||
@SerializedName("Name")
|
||||
val name: String = "",
|
||||
@SerializedName("Sex")
|
||||
val sex: Boolean = false,
|
||||
@SerializedName("Sort")
|
||||
val sort: Int = 0,
|
||||
@SerializedName("Tel")
|
||||
val tel: String = "",
|
||||
@SerializedName("Xiang")
|
||||
val xiang: String = ""
|
||||
)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user