[feedback]优化弹窗实现细节

This commit is contained in:
renwj
2022-03-22 16:21:17 +08:00
parent 8072ac7d1e
commit 1aa36e2471
10 changed files with 111 additions and 90 deletions

View File

@@ -5,6 +5,7 @@ import android.transition.TransitionManager
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle.Event
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.LifecycleCoroutineScope
@@ -13,19 +14,20 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_DEVA
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.kotlin.PX
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.kotlin.lifecycleOwner
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.util.Utils
import com.zhjt.mogo_core_function_devatools.badcase.repository.net.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCasePresenter
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCaseView
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import com.zhjt.mogo_core_function_devatools.ext.pop
import com.zhjt.mogo_core_function_devatools.ext.toast
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import record_cache.RecordPanelOuterClass
import java.lang.IllegalStateException
import java.lang.ref.WeakReference
import java.util.concurrent.TimeUnit
@@ -247,13 +249,10 @@ internal object BadCaseManager : LifecycleEventObserver {
}
private fun showBadCaseFloat(onDismiss: () -> Unit, onSelect:suspend (reason: Reason) -> Unit) {
val context = viewHolder?.get()?.context ?: Utils.getApp()
BadCaseView(context).also { itx ->
val activity = viewHolder?.get()?.context as? FragmentActivity ?: throw IllegalStateException("please ensure context is FragmentActivity.")
BadCaseView(activity).also { itx ->
itx.register(record, onDismiss, onSelect)
CallerHmiManager.showFloatWindow("BadCaseFloat", floatView = itx, WindowManager.LayoutParams().also {
it.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
}
).also {
activity.pop(itx, 960.PX, WindowManager.LayoutParams.MATCH_PARENT).also {
hideFloat = it
}
}

View File

@@ -58,10 +58,10 @@ internal class BadCaseView: ConstraintLayout {
LayoutInflater.from(context).inflate(R.layout.layout_badcase_collect, this, true)
background = ColorDrawable(Color.parseColor("#F0151D41"))
isClickable = true
layoutParams = ViewGroup.LayoutParams(960.PX, 1600.PX)
close?.onClick {
onDismiss?.invoke()
}
fitsSystemWindows = true
cancel?.also {
it.background = shape(solid = Color.parseColor("#3B4577"), radius = 16)
it.onClick {

View File

@@ -99,7 +99,9 @@ internal object BadCaseStore {
@OptIn(FlowPreview::class)
suspend fun records(isDriven: Boolean): List<Reason> {
CallerLogger.d("$M_DEVA${BadCaseManager.TAG}", "-- load cases from pb -- isDriven: $isDriven")
val causes = store.data.firstOrNull()
CallerLogger.d("$M_DEVA${BadCaseManager.TAG}", "-- load cases from pb -- result: $causes")
val list = if (isDriven) causes?.drivenDataList else causes?.drivingDataList
return list?.map {
Reason().also { itx ->

View File

@@ -1,16 +1,21 @@
package com.zhjt.mogo_core_function_devatools.ext
import android.R.id
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.transition.Slide
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewTreeObserver
import android.view.*
import android.view.WindowManager.LayoutParams
import android.view.animation.OvershootInterpolator
import android.widget.PopupWindow
import android.widget.PopupWindow.INPUT_METHOD_NEEDED
import android.widget.TextView
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
@@ -20,10 +25,12 @@ import com.mogo.eagle.core.utilcode.kotlin.lifeCycleScope
import com.mogo.eagle.core.utilcode.kotlin.shape
import com.mogo.eagle.core.utilcode.reminder.Reminder
import com.mogo.eagle.core.utilcode.reminder.api.impl.PopupWindowReminder
import com.mogo.eagle.core.utilcode.util.WindowUtils
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.SECONDS
import kotlin.math.abs
/**
@@ -58,6 +65,72 @@ internal fun Context.toast(text: CharSequence, duration: Long = 2, unit: TimeUni
}
}
@SuppressLint("ClickableViewAccessibility")
internal fun Context.pop(content: View, width: Int, height: Int, fitSystemWindow: Boolean = true, onOuterViewClicked:((focus: View) -> Unit)? = null): () -> Unit {
val activity = (this as? FragmentActivity) ?: throw IllegalStateException("please use Activity to trigger pop show.")
var tempReminder: PopupWindowReminder? = null
activity.lifecycleScope.launchWhenResumed {
val pop = PopupWindow(width, height).also {
it.isOutsideTouchable = true
it.isTouchable = true
it.isFocusable = true
it.inputMethodMode = INPUT_METHOD_NEEDED
it.isClippingEnabled = false
var startX = 0f
var startY = 0f
val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop
var isClicked = false
it.setTouchInterceptor { v, event ->
val out = Rect()
v.getGlobalVisibleRect(out)
if (out.contains(event.x.toInt(), event.y.toInt())) {
return@setTouchInterceptor false
}
when(event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
startX = event.x
startY = event.y
}
MotionEvent.ACTION_MOVE -> {
val deltaX = event.x - startX
val deltaY = event.y - startY
isClicked = !(abs(deltaX) > touchSlop || abs(deltaY) > touchSlop)
}
MotionEvent.ACTION_UP -> {
if (isClicked) {
isClicked = false
onOuterViewClicked?.invoke(v)
}
}
}
return@setTouchInterceptor true
}
it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
}
if (VERSION.SDK_INT >= VERSION_CODES.M) {
val transition = Slide(Gravity.START).also {
it.interpolator = OvershootInterpolator()
it.duration = 300
}
pop.enterTransition = transition
pop.exitTransition = transition
}
pop.contentView = content
val reminder = object : PopupWindowReminder(pop) {
override fun show() {
pop.showAtLocation(activity.window.decorView, Gravity.START, 0, if (fitSystemWindow) WindowUtils.getStatusBarHeight(activity) else 0)
}
override fun isOverride(): Boolean = true
}
tempReminder = reminder
Reminder.enqueue(activity.lifeCycleOwner, reminder)
}
return {
tempReminder?.hide()
}
}
internal fun FragmentActivity.softKeyboardHeightChanged(block: ((height: Int) -> Unit)): ()-> Unit {
val decor = window.decorView
var softKeyboardDisplayed = false
@@ -115,3 +188,5 @@ private fun getNavigationBarHeight(activity: FragmentActivity): Int {
}
return result
}

View File

@@ -3,21 +3,24 @@ package com.zhjt.mogo_core_function_devatools.feedback
import android.content.Context
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import androidx.fragment.app.FragmentActivity
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.kotlin.*
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast
import com.mogo.eagle.core.utilcode.util.KeyboardUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.zhjt.mogo_core_function_devatools.R
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
import com.zhjt.mogo_core_function_devatools.badcase.repository.net.api.entity.BadCaseResponse.Reason
import com.zhjt.mogo_core_function_devatools.badcase.toRecord
import com.zhjt.mogo_core_function_devatools.ext.pop
import com.zhjt.mogo_core_function_devatools.ext.toast
import com.zhjt.mogo_core_function_devatools.feedback.biz.FeedBackView
import com.zhjt.mogo_core_function_devatools.feedback.biz.bean.Feedback.BadCase
@@ -35,9 +38,11 @@ internal object FeedbackManager {
private val presenter by lazy { FeedbackPresenter() }
fun showFeedbackWindow(ctx: Context) {
CallerHmiManager.showFloatWindow("Feedback", FeedBackView(ctx).also { itx ->
val activity = ctx as? FragmentActivity ?: throw IllegalStateException("please ensure context is FragmentActivity.")
activity.pop(FeedBackView(ctx).also { itx ->
itx.registerCallback(object : IFeedbackCallback {
override fun onClose() {
override fun onClose(v: View) {
KeyboardUtils.hideSoftInput(v)
hideFloat?.invoke()
}
override fun onBadCaseItemClicked(reason: Reason) {
@@ -139,9 +144,12 @@ internal object FeedbackManager {
}
}
})
}, WindowManager.LayoutParams().also {
it.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
}
},
width = 960.PX,
height = WindowManager.LayoutParams.MATCH_PARENT,
onOuterViewClicked = {
KeyboardUtils.hideSoftInput(it)
}
).also { hideFloat = it }
}

View File

@@ -15,7 +15,10 @@ import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.mogo.eagle.core.utilcode.kotlin.*
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_DEVA
import com.zhjt.mogo_core_function_devatools.R
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
import com.zhjt.mogo_core_function_devatools.ext.softKeyboardHeightChanged
import com.zhjt.mogo_core_function_devatools.feedback.FeedbackManager
import com.zhjt.mogo_core_function_devatools.feedback.biz.adapter.FeedbackAdapter
@@ -68,13 +71,12 @@ internal class FeedBackView : ConstraintLayout {
}
}
}
layoutParams = ViewGroup.LayoutParams(960.PX, 1600.PX)
background = ColorDrawable(Color.parseColor("#F0151D41"))
close.onClick {
cb?.onClose()
cb?.onClose(this)
}
top_mask?.background = gradient(orientation = GradientDrawable.Orientation.TOP_BOTTOM, startColor = Color.parseColor("#151D41"), endColor = Color.parseColor("#05151D41"))
background = ColorDrawable(Color.parseColor("#F0151D41"))
rv?.also {
it.fixGestureConflictForViews(listOf(R.id.et))
it.itemAnimator?.run {
@@ -116,9 +118,12 @@ internal class FeedBackView : ConstraintLayout {
private fun loadFeedbackAndRefresh() {
scope.launchWhenCreated {
showLoading()
CallerLogger.d("$M_DEVA${BadCaseManager.TAG}", "-- show loading ---")
presenter.loadFeedBacks().also {
CallerLogger.d("$M_DEVA${BadCaseManager.TAG}", "-- data load ${it.joinToString(",")} ---")
adapter.data = it
}
CallerLogger.d("$M_DEVA${BadCaseManager.TAG}", "-- hide loading ---")
hideLoading()
}
}

View File

@@ -9,7 +9,7 @@ internal interface IFeedbackCallback {
/**
* 点击关闭弹窗按钮时回调
*/
fun onClose()
fun onClose(v: View)
/**
* BadCase某一条目被点击了

View File

@@ -907,60 +907,8 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
override fun showFloatWindow(tag: String, floatView: View, attrs: LayoutParams?): () -> Unit {
var floatWindow: WarningFloat.Builder?
WarningFloat.with(context ?: Utils.getApp())
.setTag(tag)
.setLayout(floatView)
.setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.START, offsetY = 72)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onDismiss() {
floatWindow = null
}
})
.also {
floatWindow = it
if (((attrs?.softInputMode?: 0) and LayoutParams.SOFT_INPUT_MASK_ADJUST) != 0) {
it.softInputMode(attrs!!.softInputMode)
}
}
.show()
return {
floatWindow?.let {
WarningFloat.dismiss(it.config.floatTag, false)
floatWindow = null
}
}
}
override fun onDestroy() {
super.onDestroy()
CallerLogger.d("$M_HMI$TAG", "onDestroy")
}
}

View File

@@ -217,13 +217,4 @@ interface IMoGoWaringProvider : IMoGoHmiViewProxy {
* @param msg
*/
fun showDockerRebootResult(code: Int,msg: String)
/**
* @param floatView: 要展示的View
* @param tag: 唯一标识
* @return 触发消失时回调
*/
fun showFloatWindow(tag: String = "BadCaseFloat", floatView: View, attrs: LayoutParams?): () -> Unit
}

View File

@@ -297,13 +297,6 @@ object CallerHmiManager : CallerBase() {
waringProviderApi?.showDockerRebootResult(code, msg)
}
/**
* 展示浮层
*/
fun showFloatWindow(tag: String, floatView: View, attrs: LayoutParams? = null): (() -> Unit)? {
return waringProviderApi?.showFloatWindow(tag, floatView, attrs)
}
/**
* 设置 红绿灯 代理View
* @param view