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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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/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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_device_detail.xml b/app/src/main/res/layout/activity_device_detail.xml
index c14da7f..33fa3c9 100644
--- a/app/src/main/res/layout/activity_device_detail.xml
+++ b/app/src/main/res/layout/activity_device_detail.xml
@@ -245,6 +245,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_device_detail.xml b/app/src/main/res/layout/activity_device_detail.xml
index c14da7f..33fa3c9 100644
--- a/app/src/main/res/layout/activity_device_detail.xml
+++ b/app/src/main/res/layout/activity_device_detail.xml
@@ -245,6 +245,21 @@
+
+
+
+
+
+
+
+
+
-
@@ -38,5 +39,21 @@
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
-
+
+
+
\ No newline at end of file