Skip to content

Commit bea7f78

Browse files
committed
Omni: Update to 1.2
Signed-off-by: AkaneTan <[email protected]>
1 parent c8bd198 commit bea7f78

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+2599
-196
lines changed

.idea/misc.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ android {
2121
applicationId = "uk.akane.omni"
2222
minSdk = 26
2323
targetSdk = 34
24-
versionCode = 2
25-
versionName = "1.1"
24+
versionCode = 3
25+
versionName = "1.2"
2626

2727
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2828
}

app/src/main/java/uk/akane/omni/logic/Extensions.kt

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import android.content.res.Configuration
77
import android.graphics.Color
88
import android.hardware.SensorManager
99
import androidx.core.graphics.Insets
10-
import android.os.Looper
11-
import android.os.StrictMode
1210
import android.view.View
1311
import android.widget.TextView
1412
import androidx.activity.ComponentActivity
@@ -20,7 +18,6 @@ import androidx.core.view.WindowInsetsCompat
2018
import androidx.core.view.children
2119
import com.google.android.material.appbar.AppBarLayout
2220
import com.google.android.material.appbar.CollapsingToolbarLayout
23-
import uk.akane.omni.BuildConfig
2421

2522
@Suppress("NOTHING_TO_INLINE")
2623
inline fun Context.doIHavePermission(perm: String) =
@@ -106,21 +103,6 @@ fun SensorManager.checkSensorAvailability(sensorType: Int): Boolean {
106103
return getDefaultSensor(sensorType) != null
107104
}
108105

109-
// the whole point of this function is to do literally nothing at all (but without impacting
110-
// performance) in release builds and ignore StrictMode violations in debug builds
111-
inline fun <reified T> allowDiskAccessInStrictMode(doIt: () -> T): T {
112-
return if (BuildConfig.DEBUG) {
113-
if (Looper.getMainLooper() != Looper.myLooper()) throw IllegalStateException()
114-
val policy = StrictMode.allowThreadDiskReads()
115-
try {
116-
StrictMode.allowThreadDiskWrites()
117-
doIt()
118-
} finally {
119-
StrictMode.setThreadPolicy(policy)
120-
}
121-
} else doIt()
122-
}
123-
124106
fun View.enableEdgeToEdgePaddingListener(ime: Boolean = false, top: Boolean = false,
125107
extra: ((Insets) -> Unit)? = null) {
126108
if (fitsSystemWindows) throw IllegalArgumentException("must have fitsSystemWindows disabled")
@@ -186,4 +168,8 @@ fun View.enableEdgeToEdgePaddingListener(ime: Boolean = false, top: Boolean = fa
186168

187169
@Suppress("NOTHING_TO_INLINE")
188170
inline fun Int.dpToPx(context: Context): Int =
189-
(this.toFloat() * context.resources.displayMetrics.density).toInt()
171+
(this.toFloat() * context.resources.displayMetrics.density).toInt()
172+
173+
@Suppress("NOTHING_TO_INLINE")
174+
inline fun Float.dpToPx(context: Context): Float =
175+
(this * context.resources.displayMetrics.density)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package uk.akane.omni.logic.services
2+
3+
import android.annotation.SuppressLint
4+
import android.app.NotificationChannel
5+
import android.app.NotificationManager
6+
import android.graphics.Bitmap
7+
import android.graphics.Canvas
8+
import android.graphics.Color
9+
import android.graphics.PorterDuff
10+
import android.graphics.drawable.Drawable
11+
import android.graphics.drawable.Icon
12+
import android.hardware.Sensor
13+
import android.hardware.SensorEvent
14+
import android.hardware.SensorEventListener
15+
import android.hardware.SensorManager
16+
import android.service.quicksettings.Tile
17+
import android.service.quicksettings.TileService
18+
import android.util.Log
19+
import android.view.Surface
20+
import androidx.appcompat.content.res.AppCompatResources
21+
import androidx.core.app.NotificationCompat
22+
import androidx.core.content.ContextCompat
23+
import androidx.core.content.getSystemService
24+
import uk.akane.omni.R
25+
import kotlin.math.absoluteValue
26+
27+
class CompassTileService : TileService(), SensorEventListener {
28+
29+
private val sensorManager
30+
get() = getSystemService<SensorManager>()
31+
private val rotationVectorSensor
32+
get() = sensorManager?.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
33+
private val notificationManager
34+
get() = getSystemService<NotificationManager>()
35+
private lateinit var rotationIcon: Drawable
36+
private lateinit var iconBitmap: Bitmap
37+
38+
39+
companion object {
40+
const val CHANNEL_ID = "COMPASS_CHANNEL"
41+
const val NOTIFICATION_ID = 1
42+
}
43+
44+
override fun onCreate() {
45+
super.onCreate()
46+
notificationManager?.createNotificationChannel(
47+
NotificationChannel(
48+
CHANNEL_ID,
49+
getString(R.string.compass_tile_notification_channel),
50+
NotificationManager.IMPORTANCE_LOW
51+
)
52+
)
53+
rotationIcon = AppCompatResources.getDrawable(this, R.drawable.ic_pointer)!!
54+
iconBitmap = Bitmap.createBitmap(
55+
rotationIcon.intrinsicWidth, rotationIcon.intrinsicHeight,
56+
Bitmap.Config.ARGB_8888
57+
)
58+
}
59+
60+
override fun onClick() {
61+
super.onClick()
62+
Log.d("TAG", "onClick")
63+
qsTile.state = when (qsTile.state) {
64+
Tile.STATE_ACTIVE -> Tile.STATE_INACTIVE
65+
Tile.STATE_INACTIVE -> Tile.STATE_ACTIVE
66+
else -> Tile.STATE_INACTIVE
67+
}
68+
if (qsTile.state == Tile.STATE_INACTIVE) {
69+
qsTile.label = getString(R.string.compass)
70+
}
71+
qsTile.updateTile()
72+
}
73+
74+
override fun onStartListening() {
75+
super.onStartListening()
76+
Log.d("TAG", "START LISTENING")
77+
if (qsTile.state == Tile.STATE_ACTIVE) {
78+
startForeground(
79+
NOTIFICATION_ID,
80+
NotificationCompat.Builder(this, CHANNEL_ID).setSmallIcon(R.drawable.ic_explorer)
81+
.setContentTitle(getString(R.string.compass_notification_title))
82+
.setContentText(getString(R.string.compass_notification_label))
83+
.build()
84+
)
85+
sensorManager?.registerListener(
86+
this,
87+
rotationVectorSensor,
88+
SensorManager.SENSOR_DELAY_FASTEST
89+
)
90+
}
91+
}
92+
93+
override fun onStopListening() {
94+
super.onStopListening()
95+
if (qsTile.state == Tile.STATE_ACTIVE) {
96+
sensorManager?.unregisterListener(this)
97+
stopForeground(STOP_FOREGROUND_REMOVE)
98+
}
99+
Log.d("TAG", "STOP LISTENING")
100+
}
101+
102+
override fun onSensorChanged(event: SensorEvent) {
103+
if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR && qsTile.state == Tile.STATE_ACTIVE) {
104+
updateCompass(event)
105+
}
106+
}
107+
108+
@SuppressLint("StringFormatMatches")
109+
private fun updateCompass(event: SensorEvent) {
110+
val rotationVector = event.values.take(3).toFloatArray()
111+
val rotationMatrix = FloatArray(9)
112+
SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVector)
113+
114+
val displayRotation = ContextCompat.getDisplayOrDefault(baseContext).rotation
115+
val remappedRotationMatrix = remapRotationMatrix(rotationMatrix, displayRotation)
116+
117+
val orientationInRadians = FloatArray(3)
118+
SensorManager.getOrientation(remappedRotationMatrix, orientationInRadians)
119+
120+
val azimuthInDegrees = Math.toDegrees(orientationInRadians[0].toDouble()).toFloat()
121+
val adjustedAzimuth = (azimuthInDegrees + 360) % 360
122+
123+
Canvas(iconBitmap).apply {
124+
drawColor(Color.BLACK, PorterDuff.Mode.CLEAR) // clear all
125+
rotate(-adjustedAzimuth, width / 2f, height / 2f)
126+
rotationIcon.setBounds(0, 0, width, height)
127+
rotationIcon.draw(this)
128+
}
129+
130+
qsTile.label = getString(R.string.degree_format_tile, adjustedAzimuth.toInt().absoluteValue)
131+
qsTile.icon = Icon.createWithBitmap(iconBitmap)
132+
133+
qsTile.updateTile()
134+
}
135+
136+
private fun remapRotationMatrix(rotationMatrix: FloatArray, displayRotation: Int): FloatArray {
137+
val (newX, newY) = when (displayRotation) {
138+
Surface.ROTATION_90 -> Pair(SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X)
139+
Surface.ROTATION_180 -> Pair(SensorManager.AXIS_MINUS_X, SensorManager.AXIS_MINUS_Y)
140+
Surface.ROTATION_270 -> Pair(SensorManager.AXIS_MINUS_Y, SensorManager.AXIS_X)
141+
else -> Pair(SensorManager.AXIS_X, SensorManager.AXIS_Y)
142+
}
143+
144+
val remappedRotationMatrix = FloatArray(9)
145+
SensorManager.remapCoordinateSystem(rotationMatrix, newX, newY, remappedRotationMatrix)
146+
return remappedRotationMatrix
147+
}
148+
149+
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
150+
}

app/src/main/java/uk/akane/omni/ui/MainActivity.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package uk.akane.omni.ui
22

33
import android.os.Bundle
4+
import android.util.Log
5+
import android.view.WindowManager
46
import androidx.appcompat.app.AppCompatActivity
57
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
68
import androidx.fragment.app.Fragment
79
import androidx.fragment.app.commit
810
import uk.akane.omni.R
911
import uk.akane.omni.logic.enableEdgeToEdgeProperly
12+
import uk.akane.omni.ui.fragments.FlashlightFragment
13+
import uk.akane.omni.ui.fragments.LevelFragment
14+
import uk.akane.omni.ui.fragments.RulerFragment
1015

1116
class MainActivity : AppCompatActivity() {
1217

@@ -17,6 +22,27 @@ class MainActivity : AppCompatActivity() {
1722
super.onCreate(savedInstanceState)
1823
enableEdgeToEdgeProperly()
1924
setContentView(R.layout.activity_main)
25+
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
26+
27+
Log.d("TAG", "YES:: ${intent.extras}")
28+
if (intent.hasExtra("targetFragment")) {
29+
intent.getIntExtra("targetFragment", 0).let {
30+
when (it) {
31+
1 -> {
32+
startFragment(LevelFragment())
33+
postComplete()
34+
}
35+
2 -> {
36+
startFragment(RulerFragment())
37+
postComplete()
38+
}
39+
3 -> {
40+
startFragment(FlashlightFragment())
41+
postComplete()
42+
}
43+
}
44+
}
45+
}
2046
}
2147

2248
fun postComplete() = run { ready = true }

app/src/main/java/uk/akane/omni/ui/components/CompassView.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ class CompassView @JvmOverloads constructor(
2020

2121
init {
2222
inflate(context, R.layout.compass_layout, this)
23-
degreeIndicatorTextView = findViewById(R.id.degree_indicator)
23+
degreeIndicatorTextView = findViewById(R.id.degree_indicator)!!
2424
directionTextViews = listOf(
25-
findViewById(R.id.north), findViewById(R.id.east), findViewById(R.id.south), findViewById(R.id.west),
26-
findViewById(R.id.direction_1), findViewById(R.id.direction_2), findViewById(R.id.direction_3),
27-
findViewById(R.id.direction_4), findViewById(R.id.direction_5), findViewById(R.id.direction_6),
28-
findViewById(R.id.direction_7), findViewById(R.id.direction_8), findViewById(R.id.direction_9),
29-
findViewById(R.id.direction_10), findViewById(R.id.direction_11), findViewById(R.id.direction_12),
25+
findViewById(R.id.north)!!, findViewById(R.id.east)!!, findViewById(R.id.south)!!, findViewById(R.id.west)!!,
26+
findViewById(R.id.direction_1)!!, findViewById(R.id.direction_2)!!, findViewById(R.id.direction_3)!!,
27+
findViewById(R.id.direction_4)!!, findViewById(R.id.direction_5)!!, findViewById(R.id.direction_6)!!,
28+
findViewById(R.id.direction_7)!!, findViewById(R.id.direction_8)!!, findViewById(R.id.direction_9)!!,
29+
findViewById(R.id.direction_10)!!, findViewById(R.id.direction_11)!!, findViewById(R.id.direction_12)!!,
3030
degreeIndicatorTextView
3131
)
3232
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package uk.akane.omni.ui.components
2+
3+
import android.content.Context
4+
import android.graphics.Canvas
5+
import android.graphics.Paint
6+
import android.util.AttributeSet
7+
import android.util.TypedValue
8+
import android.view.View
9+
import uk.akane.omni.logic.dpToPx
10+
import com.google.android.material.color.MaterialColors
11+
import uk.akane.omni.R
12+
13+
class RulerView @JvmOverloads constructor(
14+
context: Context,
15+
attrs: AttributeSet? = null,
16+
defStyleAttr: Int = 0
17+
) : View(context, attrs, defStyleAttr) {
18+
19+
private val paintText = Paint().apply {
20+
color = MaterialColors.getColor(this@RulerView, com.google.android.material.R.attr.colorOutline)
21+
strokeWidth = 2f.dpToPx(context)
22+
textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20f, resources.displayMetrics)
23+
typeface = resources.getFont(R.font.hgm)
24+
isAntiAlias = true
25+
}
26+
27+
private val paintMain = Paint().apply {
28+
color = MaterialColors.getColor(this@RulerView, com.google.android.material.R.attr.colorOutline)
29+
strokeWidth = 2f.dpToPx(context)
30+
isAntiAlias = true
31+
}
32+
33+
private val paintSide = Paint().apply {
34+
color = MaterialColors.getColor(this@RulerView, com.google.android.material.R.attr.colorOutline)
35+
alpha = 127
36+
strokeWidth = 2f.dpToPx(context)
37+
isAntiAlias = true
38+
}
39+
40+
// Calculate 1mm in pixels
41+
private val mmToPx: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, resources.displayMetrics)
42+
private val topPadding: Float = 24f.dpToPx(context)
43+
44+
override fun onDraw(canvas: Canvas) {
45+
super.onDraw(canvas)
46+
47+
val width = width.toFloat()
48+
val height = height.toFloat()
49+
50+
val numDivisions = ((height - topPadding) / mmToPx).toInt()
51+
val longLineLength = width * 0.53f
52+
val midLineLength = width * 0.43f
53+
val shortLineLength = width * 0.34f
54+
55+
for (i in 0..numDivisions) {
56+
val y = topPadding + i * mmToPx
57+
when {
58+
i % 10 == 0 -> {
59+
// Draw longer lines and numbers for every 10mm (1cm)
60+
canvas.drawLine(width - longLineLength, y, width, y, paintMain)
61+
val text = (i / 10).toString()
62+
val textWidth = paintText.measureText(text)
63+
val textHeight = paintText.descent() - paintText.ascent()
64+
val textX = (width - longLineLength) / 2 - textWidth / 2
65+
val textY = y + textHeight / 3
66+
paintText.color = MaterialColors.getColor(this@RulerView,
67+
if ((i / 10) % 5 == 0)
68+
com.google.android.material.R.attr.colorOnSurface
69+
else
70+
com.google.android.material.R.attr.colorOutline
71+
)
72+
canvas.drawText(text, textX, textY, paintText)
73+
}
74+
i % 5 == 0 -> {
75+
// Draw medium lines for every 5mm (0.5cm)
76+
canvas.drawLine(width - midLineLength, y, width, y, paintSide)
77+
}
78+
else -> {
79+
// Draw shorter lines for other millimeters
80+
canvas.drawLine(width - shortLineLength, y, width, y, paintSide)
81+
}
82+
}
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)