Skip to content

Commit

Permalink
Omni: Finishing touches on compass feature
Browse files Browse the repository at this point in the history
Signed-off-by: AkaneTan <[email protected]>
Co-authored-by: 123Duo3 <[email protected]>
  • Loading branch information
AkaneTan and 123Duo3 committed May 28, 2024
1 parent 7f33022 commit 9d1a22a
Show file tree
Hide file tree
Showing 48 changed files with 1,962 additions and 102 deletions.
47 changes: 34 additions & 13 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Log/OS Files
*.log

# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json

# IntelliJ
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml

# Keystore files
*.jks
*.keystore

# Google Services (e.g. APIs or Firebase)
google-services.json

# Android Profiling
*.hprof

#macOS
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
8 changes: 8 additions & 0 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Omni
![GitHub](https://img.shields.io/github/license/AkaneFoundation/Omni?style=flat-square&logoColor=white&labelColor=black&color=white)
![GitHub tag (with filter)](https://img.shields.io/github/v/tag/AkaneFoundation/Omni?style=flat-square&logoColor=white&labelColor=black&color=white)
[![Static Badge](https://img.shields.io/badge/Telegram-Content?style=flat-square&logo=telegram&logoColor=black&color=white)](https://t.me/AkaneDev)

## Features
- Up-to-date material 3 design
- Lightweight, no spyware or bloat
- Compass with latitude & longitude
- Gradienter (WIP)
- Barometer (WIP)
- Coin flipper (WIP)
- Ruler (WIP)
- Strength-adjustable flashlight (WIP)

## Installation
You can download the latest stable version of the app from [GitHub releases](https://github.com/AkaneFoundation/Omni/releases/latest).

## Building
To build this app, you will need the latest beta version of [Android Studio](https://developer.android.com/studio) and fast network environment.

## License
This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](https://github.com/AkaneFoundation/Omni/blob/master/LICENSE) file for details.

## Notice
- For bug reporting: [Telegram](https://t.me/AkaneDev)
10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ android {
namespace = "uk.akane.omni"
compileSdk = 34

androidResources {
generateLocaleConfig = true
}

buildFeatures {
buildConfig = true
}

defaultConfig {
applicationId = "uk.akane.omni"
minSdk = 26
Expand Down Expand Up @@ -70,6 +78,8 @@ dependencies {
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.preference.ktx)
implementation(libs.androidx.core.splashscreen)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.sensor.accelerometer" />
<uses-feature android:name="android.hardware.sensor.compass" />
<uses-feature android:name="android.hardware.sensor.gyroscope" />
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".Omni"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/uk/akane/omni/Omni.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import com.google.android.material.color.DynamicColors
class Omni : Application() {
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
// DynamicColors.applyToActivitiesIfAvailable(this)
}
}
190 changes: 190 additions & 0 deletions app/src/main/java/uk/akane/omni/logic/Extensions.kt
Original file line number Diff line number Diff line change
@@ -1,2 +1,192 @@
package uk.akane.omni.logic

import android.animation.TimeInterpolator
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.Interpolator
import android.hardware.SensorManager
import androidx.core.graphics.Insets
import android.os.Build
import android.os.Looper
import android.os.StrictMode
import android.view.Display
import android.view.View
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.children
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
import uk.akane.omni.BuildConfig

@Suppress("NOTHING_TO_INLINE")
inline fun Context.doIHavePermission(perm: String) =
ContextCompat.checkSelfPermission(this, perm) == PackageManager.PERMISSION_GRANTED

val Context.isLocationPermissionGranted: Boolean
get() = doIHavePermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
|| doIHavePermission(android.Manifest.permission.ACCESS_FINE_LOCATION)

fun ComponentActivity.enableEdgeToEdgeProperly() {
if ((resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
Configuration.UI_MODE_NIGHT_YES
) {
enableEdgeToEdge(navigationBarStyle = SystemBarStyle.dark(Color.TRANSPARENT))
} else {
val darkScrim = Color.argb(0x80, 0x1b, 0x1b, 0x1b)
enableEdgeToEdge(navigationBarStyle = SystemBarStyle.light(Color.TRANSPARENT, darkScrim))
}
}

fun View.fadOutAnimation(
duration: Long = 300,
visibility: Int = View.INVISIBLE,
interpolator: TimeInterpolator,
completion: (() -> Unit)? = null
) {
animate()
.alpha(0f)
.setDuration(duration)
.setInterpolator(interpolator)
.withEndAction {
this.visibility = visibility
completion?.let {
it()
}
}
}

fun View.fadInAnimation(
duration: Long = 300,
completion: (() -> Unit)? = null,
interpolator: TimeInterpolator
) {
alpha = 0f
visibility = View.VISIBLE
animate()
.alpha(1f)
.setDuration(duration)
.setInterpolator(interpolator)
.withEndAction {
completion?.let {
it()
}
}
}

fun TextView.setTextAnimation(
text: CharSequence?,
duration: Long = 300,
completion: (() -> Unit)? = null,
skipAnimation: Boolean = false,
fadeInInterpolator: TimeInterpolator,
fadeOutInterpolator: TimeInterpolator
) {
if (skipAnimation) {
this.text = text
completion?.let { it() }
} else if (this.text != text) {
fadOutAnimation(duration, View.INVISIBLE, fadeOutInterpolator) {
this.text = text
fadInAnimation(duration, interpolator = fadeInInterpolator, completion = {
completion?.let {
it()
}
})
}
} else {
completion?.let { it() }
}
}

fun SensorManager.checkSensorAvailability(sensorType: Int): Boolean {
return getDefaultSensor(sensorType) != null
}

// the whole point of this function is to do literally nothing at all (but without impacting
// performance) in release builds and ignore StrictMode violations in debug builds
inline fun <reified T> allowDiskAccessInStrictMode(doIt: () -> T): T {
return if (BuildConfig.DEBUG) {
if (Looper.getMainLooper() != Looper.myLooper()) throw IllegalStateException()
val policy = StrictMode.allowThreadDiskReads()
try {
StrictMode.allowThreadDiskWrites()
doIt()
} finally {
StrictMode.setThreadPolicy(policy)
}
} else doIt()
}

fun View.enableEdgeToEdgePaddingListener(ime: Boolean = false, top: Boolean = false,
extra: ((Insets) -> Unit)? = null) {
if (fitsSystemWindows) throw IllegalArgumentException("must have fitsSystemWindows disabled")
if (this is AppBarLayout) {
if (ime) throw IllegalArgumentException("AppBarLayout must have ime flag disabled")
// AppBarLayout fitsSystemWindows does not handle left/right for a good reason, it has
// to be applied to children to look good; we rewrite fitsSystemWindows in a way mostly specific
// to Gramophone to support shortEdges displayCutout
val collapsingToolbarLayout = children.find { it is CollapsingToolbarLayout } as CollapsingToolbarLayout?
collapsingToolbarLayout?.let {
// The CollapsingToolbarLayout mustn't consume insets, we handle padding here anyway
ViewCompat.setOnApplyWindowInsetsListener(it) { _, insets -> insets }
}
val expandedTitleMarginStart = collapsingToolbarLayout?.expandedTitleMarginStart
val expandedTitleMarginEnd = collapsingToolbarLayout?.expandedTitleMarginEnd
ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->
val cutoutAndBars = insets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout()
)
(v as AppBarLayout).children.forEach {
if (it is CollapsingToolbarLayout) {
val es = expandedTitleMarginStart!! + if (it.layoutDirection
== View.LAYOUT_DIRECTION_LTR) cutoutAndBars.left else cutoutAndBars.right
if (es != it.expandedTitleMarginStart) it.expandedTitleMarginStart = es
val ee = expandedTitleMarginEnd!! + if (it.layoutDirection
== View.LAYOUT_DIRECTION_RTL) cutoutAndBars.left else cutoutAndBars.right
if (ee != it.expandedTitleMarginEnd) it.expandedTitleMarginEnd = ee
}
it.setPadding(cutoutAndBars.left, 0, cutoutAndBars.right, 0)
}
v.setPadding(0, cutoutAndBars.top, 0, 0)
val i = insets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout())
extra?.invoke(cutoutAndBars)
return@setOnApplyWindowInsetsListener WindowInsetsCompat.Builder(insets)
.setInsets(WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout(), Insets.of(cutoutAndBars.left, 0, cutoutAndBars.right, cutoutAndBars.bottom))
.setInsetsIgnoringVisibility(WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout(), Insets.of(i.left, 0, i.right, i.bottom))
.build()
}
} else {
val pl = paddingLeft
val pt = paddingTop
val pr = paddingRight
val pb = paddingBottom
ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->
val mask = WindowInsetsCompat.Type.systemBars() or
WindowInsetsCompat.Type.displayCutout() or
if (ime) WindowInsetsCompat.Type.ime() else 0
val i = insets.getInsets(mask)
v.setPadding(pl + i.left, pt + (if (top) i.top else 0), pr + i.right,
pb + i.bottom)
extra?.invoke(i)
return@setOnApplyWindowInsetsListener WindowInsetsCompat.Builder(insets)
.setInsets(mask, Insets.NONE)
.setInsetsIgnoringVisibility(mask, Insets.NONE)
.build()
}
}
}

@Suppress("NOTHING_TO_INLINE")
inline fun Int.dpToPx(context: Context): Int =
(this.toFloat() * context.resources.displayMetrics.density).toInt()
26 changes: 18 additions & 8 deletions app/src/main/java/uk/akane/omni/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
package uk.akane.omni.ui

import android.content.Context
import android.hardware.SensorManager
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import uk.akane.omni.R
import uk.akane.omni.logic.enableEdgeToEdgeProperly

class MainActivity : AppCompatActivity() {

private val sensorManager: SensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager
}
private var ready: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen().setKeepOnScreenCondition { !ready }
super.onCreate(savedInstanceState)
enableEdgeToEdge()
enableEdgeToEdgeProperly()
setContentView(R.layout.activity_main)
}

fun fetchSensorManager(): SensorManager = sensorManager
fun postComplete() = run { ready = true }

fun isInflationStarted() = ready

fun startFragment(frag: Fragment, args: (Bundle.() -> Unit)? = null) {
supportFragmentManager.commit {
addToBackStack(System.currentTimeMillis().toString())
hide(supportFragmentManager.fragments.last())
replace(R.id.container, frag.apply { args?.let { arguments = Bundle().apply(it) } })
}
}

}
Loading

0 comments on commit 9d1a22a

Please sign in to comment.