diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 56012fd..63b6d9f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -55,6 +55,8 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) + implementation(libs.android.core) + implementation(libs.core) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ddfff75..db2c7b8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools"> + @@ -34,6 +36,9 @@ + ) { Log.d(TAG, "getTokenSuccess: $data") data.result?.let { - App.getInstance().accessToken = it.toString() + App.accessToken = it.toString() //UIUtils.toast(it.toString()) - viewModel.getShelfList(deviceId = App.getInstance().deviceId) + viewModel.getShelfList(deviceId = App.deviceId) } } @@ -201,7 +201,7 @@ class HomeActivity : BaseActivity() { loadEmptyView() return } - App.getInstance().canteenId = data.result.canteenId + App.canteenId = data.result.canteenId val tempList = data.result.containerGoodsList if (tempList.isNullOrEmpty()) { loadEmptyView() @@ -215,8 +215,8 @@ class HomeActivity : BaseActivity() { val subList01 = tempList.subList(0, 5) val subList02 = tempList.subList(5, 10) repeat(5) { index -> - list.add(subList01[index].also { it.deviceId = App.getInstance().deviceId }) - list.add(subList02[index].also { it.deviceId = App.getInstance().deviceId }) + list.add(subList01[index].also { it.deviceId = App.deviceId }) + list.add(subList02[index].also { it.deviceId = App.deviceId }) } shelfAdapter.notifyDataSetChanged() } @@ -491,7 +491,7 @@ class HomeActivity : BaseActivity() { binding.include?.let { it.root.visible() it.root.setOnClickListener { - viewModel.getAccessToken(App.getInstance().deviceId) + viewModel.getAccessToken(App.deviceId) } it.ivEmptyIcon.setImageResource(R.drawable.ic_empty_white) it.tvEmptyContent.setTextColor(Color.WHITE) diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/InitActivity.kt b/app/src/main/java/com/shuwei/intelligent/shelves/InitActivity.kt new file mode 100644 index 0000000..0c48b00 --- /dev/null +++ b/app/src/main/java/com/shuwei/intelligent/shelves/InitActivity.kt @@ -0,0 +1,169 @@ +package com.shuwei.intelligent.shelves + +import android.annotation.SuppressLint +import android.os.Bundle +import android.provider.Settings +import android.util.Log +import androidx.activity.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.shuwei.intelligent.shelves.base.BaseActivity +import com.shuwei.intelligent.shelves.databinding.ActivityInitBinding +import com.shuwei.intelligent.shelves.model.DeviceConfigInfo +import com.shuwei.intelligent.shelves.net.NetViewModel +import com.shuwei.intelligent.shelves.net.UiState +import com.shuwei.intelligent.shelves.net.UrlConfig +import com.shuwei.intelligent.shelves.utils.AppUtil +import com.shuwei.intelligent.shelves.utils.QRCodeUtil +import com.shuwei.intelligent.shelves.utils.SpTool +import com.shuwei.intelligent.shelves.utils.ext.dp +import com.shuwei.intelligent.shelves.utils.ext.invisible +import com.shuwei.intelligent.shelves.utils.ext.startActivity +import com.shuwei.intelligent.shelves.utils.ext.toJsonString +import com.shuwei.intelligent.shelves.utils.ext.toObject +import com.shuwei.intelligent.shelves.utils.ext.toast +import com.shuwei.intelligent.shelves.utils.ext.visible +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch +import kotlin.collections.forEach +import kotlin.getValue +import kotlin.jvm.java +import kotlin.ranges.downTo +import kotlin.text.isBlank +import kotlin.to + +class InitActivity : BaseActivity() { + companion object { + const val TAG = "InitActivity" + } + + private lateinit var binding: ActivityInitBinding + + private val viewModel: NetViewModel by viewModels() + + @SuppressLint("HardwareIds") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityInitBinding.inflate(layoutInflater) + setContentView(binding.root) + + // 获取 ANDROID_ID + val androidId = + Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) + App.deviceId = androidId + SpTool.put(SpTool.DEVICE_ID, androidId) + App.configUrl = UrlConfig.BASE_URL + App.canteenId = "0" + +// var deviceId = AppUtil.getUDID(this) +// Log.d(TAG, "onCreate: deviceId=$deviceId") +//// deviceId = "39a7abdd06b3c7ab" +//// deviceId = "3ea47dc0-3cf0-3c2f-909c-265a9a65572e" +// App.deviceId = deviceId +// +// SpTool.put(SpTool.DEVICE_ID, deviceId) +// val deviceConfigCache = SpTool.getString(SpTool.DEVICE_CONFIG_CACHE) +// val checkResult = checkConfigData(deviceConfigCache) +// if (checkResult.not()) { +// binding.ivQrCode.visible() +// binding.btnInit.visible() +// //进行初始化操作 +// initConfig() +// return +// } + binding.ivQrCode.invisible() + binding.btnInit.invisible() + + countDown() + } + + private fun countDown() { + lifecycleScope.launch { + flow { + (2 downTo 1).forEach { + delay(1000) + emit(it) + } + }.collect { + // 倒计时结束执行跳转 + if (it == 1) { + startActivity() + finish() + } + } + } + } + + private fun initConfig() { + binding.ivQrCode.setImageBitmap( + QRCodeUtil.generateQRCode( + content = App.deviceId, + size = 200.dp + ) + ) + binding.btnInit.setOnClickListener { + viewModel.getDeviceToken(App.deviceId) + } + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + launch { + viewModel.getDeviceTokenUiState.collect { state -> + when (state) { + is UiState.Loading -> {} + is UiState.Success<*> -> { + state.data.result?.let { deviceToken -> + Log.d(TAG, "initConfig: $deviceToken") + viewModel.getDeviceConfig( + deviceId = App.deviceId, + deviceToken = deviceToken.toString() + ) + } + } + is UiState.Error -> toast(state.msg) + else -> {} + } + } + } + launch { + viewModel.getDeviceConfigUiState.collect { state -> + when (state) { + is UiState.Loading -> {} + is UiState.Success<*> -> { + state.data.result?.let { + if (it is DeviceConfigInfo) { + SpTool.put(SpTool.DEVICE_CONFIG_CACHE, it.toJsonString()) + App.configUrl = it.appPackageUrl?:"" + App.canteenId = it.canteenId?:"" + + startActivity() + finish() + } + } + } + is UiState.Error -> toast(message = state.msg) + else -> {} + } + } + } + } + } + } + + private fun checkConfigData(data: String): Boolean { + if (data.isBlank()) { + return false + } + val config = data.toObject() + if (config == null) { + return false + } + App.configUrl = config.appPackageUrl?:"" + App.canteenId = config.canteenId?:"" + return true + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/ShelfActivity.kt b/app/src/main/java/com/shuwei/intelligent/shelves/ShelfActivity.kt index 1a9de57..2235132 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/ShelfActivity.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/ShelfActivity.kt @@ -202,7 +202,7 @@ class ShelfActivity : BaseActivity() { private fun getGoodsList() { viewModel.getGoodsList( - canteenId = App.getInstance().canteenId, + canteenId = App.canteenId, goodsName = binding.etInputFood.text.trim().toString(), pageNo = pageNo, pageSize = PAGE_SIZE diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/model/DeviceConfigInfo.kt b/app/src/main/java/com/shuwei/intelligent/shelves/model/DeviceConfigInfo.kt new file mode 100644 index 0000000..5f496bb --- /dev/null +++ b/app/src/main/java/com/shuwei/intelligent/shelves/model/DeviceConfigInfo.kt @@ -0,0 +1,6 @@ +package com.shuwei.intelligent.shelves.model + +class DeviceConfigInfo { + var appPackageUrl:String? = null + var canteenId:String? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/net/ApiService.kt b/app/src/main/java/com/shuwei/intelligent/shelves/net/ApiService.kt index 76ed393..51f1d6d 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/net/ApiService.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/net/ApiService.kt @@ -1,31 +1,66 @@ package com.shuwei.intelligent.shelves.net +import com.shuwei.intelligent.shelves.App +import com.shuwei.intelligent.shelves.model.DeviceConfigInfo import com.shuwei.intelligent.shelves.model.GoodsRecord import com.shuwei.intelligent.shelves.model.ShelfBody import com.shuwei.intelligent.shelves.model.ShelfResult import retrofit2.http.Body import retrofit2.http.GET +import retrofit2.http.Header import retrofit2.http.POST import retrofit2.http.Query +import retrofit2.http.Url interface ApiService { + /** + * device获取token + */ + @GET("/sys/getEquipmentToken") + suspend fun getDeviceToken( + @Query("qrcodeId") qrcodeId: String, + @Query("appVersion") appVersion: String = App.appVersion + ): RespData - @GET(UrlConfig.GET_ACCESS_TOKEN) - suspend fun getAccessToken(@Query("qrcodeId") qrcodeId: String): RespData + /** + *获取配置信息 + */ + @GET("/equipment/stEquipment/queryByEquipmentCode") + suspend fun getDeviceInfo( + @Query("equipmentCode") equipmentCode: String, + @Query("appVersion") appVersion: String = App.appVersion, + @Header("X-Access-Token") token: String + ): RespData - @GET(UrlConfig.GET_SHELF_LIST) - suspend fun getShelfList(@Query("deviceId") deviceId: String): RespData + @GET + suspend fun getAccessToken( + @Url url: String = UrlConfig.GET_ACCESS_TOKEN, + @Query("qrcodeId") qrcodeId: String, + @Query("x-access-token") token: String = App.accessToken + ): RespData - @GET(UrlConfig.GET_GOODS_LIST) + @GET + suspend fun getShelfList( + @Url url: String = UrlConfig.GET_SHELF_LIST, + @Query("deviceId") deviceId: String, + @Query("x-access-token") token: String = App.accessToken + ): RespData + + @GET suspend fun getGoodsList( + @Url url: String = UrlConfig.GET_GOODS_LIST, @Query("canteenId") canteenId: String = "0", @Query("goodsName") goodsName: String? = null, @Query("pageNo") pageNo: Int = 1, @Query("pageSize") pageSize: Int = 50, + @Query("x-access-token") token: String = App.accessToken ): RespData - - @POST(UrlConfig.SAVE_SHELF_GOODS_LIST) - suspend fun saveShelfGoodsList(@Body body: ShelfBody): RespData + @POST + suspend fun saveShelfGoodsList( + @Url url: String = UrlConfig.SAVE_SHELF_GOODS_LIST, + @Body body: ShelfBody, + @Query("x-access-token") token: String = App.accessToken + ): RespData } \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/net/HttpManager.kt b/app/src/main/java/com/shuwei/intelligent/shelves/net/HttpManager.kt index 4751825..d883cf6 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/net/HttpManager.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/net/HttpManager.kt @@ -16,7 +16,8 @@ import java.security.cert.X509Certificate import kotlin.apply val apiService: ApiService = Retrofit.Builder() - .baseUrl(UrlConfig.BASE_URL) +// .baseUrl(UrlConfig.BASE_URL) + .baseUrl(UrlConfig.DEVICE_BASE_URL) .client(HttpManager.instance.client) .addConverterFactory(GsonConverterFactory.create()) .build() @@ -32,12 +33,12 @@ class HttpManager private constructor() { sslSocketFactory(createSSLSocketFactory(), TrustAllCerts()) hostnameVerifier { _, _ -> true } addInterceptor(LoggingInterceptor()) - addInterceptor { chain -> - val request = chain.request().newBuilder() - .header("x-access-token", App.getInstance().accessToken) - .build() - chain.proceed(request) - } +// addInterceptor { chain -> +// val request = chain.request().newBuilder() +// .header("x-access-token", App.accessToken) +// .build() +// chain.proceed(request) +// } } .build() } diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/net/NetViewModel.kt b/app/src/main/java/com/shuwei/intelligent/shelves/net/NetViewModel.kt index ed75037..6ffb63f 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/net/NetViewModel.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/net/NetViewModel.kt @@ -32,11 +32,50 @@ class NetViewModel : ViewModel() { private val _getAccessTokenUiState = MutableStateFlow(UiState.Initial) val getAccessTokenUiState: StateFlow = _getAccessTokenUiState + private val _getDeviceTokenUiState = MutableStateFlow(UiState.Initial) + val getDeviceTokenUiState: StateFlow = _getDeviceTokenUiState + + private val _getDeviceConfigUiState = MutableStateFlow(UiState.Initial) + val getDeviceConfigUiState: StateFlow = _getDeviceConfigUiState + + + fun getDeviceToken(deviceId: String) { + viewModelScope.launch { + _getDeviceTokenUiState.value = UiState.Loading + runCatching { + val response = apiService.getDeviceToken(qrcodeId = deviceId) + if (response.success) { + _getDeviceTokenUiState.value = UiState.Success(response) + } else { + _getDeviceTokenUiState.value = UiState.Error(response.message ?: "请求失败") + } + }.onFailure { + _getDeviceTokenUiState.value = UiState.Error(it.message ?: "请求异常") + } + } + } + + fun getDeviceConfig(deviceId: String, deviceToken: String) { + viewModelScope.launch { + _getDeviceTokenUiState.value = UiState.Loading + runCatching { + val response = apiService.getDeviceInfo(equipmentCode = deviceId, token = deviceToken) + if (response.success) { + _getDeviceTokenUiState.value = UiState.Success(response) + } else { + _getDeviceTokenUiState.value = UiState.Error(response.message ?: "请求失败") + } + }.onFailure { + _getDeviceTokenUiState.value = UiState.Error(it.message ?: "请求异常") + } + } + } + fun getAccessToken(deviceId: String) { viewModelScope.launch { _getAccessTokenUiState.value = UiState.Loading runCatching { - val response = apiService.getAccessToken(deviceId) + val response = apiService.getAccessToken(qrcodeId = deviceId) if (response.success) { _getAccessTokenUiState.value = UiState.Success(response) } else { @@ -52,7 +91,7 @@ class NetViewModel : ViewModel() { viewModelScope.launch { _getShelfListUiState.value = UiState.Loading runCatching { - val response = apiService.getShelfList(deviceId) + val response = apiService.getShelfList(deviceId = deviceId) if (response.success) { _getShelfListUiState.value = UiState.Success(response) } else { diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/net/UrlConfig.kt b/app/src/main/java/com/shuwei/intelligent/shelves/net/UrlConfig.kt index d80a96b..8c87dc1 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/net/UrlConfig.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/net/UrlConfig.kt @@ -1,13 +1,24 @@ package com.shuwei.intelligent.shelves.net +import com.shuwei.intelligent.shelves.App + object UrlConfig { + const val DEVICE_BASE_URL = "http://device.shuziweidao.com:8889" +// /** +// * device获取token +// */ +// const val DEVICE_TOKEN = "${DEVICE_BASE_URL}/sys/getEquipmentToken" +// /** +// *获取配置信息 +// */ +// const val DEVICE_CONFIG = "${DEVICE_BASE_URL}/equipment/stEquipment/queryByEquipmentCode" // const val BASE_URL = "http://192.168.1.3:9002" - const val BASE_URL = "https://yyjk.shuziweidao.com/gateway/" - const val GET_ACCESS_TOKEN = "restaurant/equipment/stEquipment/getEquipmentToken" - const val GET_SHELF_LIST = "inventory/smartShelves/app/getShelvesListByDeviceId" - const val GET_GOODS_LIST = "inventory/smartShelves/app/getGoodsPageList" - const val SAVE_SHELF_GOODS_LIST = "inventory/smartShelves/app/saveShelvesGoodsList" + const val BASE_URL = "https://yyjk.shuziweidao.com/gateway" + var GET_ACCESS_TOKEN = "${App.configUrl}/restaurant/equipment/stEquipment/getEquipmentToken" + var GET_SHELF_LIST = "${App.configUrl}/inventory/smartShelves/app/getShelvesListByDeviceId" + var GET_GOODS_LIST = "${App.configUrl}/inventory/smartShelves/app/getGoodsPageList" + var SAVE_SHELF_GOODS_LIST = "${App.configUrl}/inventory/smartShelves/app/saveShelvesGoodsList" } diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/task/SyncTask.kt b/app/src/main/java/com/shuwei/intelligent/shelves/task/SyncTask.kt index c0cb6d8..f51b4c8 100644 --- a/app/src/main/java/com/shuwei/intelligent/shelves/task/SyncTask.kt +++ b/app/src/main/java/com/shuwei/intelligent/shelves/task/SyncTask.kt @@ -43,13 +43,13 @@ class SyncTask(appContext: Context, workerParams: WorkerParameters) : it.humidity = HomeActivity.showHumidity } val body = ShelfBody().also { - it.deviceId = App.getInstance().deviceId - it.canteenId = App.getInstance().canteenId + it.deviceId = App.deviceId ?: "" + it.canteenId = App.canteenId it.temperature = HomeActivity.showTemperatureC it.humidity = HomeActivity.showHumidity it.goodsList = submitList } - val resp = apiService.saveShelfGoodsList(body) + val resp = apiService.saveShelfGoodsList(body = body) Log.d(TAG, "performSync: body:${body.toJsonString()}") Log.d(TAG, "performSync: resp:${resp.toJsonString()}") } diff --git a/app/src/main/java/com/shuwei/intelligent/shelves/utils/AppUtil.java b/app/src/main/java/com/shuwei/intelligent/shelves/utils/AppUtil.java new file mode 100644 index 0000000..c821e73 --- /dev/null +++ b/app/src/main/java/com/shuwei/intelligent/shelves/utils/AppUtil.java @@ -0,0 +1,751 @@ +package com.shuwei.intelligent.shelves.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/shuwei/intelligent/shelves/utils/QRCodeUtil.kt b/app/src/main/java/com/shuwei/intelligent/shelves/utils/QRCodeUtil.kt new file mode 100644 index 0000000..b05a2ca --- /dev/null +++ b/app/src/main/java/com/shuwei/intelligent/shelves/utils/QRCodeUtil.kt @@ -0,0 +1,101 @@ +package com.shuwei.intelligent.shelves.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 + + +/** + * 二维码生成工具类 + */ +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/shuwei/intelligent/shelves/utils/SpTool.kt b/app/src/main/java/com/shuwei/intelligent/shelves/utils/SpTool.kt new file mode 100644 index 0000000..dbb0bc4 --- /dev/null +++ b/app/src/main/java/com/shuwei/intelligent/shelves/utils/SpTool.kt @@ -0,0 +1,31 @@ +package com.shuwei.intelligent.shelves.utils + +import com.shuwei.intelligent.shelves.App +import com.shuwei.intelligent.shelves.utils.ext.put +import kotlin.to + +object SpTool { + + const val DEVICE_CONFIG_CACHE = "deviceConfigCache" + const val TOKEN = "token" + const val LAUNCH_PAGE_TYPE = "launchPageType" + const val DEVICE_ID = "deviceId" + + val pref = App.getSharedPref()!! + + fun put(key: String, value: Any) { + pref.put(key to value) + } + + fun getInt(key: String, defValue: Int = 0): Int { + return pref.getInt(key, defValue) + } + + fun getString(key: String, defValue: String? = ""): String { + return pref.getString(key, defValue) ?: "" + } + + fun getBoolean(key: String, defValue: Boolean = false): Boolean { + return pref.getBoolean(key, defValue) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_init_button.xml b/app/src/main/res/drawable/bg_init_button.xml new file mode 100644 index 0000000..71ff7c1 --- /dev/null +++ b/app/src/main/res/drawable/bg_init_button.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ 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/layout/activity_init.xml b/app/src/main/res/layout/activity_init.xml new file mode 100644 index 0000000..6839fee --- /dev/null +++ b/app/src/main/res/layout/activity_init.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_logo512.png b/app/src/main/res/mipmap-xxxhdpi/ic_logo512.png new file mode 100644 index 0000000..55e3eac Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_logo512.png differ diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 1564dab..7c1df30 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -1,7 +1,8 @@ - - 192.168.1.3 - 192.168.1.7 - + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 248ed79..8c889e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,8 +8,12 @@ junitVersion = "1.1.5" espressoCore = "3.5.1" appcompat = "1.6.1" material = "1.10.0" +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" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }