diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt new file mode 100644 index 0000000..1e6b6f6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt @@ -0,0 +1,332 @@ +package com.casic.smarttube.view + +import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import cn.bertsir.zbar.QrManager +import com.amap.api.location.AMapLocation +import com.casic.smarttube.R +import com.casic.smarttube.extensions.combineImagePath +import com.casic.smarttube.utils.GlideLoadEngine +import com.casic.smarttube.utils.LocationHelper +import com.casic.smarttube.utils.QrConfigCreator +import com.casic.smarttube.vm.UploadImageViewModel +import com.gyf.immersionbar.ImmersionBar +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener +import com.pengxh.kt.lite.activity.BigImageActivity +import com.pengxh.kt.lite.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.callback.OnImageCompressListener +import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog +import kotlinx.android.synthetic.main.activity_add_device.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class AddDeviceActivity : KotlinBaseActivity() { + + private val kTag = "AddDeviceActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadImageViewModel: UploadImageViewModel + private val context: Context = this@AddDeviceActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_add_device + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + titleView.text = "添加设备" + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadImageViewModel = ViewModelProvider(this).get(UploadImageViewModel::class.java) + + val currentTimeMillis = System.currentTimeMillis() + + //设置相关数据 + deviceNameView.setText(String.format("管盯$currentTimeMillis")) + + val manager = QrManager.getInstance().init(QrConfigCreator.create(this)) + scannerView.setOnClickListener { + manager.startScan(this) { + //TODO isNumber方法无效 + if (it.content.isNumber()) { + deviceCodeView.setText(it.content) + } else { + "设备编号错误,请检查标签".show(context) + } + } + } + + ownerShipTempView.setOnClickListener { + "尽情期待~".show(this) + } + + collectIntervalView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle( + listOf( + "1min", + "2min", + "5min", + "10min", + "15min", + "30min", + "60min" + ) + ) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + + } + 1 -> { + + } + 2 -> { + + } + 3 -> { + + } + 4 -> { + + } + 5 -> { + + } + 6 -> { + + } + } + } + }).build().show() + } + + locationImageView.setOnClickListener { + showLoadingDialog(this, "定位中中,请稍后...") + LocationHelper.obtainCurrentLocation(this, + object : LocationHelper.ILocationListener { + override fun onAMapLocationGet(aMapLocation: AMapLocation?) { + dismissLoadingDialog() + if (aMapLocation == null) { + longitudeView.text = "定位失败" + latitudeView.text = "定位失败" + + longitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + latitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + } else { + longitudeView.text = aMapLocation.longitude.toString() + latitudeView.text = aMapLocation.latitude.toString() + } + } + }) + } + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + + addDeviceTimeView.text = currentTimeMillis.timestampToCompleteDate() + } + + override fun initEvent() { + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + selectPicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(context) + } else { + navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imagePaths.removeAt(position) + imageAdapter.deleteImage(position) + } + }) + + uploadImageViewModel.resultModel.observe(this, { + if (it.code == 200) { + val sumItemCount: Int = imageAdapter.itemCount + 1 //每上传一张图片,图片总数都是在原有的基础上+1 + if (sumItemCount <= 4) { + val url = it.data.toString() + if (url.isNotBlank()) { + imagePaths.add(url) + realPaths.add(url.combineImagePath()) + } + imageAdapter.setupImage(realPaths) + } else { + "最多只能上传3张图片".show(this) + } + } + }) + uploadImageViewModel.loadState.observe(this, { + dismissLoadingDialog() + }) + + sceneEditView.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + val text = s.toString().trim() + inputLengthView.text = String.format("${text.length}/100") + if (text.length > 100) { + inputLengthView.setTextColor(R.color.redTextColor.convertColor(context)) + "现场情况字符不能超过100个字符".show(context) + } else { + inputLengthView.setTextColor(R.color.subTextColor.convertColor(context)) + } + } + }) + } + + private fun selectPicture() { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle(listOf("拍照", "相册")) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + PictureSelector.create(context) + .openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + if (result == null) { + "拍照保存失败,请重试".show(context) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + 1 -> { + PictureSelector.create(context) + .openGallery(SelectMimeType.ofImage()) + .isGif(false) + .isMaxSelectEnabledMask(true) + .setFilterMinFileSize(100) + .setMaxSelectNum(3) + .isDisplayCamera(false) + .setImageEngine(GlideLoadEngine.instance) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + showLoadingDialog(context, "图片上传中,请稍后...") + if (result == null) { + "选择照片失败,请重试".show(context) + return + } + // 线程控制图片压缩上传过程,防止速度过快导致压缩失败 + val sum = (result.size * 500).toLong() + object : CountDownTimer(sum, 500) { + override fun onTick(millisUntilFinished: Long) { + val i = millisUntilFinished / 500 + val message = weakReferenceHandler.obtainMessage() + message.obj = result[i.toInt()] + message.what = 2022062501 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022062501) { + analyticalSelectResults(it.obj as LocalMedia) + } + true + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 +// val realPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { +// result.realPath +// } else { +// result.sandboxPath +// } +// Log.d(kTag, "初始路径:" + result.path) +// Log.d(kTag, "绝对路径:" + result.realPath) +// Log.d(kTag, "原图路径:" + result.originalPath) +// Log.d(kTag, "沙盒路径:" + result.sandboxPath) + result.realPath.compressImage(this, object : OnImageCompressListener { + override fun onSuccess(file: File) { + Log.d(kTag, "onSuccess: " + file.absolutePath) + //上传图片 + uploadImageViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + + private var loadingDialog: QMUITipDialog? = null + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt new file mode 100644 index 0000000..1e6b6f6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt @@ -0,0 +1,332 @@ +package com.casic.smarttube.view + +import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import cn.bertsir.zbar.QrManager +import com.amap.api.location.AMapLocation +import com.casic.smarttube.R +import com.casic.smarttube.extensions.combineImagePath +import com.casic.smarttube.utils.GlideLoadEngine +import com.casic.smarttube.utils.LocationHelper +import com.casic.smarttube.utils.QrConfigCreator +import com.casic.smarttube.vm.UploadImageViewModel +import com.gyf.immersionbar.ImmersionBar +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener +import com.pengxh.kt.lite.activity.BigImageActivity +import com.pengxh.kt.lite.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.callback.OnImageCompressListener +import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog +import kotlinx.android.synthetic.main.activity_add_device.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class AddDeviceActivity : KotlinBaseActivity() { + + private val kTag = "AddDeviceActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadImageViewModel: UploadImageViewModel + private val context: Context = this@AddDeviceActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_add_device + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + titleView.text = "添加设备" + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadImageViewModel = ViewModelProvider(this).get(UploadImageViewModel::class.java) + + val currentTimeMillis = System.currentTimeMillis() + + //设置相关数据 + deviceNameView.setText(String.format("管盯$currentTimeMillis")) + + val manager = QrManager.getInstance().init(QrConfigCreator.create(this)) + scannerView.setOnClickListener { + manager.startScan(this) { + //TODO isNumber方法无效 + if (it.content.isNumber()) { + deviceCodeView.setText(it.content) + } else { + "设备编号错误,请检查标签".show(context) + } + } + } + + ownerShipTempView.setOnClickListener { + "尽情期待~".show(this) + } + + collectIntervalView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle( + listOf( + "1min", + "2min", + "5min", + "10min", + "15min", + "30min", + "60min" + ) + ) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + + } + 1 -> { + + } + 2 -> { + + } + 3 -> { + + } + 4 -> { + + } + 5 -> { + + } + 6 -> { + + } + } + } + }).build().show() + } + + locationImageView.setOnClickListener { + showLoadingDialog(this, "定位中中,请稍后...") + LocationHelper.obtainCurrentLocation(this, + object : LocationHelper.ILocationListener { + override fun onAMapLocationGet(aMapLocation: AMapLocation?) { + dismissLoadingDialog() + if (aMapLocation == null) { + longitudeView.text = "定位失败" + latitudeView.text = "定位失败" + + longitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + latitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + } else { + longitudeView.text = aMapLocation.longitude.toString() + latitudeView.text = aMapLocation.latitude.toString() + } + } + }) + } + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + + addDeviceTimeView.text = currentTimeMillis.timestampToCompleteDate() + } + + override fun initEvent() { + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + selectPicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(context) + } else { + navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imagePaths.removeAt(position) + imageAdapter.deleteImage(position) + } + }) + + uploadImageViewModel.resultModel.observe(this, { + if (it.code == 200) { + val sumItemCount: Int = imageAdapter.itemCount + 1 //每上传一张图片,图片总数都是在原有的基础上+1 + if (sumItemCount <= 4) { + val url = it.data.toString() + if (url.isNotBlank()) { + imagePaths.add(url) + realPaths.add(url.combineImagePath()) + } + imageAdapter.setupImage(realPaths) + } else { + "最多只能上传3张图片".show(this) + } + } + }) + uploadImageViewModel.loadState.observe(this, { + dismissLoadingDialog() + }) + + sceneEditView.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + val text = s.toString().trim() + inputLengthView.text = String.format("${text.length}/100") + if (text.length > 100) { + inputLengthView.setTextColor(R.color.redTextColor.convertColor(context)) + "现场情况字符不能超过100个字符".show(context) + } else { + inputLengthView.setTextColor(R.color.subTextColor.convertColor(context)) + } + } + }) + } + + private fun selectPicture() { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle(listOf("拍照", "相册")) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + PictureSelector.create(context) + .openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + if (result == null) { + "拍照保存失败,请重试".show(context) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + 1 -> { + PictureSelector.create(context) + .openGallery(SelectMimeType.ofImage()) + .isGif(false) + .isMaxSelectEnabledMask(true) + .setFilterMinFileSize(100) + .setMaxSelectNum(3) + .isDisplayCamera(false) + .setImageEngine(GlideLoadEngine.instance) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + showLoadingDialog(context, "图片上传中,请稍后...") + if (result == null) { + "选择照片失败,请重试".show(context) + return + } + // 线程控制图片压缩上传过程,防止速度过快导致压缩失败 + val sum = (result.size * 500).toLong() + object : CountDownTimer(sum, 500) { + override fun onTick(millisUntilFinished: Long) { + val i = millisUntilFinished / 500 + val message = weakReferenceHandler.obtainMessage() + message.obj = result[i.toInt()] + message.what = 2022062501 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022062501) { + analyticalSelectResults(it.obj as LocalMedia) + } + true + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 +// val realPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { +// result.realPath +// } else { +// result.sandboxPath +// } +// Log.d(kTag, "初始路径:" + result.path) +// Log.d(kTag, "绝对路径:" + result.realPath) +// Log.d(kTag, "原图路径:" + result.originalPath) +// Log.d(kTag, "沙盒路径:" + result.sandboxPath) + result.realPath.compressImage(this, object : OnImageCompressListener { + override fun onSuccess(file: File) { + Log.d(kTag, "onSuccess: " + file.absolutePath) + //上传图片 + uploadImageViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + + private var loadingDialog: QMUITipDialog? = null + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt new file mode 100644 index 0000000..49d1775 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState +import java.io.File + +class UploadImageViewModel : BaseViewModel() { + + private val gson = Gson() + val resultModel = MutableLiveData() + + fun uploadImage(image: File) = launch({ + val response = RetrofitServiceManager.uploadImage(image) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + resultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt new file mode 100644 index 0000000..1e6b6f6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt @@ -0,0 +1,332 @@ +package com.casic.smarttube.view + +import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import cn.bertsir.zbar.QrManager +import com.amap.api.location.AMapLocation +import com.casic.smarttube.R +import com.casic.smarttube.extensions.combineImagePath +import com.casic.smarttube.utils.GlideLoadEngine +import com.casic.smarttube.utils.LocationHelper +import com.casic.smarttube.utils.QrConfigCreator +import com.casic.smarttube.vm.UploadImageViewModel +import com.gyf.immersionbar.ImmersionBar +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener +import com.pengxh.kt.lite.activity.BigImageActivity +import com.pengxh.kt.lite.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.callback.OnImageCompressListener +import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog +import kotlinx.android.synthetic.main.activity_add_device.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class AddDeviceActivity : KotlinBaseActivity() { + + private val kTag = "AddDeviceActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadImageViewModel: UploadImageViewModel + private val context: Context = this@AddDeviceActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_add_device + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + titleView.text = "添加设备" + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadImageViewModel = ViewModelProvider(this).get(UploadImageViewModel::class.java) + + val currentTimeMillis = System.currentTimeMillis() + + //设置相关数据 + deviceNameView.setText(String.format("管盯$currentTimeMillis")) + + val manager = QrManager.getInstance().init(QrConfigCreator.create(this)) + scannerView.setOnClickListener { + manager.startScan(this) { + //TODO isNumber方法无效 + if (it.content.isNumber()) { + deviceCodeView.setText(it.content) + } else { + "设备编号错误,请检查标签".show(context) + } + } + } + + ownerShipTempView.setOnClickListener { + "尽情期待~".show(this) + } + + collectIntervalView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle( + listOf( + "1min", + "2min", + "5min", + "10min", + "15min", + "30min", + "60min" + ) + ) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + + } + 1 -> { + + } + 2 -> { + + } + 3 -> { + + } + 4 -> { + + } + 5 -> { + + } + 6 -> { + + } + } + } + }).build().show() + } + + locationImageView.setOnClickListener { + showLoadingDialog(this, "定位中中,请稍后...") + LocationHelper.obtainCurrentLocation(this, + object : LocationHelper.ILocationListener { + override fun onAMapLocationGet(aMapLocation: AMapLocation?) { + dismissLoadingDialog() + if (aMapLocation == null) { + longitudeView.text = "定位失败" + latitudeView.text = "定位失败" + + longitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + latitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + } else { + longitudeView.text = aMapLocation.longitude.toString() + latitudeView.text = aMapLocation.latitude.toString() + } + } + }) + } + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + + addDeviceTimeView.text = currentTimeMillis.timestampToCompleteDate() + } + + override fun initEvent() { + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + selectPicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(context) + } else { + navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imagePaths.removeAt(position) + imageAdapter.deleteImage(position) + } + }) + + uploadImageViewModel.resultModel.observe(this, { + if (it.code == 200) { + val sumItemCount: Int = imageAdapter.itemCount + 1 //每上传一张图片,图片总数都是在原有的基础上+1 + if (sumItemCount <= 4) { + val url = it.data.toString() + if (url.isNotBlank()) { + imagePaths.add(url) + realPaths.add(url.combineImagePath()) + } + imageAdapter.setupImage(realPaths) + } else { + "最多只能上传3张图片".show(this) + } + } + }) + uploadImageViewModel.loadState.observe(this, { + dismissLoadingDialog() + }) + + sceneEditView.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + val text = s.toString().trim() + inputLengthView.text = String.format("${text.length}/100") + if (text.length > 100) { + inputLengthView.setTextColor(R.color.redTextColor.convertColor(context)) + "现场情况字符不能超过100个字符".show(context) + } else { + inputLengthView.setTextColor(R.color.subTextColor.convertColor(context)) + } + } + }) + } + + private fun selectPicture() { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle(listOf("拍照", "相册")) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + PictureSelector.create(context) + .openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + if (result == null) { + "拍照保存失败,请重试".show(context) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + 1 -> { + PictureSelector.create(context) + .openGallery(SelectMimeType.ofImage()) + .isGif(false) + .isMaxSelectEnabledMask(true) + .setFilterMinFileSize(100) + .setMaxSelectNum(3) + .isDisplayCamera(false) + .setImageEngine(GlideLoadEngine.instance) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + showLoadingDialog(context, "图片上传中,请稍后...") + if (result == null) { + "选择照片失败,请重试".show(context) + return + } + // 线程控制图片压缩上传过程,防止速度过快导致压缩失败 + val sum = (result.size * 500).toLong() + object : CountDownTimer(sum, 500) { + override fun onTick(millisUntilFinished: Long) { + val i = millisUntilFinished / 500 + val message = weakReferenceHandler.obtainMessage() + message.obj = result[i.toInt()] + message.what = 2022062501 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022062501) { + analyticalSelectResults(it.obj as LocalMedia) + } + true + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 +// val realPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { +// result.realPath +// } else { +// result.sandboxPath +// } +// Log.d(kTag, "初始路径:" + result.path) +// Log.d(kTag, "绝对路径:" + result.realPath) +// Log.d(kTag, "原图路径:" + result.originalPath) +// Log.d(kTag, "沙盒路径:" + result.sandboxPath) + result.realPath.compressImage(this, object : OnImageCompressListener { + override fun onSuccess(file: File) { + Log.d(kTag, "onSuccess: " + file.absolutePath) + //上传图片 + uploadImageViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + + private var loadingDialog: QMUITipDialog? = null + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt new file mode 100644 index 0000000..49d1775 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState +import java.io.File + +class UploadImageViewModel : BaseViewModel() { + + private val gson = Gson() + val resultModel = MutableLiveData() + + fun uploadImage(image: File) = launch({ + val response = RetrofitServiceManager.uploadImage(image) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + resultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml new file mode 100644 index 0000000..bca9c08 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt new file mode 100644 index 0000000..1e6b6f6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt @@ -0,0 +1,332 @@ +package com.casic.smarttube.view + +import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import cn.bertsir.zbar.QrManager +import com.amap.api.location.AMapLocation +import com.casic.smarttube.R +import com.casic.smarttube.extensions.combineImagePath +import com.casic.smarttube.utils.GlideLoadEngine +import com.casic.smarttube.utils.LocationHelper +import com.casic.smarttube.utils.QrConfigCreator +import com.casic.smarttube.vm.UploadImageViewModel +import com.gyf.immersionbar.ImmersionBar +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener +import com.pengxh.kt.lite.activity.BigImageActivity +import com.pengxh.kt.lite.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.callback.OnImageCompressListener +import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog +import kotlinx.android.synthetic.main.activity_add_device.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class AddDeviceActivity : KotlinBaseActivity() { + + private val kTag = "AddDeviceActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadImageViewModel: UploadImageViewModel + private val context: Context = this@AddDeviceActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_add_device + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + titleView.text = "添加设备" + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadImageViewModel = ViewModelProvider(this).get(UploadImageViewModel::class.java) + + val currentTimeMillis = System.currentTimeMillis() + + //设置相关数据 + deviceNameView.setText(String.format("管盯$currentTimeMillis")) + + val manager = QrManager.getInstance().init(QrConfigCreator.create(this)) + scannerView.setOnClickListener { + manager.startScan(this) { + //TODO isNumber方法无效 + if (it.content.isNumber()) { + deviceCodeView.setText(it.content) + } else { + "设备编号错误,请检查标签".show(context) + } + } + } + + ownerShipTempView.setOnClickListener { + "尽情期待~".show(this) + } + + collectIntervalView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle( + listOf( + "1min", + "2min", + "5min", + "10min", + "15min", + "30min", + "60min" + ) + ) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + + } + 1 -> { + + } + 2 -> { + + } + 3 -> { + + } + 4 -> { + + } + 5 -> { + + } + 6 -> { + + } + } + } + }).build().show() + } + + locationImageView.setOnClickListener { + showLoadingDialog(this, "定位中中,请稍后...") + LocationHelper.obtainCurrentLocation(this, + object : LocationHelper.ILocationListener { + override fun onAMapLocationGet(aMapLocation: AMapLocation?) { + dismissLoadingDialog() + if (aMapLocation == null) { + longitudeView.text = "定位失败" + latitudeView.text = "定位失败" + + longitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + latitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + } else { + longitudeView.text = aMapLocation.longitude.toString() + latitudeView.text = aMapLocation.latitude.toString() + } + } + }) + } + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + + addDeviceTimeView.text = currentTimeMillis.timestampToCompleteDate() + } + + override fun initEvent() { + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + selectPicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(context) + } else { + navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imagePaths.removeAt(position) + imageAdapter.deleteImage(position) + } + }) + + uploadImageViewModel.resultModel.observe(this, { + if (it.code == 200) { + val sumItemCount: Int = imageAdapter.itemCount + 1 //每上传一张图片,图片总数都是在原有的基础上+1 + if (sumItemCount <= 4) { + val url = it.data.toString() + if (url.isNotBlank()) { + imagePaths.add(url) + realPaths.add(url.combineImagePath()) + } + imageAdapter.setupImage(realPaths) + } else { + "最多只能上传3张图片".show(this) + } + } + }) + uploadImageViewModel.loadState.observe(this, { + dismissLoadingDialog() + }) + + sceneEditView.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + val text = s.toString().trim() + inputLengthView.text = String.format("${text.length}/100") + if (text.length > 100) { + inputLengthView.setTextColor(R.color.redTextColor.convertColor(context)) + "现场情况字符不能超过100个字符".show(context) + } else { + inputLengthView.setTextColor(R.color.subTextColor.convertColor(context)) + } + } + }) + } + + private fun selectPicture() { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle(listOf("拍照", "相册")) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + PictureSelector.create(context) + .openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + if (result == null) { + "拍照保存失败,请重试".show(context) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + 1 -> { + PictureSelector.create(context) + .openGallery(SelectMimeType.ofImage()) + .isGif(false) + .isMaxSelectEnabledMask(true) + .setFilterMinFileSize(100) + .setMaxSelectNum(3) + .isDisplayCamera(false) + .setImageEngine(GlideLoadEngine.instance) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + showLoadingDialog(context, "图片上传中,请稍后...") + if (result == null) { + "选择照片失败,请重试".show(context) + return + } + // 线程控制图片压缩上传过程,防止速度过快导致压缩失败 + val sum = (result.size * 500).toLong() + object : CountDownTimer(sum, 500) { + override fun onTick(millisUntilFinished: Long) { + val i = millisUntilFinished / 500 + val message = weakReferenceHandler.obtainMessage() + message.obj = result[i.toInt()] + message.what = 2022062501 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022062501) { + analyticalSelectResults(it.obj as LocalMedia) + } + true + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 +// val realPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { +// result.realPath +// } else { +// result.sandboxPath +// } +// Log.d(kTag, "初始路径:" + result.path) +// Log.d(kTag, "绝对路径:" + result.realPath) +// Log.d(kTag, "原图路径:" + result.originalPath) +// Log.d(kTag, "沙盒路径:" + result.sandboxPath) + result.realPath.compressImage(this, object : OnImageCompressListener { + override fun onSuccess(file: File) { + Log.d(kTag, "onSuccess: " + file.absolutePath) + //上传图片 + uploadImageViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + + private var loadingDialog: QMUITipDialog? = null + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt new file mode 100644 index 0000000..49d1775 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState +import java.io.File + +class UploadImageViewModel : BaseViewModel() { + + private val gson = Gson() + val resultModel = MutableLiveData() + + fun uploadImage(image: File) = launch({ + val response = RetrofitServiceManager.uploadImage(image) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + resultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml new file mode 100644 index 0000000..bca9c08 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..3d1695b --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt new file mode 100644 index 0000000..1e6b6f6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt @@ -0,0 +1,332 @@ +package com.casic.smarttube.view + +import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import cn.bertsir.zbar.QrManager +import com.amap.api.location.AMapLocation +import com.casic.smarttube.R +import com.casic.smarttube.extensions.combineImagePath +import com.casic.smarttube.utils.GlideLoadEngine +import com.casic.smarttube.utils.LocationHelper +import com.casic.smarttube.utils.QrConfigCreator +import com.casic.smarttube.vm.UploadImageViewModel +import com.gyf.immersionbar.ImmersionBar +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener +import com.pengxh.kt.lite.activity.BigImageActivity +import com.pengxh.kt.lite.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.callback.OnImageCompressListener +import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog +import kotlinx.android.synthetic.main.activity_add_device.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class AddDeviceActivity : KotlinBaseActivity() { + + private val kTag = "AddDeviceActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadImageViewModel: UploadImageViewModel + private val context: Context = this@AddDeviceActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_add_device + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + titleView.text = "添加设备" + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadImageViewModel = ViewModelProvider(this).get(UploadImageViewModel::class.java) + + val currentTimeMillis = System.currentTimeMillis() + + //设置相关数据 + deviceNameView.setText(String.format("管盯$currentTimeMillis")) + + val manager = QrManager.getInstance().init(QrConfigCreator.create(this)) + scannerView.setOnClickListener { + manager.startScan(this) { + //TODO isNumber方法无效 + if (it.content.isNumber()) { + deviceCodeView.setText(it.content) + } else { + "设备编号错误,请检查标签".show(context) + } + } + } + + ownerShipTempView.setOnClickListener { + "尽情期待~".show(this) + } + + collectIntervalView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle( + listOf( + "1min", + "2min", + "5min", + "10min", + "15min", + "30min", + "60min" + ) + ) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + + } + 1 -> { + + } + 2 -> { + + } + 3 -> { + + } + 4 -> { + + } + 5 -> { + + } + 6 -> { + + } + } + } + }).build().show() + } + + locationImageView.setOnClickListener { + showLoadingDialog(this, "定位中中,请稍后...") + LocationHelper.obtainCurrentLocation(this, + object : LocationHelper.ILocationListener { + override fun onAMapLocationGet(aMapLocation: AMapLocation?) { + dismissLoadingDialog() + if (aMapLocation == null) { + longitudeView.text = "定位失败" + latitudeView.text = "定位失败" + + longitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + latitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + } else { + longitudeView.text = aMapLocation.longitude.toString() + latitudeView.text = aMapLocation.latitude.toString() + } + } + }) + } + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + + addDeviceTimeView.text = currentTimeMillis.timestampToCompleteDate() + } + + override fun initEvent() { + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + selectPicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(context) + } else { + navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imagePaths.removeAt(position) + imageAdapter.deleteImage(position) + } + }) + + uploadImageViewModel.resultModel.observe(this, { + if (it.code == 200) { + val sumItemCount: Int = imageAdapter.itemCount + 1 //每上传一张图片,图片总数都是在原有的基础上+1 + if (sumItemCount <= 4) { + val url = it.data.toString() + if (url.isNotBlank()) { + imagePaths.add(url) + realPaths.add(url.combineImagePath()) + } + imageAdapter.setupImage(realPaths) + } else { + "最多只能上传3张图片".show(this) + } + } + }) + uploadImageViewModel.loadState.observe(this, { + dismissLoadingDialog() + }) + + sceneEditView.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + val text = s.toString().trim() + inputLengthView.text = String.format("${text.length}/100") + if (text.length > 100) { + inputLengthView.setTextColor(R.color.redTextColor.convertColor(context)) + "现场情况字符不能超过100个字符".show(context) + } else { + inputLengthView.setTextColor(R.color.subTextColor.convertColor(context)) + } + } + }) + } + + private fun selectPicture() { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle(listOf("拍照", "相册")) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + PictureSelector.create(context) + .openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + if (result == null) { + "拍照保存失败,请重试".show(context) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + 1 -> { + PictureSelector.create(context) + .openGallery(SelectMimeType.ofImage()) + .isGif(false) + .isMaxSelectEnabledMask(true) + .setFilterMinFileSize(100) + .setMaxSelectNum(3) + .isDisplayCamera(false) + .setImageEngine(GlideLoadEngine.instance) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + showLoadingDialog(context, "图片上传中,请稍后...") + if (result == null) { + "选择照片失败,请重试".show(context) + return + } + // 线程控制图片压缩上传过程,防止速度过快导致压缩失败 + val sum = (result.size * 500).toLong() + object : CountDownTimer(sum, 500) { + override fun onTick(millisUntilFinished: Long) { + val i = millisUntilFinished / 500 + val message = weakReferenceHandler.obtainMessage() + message.obj = result[i.toInt()] + message.what = 2022062501 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022062501) { + analyticalSelectResults(it.obj as LocalMedia) + } + true + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 +// val realPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { +// result.realPath +// } else { +// result.sandboxPath +// } +// Log.d(kTag, "初始路径:" + result.path) +// Log.d(kTag, "绝对路径:" + result.realPath) +// Log.d(kTag, "原图路径:" + result.originalPath) +// Log.d(kTag, "沙盒路径:" + result.sandboxPath) + result.realPath.compressImage(this, object : OnImageCompressListener { + override fun onSuccess(file: File) { + Log.d(kTag, "onSuccess: " + file.absolutePath) + //上传图片 + uploadImageViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + + private var loadingDialog: QMUITipDialog? = null + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt new file mode 100644 index 0000000..49d1775 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState +import java.io.File + +class UploadImageViewModel : BaseViewModel() { + + private val gson = Gson() + val resultModel = MutableLiveData() + + fun uploadImage(image: File) = launch({ + val response = RetrofitServiceManager.uploadImage(image) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + resultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml new file mode 100644 index 0000000..bca9c08 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..3d1695b --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_right.xml b/app/src/main/res/drawable/ic_right.xml index 6020f09..d43c7b8 100644 --- a/app/src/main/res/drawable/ic_right.xml +++ b/app/src/main/res/drawable/ic_right.xml @@ -1,6 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt new file mode 100644 index 0000000..1e6b6f6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt @@ -0,0 +1,332 @@ +package com.casic.smarttube.view + +import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import cn.bertsir.zbar.QrManager +import com.amap.api.location.AMapLocation +import com.casic.smarttube.R +import com.casic.smarttube.extensions.combineImagePath +import com.casic.smarttube.utils.GlideLoadEngine +import com.casic.smarttube.utils.LocationHelper +import com.casic.smarttube.utils.QrConfigCreator +import com.casic.smarttube.vm.UploadImageViewModel +import com.gyf.immersionbar.ImmersionBar +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener +import com.pengxh.kt.lite.activity.BigImageActivity +import com.pengxh.kt.lite.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.callback.OnImageCompressListener +import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog +import kotlinx.android.synthetic.main.activity_add_device.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class AddDeviceActivity : KotlinBaseActivity() { + + private val kTag = "AddDeviceActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadImageViewModel: UploadImageViewModel + private val context: Context = this@AddDeviceActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_add_device + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + titleView.text = "添加设备" + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadImageViewModel = ViewModelProvider(this).get(UploadImageViewModel::class.java) + + val currentTimeMillis = System.currentTimeMillis() + + //设置相关数据 + deviceNameView.setText(String.format("管盯$currentTimeMillis")) + + val manager = QrManager.getInstance().init(QrConfigCreator.create(this)) + scannerView.setOnClickListener { + manager.startScan(this) { + //TODO isNumber方法无效 + if (it.content.isNumber()) { + deviceCodeView.setText(it.content) + } else { + "设备编号错误,请检查标签".show(context) + } + } + } + + ownerShipTempView.setOnClickListener { + "尽情期待~".show(this) + } + + collectIntervalView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle( + listOf( + "1min", + "2min", + "5min", + "10min", + "15min", + "30min", + "60min" + ) + ) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + + } + 1 -> { + + } + 2 -> { + + } + 3 -> { + + } + 4 -> { + + } + 5 -> { + + } + 6 -> { + + } + } + } + }).build().show() + } + + locationImageView.setOnClickListener { + showLoadingDialog(this, "定位中中,请稍后...") + LocationHelper.obtainCurrentLocation(this, + object : LocationHelper.ILocationListener { + override fun onAMapLocationGet(aMapLocation: AMapLocation?) { + dismissLoadingDialog() + if (aMapLocation == null) { + longitudeView.text = "定位失败" + latitudeView.text = "定位失败" + + longitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + latitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + } else { + longitudeView.text = aMapLocation.longitude.toString() + latitudeView.text = aMapLocation.latitude.toString() + } + } + }) + } + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + + addDeviceTimeView.text = currentTimeMillis.timestampToCompleteDate() + } + + override fun initEvent() { + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + selectPicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(context) + } else { + navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imagePaths.removeAt(position) + imageAdapter.deleteImage(position) + } + }) + + uploadImageViewModel.resultModel.observe(this, { + if (it.code == 200) { + val sumItemCount: Int = imageAdapter.itemCount + 1 //每上传一张图片,图片总数都是在原有的基础上+1 + if (sumItemCount <= 4) { + val url = it.data.toString() + if (url.isNotBlank()) { + imagePaths.add(url) + realPaths.add(url.combineImagePath()) + } + imageAdapter.setupImage(realPaths) + } else { + "最多只能上传3张图片".show(this) + } + } + }) + uploadImageViewModel.loadState.observe(this, { + dismissLoadingDialog() + }) + + sceneEditView.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + val text = s.toString().trim() + inputLengthView.text = String.format("${text.length}/100") + if (text.length > 100) { + inputLengthView.setTextColor(R.color.redTextColor.convertColor(context)) + "现场情况字符不能超过100个字符".show(context) + } else { + inputLengthView.setTextColor(R.color.subTextColor.convertColor(context)) + } + } + }) + } + + private fun selectPicture() { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle(listOf("拍照", "相册")) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + PictureSelector.create(context) + .openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + if (result == null) { + "拍照保存失败,请重试".show(context) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + 1 -> { + PictureSelector.create(context) + .openGallery(SelectMimeType.ofImage()) + .isGif(false) + .isMaxSelectEnabledMask(true) + .setFilterMinFileSize(100) + .setMaxSelectNum(3) + .isDisplayCamera(false) + .setImageEngine(GlideLoadEngine.instance) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + showLoadingDialog(context, "图片上传中,请稍后...") + if (result == null) { + "选择照片失败,请重试".show(context) + return + } + // 线程控制图片压缩上传过程,防止速度过快导致压缩失败 + val sum = (result.size * 500).toLong() + object : CountDownTimer(sum, 500) { + override fun onTick(millisUntilFinished: Long) { + val i = millisUntilFinished / 500 + val message = weakReferenceHandler.obtainMessage() + message.obj = result[i.toInt()] + message.what = 2022062501 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022062501) { + analyticalSelectResults(it.obj as LocalMedia) + } + true + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 +// val realPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { +// result.realPath +// } else { +// result.sandboxPath +// } +// Log.d(kTag, "初始路径:" + result.path) +// Log.d(kTag, "绝对路径:" + result.realPath) +// Log.d(kTag, "原图路径:" + result.originalPath) +// Log.d(kTag, "沙盒路径:" + result.sandboxPath) + result.realPath.compressImage(this, object : OnImageCompressListener { + override fun onSuccess(file: File) { + Log.d(kTag, "onSuccess: " + file.absolutePath) + //上传图片 + uploadImageViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + + private var loadingDialog: QMUITipDialog? = null + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt new file mode 100644 index 0000000..49d1775 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState +import java.io.File + +class UploadImageViewModel : BaseViewModel() { + + private val gson = Gson() + val resultModel = MutableLiveData() + + fun uploadImage(image: File) = launch({ + val response = RetrofitServiceManager.uploadImage(image) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + resultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml new file mode 100644 index 0000000..bca9c08 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..3d1695b --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_right.xml b/app/src/main/res/drawable/ic_right.xml index 6020f09..d43c7b8 100644 --- a/app/src/main/res/drawable/ic_right.xml +++ b/app/src/main/res/drawable/ic_right.xml @@ -1,6 +1,6 @@ + + diff --git a/app/build.gradle b/app/build.gradle index bd23625..1e8ca15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,7 @@ implementation 'top.zibin:Luban:1.1.8' //高德导航(高德导航和高德地图会重复,不兼容,只能选其一) implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0' + implementation 'com.amap.api:location:5.3.1' //返回值转换器 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' implementation 'com.squareup.retrofit2:converter-scalars:2.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6b1132e..58bd05b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,19 +48,25 @@ android:screenOrientation="landscape" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt index 1c43abb..b932882 100644 --- a/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/smarttube/fragment/HomePageFragment.kt @@ -9,6 +9,8 @@ import com.amap.api.maps.AMapOptions import com.casic.smarttube.R import com.casic.smarttube.utils.LocaleConstant +import com.casic.smarttube.view.AddDeviceActivity +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.widget.EasyPopupWindow import kotlinx.android.synthetic.main.fragment_home.view.* @@ -21,7 +23,7 @@ inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) @@ -44,6 +46,9 @@ //地图初始化 initMap(savedInstanceState) + homeView.addDeviceButton.setOnClickListener { + requireContext().navigatePageTo() + } return homeView } @@ -54,6 +59,7 @@ val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER + uiSettings.isZoomControlsEnabled = false//不显示+/- uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 diff --git a/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt new file mode 100644 index 0000000..66bce56 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/GlideLoadEngine.kt @@ -0,0 +1,92 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.widget.ImageView +import androidx.annotation.Nullable +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import com.casic.smarttube.R +import com.luck.picture.lib.engine.ImageEngine +import com.luck.picture.lib.interfaces.OnCallbackListener +import com.luck.picture.lib.utils.ActivityCompatHelper + + +class GlideLoadEngine private constructor() : ImageEngine { + + companion object { + val instance: GlideLoadEngine by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + GlideLoadEngine() + } + } + + override fun loadImage(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context).load(url).into(imageView); + } + + override fun loadImageBitmap( + context: Context, + url: String, + maxWidth: Int, + maxHeight: Int, + call: OnCallbackListener? + ) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .override(maxWidth, maxHeight) + .load(url) + .into(object : CustomTarget() { + override fun onResourceReady( + resource: Bitmap, @Nullable transition: Transition? + ) { + call?.onCall(resource) + } + + override fun onLoadFailed(@Nullable errorDrawable: Drawable?) { + call?.onCall(null) + } + + override fun onLoadCleared(@Nullable placeholder: Drawable?) {} + }) + } + + override fun loadAlbumCover(context: Context, url: String, imageView: ImageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(CenterCrop(), RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView) + } + + override fun pauseRequests(context: Context?) { + context?.let { Glide.with(it).pauseRequests() } + } + + override fun resumeRequests(context: Context?) { + context?.let { Glide.with(it).resumeRequests() } + } + + override fun loadGridImage(context: Context, url: String, imageView: ImageView) { + Glide.with(context) + .load(url) + .apply(RequestOptions().placeholder(R.drawable.ps_image_placeholder)) + .into(imageView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt new file mode 100644 index 0000000..372a521 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/LocationHelper.kt @@ -0,0 +1,37 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.util.Log +import com.amap.api.location.AMapLocation +import com.amap.api.location.AMapLocationClient +import com.amap.api.location.AMapLocationClientOption + +object LocationHelper { + private const val kTag = "LocationHelper" + + fun obtainCurrentLocation(context: Context, listener: ILocationListener) { + val locationClient = AMapLocationClient(context) + val locationOption = AMapLocationClientOption() + //设置定位模式为高精度模式,AMapLocationMode.Battery_Saving为低功耗模式,AMapLocationMode.Device_Sensors是仅设备模式 + locationOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy + locationOption.isNeedAddress = false //设置是否返回地址信息(默认返回地址信息) + //给定位客户端对象设置定位参数 + locationClient.setLocationOption(locationOption) + locationClient.setLocationListener { + if (it.errorCode == 0) { + listener.onAMapLocationGet(it) + } else { + listener.onAMapLocationGet(null) + //显示错误信息ErrCode是错误码,errInfo是错误信息,详见错误码表。 + Log.e( + kTag, "location Error, ErrCode: ${it.errorCode} , errInfo: ${it.errorInfo}" + ) + } + } + locationClient.startLocation() + } + + interface ILocationListener { + fun onAMapLocationGet(aMapLocation: AMapLocation?) //高德定位数据 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt new file mode 100644 index 0000000..fc75052 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/utils/QrConfigCreator.kt @@ -0,0 +1,29 @@ +package com.casic.smarttube.utils + +import android.content.Context +import android.graphics.Color +import cn.bertsir.zbar.QrConfig +import cn.bertsir.zbar.view.ScanLineView +import com.casic.smarttube.R +import com.pengxh.kt.lite.extensions.convertColor + +object QrConfigCreator { + fun create(context: Context): QrConfig = QrConfig.Builder().setTitleText("扫一扫") //设置Title文字 + .setShowLight(true) //显示手电筒按钮 + .setShowTitle(true) //显示Title + .setScanType(QrConfig.TYPE_ALL)//识别二维码和条形码 + .setShowAlbum(false) //显示从相册选择按钮 + .setCornerColor(R.color.mainThemeColor.convertColor(context)) //设置扫描框颜色 + .setLineColor(R.color.mainThemeColor.convertColor(context)) //设置扫描线颜色 + .setLineSpeed(QrConfig.LINE_MEDIUM) //设置扫描线速度 + .setDesText(null) //扫描框下文字 + .setShowDes(true) //是否显示扫描框下面文字 + .setPlaySound(true) //是否扫描成功后bi~的声音 + .setIsOnlyCenter(true) //是否只识别框中内容(默认为全屏识别) + .setTitleBackgroudColor(R.color.mainThemeColor.convertColor(context)) //设置状态栏颜色 + .setTitleTextColor(Color.WHITE) //设置Title文字颜色 + .setScreenOrientation(QrConfig.SCREEN_PORTRAIT) //设置屏幕方式 + .setScanLineStyle(ScanLineView.style_hybrid) //扫描线样式 + .setShowVibrator(true) //是否震动提醒 + .create() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt new file mode 100644 index 0000000..1e6b6f6 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/view/AddDeviceActivity.kt @@ -0,0 +1,332 @@ +package com.casic.smarttube.view + +import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.os.Handler +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.View +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import cn.bertsir.zbar.QrManager +import com.amap.api.location.AMapLocation +import com.casic.smarttube.R +import com.casic.smarttube.extensions.combineImagePath +import com.casic.smarttube.utils.GlideLoadEngine +import com.casic.smarttube.utils.LocationHelper +import com.casic.smarttube.utils.QrConfigCreator +import com.casic.smarttube.vm.UploadImageViewModel +import com.gyf.immersionbar.ImmersionBar +import com.luck.picture.lib.basic.PictureSelector +import com.luck.picture.lib.config.SelectMimeType +import com.luck.picture.lib.entity.LocalMedia +import com.luck.picture.lib.interfaces.OnResultCallbackListener +import com.pengxh.kt.lite.activity.BigImageActivity +import com.pengxh.kt.lite.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.callback.OnImageCompressListener +import com.pengxh.kt.lite.extensions.* +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog +import kotlinx.android.synthetic.main.activity_add_device.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class AddDeviceActivity : KotlinBaseActivity() { + + private val kTag = "AddDeviceActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadImageViewModel: UploadImageViewModel + private val context: Context = this@AddDeviceActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_add_device + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + titleView.text = "添加设备" + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadImageViewModel = ViewModelProvider(this).get(UploadImageViewModel::class.java) + + val currentTimeMillis = System.currentTimeMillis() + + //设置相关数据 + deviceNameView.setText(String.format("管盯$currentTimeMillis")) + + val manager = QrManager.getInstance().init(QrConfigCreator.create(this)) + scannerView.setOnClickListener { + manager.startScan(this) { + //TODO isNumber方法无效 + if (it.content.isNumber()) { + deviceCodeView.setText(it.content) + } else { + "设备编号错误,请检查标签".show(context) + } + } + } + + ownerShipTempView.setOnClickListener { + "尽情期待~".show(this) + } + + collectIntervalView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle( + listOf( + "1min", + "2min", + "5min", + "10min", + "15min", + "30min", + "60min" + ) + ) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + + } + 1 -> { + + } + 2 -> { + + } + 3 -> { + + } + 4 -> { + + } + 5 -> { + + } + 6 -> { + + } + } + } + }).build().show() + } + + locationImageView.setOnClickListener { + showLoadingDialog(this, "定位中中,请稍后...") + LocationHelper.obtainCurrentLocation(this, + object : LocationHelper.ILocationListener { + override fun onAMapLocationGet(aMapLocation: AMapLocation?) { + dismissLoadingDialog() + if (aMapLocation == null) { + longitudeView.text = "定位失败" + latitudeView.text = "定位失败" + + longitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + latitudeView.setTextColor(R.color.redTextColor.convertColor(context)) + } else { + longitudeView.text = aMapLocation.longitude.toString() + latitudeView.text = aMapLocation.latitude.toString() + } + } + }) + } + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + + addDeviceTimeView.text = currentTimeMillis.timestampToCompleteDate() + } + + override fun initEvent() { + imageAdapter.setOnItemClickListener(object : EditableImageAdapter.OnItemClickListener { + override fun onAddImageClick() { + selectPicture() + } + + override fun onItemClick(position: Int) { + if (realPaths[position].isEmpty()) { + "图片加载失败,无法查看大图".show(context) + } else { + navigatePageTo(position, realPaths) + } + } + + override fun onItemLongClick(view: View?, position: Int) { + imagePaths.removeAt(position) + imageAdapter.deleteImage(position) + } + }) + + uploadImageViewModel.resultModel.observe(this, { + if (it.code == 200) { + val sumItemCount: Int = imageAdapter.itemCount + 1 //每上传一张图片,图片总数都是在原有的基础上+1 + if (sumItemCount <= 4) { + val url = it.data.toString() + if (url.isNotBlank()) { + imagePaths.add(url) + realPaths.add(url.combineImagePath()) + } + imageAdapter.setupImage(realPaths) + } else { + "最多只能上传3张图片".show(this) + } + } + }) + uploadImageViewModel.loadState.observe(this, { + dismissLoadingDialog() + }) + + sceneEditView.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable?) { + val text = s.toString().trim() + inputLengthView.text = String.format("${text.length}/100") + if (text.length > 100) { + inputLengthView.setTextColor(R.color.redTextColor.convertColor(context)) + "现场情况字符不能超过100个字符".show(context) + } else { + inputLengthView.setTextColor(R.color.subTextColor.convertColor(context)) + } + } + }) + } + + private fun selectPicture() { + BottomActionSheet.Builder() + .setContext(this) + .setItemTextColor(Color.BLUE) + .setActionItemTitle(listOf("拍照", "相册")) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + PictureSelector.create(context) + .openCamera(SelectMimeType.ofImage()) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + if (result == null) { + "拍照保存失败,请重试".show(context) + return + } + analyticalSelectResults(result[0]) + } + + override fun onCancel() { + + } + }) + } + 1 -> { + PictureSelector.create(context) + .openGallery(SelectMimeType.ofImage()) + .isGif(false) + .isMaxSelectEnabledMask(true) + .setFilterMinFileSize(100) + .setMaxSelectNum(3) + .isDisplayCamera(false) + .setImageEngine(GlideLoadEngine.instance) + .forResult(object : OnResultCallbackListener { + override fun onResult(result: ArrayList?) { + showLoadingDialog(context, "图片上传中,请稍后...") + if (result == null) { + "选择照片失败,请重试".show(context) + return + } + // 线程控制图片压缩上传过程,防止速度过快导致压缩失败 + val sum = (result.size * 500).toLong() + object : CountDownTimer(sum, 500) { + override fun onTick(millisUntilFinished: Long) { + val i = millisUntilFinished / 500 + val message = weakReferenceHandler.obtainMessage() + message.obj = result[i.toInt()] + message.what = 2022062501 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022062501) { + analyticalSelectResults(it.obj as LocalMedia) + } + true + } + + private fun analyticalSelectResults(result: LocalMedia) { + //压缩图片 +// val realPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { +// result.realPath +// } else { +// result.sandboxPath +// } +// Log.d(kTag, "初始路径:" + result.path) +// Log.d(kTag, "绝对路径:" + result.realPath) +// Log.d(kTag, "原图路径:" + result.originalPath) +// Log.d(kTag, "沙盒路径:" + result.sandboxPath) + result.realPath.compressImage(this, object : OnImageCompressListener { + override fun onSuccess(file: File) { + Log.d(kTag, "onSuccess: " + file.absolutePath) + //上传图片 + uploadImageViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + + private var loadingDialog: QMUITipDialog? = null + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt new file mode 100644 index 0000000..49d1775 --- /dev/null +++ b/app/src/main/java/com/casic/smarttube/vm/UploadImageViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.smarttube.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.smarttube.base.BaseApplication +import com.casic.smarttube.extensions.separateResponseCode +import com.casic.smarttube.extensions.toErrorMessage +import com.casic.smarttube.model.CommonResultModel +import com.casic.smarttube.utils.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState +import java.io.File + +class UploadImageViewModel : BaseViewModel() { + + private val gson = Gson() + val resultModel = MutableLiveData() + + fun uploadImage(image: File) = launch({ + val response = RetrofitServiceManager.uploadImage(image) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + resultModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.obtainInstance()) + } + }, { + loadState.value = LoadState.Fail + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml new file mode 100644 index 0000000..bca9c08 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_radius_3.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..3d1695b --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_right.xml b/app/src/main/res/drawable/ic_right.xml index 6020f09..d43c7b8 100644 --- a/app/src/main/res/drawable/ic_right.xml +++ b/app/src/main/res/drawable/ic_right.xml @@ -1,6 +1,6 @@ + + diff --git a/app/src/main/res/layout/activity_add_device.xml b/app/src/main/res/layout/activity_add_device.xml new file mode 100644 index 0000000..658b1f2 --- /dev/null +++ b/app/src/main/res/layout/activity_add_device.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +