diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5368233..733261f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -28,9 +28,9 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" -// ndk { -// abiFilters.addAll(listOf("armeabi-v7a"/*, "arm64-v8a"*/)) -// } + ndk { + abiFilters.addAll(listOf("armeabi-v7a", "x86_64"/*, "arm64-v8a"*/)) + } // 设置输出APK文件名格式 setProperty("archivesBaseName", "入库客户端_${versionName}") @@ -116,4 +116,10 @@ dependencies { implementation(libs.timber) // 图片显示 implementation("io.coil-kt:coil-compose:2.4.0") + + implementation(libs.core) + implementation(libs.android.core) + + implementation("androidx.compose.foundation:foundation:1.7.0") + implementation("androidx.compose.foundation:foundation-layout:1.7.0") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 750c680..8bd7739 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,9 +19,9 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher512" + android:icon="@mipmap/ic_logo512" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher512" + android:roundIcon="@mipmap/ic_logo512" android:supportsRtl="true" android:theme="@style/Theme.Inbound" android:usesCleartextTraffic="true" @@ -41,6 +41,14 @@ android:configChanges="orientation|screenSize" android:exported="true" android:label="@string/app_name" + android:screenOrientation="landscape"/> + + + diff --git a/app/src/main/java/com/sw/inbound/GlobalData.kt b/app/src/main/java/com/sw/inbound/GlobalData.kt index e6346dd..1829742 100644 --- a/app/src/main/java/com/sw/inbound/GlobalData.kt +++ b/app/src/main/java/com/sw/inbound/GlobalData.kt @@ -39,12 +39,29 @@ object GlobalData { * 单位类型列表 */ var unitTypeList: List = arrayListOf() + + /** + * 具体业务baseurl + */ + var appBaseUrl: String = "" + + /** + * 设备id + */ + var deviceId: String = "" + var restId: String = "" + var appVersion: String = "1" + var appId = "Hkz1rBk6PZXbS8KwKr67K2eZtsz8bRoMHLg64bUdgCZj" + var sdkKey = "AabXs3sHM8UhhE7oCGf4LVMLrdkGb1nJNksbjjTdVt7k" + + var appToken = "" } /** * */ object GlobalKey { + const val KEY_EQUIPMENT_INFO = "equipmentInfo" const val KEY_TOKEN = "tokenKey" const val KEY_USER_INFO = "userInfoKey" const val KEY_USER_NAME = "userNameKey" diff --git a/app/src/main/java/com/sw/inbound/InitActivity.kt b/app/src/main/java/com/sw/inbound/InitActivity.kt new file mode 100644 index 0000000..0fdb357 --- /dev/null +++ b/app/src/main/java/com/sw/inbound/InitActivity.kt @@ -0,0 +1,183 @@ +package com.sw.inbound + +import android.content.Intent +import android.os.Bundle +import android.view.WindowManager +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandIn +import androidx.compose.animation.shrinkOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsBottomHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import com.sw.inbound.utils.AppUtil +import com.sw.inbound.utils.QRCodeUtil +import com.sw.inbound.viewmodel.DeviceViewModel +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.StateFlow + +/** + * 设备初始化界面 + */ +@AndroidEntryPoint +class InitActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + window.setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) + setContent { + InitScreen() + } + } + + private var scale = 0.8F + + private val qrCodeBmp by lazy { + QRCodeUtil.generateQRCode( + content = GlobalData.deviceId, + size = (scale * 150).toInt() + ) + } + + @Preview(widthDp = 1920, heightDp = 1080) + @Composable + fun InitScreen( + viewModel: DeviceViewModel = hiltViewModel() + ) { +// val context = LocalContext.current + val hasDeviceData = viewModel.checkEquipmentInfo() + val deviceInfoResult = viewModel.deviceInfoResult.collectAsState() + + if (hasDeviceData) { + LaunchedEffect(this) { + delay(1500) + goLoginActivity() + } + } else { + FirstPage(viewModel) + } + if (deviceInfoResult.value == true) { + goLoginActivity() + } + } + + + @Composable + fun FirstPage(viewModel: DeviceViewModel) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.size( + 1920.dp, 1080.dp + ) + ) { + Spacer( + modifier = Modifier + .padding(top = (scale * 112).dp) + .background(Color.White.copy(alpha = 0.0F)) + .size((scale * 337).dp, (scale * 322).dp) + ) + + Button( + onClick = { + viewModel.getDeviceToken() + }, + colors = ButtonDefaults.buttonColors( + contentColor = Color.Transparent, + containerColor = Color(0xFF08C6DC) + ), + shape = RoundedCornerShape((scale * 8).dp), + modifier = Modifier + .width((scale * 225).dp) + .padding(top = (scale * 75).dp) + .padding((scale * 10).dp) + ) { + Text( + text = "设备初始化", + color = Color.White, + fontSize = (scale * 22).sp, + fontWeight = FontWeight.Bold, + ) + } + Spacer( + modifier = Modifier + .wrapContentWidth() + .weight(1F) + ) + Image( + bitmap = qrCodeBmp!!.asImageBitmap(), + contentDescription = "", + modifier = Modifier + .padding(bottom = 10.dp) + .size((scale * 150).dp, (scale * 150).dp) + ) + Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + } + } +// AnimatedVisibility( +// visible = isSuccess.not(), +// enter = expandIn(clip = false), +// exit = shrinkOut(clip = false) +// ) {} +// fun initialize() { +// var deviceId = AppUtil.getUDID(this) +// deviceId = "6ce7cd59-b875-38b2-b73d-88a570be3212" +// GlobalData.deviceId = deviceId +// val isSuccess = deviceViewModel.checkEquipmentInfo() +// if (isSuccess) { +// goLoginActivity() +// return +// } +// binding.ivQrCode.setImageBitmap( +// QRCodeUtil.generateQRCode( +// content = GlobalData.deviceId, +// size = 200 +// ) +// ) +// binding.btnInit.setOnClickListener { +// deviceViewModel.getDeviceToken() +// } +// } + + private fun goLoginActivity() { + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + finish() + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/MainActivity.kt b/app/src/main/java/com/sw/inbound/MainActivity.kt index e23c952..bb5710f 100644 --- a/app/src/main/java/com/sw/inbound/MainActivity.kt +++ b/app/src/main/java/com/sw/inbound/MainActivity.kt @@ -7,6 +7,8 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import com.sw.inbound.sdk.SensorScaleUtils import com.sw.inbound.ui.AppScreen +import com.sw.inbound.utils.ContextUtils +import com.sw.inbound.utils.SPUtil import com.sw.inbound.utils.ThreadUtils import dagger.hilt.android.AndroidEntryPoint @@ -15,6 +17,7 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + window.setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN diff --git a/app/src/main/java/com/sw/inbound/MyApp.kt b/app/src/main/java/com/sw/inbound/MyApp.kt index 1fbc521..8056ed3 100644 --- a/app/src/main/java/com/sw/inbound/MyApp.kt +++ b/app/src/main/java/com/sw/inbound/MyApp.kt @@ -1,8 +1,10 @@ package com.sw.inbound import android.app.Application +import com.sw.inbound.utils.AppUtil import com.sw.inbound.utils.ContextUtils import com.sw.inbound.utils.CrashHandler +import com.sw.inbound.utils.SPUtil import dagger.hilt.android.HiltAndroidApp import timber.log.Timber @@ -16,6 +18,13 @@ class MyApp : Application() { super.onCreate() Timber.plant(Timber.DebugTree()) ContextUtils.initAppContext(this) + GlobalData.appVersion = AppUtil.getAppVersionCode(this).toString() + var deviceId = AppUtil.getUDID(this) + deviceId = "6ce7cd59-b875-38b2-b73d-88a570be3212" + GlobalData.deviceId = deviceId + + val spUtil = SPUtil.getInstance(context = ContextUtils.getAppContext()) + GlobalData.appToken = spUtil.get(GlobalKey.KEY_TOKEN, "") as String // 初始化崩溃处理器 CrashHandler.init(this) diff --git a/app/src/main/java/com/sw/inbound/di/NetworkModule.kt b/app/src/main/java/com/sw/inbound/di/NetworkModule.kt index a5f04b0..271a202 100644 --- a/app/src/main/java/com/sw/inbound/di/NetworkModule.kt +++ b/app/src/main/java/com/sw/inbound/di/NetworkModule.kt @@ -18,6 +18,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object NetworkModule { + private const val DEVICE_BASE_URL = "http://device.shuziweidao.com:8889/" // private const val BASE_URL = "https://127.0.0.1" private const val BASE_URL = "https://yyjk.shuziweidao.com/gateway/" @@ -51,7 +52,7 @@ object NetworkModule { @Singleton fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { return Retrofit.Builder() - .baseUrl(BASE_URL) + .baseUrl(DEVICE_BASE_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build() diff --git a/app/src/main/java/com/sw/inbound/model/response/EquipmentInfo.kt b/app/src/main/java/com/sw/inbound/model/response/EquipmentInfo.kt new file mode 100644 index 0000000..aba530e --- /dev/null +++ b/app/src/main/java/com/sw/inbound/model/response/EquipmentInfo.kt @@ -0,0 +1,72 @@ +package com.sw.inbound.model.response + + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize + +@Parcelize +data class EquipmentInfo( + @SerializedName("appPackageLocalUrl") + val appPackageLocalUrl: String? = "", + @SerializedName("appPackageUrl") + val appPackageUrl: String? = "", + @SerializedName("arcsoftActiveKey") + val arcsoftActiveKey: String? = "", + @SerializedName("arcsoftAppId") + val arcsoftAppId: String? = "", + @SerializedName("arcsoftSdkKey") + val arcsoftSdkKey: String? = "", + @SerializedName("arrayCross") + val arrayCross: Int? = 0, + @SerializedName("arrayMode") + val arrayMode: String? = "", + @SerializedName("arrayVertical") + val arrayVertical: Int? = 0, + @SerializedName("canteenId") + val canteenId: String? = "", + @SerializedName("canteenName") + val canteenName: String? = "", + @SerializedName("clientServerIp") + val clientServerIp: String? = "", + @SerializedName("createBy") + val createBy: String? = "", + @SerializedName("createTime") + val createTime: String? = "", + @SerializedName("customerName") + val customerName: String? = "", + @SerializedName("equipmentCode") + val equipmentCode: String? = "", + @SerializedName("equipmentName") + val equipmentName: String? = "", + @SerializedName("equipmentName_dictText") + val equipmentNameDictText: String? = "", + @SerializedName("id") + val id: String? = "", + @SerializedName("isSync") + val isSync: Int? = 0, + @SerializedName("mqName") + val mqName: String? = "", + @SerializedName("mqPassword") + val mqPassword: String? = "", + @SerializedName("orgCode") + val orgCode: String? = "", + @SerializedName("owningTrack") + val owningTrack: Int? = 0, + @SerializedName("owningTrackOrder") + val owningTrackOrder: Int? = 0, + @SerializedName("screenHtml") + val screenHtml: String? = "", + @SerializedName("serviceRequestAddress") + val serviceRequestAddress: String? = "", + @SerializedName("status") + val status: Int? = 0, + @SerializedName("sysOrgCode") + val sysOrgCode: String? = "", + @SerializedName("updateBy") + val updateBy: String? = "", + @SerializedName("updateTime") + val updateTime: String? = "", + @SerializedName("zhstServerIp") + val zhstServerIp: String? = "" +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/network/api/ApiService.kt b/app/src/main/java/com/sw/inbound/network/api/ApiService.kt index 633753f..98aeece 100644 --- a/app/src/main/java/com/sw/inbound/network/api/ApiService.kt +++ b/app/src/main/java/com/sw/inbound/network/api/ApiService.kt @@ -1,5 +1,6 @@ package com.sw.inbound.network.api +import com.sw.inbound.GlobalData import com.sw.inbound.model.request.GoodsAddParam import com.sw.inbound.model.request.GoodsPurchaseUnitParam import com.sw.inbound.model.request.LoginParam @@ -7,6 +8,7 @@ import com.sw.inbound.model.request.PurchaseWarehouseParam import com.sw.inbound.model.request.UploadInfo import com.sw.inbound.model.response.ApiResponse import com.sw.inbound.model.response.DictType +import com.sw.inbound.model.response.EquipmentInfo import com.sw.inbound.model.response.GoodsType import com.sw.inbound.model.response.PurchaseInfo import com.sw.inbound.model.response.SearchGoodsInfo @@ -17,102 +19,168 @@ import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.Header import retrofit2.http.Multipart import retrofit2.http.POST import retrofit2.http.Part import retrofit2.http.Query +import retrofit2.http.Url /** * api接口 */ interface ApiService { + /** + * device获取token + */ + @GET("sys/getEquipmentToken") + suspend fun getDeviceToken( + @Query("qrcodeId") qrcodeId: String, + @Query("appVersion") appVersion: String = GlobalData.appVersion + ): ApiResponse + + /** + *获取配置信息 + */ + @GET("equipment/stEquipment/queryByEquipmentCode") + suspend fun getDeviceInfo( + @Query("equipmentCode") equipmentCode: String, + @Query("appVersion") appVersion: String = GlobalData.appVersion, + @Header("X-Access-Token") token: String + ): ApiResponse + /** * 登录 */ - @POST("sys/loginPad") - suspend fun login(@Body body: LoginParam): ApiResponse + @POST//("sys/loginPad") + suspend fun login( + @Url url: String = "${GlobalData.appBaseUrl}/sys/loginPad", + @Body body: LoginParam, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse /** * 采购单入库-列表 */ - @GET("pad/receivePage") + @GET//("pad/receivePage") suspend fun getReceiveList( + @Url url: String = "${GlobalData.appBaseUrl}/pad/receivePage", @Query("pageNo") pageNo: Int, - @Query("pageSize") pageSize: Int + @Query("pageSize") pageSize: Int, + @Header("x-access-token") token: String = GlobalData.appToken ): ApiResponse /** * 采购单入库-详情 */ - @GET("pad/receiveDetail") - suspend fun getReceiveDetail(@Query("id") id: Int): ApiResponse + @GET//("pad/receiveDetail") + suspend fun getReceiveDetail( + @Url url: String = "${GlobalData.appBaseUrl}/pad/receiveDetail", + @Query("id") id: Int, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse /** * 部分收货 */ - @POST("pad/partialReceiptGoods") - suspend fun partialReceipt(@Body uploadInfo: UploadInfo): ApiResponse + @POST//("pad/partialReceiptGoods") + suspend fun partialReceipt( + @Url url: String = "${GlobalData.appBaseUrl}/pad/partialReceiptGoods", + @Body uploadInfo: UploadInfo, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse /** * 确认收货 */ - @POST("pad/saveAndReceiveAndGoWare") - suspend fun confirmReceipt(@Body uploadInfo: UploadInfo): ApiResponse + @POST//("pad/saveAndReceiveAndGoWare") + suspend fun confirmReceipt( + @Url url: String = "${GlobalData.appBaseUrl}/pad/saveAndReceiveAndGoWare", + @Body uploadInfo: UploadInfo, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse /** * 存储方式 */ - @GET("sys/dict/getDictItemByF") - suspend fun getGoodsStorageType(@Query("dictCode") dictCode: String = "goods_storage_type"): ApiResponse> + @GET//("sys/dict/getDictItemByF") + suspend fun getGoodsStorageType( + @Url url: String = "${GlobalData.appBaseUrl}/sys/dict/getDictItemByF", + @Query("dictCode") dictCode: String = "goods_storage_type", + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse> /** * 物品类型 */ - @GET("pad/notLimitList") - suspend fun getGoodsType(): ApiResponse + @GET//("pad/notLimitList") + suspend fun getGoodsType( + @Url url: String = "${GlobalData.appBaseUrl}/pad/notLimitList", + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse /** * 获取字典数据 */ - @GET("pad/dataList") - suspend fun getDictType(@Query("type") type: String): ApiResponse> + @GET//("pad/dataList") + suspend fun getDictType( + @Url url: String = "${GlobalData.appBaseUrl}/pad/dataList", + @Query("type") type: String, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse> /** * 上传图片 */ @Multipart - @POST("sys/common/upload") + @POST//("sys/common/upload") suspend fun uploadImage( + @Url url: String = "${GlobalData.appBaseUrl}/sys/common/upload", @Part("code") code: RequestBody, - @Part file: MultipartBody.Part + @Part file: MultipartBody.Part, + @Header("x-access-token") token: String = GlobalData.appToken ): ApiResponse /** * 搜索物品列表 */ - @GET("pad/goodsInfoList") + @GET//("pad/goodsInfoList") suspend fun searchGoodsInfoList( + @Url url: String = "${GlobalData.appBaseUrl}/pad/goodsInfoList", @Query("goodsName") goodsName: String, @Query("pageNo") pageNo: Int, - @Query("pageSize") pageSize: Int + @Query("pageSize") pageSize: Int, + @Header("x-access-token") token: String = GlobalData.appToken ): ApiResponse /** * 自采添加商品 */ - @POST("pad/goodsAdd") - suspend fun selfPurchaseGoodsAdd(@Body goodsAddParam: GoodsAddParam): ApiResponse + @POST//("pad/goodsAdd") + suspend fun selfPurchaseGoodsAdd( + @Url url: String = "${GlobalData.appBaseUrl}/pad/goodsAdd", + @Body goodsAddParam: GoodsAddParam, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse /** * 添加物品采购单位 */ - @POST("pad/goodsAddUnit") - suspend fun goodsAddUnit(@Body purchaseUnitParam: GoodsPurchaseUnitParam): ApiResponse + @POST//("pad/goodsAddUnit") + suspend fun goodsAddUnit( + @Url url: String = "${GlobalData.appBaseUrl}/pad/goodsAddUnit", + @Body purchaseUnitParam: GoodsPurchaseUnitParam, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse /** * 自采入库 */ - @POST("pad/SelfPurchasedGoods") - suspend fun selfPurchaseWarehousing(@Body list: List): ApiResponse + @POST//("pad/SelfPurchasedGoods") + suspend fun selfPurchaseWarehousing( + @Url url: String = "${GlobalData.appBaseUrl}/pad/SelfPurchasedGoods", + @Body list: List, + @Header("x-access-token") token: String = GlobalData.appToken + ): ApiResponse } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt b/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt index 3202107..641ed7e 100644 --- a/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt +++ b/app/src/main/java/com/sw/inbound/network/interceptor/RequestInterceptor.kt @@ -16,7 +16,7 @@ class RequestInterceptor : Interceptor { .header("Content-Type", "application/json") .header("Accept", "application/json") // .header("Authorization", "Bearer ${getToken()}") - .header("x-access-token", getToken()) +// .header("x-access-token", getToken()) val newRequest = requestBuilder.build() diff --git a/app/src/main/java/com/sw/inbound/receiver/BootReceiver.kt b/app/src/main/java/com/sw/inbound/receiver/BootReceiver.kt index 9750633..ea3d33b 100644 --- a/app/src/main/java/com/sw/inbound/receiver/BootReceiver.kt +++ b/app/src/main/java/com/sw/inbound/receiver/BootReceiver.kt @@ -3,7 +3,7 @@ package com.sw.inbound.receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import com.sw.inbound.MainActivity +import com.sw.inbound.InitActivity import timber.log.Timber /** @@ -14,7 +14,7 @@ class BootReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (Intent.ACTION_BOOT_COMPLETED == intent.action) { Timber.d("设备启动完成,开始执行自启动逻辑") - val intent = Intent(context, MainActivity::class.java) + val intent = Intent(context, InitActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) context.startActivity(intent) } diff --git a/app/src/main/java/com/sw/inbound/repository/RemoteRepository.kt b/app/src/main/java/com/sw/inbound/repository/RemoteRepository.kt index 73a0d90..ef48b7e 100644 --- a/app/src/main/java/com/sw/inbound/repository/RemoteRepository.kt +++ b/app/src/main/java/com/sw/inbound/repository/RemoteRepository.kt @@ -8,6 +8,7 @@ import com.sw.inbound.model.request.PurchaseWarehouseParam import com.sw.inbound.model.request.UploadInfo import com.sw.inbound.model.response.ApiResponse import com.sw.inbound.model.response.DictType +import com.sw.inbound.model.response.EquipmentInfo import com.sw.inbound.model.response.GoodsType import com.sw.inbound.model.response.PurchaseInfo import com.sw.inbound.model.response.SearchGoodsInfo @@ -28,20 +29,42 @@ import javax.inject.Inject class RemoteRepository @Inject constructor( private val apiService: ApiService ) : BaseRepository() { + /** + * 生成token + */ + suspend fun getDeviceToken(qrcodeId: String): ApiResponse { + return safeApiCall { + apiService.getDeviceToken( + qrcodeId + ) + } + } + + /** + * 获取设备信息 + */ + suspend fun getDeviceInfo(equipmentCode: String, token: String): ApiResponse { + return safeApiCall { + apiService.getDeviceInfo( + equipmentCode = equipmentCode, + token = token + ) + } + } suspend fun login(loginParam: LoginParam): ApiResponse { - return safeApiCall { apiService.login(loginParam) } + return safeApiCall { apiService.login(body = loginParam) } } suspend fun getReceiveList(pageNum: Int, pageSize: Int): ApiResponse { return safeApiCall { - apiService.getReceiveList(pageNum, pageSize) + apiService.getReceiveList(pageNo = pageNum, pageSize = pageSize) } } suspend fun getReceiveDetail(id: Int): ApiResponse { return safeApiCall { - apiService.getReceiveDetail(id) + apiService.getReceiveDetail(id = id) } } @@ -76,7 +99,7 @@ class RemoteRepository @Inject constructor( suspend fun getDictType(type: TypeEnum): ApiResponse> { return safeApiCall { - apiService.getDictType(type.string) + apiService.getDictType(type = type.string) } } @@ -104,7 +127,7 @@ class RemoteRepository @Inject constructor( */ suspend fun partialReceipt(uploadInfo: UploadInfo): ApiResponse { return safeApiCall { - apiService.partialReceipt(uploadInfo) + apiService.partialReceipt(uploadInfo = uploadInfo) } } @@ -113,7 +136,7 @@ class RemoteRepository @Inject constructor( */ suspend fun confirmReceipt(uploadInfo: UploadInfo): ApiResponse { return safeApiCall { - apiService.confirmReceipt(uploadInfo) + apiService.confirmReceipt(uploadInfo = uploadInfo) } } @@ -123,7 +146,11 @@ class RemoteRepository @Inject constructor( pageSize: Int = 10 ): ApiResponse { return safeApiCall { - apiService.searchGoodsInfoList(goodsName, pageNo, pageSize) + apiService.searchGoodsInfoList( + goodsName = goodsName, + pageNo = pageNo, + pageSize = pageSize + ) } } @@ -132,13 +159,13 @@ class RemoteRepository @Inject constructor( */ suspend fun selfPurchaseGoodsAdd(goodsAddParam: GoodsAddParam): ApiResponse { return safeApiCall { - apiService.selfPurchaseGoodsAdd(goodsAddParam) + apiService.selfPurchaseGoodsAdd(goodsAddParam = goodsAddParam) } } suspend fun selfPurchaseGoodsAddUnit(addParam: GoodsPurchaseUnitParam): ApiResponse { return safeApiCall { - apiService.goodsAddUnit(addParam) + apiService.goodsAddUnit(purchaseUnitParam = addParam) } } @@ -147,7 +174,7 @@ class RemoteRepository @Inject constructor( */ suspend fun selfPurchaseWarehousing(list: List): ApiResponse { return safeApiCall { - apiService.selfPurchaseWarehousing(list) + apiService.selfPurchaseWarehousing(list = list) } } } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/utils/AppUtil.java b/app/src/main/java/com/sw/inbound/utils/AppUtil.java new file mode 100644 index 0000000..183989e --- /dev/null +++ b/app/src/main/java/com/sw/inbound/utils/AppUtil.java @@ -0,0 +1,751 @@ +package com.sw.inbound.utils; + +import static android.content.Context.TELEPHONY_SERVICE; + +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothAdapter; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.core.content.FileProvider; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.net.NetworkInterface; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.UUID; + +public class AppUtil { + public static String getAppPackageName(Context context) { + String packageName = ""; + try { + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0); + packageName = pi.packageName; + if (AppUtil.isEmpty(packageName)) { + return ""; + } + } catch (Exception e) { + e.printStackTrace(); + } + return packageName; + } + + public static String getAppVersionName(Context context) { + String versionName = ""; + // int versioncode=1; + try { + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0); + versionName = pi.versionName; + // versioncode = pi.versionCode;表示更新了多少次 + if (versionName == null || versionName.length() <= 0) { + return ""; + } + } catch (Exception e) { + e.printStackTrace(); + } + return versionName; + } + + + public static int getAppVersionCode(Context context) { + int versioncode = 1; + try { + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0); + versioncode = pi.versionCode; + + } catch (Exception e) { + e.printStackTrace(); + } + return versioncode; + } + + //判断微信是否安装 + public static boolean isWeixinInstalled(Context context) { + final PackageManager packageManager = context.getPackageManager();// 获取packagemanager + List pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息 + if (pinfo != null) { + for (int i = 0; i < pinfo.size(); i++) { + String pn = pinfo.get(i).packageName; + if (pn.equals("com.tencent.mm")) { + return true; + } + } + } + + return false; + } + + /** + * 打电话 + *

+ * Intent.ACTION_DIAL Intent.ACTION_CALL + * + * @param context + * @param mobile + */ + public static void callUp(Context context, String mobile) { + Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + + mobile)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + + /** + * 获取设备ID + * + * @param context + * @return + */ + public static String getDevId(Context context) { + TelephonyManager TelephonyMgr = (TelephonyManager) context + .getSystemService(Context.TELEPHONY_SERVICE); + return TelephonyMgr.getDeviceId(); + } + + /** + * 姓名脱敏 + * + * @param fullName + * @return + */ + public static String desensitizedName(String fullName) { + if (fullName == null || fullName.length() <= 1) { + return fullName; + } + char[] nameArr = fullName.toCharArray(); + if (nameArr.length > 2) { + for (int i = 1; i < nameArr.length - 1; i++) { + nameArr[i] = '*'; + } + } else { + nameArr[1] = '*'; + } + + return new String(nameArr); + } + + + public static String formatDateGetFull(String date) { + if (isEmpty(date)) { + return ""; + } + Date d = new Date(Long.parseLong(date)); + SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + return dateFormat1.format(d); + } + + public static String formatDateGetCurrentTime() { + Date d = new Date(System.currentTimeMillis()); + SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//.SSS + return dateFormat1.format(d); + } + + + public static String formatDateGetFull(long date) { + if (date == 0) { + return ""; + } + Date d = new Date(date); + SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + return dateFormat1.format(d); + } + + public static String formatDateGetDay(long date) { + if (date == 0) { + return ""; + } + Date d = new Date(date); + SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd"); + return dateFormat1.format(d); + } + + public static boolean isEmpty(String s) { + if (TextUtils.isEmpty(s) || s.trim().equals("null")) { + return true; + } else { + return false; + } + } + + + /** + * 格式化浮点型 + * + * @param data + * @return + */ + public static String formatDouble(double data) { + return new DecimalFormat("0.00").format(data); + } + + + /** + * 格式化分钟 + * + * @param minutes + * @return + */ + public static String formatMinutes(int minutes) { + int hour = minutes / 60; + int minute = minutes % 60; + if (hour > 0 && minute > 0) { + return hour + "小时" + minute + "分钟"; + } else if (hour > 0) { + return hour + "小时"; + } else { + return minute + "分钟"; + } + } + + + //com.fawan.news + public static void goToMarket(Context context, String packageName) { + Uri uri = Uri.parse("market://details?id=" + packageName); + Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri); + try { + context.startActivity(goToMarket); + } catch (ActivityNotFoundException e) { + e.printStackTrace(); + } + } + + /** + * true为存在,false为不存在 + * + * @param context + * @param packageName + * @return + */ + public static boolean isInstallApp(Context context, String packageName) { + try { + context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + /** + * 格式化float 保留两位小数 + * + * @param data + * @return + */ + public static float formatFloat2(float data) { +// DecimalFormat decimalFormat = new DecimalFormat("0.00");//构造方法的字符格式这里如果小数不足2位,会以0补足. +// return decimalFormat.format(data);//返回字符串 + + int scale = 1;//设置位数 + int roundingMode = 4;//表示四舍五入,可以选择其他舍值方式,例如去尾,等等. + BigDecimal bd = new BigDecimal((double) data); + bd = bd.setScale(scale, roundingMode); + data = bd.floatValue(); + return data; + } + + /** + * Android 6.0 之前(不包括6.0)获取mac地址 + * 必须的权限 + * + * @param context * @return + */ + public static String getMacDefault(Context context) { + String mac = ""; + if (context == null) { + return mac; + } + WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + WifiInfo info = null; + try { + info = wifi.getConnectionInfo(); + } catch (Exception e) { + e.printStackTrace(); + } + + if (info == null) { + return null; + } + mac = info.getMacAddress(); + if (!TextUtils.isEmpty(mac)) { + mac = mac.toUpperCase(Locale.ENGLISH); + } + return mac; + } + + /** + * Android 6.0-Android 7.0 获取mac地址 + */ + public static String getMacAddress() { + String macSerial = null; + String str = ""; + + try { + Process pp = Runtime.getRuntime().exec("cat/sys/class/net/wlan0/address"); + InputStreamReader ir = new InputStreamReader(pp.getInputStream()); + LineNumberReader input = new LineNumberReader(ir); + + while (null != str) { + str = input.readLine(); + if (str != null) { + macSerial = str.trim();//去空格 + break; + } + } + } catch (IOException ex) { + // 赋予默认值 + ex.printStackTrace(); + } + + return macSerial; + } + + /** + * Android 7.0之后获取Mac地址 + * 遍历循环所有的网络接口,找到接口是 wlan0 + * 必须的权限 + * + * @return + */ + public static String getMacFromHardware() { + try { + ArrayList all = Collections.list(NetworkInterface.getNetworkInterfaces()); + for (NetworkInterface nif : all) { + if (!nif.getName().equals("wlan0")) + continue; + byte[] macBytes = nif.getHardwareAddress(); + if (macBytes == null) return ""; + StringBuilder res1 = new StringBuilder(); + for (Byte b : macBytes) { + res1.append(String.format("%02X:", b)); + } + if (!TextUtils.isEmpty(res1)) { + res1.deleteCharAt(res1.length() - 1); + } + return res1.toString(); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return ""; + } + + /** + * 获取mac地址(适配所有Android版本) + * + * @return + */ + public static String getMac(Context context) { + String mac = ""; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + mac = getMacDefault(context); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + mac = getMacAddress(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mac = getMacFromHardware(); + } + return mac; + } + + //把String转化为float + public static double convertToFloat(String number, double defaultValue) { + if (TextUtils.isEmpty(number)) { + return defaultValue; + } + try { + return Double.parseDouble(number); + } catch (Exception e) { + return defaultValue; + } + + } + + /** + * 获取AndroidId + * + * @param context + * @return + */ + public static String getAndroidId(Context context) { + String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + return androidId; + } + + /** + * 获取设备唯一 UDID + * + * @param context + * @return + */ + @SuppressLint("MissingPermission") + public static String getUDID(Context context) { +// String androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); +// L.e("androidID===" + androidID); +// return androidID; + String androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + if (!androidID.equals("")) { + try { + if (!"9774d56d682e549c".equals(androidID)) { + androidID = UUID.nameUUIDFromBytes(androidID.getBytes("utf8")).toString(); + } else { + @SuppressLint("MissingPermission") final String deviceId = ((TelephonyManager) context.getSystemService(TELEPHONY_SERVICE)).getDeviceId(); + androidID = deviceId != null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")).toString() : UUID.randomUUID().toString(); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return androidID; + } + + //需要权限 android.permission.READ_PHONE_STATE + TelephonyManager TelephonyMgr = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE); + String szImei = TelephonyMgr.getDeviceId(); + if (!szImei.equals("")) { + return szImei; + } + + //需要权限 android.permission.ACCESS_WIFI_STATE + WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + String m_szWLANMAC = wm.getConnectionInfo().getMacAddress(); + if (!m_szWLANMAC.equals("")) { + return m_szWLANMAC; + } + + //需要权限 android.permission.BLUETOOTH + BluetoothAdapter m_BluetoothAdapter = null; + m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + String m_szBTMAC = m_BluetoothAdapter.getAddress(); + if (!m_szBTMAC.equals("")) { + return m_szBTMAC; + } + return getUniquePsuedoID(); + } + + //获得 Psuedo ID + public static String getUniquePsuedoID() { + String serial = null; + String m_szDevIDShort = "35" + + Build.BOARD.length() % 10 + Build.BRAND.length() % 10 + + Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 + + Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 + + Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 + + Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 + + Build.TAGS.length() % 10 + Build.TYPE.length() % 10 + + Build.USER.length() % 10; //13 位 + try { + serial = Build.class.getField("SERIAL").get(null).toString(); + //API>=9 使用serial号 + return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); + } catch (Exception exception) { + //serial需要一个初始化,随意值 + serial = "serial"; + } + + //使用硬件信息拼凑出来的15位号码 + return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); + } + + public static String getCPUSerial() { + String line = ""; + String TAG = "aaa"; + Log.e(TAG, " get_quck_Sn() "); + Class c = null; + try { + c = Class.forName("android.os.SystemProperties"); + + Method get = c.getMethod("get", String.class); + line = (String) get.invoke(c, "ro.serialno"); + } catch (ClassNotFoundException | NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + + Log.e(TAG, " get_quck_Sn() " + line); + System.out.println("设备串号" + line); + return line; + } + + /** + * 判断网络连接状态 + * + * @param context + * @return + */ + public static boolean isNetworkConnected(Context context) { + if (context != null) { + ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); + if (mNetworkInfo != null) { + return mNetworkInfo.isAvailable(); + } + } + return false; + } + + /** + * 判断WiFi连接状态 + * + * @param context + * @return + */ + public static boolean isWifiConnected(Context context) { + if (context != null) { + ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mWiFiNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + if (mWiFiNetworkInfo != null) { + return mWiFiNetworkInfo.isAvailable(); + } + } + return false; + } + + /** + * 判断移动网络状态 + * + * @param context + * @return + */ + public static boolean isMobileConnected(Context context) { + if (context != null) { + ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mMobileNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + if (mMobileNetworkInfo != null) { + return mMobileNetworkInfo.isAvailable(); + } + } + return false; + } + + /** + * 获取网络连接类型 + * + * @param context + * @return + */ + public static int getConnectedType(Context context) { + if (context != null) { + ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); + if (mNetworkInfo != null && mNetworkInfo.isAvailable()) { + return mNetworkInfo.getType(); + } + } + return -1; + } + + /** + * 根据字符的起始和结束索引提取子串 + * + * @param input 原始字符串 + * @param startIndex 起始索引(包含,从0开始) + * @param endIndex 结束索引(不包含) + * @return 子串,若输入无效或索引越界则返回空字符串 + */ + public static String getSubstringByIndices(String input, int startIndex, int endIndex) { + if (input == null) { + return ""; + } + // 处理索引越界问题 + int safeStart = Math.max(startIndex, 0); + int safeEnd = Math.min(endIndex, input.length()); + if (safeStart > safeEnd) { + return ""; + } + return input.substring(safeStart, safeEnd); + } + + public static String getSubstringByIndex(String input, int startIndex, int length) { + if (input == null) { + return ""; + } + // 处理索引越界问题 + int safeStart = Math.max(startIndex, 0); + int safeEnd = Math.min(startIndex + length, input.length()); + if (safeStart > safeEnd) { + return ""; + } + return input.substring(safeStart, safeEnd); + } + + /** + * 十进制转十六进制 + * + * @param decimal + * @return + */ + public static String decimalToHexWithPadding(int decimal, int padding) { + // 将十进制转换为十六进制,并转换为字符串 + String hex = Integer.toHexString(decimal); + + // 确保字符串长度为至少4位,不足部分前面补0 + while (hex.length() < padding) { + hex = "0" + hex; + } + + return hex.toUpperCase(); // 返回大写形式的十六进制字符串 + } + + /** + * 十进制转二进制,且返回的二进制为至少7位数 + * + * @param decimal + * @return + */ + public static String decimalToBinary(int decimal) { + // 如果输入为0,直接返回"0" + if (decimal == 0) { + return "0"; + } + + StringBuilder binary = new StringBuilder(); + + // 除2取余法,将余数加入二进制字符串 + while (decimal > 0) { + int remainder = decimal % 2; + binary.insert(0, remainder); + decimal = decimal / 2; + } + int length = binary.length(); + if (length < 7) { + int padding = 7 - length; + for (int i = 0; i < padding; i++) { + binary.insert(0, '0'); + } + } + return binary.toString(); + } + + /** + * 将二进制字符串转换为十六进制字符串,每8位转换为两位十六进制,不足两位前面补零 + * + * @param binaryStr 输入的二进制字符串(仅包含0和1) + * @return 转换后的十六进制字符串 + * @throws IllegalArgumentException 如果输入不是有效的二进制字符串 + */ + public static String binaryToHex(String binaryStr) { + // 校验输入合法性 + if (binaryStr == null || !binaryStr.matches("[01]+")) { + throw new IllegalArgumentException("Invalid binary string"); + } + + // 补前导零使长度成为8的倍数 + int length = binaryStr.length(); + int padding = (8 - (length % 8)) % 8; // 计算需要补零的数量 + StringBuilder paddedBinary = new StringBuilder(); + for (int i = 0; i < padding; i++) { + paddedBinary.append('0'); + } + paddedBinary.append(binaryStr); + + // 每8位转换为两位十六进制 + StringBuilder hexStr = new StringBuilder(); + for (int i = 0; i < paddedBinary.length(); i += 8) { + String byteStr = paddedBinary.substring(i, i + 8); + int decimalValue = Integer.parseInt(byteStr, 2); + hexStr.append(String.format("%02X", decimalValue & 0xFF)); + } + + return hexStr.toString(); + } + + /** + * 数据校验 异或处理 + */ + public static String getXor(String content) { + int a = 0; + for (int i = 0; i < content.length() / 2; i++) { + a = a ^ Integer.parseInt(content.substring(i * 2, (i * 2) + 2), 16); + } + String result = Integer.toHexString(a).toUpperCase(); + if (result.length() == 1) { + return "0" + result; + } else { + return result; + } + } + + public static double formatPersonInfo(String input, int startIndex, int length) { + if (input == null) { + return 0; + } + // 处理索引越界问题 + int safeStart = Math.max(startIndex, 0); + int safeEnd = Math.min(startIndex + length, input.length()); + if (safeStart > safeEnd) { + return 0; + } + String result = input.substring(safeStart, safeEnd); + double num = Integer.parseInt(result, 16); + return num; + } + + /** + * 安装apk + * + * @param activity + * @param apkFile + */ + public static void installApk(Context activity, File apkFile) { + //文件有所有者概念,现在是属于当前进程的,需要把这个文件暴露给系统安装程序(其他进程)去安装 + //因此,可能会存在权限问题,需要做下面的设置 + //如果文件是sdcard上的,就不需要这个操作了 + try { + apkFile.setExecutable(true, false); + apkFile.setReadable(true, false); + apkFile.setWritable(true, false); + } catch (Exception e) { + e.printStackTrace(); + } + + Intent intent = new Intent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction(Intent.ACTION_VIEW); + Uri uri; + + //TODO N FileProvider + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileProvider", apkFile); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); +// intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } else { + uri = Uri.fromFile(apkFile); + } + + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + activity.startActivity(intent); + + //TODO 0 INSTALL PERMISSION + //在AndroidManifest中加入权限即可 + } + +} diff --git a/app/src/main/java/com/sw/inbound/utils/QRCodeUtil.kt b/app/src/main/java/com/sw/inbound/utils/QRCodeUtil.kt new file mode 100644 index 0000000..6b8c2ec --- /dev/null +++ b/app/src/main/java/com/sw/inbound/utils/QRCodeUtil.kt @@ -0,0 +1,103 @@ +package com.sw.inbound.utils + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.WriterException +import com.google.zxing.common.BitMatrix +import com.google.zxing.qrcode.QRCodeWriter +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import kotlin.apply +import kotlin.ranges.until +import kotlin.text.isEmpty + +/** + * 二维码生成工具类 + */ +object QRCodeUtil { + + /** + * 生成二维码(默认大小) + * @param content 二维码内容 + * @return 生成的二维码Bitmap + */ + @JvmOverloads + fun generateQRCode(content: String, size: Int = 500): Bitmap? { + return generateQRCode(content, size, Color.BLACK, Color.WHITE) + } + + /** + * 生成二维码(自定义颜色) + * @param content 二维码内容 + * @param size 二维码边长(像素) + * @param colorCode 二维码颜色 + * @param backgroundColor 背景颜色 + * @return 生成的二维码Bitmap + */ + fun generateQRCode( + content: String, + size: Int, + colorCode: Int, + backgroundColor: Int + ): Bitmap? { + if (content.isEmpty()) { + return null + } + + return try { + val hints = mutableMapOf().apply { + put(EncodeHintType.CHARACTER_SET, "UTF-8") + put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H) // 纠错级别 + put(EncodeHintType.MARGIN, 1) // 边距 + } + + val bitMatrix = QRCodeWriter().encode( + content, + BarcodeFormat.QR_CODE, + size, + size, + hints + ) + + val pixels = IntArray(size * size).apply { + for (y in 0 until size) { + for (x in 0 until size) { + this[y * size + x] = if (bitMatrix.get(x, y)) colorCode else backgroundColor + } + } + } + + Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888).apply { + setPixels(pixels, 0, size, 0, 0, size, size) + } + + } catch (e: WriterException) { + e.printStackTrace() + null + } + } + + /** + * 生成带Logo的二维码 + * @param content 二维码内容 + * @param size 二维码边长(像素) + * @param logo Logo Bitmap + * @return 带Logo的二维码Bitmap + */ + fun generateQRCodeWithLogo(content: String, size: Int, logo: Bitmap?): Bitmap? { + val qrCode = generateQRCode(content, size) ?: return null + logo ?: return qrCode + + val logoSize = size / 5 // Logo大小约为二维码的1/5 + val scaledLogo = Bitmap.createScaledBitmap(logo, logoSize, logoSize, false) + + val offset = (size - logoSize) / 2 + return Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888).apply { + val canvas = Canvas(this) + canvas.drawBitmap(qrCode, 0f, 0f, null) + canvas.drawBitmap(scaledLogo, offset.toFloat(), offset.toFloat(), null) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt index 078cd9b..27e8b5f 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope import com.sw.inbound.GlobalData import com.sw.inbound.model.response.ApiResponse import com.sw.inbound.model.response.DictType +import com.sw.inbound.model.response.EquipmentInfo import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.network.LoadingState @@ -270,4 +271,13 @@ abstract class BaseViewModel( } } } + + + fun parseEquipmentInfo(equipmentInfo: EquipmentInfo) { + GlobalData.appBaseUrl = equipmentInfo.appPackageUrl!! + GlobalData.sdkKey = equipmentInfo.arcsoftSdkKey!! + GlobalData.appId = equipmentInfo.arcsoftAppId!! +// GlobalData.activeKey = equipmentInfo.arcsoftActiveKey!! + GlobalData.restId = equipmentInfo.canteenId!! + } } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/viewmodel/DeviceViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/DeviceViewModel.kt new file mode 100644 index 0000000..cbee992 --- /dev/null +++ b/app/src/main/java/com/sw/inbound/viewmodel/DeviceViewModel.kt @@ -0,0 +1,81 @@ +package com.sw.inbound.viewmodel + +import com.sw.inbound.GlobalData +import com.sw.inbound.GlobalKey +import com.sw.inbound.model.response.EquipmentInfo +import com.sw.inbound.repository.RemoteRepository +import com.sw.inbound.utils.GsonUtils +import com.sw.inbound.utils.SPUtil +import com.sw.inbound.utils.ToastUtils +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import timber.log.Timber +import javax.inject.Inject +import kotlin.jvm.java + +/** + * 初始化的viewmodel + */ +@HiltViewModel +class DeviceViewModel @Inject constructor( + private val repository: RemoteRepository +) : BaseViewModel(repository) { + private val _deviceInfoResult = MutableStateFlow(null) + val deviceInfoResult: StateFlow = _deviceInfoResult + + /** + * 获取token + */ + fun getDeviceToken(deviceId: String = GlobalData.deviceId) { + launchWithLoading { + val response = repository.getDeviceToken(deviceId) + if (parseResponse(response)) { + val response1 = repository.getDeviceInfo(deviceId, response.result!!) + + if (parseResponse(response1)) { + val equipmentInfo = response1.result + if (equipmentInfo == null) return@launchWithLoading + try { + parseEquipmentInfo(equipmentInfo) + SPUtil.getInstance() + .put(GlobalKey.KEY_EQUIPMENT_INFO, GsonUtils.toJson(equipmentInfo)) + _deviceInfoResult.value = true + } catch (e: Exception) { + Timber.e(e) + } + } else { + ToastUtils.showToast("获取设备信息失败") + } + } + } + } + + /** + * 检查缓存数据 + */ + fun checkEquipmentInfo(): Boolean { + val equipmentInfoStr = SPUtil.getInstance().get(GlobalKey.KEY_EQUIPMENT_INFO, "") + if (equipmentInfoStr == null) { + Timber.e("获取缓存设备信息失败") + return false + } + val equipmentInfo = + GsonUtils.fromJson(equipmentInfoStr, EquipmentInfo::class.java) + if (equipmentInfo == null) { + Timber.e("解析缓存设备信息失败") + return false + } + try { + parseEquipmentInfo(equipmentInfo) + return true + } catch (e: Exception) { + Timber.e(e) + return false + } + } + + override fun handleSensorScale(weight: Double) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/viewmodel/UserViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/UserViewModel.kt index 39c6eef..285112a 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/UserViewModel.kt @@ -3,6 +3,7 @@ package com.sw.inbound.viewmodel import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import com.sw.inbound.GlobalData import com.sw.inbound.GlobalKey import com.sw.inbound.model.request.LoginParam import com.sw.inbound.model.response.User @@ -57,6 +58,7 @@ class UserViewModel @Inject constructor( user?.let { SPUtil.getInstance().put(GlobalKey.KEY_USER_INFO, GsonUtils.toJson(it)) SPUtil.getInstance().put(GlobalKey.KEY_TOKEN, it.token) + GlobalData.appToken = it.token?:"" } } diff --git a/app/src/main/res/drawable/bg_splash.xml b/app/src/main/res/drawable/bg_splash.xml new file mode 100644 index 0000000..77b52a5 --- /dev/null +++ b/app/src/main/res/drawable/bg_splash.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/img_init.png b/app/src/main/res/drawable/img_init.png new file mode 100644 index 0000000..e8d0844 Binary files /dev/null and b/app/src/main/res/drawable/img_init.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher512.png b/app/src/main/res/mipmap-xxxhdpi/ic_logo512.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher512.png rename to app/src/main/res/mipmap-xxxhdpi/ic_logo512.png diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 68db9bb..d2530eb 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,7 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3e63698..9457d20 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,8 +18,12 @@ navigationCompose = "2.7.0" retrofit = "3.0.0" okhttp = "4.12.0" timber = "5.0.1" +core = "3.4.1" +androidCore = "3.3.0" [libraries] +android-core = { module = "com.google.zxing:android-core", version.ref = "androidCore" } +core = { module = "com.google.zxing:core", version.ref = "core" } accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCore" } androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "cameraCore" }