From 8c67ec4808a8b4fec4c09f1eefdd8563e0156c8e Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sat, 3 Apr 2021 17:22:01 +0100 Subject: [PATCH] Extracted sensor listening logic from PolarFragment.kt to Orientation.kt --- .../rtbishop/look4sat/di/ApplicationModule.kt | 18 ++- .../look4sat/ui/polarScreen/Orientation.kt | 94 ++++++++++++ .../look4sat/ui/polarScreen/PolarFragment.kt | 142 +++++------------- 3 files changed, 149 insertions(+), 105 deletions(-) create mode 100644 app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/Orientation.kt diff --git a/app/src/main/java/com/rtbishop/look4sat/di/ApplicationModule.kt b/app/src/main/java/com/rtbishop/look4sat/di/ApplicationModule.kt index e09206e6..6da7115a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/di/ApplicationModule.kt +++ b/app/src/main/java/com/rtbishop/look4sat/di/ApplicationModule.kt @@ -20,7 +20,9 @@ package com.rtbishop.look4sat.di import android.content.ContentResolver import android.content.Context import android.content.SharedPreferences +import android.hardware.SensorManager import android.location.LocationManager +import android.view.WindowManager import androidx.preference.PreferenceManager import dagger.Module import dagger.Provides @@ -31,17 +33,27 @@ import dagger.hilt.components.SingletonComponent @Module @InstallIn(SingletonComponent::class) object ApplicationModule { - + @Provides fun provideContentResolver(@ApplicationContext context: Context): ContentResolver { return context.contentResolver } - + @Provides fun provideLocationManager(@ApplicationContext context: Context): LocationManager { return context.getSystemService(Context.LOCATION_SERVICE) as LocationManager } - + + @Provides + fun provideSensorManager(@ApplicationContext context: Context): SensorManager { + return context.getSystemService(Context.SENSOR_SERVICE) as SensorManager + } + + @Provides + fun provideWindowManager(@ApplicationContext context: Context): WindowManager { + return context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + } + @Provides fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences { return PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/Orientation.kt b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/Orientation.kt new file mode 100644 index 00000000..ae3005b1 --- /dev/null +++ b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/Orientation.kt @@ -0,0 +1,94 @@ +/* + * Look4Sat. Amateur radio satellite tracker and pass predictor. + * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.rtbishop.look4sat.ui.polarScreen + +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.view.Surface +import android.view.WindowManager +import javax.inject.Inject +import kotlin.math.atan2 +import kotlin.math.round + +class Orientation @Inject constructor( + private val mSensorManager: SensorManager, + private val mWindowManager: WindowManager +) : SensorEventListener { + + interface OrientationListener { + fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) + } + + private val mRotationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) + private val mRotationMatrix = FloatArray(9) + private val mOrientationValues = FloatArray(3) + private val mOneRadDegrees = 57.295779513f + private var mLastAccuracy: Int = SensorManager.SENSOR_STATUS_UNRELIABLE + private var mListener: OrientationListener? = null + + fun startListening(listener: OrientationListener) { + when { + mListener === listener -> return + else -> { + mListener = listener + mSensorManager.registerListener(this, mRotationSensor, 16000) + } + } + } + + fun stopListening() { + mSensorManager.unregisterListener(this) + mListener = null + } + + override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { + mLastAccuracy = accuracy + } + + override fun onSensorChanged(event: SensorEvent) { + when { + mListener == null -> return + mLastAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE -> return + event.sensor == mRotationSensor -> updateOrientation(event.values) + } + } + + @Suppress("DEPRECATION") + private fun updateOrientation(rotationVector: FloatArray) { + SensorManager.getRotationMatrixFromVector(mRotationMatrix, rotationVector) + val (matrixColumn, sense) = when (mWindowManager.defaultDisplay.rotation) { + Surface.ROTATION_0 -> Pair(0, 1) + Surface.ROTATION_90 -> Pair(1, -1) + Surface.ROTATION_180 -> Pair(0, -1) + Surface.ROTATION_270 -> Pair(1, 1) + else -> error("Invalid screen rotation value") + } + val x = sense * mRotationMatrix[matrixColumn] + val y = sense * mRotationMatrix[matrixColumn + 3] + val azimuth = -atan2(y, x) * mOneRadDegrees +// SensorManager.getOrientation(mRotationMatrix, mOrientationValues) +// val azimuth = mOrientationValues[0] * mOneRadDegrees + val pitch = mOrientationValues[1] * mOneRadDegrees + val roll = mOrientationValues[2] * mOneRadDegrees + val magneticAzimuth = (azimuth + 360f) % 360f + val roundedAzimuth = round(magneticAzimuth * 10) / 10 + mListener?.onOrientationChanged(roundedAzimuth, pitch, roll) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt index 8ba57f01..f8de9777 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/polarScreen/PolarFragment.kt @@ -17,13 +17,7 @@ */ package com.rtbishop.look4sat.ui.polarScreen -import android.content.Context -import android.hardware.Sensor -import android.hardware.SensorEvent -import android.hardware.SensorEventListener -import android.hardware.SensorManager import android.os.Bundle -import android.view.Surface import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -37,129 +31,73 @@ import com.rtbishop.look4sat.utility.RecyclerDivider import com.rtbishop.look4sat.utility.formatForTimer import dagger.hilt.android.AndroidEntryPoint import java.util.* -import kotlin.math.atan2 -import kotlin.math.round +import javax.inject.Inject @AndroidEntryPoint -class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener { +class PolarFragment : Fragment(R.layout.fragment_polar), Orientation.OrientationListener { + + @Inject + lateinit var orientation: Orientation - private lateinit var transmitterAdapter: TransAdapter - private lateinit var binding: FragmentPolarBinding - private lateinit var satPass: SatPass - private lateinit var sensorManager: SensorManager private val viewModel: PolarViewModel by viewModels() - private val rotationMatrix = FloatArray(9) - private val orientationValues = FloatArray(3) - private var magneticDeclination = 0f private var polarView: PolarView? = null + private var magneticDeclination = 0f override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding = FragmentPolarBinding.bind(view) - sensorManager = requireContext().getSystemService(Context.SENSOR_SERVICE) as SensorManager + val binding = FragmentPolarBinding.bind(view) + val catNum = requireArguments().getInt("catNum") + val aosTime = requireArguments().getLong("aosTime") magneticDeclination = viewModel.getMagDeclination() - observePass() + viewModel.getPass(catNum, aosTime).observe(viewLifecycleOwner) { pass -> + polarView = PolarView(requireContext()).apply { setPass(pass) } + binding.frame.addView(polarView) + observeTransmitters(pass, binding) + } } override fun onResume() { super.onResume() - if (viewModel.shouldUseCompass()) { - sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR).also { sensor -> - sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME) - } - } + if (viewModel.shouldUseCompass()) orientation.startListening(this) } override fun onPause() { super.onPause() - if (viewModel.shouldUseCompass()) { - sensorManager.unregisterListener(this) - } + if (viewModel.shouldUseCompass()) orientation.stopListening() } - override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} - - override fun onSensorChanged(event: SensorEvent?) { - event?.let { calculateAzimuth(it) } + override fun onOrientationChanged(azimuth: Float, pitch: Float, roll: Float) { + polarView?.rotation = -(azimuth + magneticDeclination) } - private fun calculateAzimuth(event: SensorEvent) { -// if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR) { -// SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values) -// SensorManager.getOrientation(rotationMatrix, orientationValues) -// val magneticAzimuth = (Math.toDegrees(orientationValues[0].toDouble()) + 360f) % 360f -// val roundedAzimuth = (round(magneticAzimuth * 10) / 10).toFloat() -// polarView?.rotation = -(roundedAzimuth + magneticDeclination) -// } - - if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR) { - SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values) - val (matrixColumn, sense) = when (val rotation = - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - requireContext().display?.rotation - } else { - @Suppress("DEPRECATION") - requireActivity().windowManager.defaultDisplay.rotation + private fun observeTransmitters(satPass: SatPass, binding: FragmentPolarBinding) { + viewModel.getSatTransmitters(satPass.catNum).observe(viewLifecycleOwner, { list -> + val transmitterAdapter = TransAdapter(satPass) + if (list.isNotEmpty()) { + transmitterAdapter.setData(list) + binding.recycler.apply { + setHasFixedSize(true) + adapter = transmitterAdapter + isVerticalScrollBarEnabled = false + layoutManager = LinearLayoutManager(context) + (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false + addItemDecoration(RecyclerDivider(R.drawable.rec_divider_dark)) + visibility = View.VISIBLE } - ) { - Surface.ROTATION_0 -> Pair(0, 1) - Surface.ROTATION_90 -> Pair(1, -1) - Surface.ROTATION_180 -> Pair(0, -1) - Surface.ROTATION_270 -> Pair(1, 1) - else -> error("Invalid screen rotation value: $rotation") + binding.noTransMsg.visibility = View.INVISIBLE + } else { + binding.recycler.visibility = View.INVISIBLE + binding.noTransMsg.visibility = View.VISIBLE } - val x = sense * rotationMatrix[matrixColumn] - val y = sense * rotationMatrix[matrixColumn + 3] - val magneticAzimuth = (Math.toDegrees(-atan2(y, x).toDouble()) + 360f) % 360f - val roundedAzimuth = (round(magneticAzimuth * 10) / 10).toFloat() - polarView?.rotation = -(roundedAzimuth + magneticDeclination) - } - } - - private fun observePass() { - val catNum = requireArguments().getInt("catNum") - val aosTime = requireArguments().getLong("aosTime") - viewModel.getPass(catNum, aosTime).observe(viewLifecycleOwner) { pass -> - satPass = pass - polarView = PolarView(requireContext()).apply { setPass(pass) } - binding.frame.addView(polarView) - observeTransmitters() - } - } - - private fun observeTransmitters() { - viewModel.getSatTransmitters(satPass.catNum) - .observe(viewLifecycleOwner, { list -> - transmitterAdapter = TransAdapter(satPass) - if (list.isNotEmpty()) { - transmitterAdapter.setData(list) - binding.recycler.apply { - setHasFixedSize(true) - adapter = transmitterAdapter - isVerticalScrollBarEnabled = false - layoutManager = LinearLayoutManager(context) - (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false - addItemDecoration(RecyclerDivider(R.drawable.rec_divider_dark)) - visibility = View.VISIBLE - } - binding.noTransMsg.visibility = View.INVISIBLE - } else { - binding.recycler.visibility = View.INVISIBLE - binding.noTransMsg.visibility = View.VISIBLE - } - observeTimer() + viewModel.getAppTimer().observe(viewLifecycleOwner, { + setPassText(it, satPass, binding) + polarView?.invalidate() + transmitterAdapter.tickTransmitters() }) - } - - private fun observeTimer() { - viewModel.getAppTimer().observe(viewLifecycleOwner, { - setPassText(it) - polarView?.invalidate() - transmitterAdapter.tickTransmitters() }) } - private fun setPassText(timeNow: Long) { + private fun setPassText(timeNow: Long, satPass: SatPass, binding: FragmentPolarBinding) { val dateNow = Date(timeNow) val satPos = satPass.predictor.getSatPos(dateNow) val polarAz = getString(R.string.pat_azimuth)