背景
項目中,需要給使用者回報提供一個入口,參考蒲公英sdk的做法,通過搖晃手機,打開回報入口。
效果圖如下:
實作方案- 添加搖晃監測類
- 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
}
}
- 在Applicationd的onCreate方法中啟動重力感應監聽
ShakeManager.initSensor(applicationContext)
- 在Activity的onResume方法中允許打開回報視窗,onPause中不允許。
public override fun onResume() {
super.onResume()
ShakeManager.start(this)
}
public override fun onPause() {
super.onPause()
ShakeManager.stop()
}