天天看點

仿蒲公英搖晃手機顯示回報問題視窗

背景

項目中,需要給使用者回報提供一個入口,參考蒲公英sdk的做法,通過搖晃手機,打開回報入口。

效果圖如下:

仿蒲公英搖晃手機顯示回報問題視窗
實作方案

  1. 添加搖晃監測類
  • SensorManagerHelper
class SensorManagerHelper(private val context: Context) : SensorEventListener {

    // 速度門檻值,當搖晃速度達到這值後産生作用
    private val SPEED_SHRESHOLD = 6000
    // 兩次檢測的時間間隔
    private val UPTATE_INTERVAL_TIME = 50
    // 傳感器管理器
    private var sensorManager: SensorManager? = null
    // 傳感器
    private var sensor: Sensor? = null
    // 重力感應監聽器
    lateinit var onShakeListener: () -> Unit
    // 手機上一個位置時重力感應坐标
    private var lastX: Float = 0.toFloat()
    private var lastY: Float = 0.toFloat()
    private var lastZ: Float = 0.toFloat()
    // 上次檢測時間
    private var lastUpdateTime: Long = 0

    init {
        start()
    }

    /**
     * 開始檢測
     */
    private fun start() {
        // 獲得傳感器管理器
        sensorManager = context
                .getSystemService(Context.SENSOR_SERVICE) as SensorManager
        if (sensorManager != null) {
            // 獲得重力傳感器
            sensor = sensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        }
        // 注冊
        if (sensor != null) {
            sensorManager!!.registerListener(this, sensor,
                    SensorManager.SENSOR_DELAY_GAME)
        }
    }

    /**
     * 停止檢測
     */
    fun stop() {
        sensorManager!!.unregisterListener(this)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}

    /**
     * 重力感應器感應獲得變化資料
     * android.hardware.SensorEventListener#onSensorChanged(android.hardware
     * .SensorEvent)
     */
    override fun onSensorChanged(event: SensorEvent) {
        // 現在檢測時間
        val currentUpdateTime = System.currentTimeMillis()
        // 兩次檢測的時間間隔
        val timeInterval = currentUpdateTime - lastUpdateTime
        // 判斷是否達到了檢測時間間隔
        if (timeInterval < UPTATE_INTERVAL_TIME) return
        // 現在的時間變成last時間
        lastUpdateTime = currentUpdateTime
        // 獲得x,y,z坐标
        val x = event.values[0]
        val y = event.values[1]
        val z = event.values[2]
        // 獲得x,y,z的變化值
        val deltaX = x - lastX
        val deltaY = y - lastY
        val deltaZ = z - lastZ
        // 将現在的坐标變成last坐标
        lastX = x
        lastY = y
        lastZ = z
        val speed = Math.sqrt((deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ).toDouble()) / timeInterval * 10000
        // 達到速度閥值,發出提示
        if (speed >= SPEED_SHRESHOLD) {
            onShakeListener()
        }
    }
}
      
  • ShakeManager
object ShakeManager {
    private var isShakeListenerOn = false
    private var dialog: FeedbackDialog? = null
    private var mActivity: WeakReference<Activity>? = null

    fun initSensor(context: Context) {
        val sensorHelper = SensorManagerHelper(context)
        sensorHelper.onShakeListener = this::onShake
    }

    fun start(activity: Activity) {
        mActivity = WeakReference(activity)
        isShakeListenerOn = true
    }

    fun stop() {
        isShakeListenerOn = false
    }

    @Synchronized
    private fun onShake() {
        val activity = mActivity!!.get()
        if (isShakeListenerOn && activity != null) {
            closeDialogWhenActivityChanges()
            if (dialog == null || hasActivityChanged()) {
                dialog = FeedbackDialog(activity)
            }
            if (!dialog!!.isShowing && !activity.isFinishing) {
                dialog!!.show()
            }
        }
    }

    /**
     * 因為dialog是綁定到activity的,如果activity變化了,就關閉之前的dialog,後面搖晃手機再次生成dialog。
     */
    private fun closeDialogWhenActivityChanges() {
        if (dialog == null || dialog!!.activity == null || dialog!!.activity.isFinishing || dialog!!.activity.isDestroyed) {
            return
        }
        if (dialog != null && hasActivityChanged()) {
            dialog!!.dismiss()
        }
    }

    private fun hasActivityChanged(): Boolean {
        var result = false
        val activity = mActivity!!.get()
        if (activity != null && activity != dialog!!.activity) {
            result = true
        }
        return result
    }
}
      
  1. 在Applicationd的onCreate方法中啟動重力感應監聽
ShakeManager.initSensor(applicationContext)
      
  1. 在Activity的onResume方法中允許打開回報視窗,onPause中不允許。
public override fun onResume() {
        super.onResume()
        ShakeManager.start(this)
    }

    public override fun onPause() {
        super.onPause()
        ShakeManager.stop()
    }