diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ? + ) { + 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/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 8d3b0d7..9f200b7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -89,7 +89,7 @@ } uploadTextView.setOnClickListener { - + navigatePageTo() } applyTextView.setOnClickListener { diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 8d3b0d7..9f200b7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -89,7 +89,7 @@ } uploadTextView.setOnClickListener { - + navigatePageTo() } applyTextView.setOnClickListener { diff --git a/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt new file mode 100644 index 0000000..6ef047c --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.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 UploadFileViewModel : 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/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 8d3b0d7..9f200b7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -89,7 +89,7 @@ } uploadTextView.setOnClickListener { - + navigatePageTo() } applyTextView.setOnClickListener { diff --git a/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt new file mode 100644 index 0000000..6ef047c --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.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 UploadFileViewModel : 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/anim/activity_in.xml b/app/src/main/res/anim/activity_in.xml new file mode 100644 index 0000000..f2696ba --- /dev/null +++ b/app/src/main/res/anim/activity_in.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 8d3b0d7..9f200b7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -89,7 +89,7 @@ } uploadTextView.setOnClickListener { - + navigatePageTo() } applyTextView.setOnClickListener { diff --git a/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt new file mode 100644 index 0000000..6ef047c --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.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 UploadFileViewModel : 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/anim/activity_in.xml b/app/src/main/res/anim/activity_in.xml new file mode 100644 index 0000000..f2696ba --- /dev/null +++ b/app/src/main/res/anim/activity_in.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_out.xml b/app/src/main/res/anim/activity_out.xml new file mode 100644 index 0000000..1e424a5 --- /dev/null +++ b/app/src/main/res/anim/activity_out.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 8d3b0d7..9f200b7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -89,7 +89,7 @@ } uploadTextView.setOnClickListener { - + navigatePageTo() } applyTextView.setOnClickListener { diff --git a/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt new file mode 100644 index 0000000..6ef047c --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.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 UploadFileViewModel : 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/anim/activity_in.xml b/app/src/main/res/anim/activity_in.xml new file mode 100644 index 0000000..f2696ba --- /dev/null +++ b/app/src/main/res/anim/activity_in.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_out.xml b/app/src/main/res/anim/activity_out.xml new file mode 100644 index 0000000..1e424a5 --- /dev/null +++ b/app/src/main/res/anim/activity_out.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml new file mode 100644 index 0000000..f00d59c --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 8d3b0d7..9f200b7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -89,7 +89,7 @@ } uploadTextView.setOnClickListener { - + navigatePageTo() } applyTextView.setOnClickListener { diff --git a/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt new file mode 100644 index 0000000..6ef047c --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.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 UploadFileViewModel : 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/anim/activity_in.xml b/app/src/main/res/anim/activity_in.xml new file mode 100644 index 0000000..f2696ba --- /dev/null +++ b/app/src/main/res/anim/activity_in.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_out.xml b/app/src/main/res/anim/activity_out.xml new file mode 100644 index 0000000..1e424a5 --- /dev/null +++ b/app/src/main/res/anim/activity_out.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml new file mode 100644 index 0000000..f00d59c --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ 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..5c85867 --- /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/libs/lite-release.aar b/app/libs/lite-release.aar index f4b81e1..440b021 100644 --- a/app/libs/lite-release.aar +++ b/app/libs/lite-release.aar Binary files differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e84bb78..16d0c19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,10 @@ + + + + + + ? + ) { + 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/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index 05eee10..ce0a6a2 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -1,9 +1,17 @@ package com.casic.br.operationsite.utils +import android.Manifest + object LocaleConstant { + val USER_PERMISSIONS = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.CAMERA + ) + const val SERVER_BASE_URL = "http://192.168.43.66:12209" const val DEFAULT_SERVER_CONFIG = "defaultServerConfig" + const val PERMISSIONS_CODE = 999 const val PAGE_LIMIT = 20 } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt new file mode 100644 index 0000000..fa4401d --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/BigImageActivity.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.view + +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import androidx.viewpager.widget.ViewPager +import com.bumptech.glide.Glide +import com.casic.br.operationsite.R +import com.luck.picture.lib.photoview.PhotoView +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import kotlinx.android.synthetic.main.activity_big_image.* +import java.util.* + +class BigImageActivity : KotlinBaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_big_image + + override fun setupTopBarLayout() { + ImmerseStatusBarUtil.setColor(this, Color.BLACK) + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + + } + + override fun initEvent() { + val index: Int = intent.getIntExtra(Constant.BIG_IMAGE_INTENT_INDEX_KEY, 0) + val urls = intent.getStringArrayListExtra(Constant.BIG_IMAGE_INTENT_DATA_KEY) + if (urls == null || urls.size == 0) { + return + } + val imageSize = urls.size + pageNumberView.text = String.format("(" + (index + 1) + "/" + imageSize + ")") + imagePagerView.adapter = BigImageAdapter(this, urls) + imagePagerView.currentItem = index + imagePagerView.offscreenPageLimit = imageSize + imagePagerView.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + pageNumberView.text = String.format("(" + (position + 1) + "/" + imageSize + ")") + } + + override fun onPageScrollStateChanged(state: Int) {} + }) + } + + inner class BigImageAdapter( + private val context: Context, private val data: ArrayList + ) : PagerAdapter() { + + override fun getCount(): Int = data.size + + override fun isViewFromObject(view: View, any: Any): Boolean { + return view == any + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val view = + LayoutInflater.from(context).inflate(R.layout.item_big_picture, container, false) + val photoView: PhotoView = view.findViewById(R.id.photoView) + Glide.with(context).load(data[position]).into(photoView) + photoView.scaleType = ImageView.ScaleType.CENTER_INSIDE + container.addView(view) + //点击大图取消预览 + photoView.setOnClickListener { finish() } + return view + } + + override fun destroyItem(container: ViewGroup, position: Int, any: Any) { + container.removeView(any as View) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt new file mode 100644 index 0000000..27d881b --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/UploadEventActivity.kt @@ -0,0 +1,249 @@ +package com.casic.br.operationsite.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 com.casic.br.operationsite.R +import com.casic.br.operationsite.callback.OnImageCompressListener +import com.casic.br.operationsite.extensions.combineImagePath +import com.casic.br.operationsite.extensions.compressImage +import com.casic.br.operationsite.utils.DialogHelper +import com.casic.br.operationsite.utils.GlideLoadEngine +import com.casic.br.operationsite.vm.UploadFileViewModel +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.adapter.EditableImageAdapter +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.convertColor +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.ImmerseStatusBarUtil +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import kotlinx.android.synthetic.main.activity_upload_activity.* +import kotlinx.android.synthetic.main.include_base_title.* +import java.io.File + +class UploadEventActivity : KotlinBaseActivity() { + + private val kTag = "UploadEventActivity" + private lateinit var imageAdapter: EditableImageAdapter + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var uploadFileViewModel: UploadFileViewModel + private val context: Context = this@UploadEventActivity + private val imagePaths: ArrayList = ArrayList() //服务器返回的拍照数据集 + private val realPaths: ArrayList = ArrayList() //真实图片路径 + + override fun initLayoutView(): Int = R.layout.activity_upload_activity + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false).init() + ImmerseStatusBarUtil.setColor(this, R.color.mainThemeColor.convertColor(this)) + + leftBackView.setOnClickListener { finish() } + titleView.text = "燃气作业现场动态感知" + } + + override fun initData() { + weakReferenceHandler = WeakReferenceHandler(callback) + + uploadFileViewModel = ViewModelProvider(this).get(UploadFileViewModel::class.java) + + imageAdapter = EditableImageAdapter(this, 3) + addImageRecyclerView.layoutManager = GridLayoutManager(this, 3) + addImageRecyclerView.adapter = imageAdapter + } + + 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) + } + }) + + uploadFileViewModel.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) + } + } + }) + uploadFileViewModel.loadState.observe(this, { + DialogHelper.dismissLoadingDialog() + }) + + siteEditView.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)) + } + } + }) + + uploadEventButton.setOnClickListener { + + } + } + + 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?) { + DialogHelper.showLoadingDialog( + this@UploadEventActivity, + "图片上传中,请稍后..." + ) + 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 = 2022071301 + weakReferenceHandler.handleMessage(message) + } + + override fun onFinish() { + + } + }.start() + } + + override fun onCancel() { + + } + }) + } + } + } + }).build().show() + } + + private val callback = Handler.Callback { + if (it.what == 2022071301) { + 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) + //上传图片 + uploadFileViewModel.uploadImage(file) + } + + override fun onError(e: Throwable) { + e.printStackTrace() + } + }) + } + +// private lateinit var loadingDialog: QMUITipDialog +// +// private fun showLoadingDialog(context: Context?, message: String?) { +// loadingDialog = QMUITipDialog.Builder(context) +// .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) +// .setTipWord(message) +// .create() +// loadingDialog.show() +// } +// +// private fun dismissLoadingDialog() { +// if (loadingDialog.isShowing) { +// loadingDialog.dismiss() +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 8d3b0d7..9f200b7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -89,7 +89,7 @@ } uploadTextView.setOnClickListener { - + navigatePageTo() } applyTextView.setOnClickListener { diff --git a/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt new file mode 100644 index 0000000..6ef047c --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/UploadFileViewModel.kt @@ -0,0 +1,38 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.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 UploadFileViewModel : 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/anim/activity_in.xml b/app/src/main/res/anim/activity_in.xml new file mode 100644 index 0000000..f2696ba --- /dev/null +++ b/app/src/main/res/anim/activity_in.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/activity_out.xml b/app/src/main/res/anim/activity_out.xml new file mode 100644 index 0000000..1e424a5 --- /dev/null +++ b/app/src/main/res/anim/activity_out.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml new file mode 100644 index 0000000..f00d59c --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_white_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ 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..5c85867 --- /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/layout/activity_alarm_detail.xml b/app/src/main/res/layout/activity_alarm_detail.xml index 44144d7..9b7c5eb 100644 --- a/app/src/main/res/layout/activity_alarm_detail.xml +++ b/app/src/main/res/layout/activity_alarm_detail.xml @@ -81,7 +81,6 @@