├── app ├── proguard-rules.pro ├── src │ └── main │ │ ├── res │ │ ├── raw │ │ │ └── bip.ogg │ │ ├── drawable-hdpi │ │ │ ├── ic_fine.png │ │ │ └── ic_coarse.png │ │ ├── drawable-ldpi │ │ │ ├── ic_fine.png │ │ │ └── ic_coarse.png │ │ ├── drawable-mdpi │ │ │ ├── ic_fine.png │ │ │ └── ic_coarse.png │ │ ├── drawable-xhdpi │ │ │ ├── ic_fine.png │ │ │ └── ic_coarse.png │ │ ├── drawable-xxhdpi │ │ │ ├── ic_fine.png │ │ │ └── ic_coarse.png │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable │ │ │ ├── baseline_height_black_24dp.png │ │ │ ├── ic_info.xml │ │ │ ├── ic_ruler.xml │ │ │ ├── ic_reset.xml │ │ │ ├── ic_calibrate.xml │ │ │ ├── ic_settings.xml │ │ │ ├── ic_bubble.xml │ │ │ ├── ic_coarse.xml │ │ │ ├── ic_launcher_monochrome.xml │ │ │ ├── ic_launcher_foreground.xml │ │ │ └── ic_fine.xml │ │ ├── values │ │ │ ├── ic_launcher_background.xml │ │ │ ├── attrs.xml │ │ │ ├── config.xml │ │ │ ├── themes.xml │ │ │ ├── dimens.xml │ │ │ ├── pref_arrays.xml │ │ │ ├── pref_keys.xml │ │ │ ├── colors.xml │ │ │ └── strings.xml │ │ ├── drawable-nodpi │ │ │ ├── marker_2d.xml │ │ │ ├── marker_1d.xml │ │ │ ├── bubble.xml │ │ │ ├── display.xml │ │ │ ├── level_1d.xml │ │ │ └── level_2d.xml │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ ├── layout │ │ │ ├── settings_activity.xml │ │ │ └── main.xml │ │ ├── menu │ │ │ └── main.xml │ │ ├── values-zh │ │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ │ └── strings.xml │ │ ├── xml │ │ │ └── preferences.xml │ │ ├── values-pt-rBR │ │ │ └── strings.xml │ │ ├── values-es │ │ │ └── strings.xml │ │ ├── values-nl │ │ │ └── strings.xml │ │ ├── values-pt │ │ │ └── strings.xml │ │ ├── values-cs │ │ │ └── strings.xml │ │ ├── values-nb │ │ │ └── strings.xml │ │ ├── values-it │ │ │ └── strings.xml │ │ ├── values-de │ │ │ └── strings.xml │ │ ├── values-tr │ │ │ └── strings.xml │ │ ├── values-ru │ │ │ └── strings.xml │ │ ├── values-fr │ │ │ └── strings.xml │ │ ├── values-eu │ │ │ └── strings.xml │ │ └── values-el │ │ │ └── strings.xml │ │ ├── assets │ │ └── fonts │ │ │ └── lcd.ttf │ │ ├── ic_launcher-playstore.png │ │ ├── java │ │ └── org │ │ │ └── woheller69 │ │ │ └── level │ │ │ ├── LevelApp.java │ │ │ ├── util │ │ │ ├── PrefKeys.java │ │ │ └── PreferenceHelper.java │ │ │ ├── orientation │ │ │ ├── OrientationListener.java │ │ │ ├── Orientation.java │ │ │ └── OrientationProvider.java │ │ │ ├── SettingsFragment.java │ │ │ ├── SettingsActivity.java │ │ │ ├── view │ │ │ ├── LevelView.java │ │ │ ├── VerticalSeekBar.java │ │ │ └── RulerView.java │ │ │ └── Level.java │ │ └── AndroidManifest.xml └── build.gradle ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── title.txt │ ├── changelogs │ │ ├── 18.txt │ │ ├── 30.txt │ │ ├── 19.txt │ │ ├── 20.txt │ │ ├── 15.txt │ │ ├── 23.txt │ │ ├── 26.txt │ │ ├── 27.txt │ │ ├── 24.txt │ │ ├── 32.txt │ │ ├── 13.txt │ │ ├── 14.txt │ │ ├── 17.txt │ │ ├── 31.txt │ │ ├── 25.txt │ │ ├── 29.txt │ │ ├── 16.txt │ │ ├── 11.txt │ │ ├── 21.txt │ │ ├── 22.txt │ │ ├── 28.txt │ │ ├── 12.txt │ │ └── 10.txt │ ├── short_description.txt │ ├── images │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── Bubble1.png │ │ │ ├── Bubble2.png │ │ │ └── Bubble3.png │ └── full_description.txt │ ├── ru │ ├── title.txt │ ├── short_description.txt │ └── full_description.txt │ ├── tr-TR │ ├── title.txt │ ├── short_description.txt │ └── full_description.txt │ └── de-DE │ ├── title.txt │ ├── changelogs │ ├── 18.txt │ ├── 19.txt │ ├── 20.txt │ ├── 26.txt │ ├── 27.txt │ ├── 30.txt │ ├── 15.txt │ ├── 23.txt │ ├── 24.txt │ ├── 32.txt │ ├── 13.txt │ ├── 17.txt │ ├── 31.txt │ ├── 16.txt │ ├── 29.txt │ ├── 25.txt │ ├── 14.txt │ ├── 21.txt │ ├── 11.txt │ ├── 22.txt │ ├── 28.txt │ ├── 12.txt │ └── 10.txt │ ├── short_description.txt │ └── full_description.txt ├── settings.gradle ├── ic_launcher.odg ├── .gitattributes ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .editorconfig ├── gradle.properties ├── .gitignore ├── gradlew.bat ├── ic_bubble.svg ├── ic_coarse.svg ├── README.md ├── ic_launcher_monochrome.svg ├── ic_launcher_foreground.svg ├── gradlew └── ic_fine.svg /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -dontobfuscate 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Bubble 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/ru/title.txt: -------------------------------------------------------------------------------- 1 | Пузырёк 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/tr-TR/title.txt: -------------------------------------------------------------------------------- 1 | Bubble 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/title.txt: -------------------------------------------------------------------------------- 1 | Wasserwaage 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/18.txt: -------------------------------------------------------------------------------- 1 | Bugfix 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/30.txt: -------------------------------------------------------------------------------- 1 | Bugfixes 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/18.txt: -------------------------------------------------------------------------------- 1 | Fehlerbehebung 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/19.txt: -------------------------------------------------------------------------------- 1 | GUI verbessert 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/20.txt: -------------------------------------------------------------------------------- 1 | GUI verbessert 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/26.txt: -------------------------------------------------------------------------------- 1 | GUI verbessert 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/27.txt: -------------------------------------------------------------------------------- 1 | GUI verbessert 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/30.txt: -------------------------------------------------------------------------------- 1 | Fehlerbehebung 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/19.txt: -------------------------------------------------------------------------------- 1 | Improved GUI 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/20.txt: -------------------------------------------------------------------------------- 1 | Improved GUI 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name = "Level" 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/15.txt: -------------------------------------------------------------------------------- 1 | Menü-Icons geändert 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/15.txt: -------------------------------------------------------------------------------- 1 | Changed menu icons 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/23.txt: -------------------------------------------------------------------------------- 1 | New feature: ruler 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/26.txt: -------------------------------------------------------------------------------- 1 | GUI improvements 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/27.txt: -------------------------------------------------------------------------------- 1 | GUI improvement 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/23.txt: -------------------------------------------------------------------------------- 1 | Neue Funktion: Lineal 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/24.txt: -------------------------------------------------------------------------------- 1 | Kalibrierung für Lineal 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/32.txt: -------------------------------------------------------------------------------- 1 | Show deprecation warning 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/24.txt: -------------------------------------------------------------------------------- 1 | Calibration for ruler 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/32.txt: -------------------------------------------------------------------------------- 1 | Show deprecation warning 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/tr-TR/short_description.txt: -------------------------------------------------------------------------------- 1 | Su terazisi ve cetvel -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/13.txt: -------------------------------------------------------------------------------- 1 | Leichte Glättung der Anzeigewerte -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/17.txt: -------------------------------------------------------------------------------- 1 | Upgrade auf Android 12 SDK 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/31.txt: -------------------------------------------------------------------------------- 1 | Android 15 Support (SDK 35) 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/short_description.txt: -------------------------------------------------------------------------------- 1 | Wasserwaage und Lineal 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/13.txt: -------------------------------------------------------------------------------- 1 | Slight averaging of display values -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/14.txt: -------------------------------------------------------------------------------- 1 | Added arrows for better alignment -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/17.txt: -------------------------------------------------------------------------------- 1 | Upgrade to Android 12 SDK 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/31.txt: -------------------------------------------------------------------------------- 1 | Android 15 Support (SDK 35) 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Spirit level and ruler 2 | -------------------------------------------------------------------------------- /ic_launcher.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/ic_launcher.odg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/16.txt: -------------------------------------------------------------------------------- 1 | Bugfix: Steigung falsch berechnet 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/29.txt: -------------------------------------------------------------------------------- 1 | Reset für Kalibrierung des Lineals 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/25.txt: -------------------------------------------------------------------------------- 1 | Improved calibration for ruler 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/29.txt: -------------------------------------------------------------------------------- 1 | Reset button for ruler calibration 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/ru/short_description.txt: -------------------------------------------------------------------------------- 1 | Инструмент уровня (ватерпас) и линейка -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/25.txt: -------------------------------------------------------------------------------- 1 | Verbesserte Kalibrierung für Lineal 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/14.txt: -------------------------------------------------------------------------------- 1 | Pfeile für einfachere Aussrichtung hinzugefügt -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/16.txt: -------------------------------------------------------------------------------- 1 | Bugfix: wrong calculation of inclination 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/11.txt: -------------------------------------------------------------------------------- 1 | Modified options menu 2 | Added link to GitHub 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/21.txt: -------------------------------------------------------------------------------- 1 | Norwegian translation 2 | Monochrome icon support 3 | -------------------------------------------------------------------------------- /app/src/main/res/raw/bip.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/raw/bip.ogg -------------------------------------------------------------------------------- /app/src/main/assets/fonts/lcd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/assets/fonts/lcd.ttf -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/21.txt: -------------------------------------------------------------------------------- 1 | Norwegische Übersetzung 2 | Unterstützung für monochromes Icon 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/22.txt: -------------------------------------------------------------------------------- 1 | Norwegian translation 2 | New icon 3 | Monochrome icon support 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/11.txt: -------------------------------------------------------------------------------- 1 | Optionsmenü geändert 2 | Link zu GitHub hinzugefügt 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/22.txt: -------------------------------------------------------------------------------- 1 | Norwegische Übersetzung 2 | Neues Icon 3 | Unterstützung für monochromes Icon 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/28.txt: -------------------------------------------------------------------------------- 1 | Android 14 support (SDK 34) 2 | Simplified color scheme 3 | Greek translation 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-hdpi/ic_fine.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/ic_fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-ldpi/ic_fine.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-mdpi/ic_fine.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-xhdpi/ic_fine.png -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/28.txt: -------------------------------------------------------------------------------- 1 | Android 14 Support (SDK 34) 2 | Vereinfachtes Farbschema 3 | Griechische Übersetzung 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_coarse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-hdpi/ic_coarse.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/ic_coarse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-ldpi/ic_coarse.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_coarse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-mdpi/ic_coarse.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_coarse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-xhdpi/ic_coarse.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-xxhdpi/ic_fine.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_coarse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable-xxhdpi/ic_coarse.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/12.txt: -------------------------------------------------------------------------------- 1 | Migrate to AppCompat and DayNight Theme 2 | Migrate to AndroidX 3 | Replace deprecated APIs 4 | Remove unused code -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_height_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/app/src/main/res/drawable/baseline_height_black_24dp.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/12.txt: -------------------------------------------------------------------------------- 1 | Auf AppCompat und DayNight Theme umgestellt 2 | Auf AndroidX umgestellt 3 | Veraltete APIs ersetzt 4 | Ungenutzter Code entfernt -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #53537B 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/Bubble1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/Bubble1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/Bubble2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/Bubble2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/Bubble3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woheller69/Level/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/Bubble3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/10.txt: -------------------------------------------------------------------------------- 1 | Based on the source tar ball from F-Droid. 2 | Updated to targetSDK 30. 3 | Removed some unnecessary options. 4 | Minor layout changes. 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/changelogs/10.txt: -------------------------------------------------------------------------------- 1 | Basierend auf dem Source tar-Ball von F-Droid. 2 | Aktualisiert auf targetSDK 30. 3 | Einige unnötige Optionen entfernt. 4 | Geringe Layoutänderungen. 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 500 6 | 7 | 8 | 60 9 | 10 | 11 | -------------------------------------------------------------------------------- /fastlane/metadata/android/ru/full_description.txt: -------------------------------------------------------------------------------- 1 | С помощью Пузырька вы можете измерять углы и наклон. 2 | 3 | Просто совместите одну из четырех сторон вашего телефона с объектом или положите его на ровную поверхность чтобы определить его ориентацию. 4 | 5 | Также доступна линейка в сантиметрах и дюймах. 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/marker_2d.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | With Bubble you can measure angles and inclination. 2 | 3 | Simply align one of your phone's four sides with an object or place it on a level surface to determine its orientation. 4 | 5 | A ruler in cm and inches is also available. 6 | 7 | Experience a user-friendly and ad-free solution with no permissions required. 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/tr-TR/full_description.txt: -------------------------------------------------------------------------------- 1 | Bubble ile açıları ve eğimi ölçebilirsiniz. 2 | 3 | Yönünü belirlemek için telefonunuzun dört kenarından birini bir nesneyle hizalamanız veya düz bir yüzeye yerleştirmeniz yeterlidir. 4 | 5 | Ayrıca cm ve inç cinsinden bir cetvel de mevcuttur. 6 | 7 | İzin gerektirmeyen, kullanıcı dostu ve reklamsız bir çözümü deneyimleyin. 8 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/LevelApp.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level; 2 | 3 | import android.app.Application; 4 | 5 | import org.woheller69.level.util.PreferenceHelper; 6 | 7 | public class LevelApp extends Application { 8 | 9 | @Override 10 | public void onCreate() { 11 | super.onCreate(); 12 | PreferenceHelper.initPrefs(this); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de-DE/full_description.txt: -------------------------------------------------------------------------------- 1 | Mit Bubble können Sie Winkel und Neigungen messen. 2 | 3 | Richten Sie einfach eine der vier Seiten Ihres Telefons an einem Objekt aus oder legen Sie es auf eine ebene Oberfläche, um seine Ausrichtung zu bestimmen. 4 | 5 | Ein Lineal in cm und inch ist ebenfalls verfügbar. 6 | 7 | Erleben Sie eine benutzerfreundliche und werbefreie Lösung, die keine Berechtigungen erfordert. 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/marker_1d.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/bubble.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ruler.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_reset.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_calibrate.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/settings_activity.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/display.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5dip 5 | 0dip 6 | 7 | 6dip 8 | 1dip 9 | 10dip 10 | 11 | 5dip 12 | 13 | 3dip 14 | 15 | 2dip 16 | 17 | 32dip 18 | 19 | 32dip 20 | 21 | 12dip 22 | 10dip 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/pref_arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @string/angle 6 | @string/inclination 7 | 8 | 9 | @string/prefKey_displayType_angle 10 | @string/prefKey_displayType_inclination 11 | 12 | 13 | 14 | @string/viscosity_low 15 | @string/viscosity_medium 16 | @string/viscosity_high 17 | 18 | 19 | @string/prefKey_viscosity_low 20 | @string/prefKey_viscosity_medium 21 | @string/prefKey_viscosity_high 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 17 | 18 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/util/PrefKeys.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.util; 2 | 3 | import org.woheller69.level.R; 4 | 5 | public class PrefKeys { 6 | public static final int PREF_SHOW_ANGLE = R.string.prefKey_showAngle; 7 | 8 | public static final int PREF_DISPLAY_TYPE = R.string.prefKey_displayType; 9 | public static final int PREF_DISPLAY_TYPE_ANGLE = R.string.prefKey_displayType_angle; 10 | public static final int PREF_DISPLAY_TYPE_INCLINATION = R.string.prefKey_displayType_inclination; 11 | 12 | public static final int PREF_LOCK_ORIENTATION = R.string.prefKey_lockOrientation; 13 | 14 | public static final int PREF_VISCOSITY = R.string.prefKey_viscosity; 15 | public static final int PREF_VISCOSITY_LOW = R.string.prefKey_viscosity_low; 16 | public static final int PREF_VISCOSITY_MEDIUM = R.string.prefKey_viscosity_medium; 17 | public static final int PREF_VISCOSITY_HIGH = R.string.prefKey_viscosity_high; 18 | 19 | public static final int PREF_ENABLE_SOUND = R.string.prefKey_enableSound; 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/pref_keys.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pref_showAngle 5 | 6 | pref_displayType 7 | pref_displayType_angle 8 | pref_displayType_inclination 9 | 10 | pref_lockOrientation 11 | 12 | pref_viscosity 13 | pref_viscosity_low 14 | pref_viscosity_medium 15 | pref_viscosity_high 16 | 17 | pref_enableSound 18 | 19 | 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/orientation/OrientationListener.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.orientation; 2 | 3 | /* 4 | * This file is part of Level (an Android Bubble Level). 5 | * 6 | * 7 | * Copyright (C) 2014 Antoine Vianey 8 | * 9 | * Level is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Level is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with Level. If not, see 21 | */ 22 | public interface OrientationListener { 23 | 24 | void onOrientationChanged(Orientation orientation, float pitch, float roll, float balance); 25 | 26 | void onCalibrationSaved(boolean success); 27 | 28 | void onCalibrationReset(boolean success); 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/level_1d.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/level_2d.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 16 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | app/debug/ 18 | app/release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/ 42 | 43 | # Keystore files 44 | # Uncomment the following lines if you do not want to check your keystore files in. 45 | #*.jks 46 | #*.keystore 47 | 48 | # External native build folder generated in Android Studio 2.2 and later 49 | .externalNativeBuild 50 | .cxx/ 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | # google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | 67 | # Version control 68 | vcs.xml 69 | 70 | # lint 71 | lint/intermediates/ 72 | lint/generated/ 73 | lint/outputs/ 74 | lint/tmp/ 75 | # lint/reports/ 76 | /app/release/output-metadata.json 77 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 35 7 | namespace 'org.woheller69.level' 8 | android.buildFeatures.buildConfig true 9 | defaultConfig { 10 | applicationId "org.woheller69.level" 11 | minSdk 16 12 | targetSdk 35 13 | versionCode 32 14 | versionName "3.2" 15 | vectorDrawables.useSupportLibrary true 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled true 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | compileOptions { 26 | encoding "UTF-8" 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | 31 | lintOptions { 32 | disable 'GoogleAppIndexingWarning' 33 | } 34 | 35 | packagingOptions { 36 | exclude 'META-INF/*' 37 | } 38 | } 39 | 40 | dependencies { 41 | implementation "androidx.appcompat:appcompat:1.6.1" 42 | implementation "androidx.preference:preference:1.2.1" 43 | implementation "androidx.lifecycle:lifecycle-viewmodel:2.5.1" //needed due to duplicate class error 44 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" //needed due to duplicate class error 45 | implementation 'com.github.woheller69:FreeDroidWarn:V1.3' 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FF000000 5 | #FFFFFFFF 6 | #FF888888 7 | #EE53537B 8 | #FFB1C800 9 | #00FFFFFF 10 | #44FFFFFF 11 | 12 | 13 | @color/green 14 | @color/black 15 | @color/green 16 | @color/black 17 | 18 | @color/black 19 | @color/black 20 | @color/black 21 | 22 | @color/violet 23 | 24 | 25 | @color/green 26 | @color/semitranslucent 27 | 28 | @color/green 29 | @color/semitranslucent 30 | 31 | @color/black 32 | @color/black 33 | @color/black 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.preference.ListPreference; 6 | import androidx.preference.PreferenceFragmentCompat; 7 | 8 | import org.woheller69.level.util.PrefKeys; 9 | import org.woheller69.level.util.PreferenceHelper; 10 | 11 | public class SettingsFragment extends PreferenceFragmentCompat { 12 | @Override 13 | public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { 14 | addPreferencesFromResource(R.xml.preferences); 15 | 16 | final ListPreference displayType = findPreference(getString(PrefKeys.PREF_DISPLAY_TYPE)); 17 | if (displayType != null) { 18 | displayType.setSummaryProvider(preference -> { 19 | if (PreferenceHelper.isDisplayTypeInclination()) { 20 | return getString(R.string.inclination_summary); 21 | } 22 | // default display type 23 | return getString(R.string.angle_summary); 24 | }); 25 | } 26 | 27 | final ListPreference viscosity = findPreference(getString(PrefKeys.PREF_VISCOSITY)); 28 | if (viscosity != null) { 29 | viscosity.setSummaryProvider(preference -> { 30 | if (PreferenceHelper.isViscosityLow()) { 31 | return getString(R.string.viscosity_low_summary); 32 | } 33 | if (PreferenceHelper.isViscosityHigh()) { 34 | return getString(R.string.viscosity_high_summary); 35 | } 36 | // default viscosity 37 | return getString(R.string.viscosity_medium_summary); 38 | }); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 水准仪 4 | 重置 5 | 不支持定向 6 | 校准 7 | 点击菜单按钮校准... 8 | 校准手机 9 | 将手机调整至水平位置,点击校准按钮。 10 | 11 | 或重置校准手机的所有边。 12 | 定制校准已保存! 13 | 校准失败! 14 | 初始校准已恢复! 15 | 设置 16 | 多角度显示 17 | 在界面上显示角度值 18 | 显示类型 19 | 音效 20 | 测量时播放声音 21 | 锁定 22 | 允许方位锁定 23 | 粘度 24 | 角度 25 | 点击锁定方位 26 | 倾斜度 27 | 角度 28 | "倾斜度: 29 | 100% ~ 45°" 30 | 31 | 32 | 33 | "低粘度 34 | 气泡快速移动" 35 | "中粘度 36 | 气泡常速移动" 37 | "高粘度 38 | 气泡慢速移动" 39 | Toggle mode 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 水准仪 4 | 重置 5 | 不支持定向 6 | 校准 7 | 点击菜单按钮校准... 8 | 校准手机 9 | 将手机调整至水平位置,点击校准按钮。 10 | 11 | 或重置校准手机的所有边。 12 | 定制校准已保存! 13 | 校准失败! 14 | 初始校准已恢复! 15 | 设置 16 | 多角度显示 17 | 在界面上显示角度值 18 | 显示类型 19 | 音效 20 | 测量时播放声音 21 | 锁定 22 | 允许方位锁定 23 | 粘度 24 | 角度 25 | 点击锁定方位 26 | 倾斜度 27 | 角度 28 | "倾斜度: 29 | 100% ~ 45°" 30 | 31 | 32 | 33 | "低粘度 34 | 气泡快速移动" 35 | "中粘度 36 | 气泡常速移动" 37 | "高粘度 38 | 气泡慢速移动" 39 | Toggle mode 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bubble.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 15 | 16 | 24 | 25 | 31 | 32 | 39 | 40 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.view.WindowInsetsController; 7 | 8 | import androidx.appcompat.app.ActionBar; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | public class SettingsActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.settings_activity); 17 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 18 | int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; 19 | boolean isDarkMode = (nightModeFlags == Configuration.UI_MODE_NIGHT_YES); 20 | WindowInsetsController insetsController = getWindow().getInsetsController(); 21 | if (insetsController != null) { 22 | if (isDarkMode) { 23 | // Dark mode: remove light status bar appearance (use light icons) 24 | insetsController.setSystemBarsAppearance( 25 | 0, 26 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS 27 | ); 28 | } else { 29 | // Light mode: enable light status bar appearance (dark icons) 30 | insetsController.setSystemBarsAppearance( 31 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, 32 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS 33 | ); 34 | } 35 | } 36 | } 37 | if (savedInstanceState == null) { 38 | getSupportFragmentManager() 39 | .beginTransaction() 40 | .replace(R.id.settings_container, new SettingsFragment()) 41 | .commit(); 42 | } 43 | ActionBar actionBar = getSupportActionBar(); 44 | if (actionBar != null) { 45 | actionBar.setDisplayHomeAsUpEnabled(true); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Reset 5 | Orientation not supported 6 | Calibrate 7 | press MENU to calibrate... 8 | Calibrate your phone 9 | Put any side (including the back) of your device on a flat horizontal surface and press the Calibrate button.\n\nOr reset the calibration for all the phone sides. 10 | Custom calibration saved ! 11 | Calibration failed ! 12 | Original calibration restored ! 13 | Settings 14 | Show numeric angle 15 | Display angle value(s) on screen 16 | Display type 17 | Sound effects 18 | Play sound when level 19 | Locking 20 | Allow level orientation locking 21 | Viscosity 22 | Angle 23 | tap to lock level orientation 24 | Inclination 25 | Angle in degrees 26 | Inclination in percents\n100% ~ 45° 27 | Low 28 | Medium 29 | High 30 | Low viscosity\nBubble moves faster 31 | Medium viscosity\nBubble moves normally 32 | High viscosity\nBubble moves slowly 33 | Toggle mode 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-pt-rBR/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Reiniciar 5 | Orientação não suportada 6 | Calibrar 7 | Clique em MENU para calibrar... 8 | Calibrar o celular 9 | Coloque o lado do celular que deseja calibrar em uma superfície plana horizontal e clique em Calibrar. 10 | A calibragem foi salvada! 11 | A calibragem falhou! 12 | Calibragem de origem aplicada! 13 | Opções 14 | Mostrar o valor do ângulo 15 | Mostrar os valores na tela 16 | Tipo de informação 17 | Efeitos de som 18 | Reproduzir som quando estabiliza a medição 19 | Bloqueio 20 | Bloquear o tipo de nível usado 21 | Viscosidade 22 | Ângulo 23 | Clicar para bloquear neste sensor 24 | Inclinação 25 | Ângulo em graus 26 | Inclinação em porcentagem\n100% ~ 45° 27 | Baixa 28 | Média 29 | Alta 30 | Baixa viscosidade\nA bolha move-se mais rápido 31 | Média viscosidade\nA bolha move-se normal 32 | Alta viscosidade\nA bolha move-se mais lento 33 | Mudar o modo 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nivel 4 | Inic. 5 | Orientación incompatibles 6 | Calibrar 7 | pulse MENU para calibrar... 8 | Calibrar el teléfono 9 | Pon el lado del teléfono que desea calibrar sobre una superficie plana horizontal y pulse el botón Calibrar. 10 | Calibración personalizada salvado ! 11 | Error de calibración ! 12 | Calibración original restaurada ! 13 | Preferencias 14 | Mostrar el ángulo 15 | Pantalla de inclinación 16 | Tipo de pantalla 17 | Efectos sonoros 18 | Alarma sonora 19 | Orientación 20 | Conservar la orientación del nivel 21 | Viscosidad 22 | Angulo 23 | hundir para conservar la orientación 24 | Inclinación 25 | Ángulo en grados 26 | Inclinación en porcentajes\n100% ~ 45° 27 | Baja 28 | Media 29 | Alta 30 | Baja viscosidad\nEl burbuja se mueve rápidamente 31 | Viscosidad media\nEl burbuja se mueve a velocidad normal 32 | Alta viscosidad\nEl burbuja se mueve lentamente 33 | Cambiar el modo 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-nl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Reset 5 | Oriëntatie wordt niet ondersteund 6 | Calibreren 7 | Druk op MENU voor calibratie 8 | Calibreer uw telefoon 9 | Plaats de zijkant van de telefoon die u wilt kalibreren op een vlak horizontaal oppervlak en druk op Calibreren 10 | Nieuwe calibratie opgeslagen ! 11 | Calibratie mislukt ! 12 | Oorspronkelijke calibratie hersteld ! 13 | Opties 14 | Toon Hoek 15 | Geef de waarde(s) van de hoek weer 16 | Weergave type 17 | Geluidseffecten 18 | Speel geluid af als het toestel horizontaal ligt 19 | Vergrendeling 20 | Sta oriëntatie-niveau vergrendeling toe 21 | Viscositeit 22 | Hoek 23 | tik hier om de hoek te vergrendelan 24 | Helling 25 | Hoek in graden 26 | Helling in procenten\n100% ~ 45° 27 | Laag 28 | Middelmatig 29 | Hoog 30 | Lage viscositeit\nLuchtbel beweegt snel 31 | Middelmatige viscositeit\nLuchtbel beweegt normaal 32 | Hoge viscositeit\nLuchtbel beweegt traag 33 | Toggle mode 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Reiniciar 5 | Orientatação não suportada 6 | Calibrar 7 | Clique em MENU para calibrar... 8 | Calibrar o telemóvel 9 | Coloque o lado do telefone que deseja calibrar numa superfície plana horizontal e clique em Calibrar. 10 | A calibragem foi guardada! 11 | A calibragem falhou! 12 | Aplicada a calibragem de origem! 13 | Opções 14 | Mostrar o valor do ângulo 15 | Mostrar os valores no ecrâ 16 | Tipo de informação 17 | Efeitos de som 18 | Reproduzir som quando a medição estabilizar 19 | Bloqueio 20 | Bloquear o tipo de nível a usar 21 | Viscosidade 22 | Ângulo 23 | Clicar para bloquear neste sensor 24 | Inclinação 25 | Ângulo em graus 26 | Inclinação em percentagem\n100% ~ 45° 27 | Baixa 28 | Média 29 | Alta 30 | Baixa viscosidade\nA bolha move-se mais rápidamente 31 | Média viscosidade\nA bolha move-se a uma velocidade normal 32 | Alta viscosidade\nA bolha move-se mais lentamente 33 | Mudar o modo 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-cs/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Resetovat 5 | Orientace není podporována 6 | Kalibrovat 7 | pro kalibraci ťukni na MENU... 8 | Kalibrovat telefon 9 | Položte libovolnou stranu (včetně zadní) zařízení Android na rovný vodorovný povrch a stiskněte tlačítko Kalibrovat.\n\nNebo resetujte kalibraci všech stran telefonu. 10 | Vlastní kalibrace uložena! 11 | Kalibrace selhala! 12 | Výchozí kalibrace obnovena! 13 | Nastavení 14 | Zobrazit úhel 15 | Zobrazovat hodnotu(y) úhlu na obrazovce 16 | Typ zobrazení 17 | Zvukové efekty 18 | Přehrát zvuk při vyrovnání 19 | Zamykání 20 | Povolit uzamknutí změny orientace 21 | Viskozita 22 | Úhel 23 | ťukněte pro uzamčení změny orientace 24 | Sklon 25 | Úhel ve stupních 26 | Sklon v procentech\n100% ~ 45° 27 | Nízká 28 | Střední 29 | Vysoká 30 | Nízká viskozita\nBulina se pohybuje rychleji 31 | Střední viskozita\nBulina se pohybuje středně rychle 32 | Vysoká viskozita\nBulina se pohybuje pomaleji 33 | Toggle mode 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-nb/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Tilbakestill 5 | Orientering ikke støttet 6 | Kalibrer 7 | trykk MENY for å kalibrere... 8 | Kalibrer telefonen 9 | Legg hvilken som helst side (inkludert baksiden) av enheten din på en flat horisontal overflate og trykk knappen Kalibrer.\n\nEller tilbakestill kalibreringen for alle sidene av telefonen. 10 | Kalibrering lagret! 11 | Kalibrering feilet! 12 | Opprinnelig kalibrering gjenopprettet! 13 | Innstillinger 14 | Vis numerisk vinkel 15 | Vis vinkelverdi(er) på skjermen 16 | Visningstype 17 | Lydeffekter 18 | Avspill lyd når i vater 19 | Låsing 20 | Tillat låsing av vaterorientering 21 | Viskositet 22 | Vinkel 23 | trykk for å låse vaterorientering 24 | Helling 25 | Vinkel i grader 26 | Helling i prosent\n100% ~ 45° 27 | Lav 28 | Middels 29 | Høy 30 | Lav viskositet\nLuftboble beveger seg raskere 31 | Middels viskositet\nLuftboble beveger seg normalt 32 | Høy viskositet\nLuftboble beveger seg sakte 33 | Skift modus 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Livella 4 | Reset 5 | Orientamento non supportato 6 | Calibrare 7 | Premere MENU per calibrare... 8 | Calibrare il telefono 9 | Posto il lato del telefono che si desidera calibrare su una superficie piana orizzontale e premere il pulsante di calibrazione. 10 | Calibrazione personalizzata salvata ! 11 | La calibrazione fallito ! 12 | Calibrazione originale restaurata ! 13 | Preferenze 14 | Mostra digitale 15 | Visualizzazione del valore di angolo 16 | Tipo di display 17 | Effetti sonori 18 | Riproduci suono quando il livello 19 | Orientamento 20 | Preserva il orientamento di livello 21 | Viscosità 22 | Angolo 23 | premere per bloccare il orientamento 24 | Inclinazione 25 | Angolo in gradi 26 | Inclinazione in percentuale\n100% ~ 45° 27 | Bassa 28 | Media 29 | Alta 30 | Bassa viscosità\nLa bolla si muove rapidamente 31 | Media viscosità\nLa bolla si muove a velocità normale 32 | Alta viscosità\nLa bolla si muove lentamente 33 | Cambiare la modalità 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Zurücksetzen 5 | Ausrichtung nicht unterstützt 6 | Kalibrieren 7 | Drücke MENU zum Kalibrieren... 8 | Das Gerät kalibrieren 9 | Lege die Seite des Telefons zum Kalibrieren auf eine flache horizontale Oberfläche und drücke die Schaltfläche Kalibrieren. 10 | Kalibrierung erfolgreich! 11 | Kalibrierung fehlgeschlagen! 12 | Originale Kalibrierung wiederhergestellt! 13 | Einstellungen 14 | Zeige numerischen Winkel 15 | Zeige den Wert als Zahl auf dem Bildschirm 16 | Anzeigewert 17 | Soundeffekte 18 | Spiele einen Sound, wenn in der Waage 19 | Sperren 20 | Erlaube das Sperren der Ausrichtung 21 | Viskosität 22 | Winkel 23 | Berühren um die Ausrichtung zu sperren 24 | Steigung 25 | Winkel in Grad 26 | Steigung in Prozent\n100% ~ 45° 27 | Gering 28 | Mittel 29 | Hoch 30 | Geringe Viskosität\nLuftblase bewegt sich schneller 31 | Mittlere Viskosität\nLuftblase bewegt sich normal 32 | Hohe Viskosität\nLuftblase bewegt sich langsam 33 | Modus umschalten 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Sıfırla 5 | Yönlendirme desteklenmiyor 6 | Kalibre et 7 | kalibre etmek için MENÜ\'ye basın... 8 | Telefonunuzu kalibre edin 9 | Android cihazınızın herhangi bir tarafını (arkası dahil) düz yatay bir yüzeye koyun ve Kalibre Et düğmesine basın.\n\nYa da tüm telefon tarafları için kalibrasyonu sıfırlayın. 10 | Özel kalibrasyon kaydedildi! 11 | Kalibrasyon başarısız oldu! 12 | Orijinal kalibre geri yüklendi! 13 | Ayarlar 14 | Sayısal açıyı göster 15 | Açı değer(ler)ini ekranda görüntüle 16 | Ekran tipi 17 | Ses efektleri 18 | Seviye geldiğinde ses çal 19 | Kilitleniyor 20 | Seviye yönünü kilitlemesine izin ver 21 | Viskozite 22 | Açı 23 | seviye yönünü kilitlemek için dokunun 24 | Eğim 25 | Derece cinsinden açı 26 | Yüzde cinsinden eğim\n100% ~ 45° 27 | Düşük 28 | Orta 29 | Yüksek 30 | Düşük viskozite\nKabarcık daha hızlı hareket eder 31 | Orta viskozite\nKabarcık normal hareket eder 32 | Yüksek viskozite\nKabarcık yavaş hareket eder 33 | Modu değiştir 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Пузырёк 4 | Сброс 5 | Ориентация не поддерживается 6 | Калибровка 7 | нажмите МЕНЮ для калибровки... 8 | Откалибруйте свой телефон 9 | Положите любую сторону (включая заднюю) вашего устройства на плоскую горизонтальную поверхность и нажмите кнопку Калибровать.\n\nНе сбрасывайте калибровку для всех сторон телефона. 10 | Своя калибровка сохранена! 11 | Ошибка калибровки! 12 | Первоначальная калибровка восстановлена! 13 | Настройки 14 | Показывать числовой угол 15 | Отображать значения углов на экране 16 | Тип отображения 17 | Звуковые эффекты 18 | Воспроизводить звук при достижении уровня 19 | Фиксация 20 | Разрешить фиксацию ориентации уровня 21 | Вязкость 22 | Угол 23 | Нажмите чтобы зафиксировать ориентацию уровня 24 | Наклон 25 | Угол в градусах 26 | Наклон в процентах\n100% ~ 45° 27 | Низкая 28 | Средняя 29 | Высокая 30 | Низкая вязкость\nПузырёк движется быстрее 31 | Средняя вязкость\nПузырёк движется нормально 32 | Высокая вязкость\nПузырёк движется медленно 33 | Toggle mode 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/orientation/Orientation.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.orientation; 2 | 3 | /* 4 | * This file is part of Level (an Android Bubble Level). 5 | * 6 | * 7 | * Copyright (C) 2014 Antoine Vianey 8 | * 9 | * Level is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Level is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with Level. If not, see 21 | */ 22 | public enum Orientation { 23 | 24 | LANDING(1, 0), 25 | TOP(1, 0), 26 | RIGHT(1, 90), 27 | BOTTOM(-1, 180), 28 | LEFT(-1, -90); 29 | 30 | private int reverse; 31 | private int rotation; 32 | 33 | private Orientation(int reverse, int rotation) { 34 | this.reverse = reverse; 35 | this.rotation = rotation; 36 | } 37 | 38 | public int getReverse() { 39 | return reverse; 40 | } 41 | 42 | public int getRotation() { 43 | return rotation; 44 | } 45 | 46 | public boolean isLevel(float pitch, float roll, float balance, float sensibility) { 47 | if (sensibility < 0.2f) { 48 | //minimum sensibility for playing sound (play sound even if sensor sensibility is better) 49 | sensibility = 0.2f; 50 | } 51 | switch (this) { 52 | case BOTTOM: 53 | case TOP: 54 | return balance <= sensibility 55 | && balance >= -sensibility; 56 | case LANDING: 57 | return roll <= sensibility 58 | && roll >= -sensibility 59 | && (Math.abs(pitch) <= sensibility 60 | || Math.abs(pitch) >= 180 - sensibility); 61 | case LEFT: 62 | case RIGHT: 63 | return Math.abs(pitch) <= sensibility 64 | || Math.abs(pitch) >= 180 - sensibility; 65 | } 66 | return false; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Niveau 4 | Init. 5 | Orientation non supportée 6 | Etalonner 7 | appuyez sur MENU pour étalonner... 8 | Etalonnage du téléphone 9 | Posez le côté du téléphone que vous souhaitez calibrer sur une surface horizontale et plane de référence puis appuyez sur le bouton Etalonner.\n\nOu réinitialisez l\'étalonnage pour l\'ensemble des côtés du téléphone. 10 | Etalonnage sauvegardé ! 11 | L\'étalonnage a échoué ! 12 | Etalonnage d\'usine restauré ! 13 | Préférences 14 | Affichage numérique 15 | Affiche la valeur de l\'inclinaison 16 | Type d\'affichage 17 | Effets sonores 18 | Joue un son à l\'équilibre 19 | Verrouillage 20 | Verrouille l\'orientation du niveau 21 | Viscosité 22 | Angle 23 | appuyer pour verrouiller l\'orientation 24 | Pente 25 | Angle en degrés 26 | Pente en pourcents\n100% ~ 45° 27 | Faible 28 | Moyenne 29 | Elevée 30 | Faible viscosité\nLa bulle se déplace rapidement 31 | Viscosité moyenne\nLa bulle se déplace a vitesse normal 32 | Viscosité élevée\nLa bulle se déplace lentement 33 | Changer de mode 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_coarse.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-eu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bubble 4 | Berrezarri 5 | Orientazioa ez da onartzen 6 | Kalibratu 7 | sakatu MENU kalibratzeko... 8 | Kalibratu zure telefonoa 9 | Jarri zure Android gailuaren edozein alde (atzekoa barne) gainazal horizontal lau batean eta sakatu Kalibratu botoia.\n\nEdo berrezarri kalibrazioa telefonoaren alde guztientzat. 10 | Kalibrazio pertsonalizatua gordeta! 11 | Kalibrazioak huts egind du! 12 | Jatorrizko kalibrazioa leheneratuta! 13 | Ezarpenak 14 | Erakutsi zenbakizko angelua 15 | Bistaratu angeluaren balioa(k) pantailan 16 | Bistaratze mota 17 | Soinu-efektuak 18 | Erreproduzitu soinua nibelean dagoenean 19 | Blokeatzea 20 | Baimendu nibelaren orientazioa blokeatzea 21 | Biskositatea 22 | Angelua 23 | ukitu nibelaren orientazioa blokeatzeko 24 | Inklinazioa 25 | Angelua gradutan 26 | Inklinazioa ehunekotan\n100% ~ 45° 27 | Baxua 28 | Ertaina 29 | Altua 30 | Biskositate baxua\nBurbuila azkarrago mugitzen da 31 | Biskositate ertaina\nBurbuila normal mugitzen da 32 | Biskositate altua\nBurbuila mantso mugitzen da 33 | Toggle mode 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-el/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Φυσαλίδα 4 | Επαναφορά 5 | Ο προσανατολισμός δεν υποστηρίζεται 6 | Βαθμονόμηση 7 | Πατήστε ΜΕΝΟΥ για βαθμονόμηση... 8 | Βαθμονομήστε το τηλέφωνο 9 | Τοποθετήστε οποιαδήποτε πλευρά (συμπεριλαμβανομένης της πίσω) της συσκευής σας σε μια επίπεδη οριζόντια επιφάνεια και πατήστε το κουμπί Βαθμονόμηση.\n\nΉ επαναφέρετε τη βαθμονόμηση για όλες τις πλευρές του τηλεφώνου. 10 | Η προσαρμοσμένη βαθμονόμηση αποθηκεύτηκε! 11 | Η βαθμονόμηση απέτυχε! 12 | Επανήλθε η αρχική βαθμονόμηση 13 | Ρυθμίσεις 14 | Εμφάνιση αριθμητικής γωνίας 15 | Εμφάνιση τιμών γωνίας στην οθόνη 16 | Μορφή εμφάνισης 17 | Ηχητικά εφέ 18 | Αναπαραγωγή ήχου κατά την οριζοντίωση 19 | Κλείδωμα 20 | Να επιτρέπεται το κλείδωμα του αλφαδιού 21 | Ιξώδες 22 | Γωνία 23 | Πατήστε για να κλειδώσετε τον προσανατολισμό του αλφαδιού 24 | Κλίση 25 | Γωνία σε μοίρες 26 | Κλίση σε ποσοστό\n100% ~ 45° 27 | Χαμηλό 28 | Μεσαίο 29 | Υψηλό 30 | Χαμηλό ιξώδες\nΗ φυσαλίδα κινείται γρήγορα 31 | Μεσαίο ιξώδες\nΗ φυσαλίδα κινείται κανονικά 32 | Υψηλό ιξώδες\nΗ φυσαλίδα κινείται αργά 33 | Toggle mode 34 | 35 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_monochrome.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/view/LevelView.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.view; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Looper; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.SurfaceHolder; 9 | import android.view.SurfaceView; 10 | import android.view.View; 11 | import android.view.View.OnTouchListener; 12 | 13 | import org.woheller69.level.orientation.Orientation; 14 | import org.woheller69.level.painter.LevelPainter; 15 | 16 | /* 17 | * This file is part of Level (an Android Bubble Level). 18 | * 19 | * 20 | * Copyright (C) 2014 Antoine Vianey 21 | * 22 | * Level is free software: you can redistribute it and/or modify 23 | * it under the terms of the GNU General Public License as published by 24 | * the Free Software Foundation, either version 3 of the License, or 25 | * (at your option) any later version. 26 | * 27 | * Level is distributed in the hope that it will be useful, 28 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 29 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 | * GNU General Public License for more details. 31 | * 32 | * You should have received a copy of the GNU General Public License 33 | * along with Level. If not, see 34 | */ 35 | public class LevelView extends SurfaceView implements SurfaceHolder.Callback, OnTouchListener { 36 | 37 | private LevelPainter painter; 38 | 39 | public LevelView(Context context, AttributeSet attrs) { 40 | super(context, attrs); 41 | getHolder().addCallback(this); 42 | setFocusable(true); 43 | setOnTouchListener(this); 44 | } 45 | 46 | @Override 47 | public void onWindowFocusChanged(boolean hasWindowFocus) { 48 | if (painter != null) { 49 | painter.pause(!hasWindowFocus); 50 | } 51 | } 52 | 53 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 54 | if (painter != null) { 55 | painter.setSurfaceSize(width, height); 56 | } 57 | } 58 | 59 | public void surfaceCreated(SurfaceHolder holder) { 60 | if (painter == null) { 61 | painter = new LevelPainter(holder, getContext(), new Handler(Looper.getMainLooper())); 62 | } 63 | } 64 | 65 | public void surfaceDestroyed(SurfaceHolder holder) { 66 | if (painter != null) { 67 | painter.pause(true); 68 | painter.clean(); 69 | painter = null; 70 | } 71 | // free resources 72 | System.gc(); 73 | } 74 | 75 | public void onOrientationChanged(Orientation orientation, float pitch, float roll, float balance) { 76 | if (painter != null) { 77 | painter.onOrientationChanged(orientation, pitch, roll, balance); 78 | } 79 | } 80 | 81 | @Override 82 | public boolean onTouch(View v, MotionEvent event) { 83 | if (event.getAction() == MotionEvent.ACTION_DOWN && painter != null) { 84 | painter.onTouch((int) event.getX(), (int) event.getY()); 85 | } 86 | return true; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/view/VerticalSeekBar.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.view; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | 9 | import androidx.appcompat.widget.AppCompatSeekBar; 10 | 11 | 12 | public class VerticalSeekBar extends AppCompatSeekBar { 13 | private static final int ROTATION_ANGLE = -90; 14 | 15 | private OnSeekBarChangeListener mOnSeekBarChangeListener; 16 | 17 | public VerticalSeekBar(final Context context) { 18 | super(context); 19 | } 20 | 21 | public VerticalSeekBar(final Context context, final AttributeSet attrs, final int defStyle) { 22 | super(context, attrs, defStyle); 23 | } 24 | 25 | public VerticalSeekBar(final Context context, final AttributeSet attrs) { 26 | super(context, attrs); 27 | } 28 | 29 | @Override 30 | protected final void onSizeChanged(final int width, final int height, final int oldWidth, final int oldHeight) { 31 | super.onSizeChanged(height, width, oldHeight, oldWidth); 32 | } 33 | 34 | @Override 35 | protected final synchronized void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 36 | super.onMeasure(heightMeasureSpec, widthMeasureSpec); 37 | setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); 38 | } 39 | 40 | @Override 41 | protected final void onDraw(final Canvas c) { 42 | c.rotate(ROTATION_ANGLE); 43 | c.translate(-getHeight(), 0); 44 | 45 | super.onDraw(c); 46 | } 47 | 48 | @Override 49 | public final void setOnSeekBarChangeListener(final OnSeekBarChangeListener l) { 50 | mOnSeekBarChangeListener = l; 51 | super.setOnSeekBarChangeListener(l); 52 | } 53 | 54 | @SuppressLint("ClickableViewAccessibility") 55 | @Override 56 | public final boolean onTouchEvent(final MotionEvent event) { 57 | if (!isEnabled()) { 58 | return false; 59 | } 60 | 61 | switch (event.getAction()) { 62 | case MotionEvent.ACTION_DOWN: 63 | setProgress(getMax() - (int) (getMax() * event.getY() / getHeight())); 64 | mOnSeekBarChangeListener.onStartTrackingTouch(this); 65 | break; 66 | 67 | case MotionEvent.ACTION_MOVE: 68 | setProgress(getMax() - (int) (getMax() * event.getY() / getHeight())); 69 | break; 70 | 71 | case MotionEvent.ACTION_UP: 72 | setProgress(getMax() - (int) (getMax() * event.getY() / getHeight())); 73 | mOnSeekBarChangeListener.onStopTrackingTouch(this); 74 | break; 75 | 76 | case MotionEvent.ACTION_CANCEL: 77 | mOnSeekBarChangeListener.onStopTrackingTouch(this); 78 | break; 79 | 80 | default: 81 | break; 82 | } 83 | 84 | return true; 85 | } 86 | 87 | @Override 88 | public final void setProgress(final int progress) { 89 | super.setProgress(progress); 90 | onSizeChanged(getWidth(), getHeight(), 0, 0); 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 27 | 34 | 41 | 48 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/util/PreferenceHelper.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.util; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.content.res.Resources; 6 | 7 | import androidx.annotation.StringRes; 8 | import androidx.preference.PreferenceManager; 9 | 10 | public class PreferenceHelper { 11 | private static SharedPreferences sharedPrefs; 12 | private static Resources mRes; 13 | 14 | public static void initPrefs(final Context context) { 15 | sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); 16 | mRes = context.getResources(); 17 | } 18 | 19 | private static String getPrefKey(@StringRes final int prefKey) { 20 | return mRes.getString(prefKey); 21 | } 22 | 23 | private static boolean getHelperBoolean(@StringRes final int prefKey, final boolean defValue) { 24 | return sharedPrefs.getBoolean(getPrefKey(prefKey), defValue); 25 | } 26 | 27 | private static String getHelperString(@StringRes final int prefKey, final int defValue) { 28 | return sharedPrefs.getString(getPrefKey(prefKey), getPrefKey(defValue)); 29 | } 30 | 31 | /** 32 | * Compares the preference's current value to the called prefKey. 33 | * 34 | * @param currentKeyValue the current key string value of a prefKey 35 | * @param prefKey a passed prefKey 36 | * @return true if the prefKey's current value is equal to the passed prefKey, false otherwise 37 | */ 38 | private static boolean getEquals(final String currentKeyValue, @StringRes final int prefKey) { 39 | return currentKeyValue.equals(getPrefKey(prefKey)); 40 | } 41 | 42 | public static boolean getShowAngle() { 43 | return getHelperBoolean(PrefKeys.PREF_SHOW_ANGLE, true); 44 | } 45 | 46 | public static String getDisplayType() { 47 | return getHelperString(PrefKeys.PREF_DISPLAY_TYPE, PrefKeys.PREF_DISPLAY_TYPE_ANGLE); 48 | } 49 | 50 | public static boolean isDisplayTypeInclination() { 51 | return getEquals(getDisplayType(), PrefKeys.PREF_DISPLAY_TYPE_INCLINATION); 52 | } 53 | 54 | public static String getDisplayTypeFormat() { 55 | // format of default display type (angle) 56 | String format = "00.0\u2009°"; 57 | if (isDisplayTypeInclination()) { 58 | format = "000.0\u2009'%'"; 59 | } 60 | return format; 61 | } 62 | 63 | public static String getDisplayTypeBackgroundText() { 64 | // background text of default display type (angle) 65 | String backgroundText = "88.8\u2009°"; 66 | if (isDisplayTypeInclination()) { 67 | backgroundText = "888.8\u2009%"; 68 | } 69 | return backgroundText; 70 | } 71 | 72 | public static float getDisplayTypeMax() { 73 | // max of default display type (angle) 74 | float max = 99.9f; 75 | if (isDisplayTypeInclination()) { 76 | max = 999.9f; 77 | } 78 | return max; 79 | } 80 | 81 | public static boolean getOrientationLocked() { 82 | return getHelperBoolean(PrefKeys.PREF_LOCK_ORIENTATION, false); 83 | } 84 | 85 | private static String getViscosity() { 86 | return getHelperString(PrefKeys.PREF_VISCOSITY, PrefKeys.PREF_VISCOSITY_MEDIUM); 87 | } 88 | 89 | public static boolean isViscosityLow() { 90 | return getEquals(getViscosity(), PrefKeys.PREF_VISCOSITY_LOW); 91 | } 92 | 93 | public static boolean isViscosityHigh() { 94 | return getEquals(getViscosity(), PrefKeys.PREF_VISCOSITY_HIGH); 95 | } 96 | 97 | public static double getViscosityCoefficient() { 98 | // coefficient of default viscosity (medium) 99 | double coeff = 0.4d; 100 | if (isViscosityLow()) { 101 | coeff = 0.6d; 102 | } else if (isViscosityHigh()) { 103 | coeff = 0.2d; 104 | } 105 | return coeff; 106 | } 107 | 108 | public static boolean getSoundEnabled() { 109 | return getHelperBoolean(PrefKeys.PREF_ENABLE_SOUND, false); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/view/RulerView.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.view; 2 | 3 | // taken from https://github.com/SecUSo/privacy-friendly-ruler, published under GPL3.0 license 4 | 5 | import android.content.Context; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | import android.graphics.Path; 9 | import android.view.View; 10 | 11 | import androidx.core.content.ContextCompat; 12 | import org.woheller69.level.R; 13 | 14 | public class RulerView extends View { 15 | 16 | double dpmm; 17 | double heightPx; 18 | double heightmm; 19 | double heightFracInch; 20 | double widthPx; 21 | double dpfi; 22 | float lineWidth; 23 | int textSize ; 24 | int db; 25 | 26 | public RulerView(Context context, double ydpmm, double ydpi) { 27 | super(context); 28 | 29 | dpmm = ydpmm; 30 | dpfi = ydpi; 31 | 32 | db = ContextCompat.getColor(context, R.color.black); 33 | textSize = (int)(dpmm *2.5); 34 | lineWidth = (float)(dpmm*0.15); 35 | } 36 | 37 | @Override 38 | protected void onDraw(Canvas canvas) { 39 | super.onDraw(canvas); 40 | 41 | heightPx = this.getHeight(); 42 | widthPx = this.getWidth(); 43 | heightmm = heightPx/dpmm; 44 | heightFracInch = heightPx/dpfi; 45 | 46 | Paint paint = new Paint(); 47 | paint.setAntiAlias(true); 48 | paint.setColor(db); 49 | paint.setAlpha(255); 50 | paint.setTextSize(textSize); 51 | paint.setStrokeWidth(lineWidth); 52 | 53 | drawLeftCm(canvas, paint); 54 | drawRightIn(canvas, paint); 55 | 56 | } 57 | 58 | private void drawLeftCm(Canvas canvas, Paint paint){ 59 | for (int i = 0; i < heightmm; i++){ 60 | if (i%10 == 0) { 61 | //draw 8mm line every cm 62 | canvas.drawLine(0, (float)dpmm*i, (float)dpmm*8, (float)dpmm*i, paint); 63 | //draw a number every cm 64 | canvas.drawText(""+i/10, (float)dpmm*8+(textSize/5f), (float)(dpmm*i+textSize), paint); 65 | } else if (i%5 == 0) { 66 | //draw 5mm line every 5mm 67 | canvas.drawLine(0, (float)dpmm*i, (float)dpmm*5, (float)dpmm*i, paint); 68 | } else { 69 | //draw 3mm line every mm 70 | canvas.drawLine(0, (float)dpmm*i, (float)dpmm*3, (float)dpmm*i, paint); 71 | } 72 | } 73 | } 74 | 75 | private void drawRightIn(Canvas canvas, Paint paint){ 76 | Path path = new Path(); 77 | 78 | for (int i = 0; i < (heightFracInch); i++){ 79 | if (i%32 == 0) { 80 | //draw 8mm line every inch 81 | canvas.drawLine((float)(widthPx-dpmm*(8)), (float)(heightPx- dpfi *(i)), 82 | (float)(widthPx), (float)(heightPx-dpfi*(i)), paint); 83 | //draw a number every inch 84 | path.reset(); 85 | path.moveTo((float)(widthPx-dpmm*(8)-textSize/5), (float)(heightPx- dpfi *(i)-textSize*0.25)); 86 | path.lineTo((float)(widthPx-dpmm*(8)-textSize/5), (float)(heightPx- dpfi *(i)-textSize)); 87 | canvas.drawTextOnPath(""+i/32, path, 0, 0, paint); 88 | } else if (i%16 == 0) { 89 | //draw 6mm line every 1/2 inch 90 | canvas.drawLine((float)(widthPx-dpmm*(6)), (float)(heightPx- dpfi *(i)), 91 | (float)(widthPx), (float)(heightPx- dpfi *(i)), paint); 92 | } else if (i%8 == 0) { 93 | //draw 4mm line every 1/4 inch 94 | canvas.drawLine((float)(widthPx-dpmm*(4)), (float)(heightPx- dpfi *(i)), 95 | (float)(widthPx), (float)(heightPx- dpfi *(i)), paint); 96 | } else if (i%4 == 0) { 97 | //draw 3mm line every 1/8 inch 98 | canvas.drawLine((float)(widthPx-dpmm*(3)), (float)(heightPx- dpfi *(i)), 99 | (float)(widthPx), (float)(heightPx- dpfi *(i)), paint); 100 | } else if (i%2 == 0) { 101 | //draw 2mm line every 1/16 inch 102 | canvas.drawLine((float)(widthPx-dpmm*(2)), (float)(heightPx- dpfi *(i)), 103 | (float)(widthPx), (float)(heightPx- dpfi *(i)), paint); 104 | } else { 105 | //draw 1.5mm line every 1/32 inch 106 | canvas.drawLine((float)(widthPx-dpmm*(1.5)), (float)(heightPx- dpfi *(i)), 107 | (float)(widthPx), (float)(heightPx- dpfi *(i)), paint); 108 | } 109 | } 110 | } 111 | 112 | public void setCalib(double ydpmm, double ydpi) { 113 | dpmm=ydpmm; 114 | dpfi=ydpi; 115 | invalidate(); 116 | requestLayout(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /ic_bubble.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fine.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /ic_coarse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | Google has announced that, starting in 2026/2027, all apps on certified Android devices 3 | will require the developer to submit personal identity details directly to Google. 4 | Since the developers of this app do not agree to this requirement, this app will no longer 5 | work on certified Android devices after that time. 6 | ``` 7 | 8 |
Send a coffee to
 9 | woheller69@t-online.de
10 | 
11 | 
12 | 
13 | Or via this link (with fees)
14 | 
15 | 16 | 17 | | **RadarWeather** | **Gas Prices** | **Smart Eggtimer** | 18 | |:---:|:---:|:---:| 19 | | [](https://f-droid.org/packages/org.woheller69.weather/) | [](https://f-droid.org/packages/org.woheller69.spritpreise/) | [](https://f-droid.org/packages/org.woheller69.eggtimer/) | 20 | | **Bubble** | **hEARtest** | **GPS Cockpit** | 21 | | [](https://f-droid.org/packages/org.woheller69.level/) | [](https://f-droid.org/packages/org.woheller69.audiometry/) | [](https://f-droid.org/packages/org.woheller69.gpscockpit/) | 22 | | **Audio Analyzer** | **LavSeeker** | **TimeLapseCam** | 23 | | [](https://f-droid.org/packages/org.woheller69.audio_analyzer_for_android/) |[](https://f-droid.org/packages/org.woheller69.lavatories/) | [](https://f-droid.org/packages/org.woheller69.TimeLapseCam/) | 24 | | **Arity** | **Cirrus** | **solXpect** | 25 | | [](https://f-droid.org/packages/org.woheller69.arity/) | [](https://f-droid.org/packages/org.woheller69.omweather/) | [](https://f-droid.org/packages/org.woheller69.solxpect/) | 26 | | **gptAssist** | **dumpSeeker** | **huggingAssist** | 27 | | [](https://f-droid.org/packages/org.woheller69.gptassist/) | [](https://f-droid.org/packages/org.woheller69.dumpseeker/) | [](https://f-droid.org/packages/org.woheller69.hugassist/) | 28 | | **FREE Browser** | **whoBIRD** | **PeakOrama** | 29 | | [](https://f-droid.org/packages/org.woheller69.browser/) | [](https://f-droid.org/packages/org.woheller69.whobird/) | [](https://f-droid.org/packages/org.woheller69.PeakOrama/) | 30 | | **Whisper** | **Seamless** | | 31 | | [](https://f-droid.org/packages/org.woheller69.whisper/) | [](https://f-droid.org/packages/org.woheller69.seemless/) | | 32 | 33 | # Bubble: Spirit level and ruler 34 | 35 | 36 | 37 | With Bubble you can measure angles and inclination. 38 | 39 | Simply align one of your phone's four sides with an object or place it on a level surface to determine its orientation. 40 | 41 | A ruler in cm and inches is also available. 42 | 43 | Experience a user-friendly and ad-free solution with no permissions required. 44 | 45 | [Get it on F-Droid](https://f-droid.org/packages/org.woheller69.level/) 48 | 49 | This is an updated fork of https://f-droid.org/packages/net.androgames.level/ which has not been revised since 2014. 50 | 51 | ## License 52 | 53 | RulerView taken from https://github.com/SecUSo/privacy-friendly-ruler, published under GPL3.0 license 54 | 55 | ``` 56 | An Android Bubble Level application. 57 | 58 | Copyright (C) 2014 Antoine Vianey 59 | Copyright (C) 2021- woheller69 60 | 61 | This program is free software: you can redistribute it and/or modify 62 | it under the terms of the GNU General Public License as published by 63 | the Free Software Foundation, either version 3 of the License, or 64 | (at your option) any later version. 65 | 66 | Level is distributed in the hope that it will be useful, 67 | but WITHOUT ANY WARRANTY; without even the implied warranty of 68 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 69 | GNU General Public License for more details. 70 | 71 | You should have received a copy of the GNU General Public License 72 | along with Level. If not, see 73 | ``` 74 | -------------------------------------------------------------------------------- /ic_launcher_monochrome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ic_launcher_foreground.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /ic_fine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/orientation/OrientationProvider.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level.orientation; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.content.SharedPreferences.Editor; 6 | import android.hardware.Sensor; 7 | import android.hardware.SensorEvent; 8 | import android.hardware.SensorEventListener; 9 | import android.hardware.SensorManager; 10 | import android.view.Surface; 11 | 12 | import androidx.appcompat.app.AppCompatActivity; 13 | 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | import java.util.List; 17 | 18 | /* 19 | * This file is part of Level (an Android Bubble Level). 20 | * 21 | * 22 | * Copyright (C) 2014 Antoine Vianey 23 | * 24 | * Level is free software: you can redistribute it and/or modify 25 | * it under the terms of the GNU General Public License as published by 26 | * the Free Software Foundation, either version 3 of the License, or 27 | * (at your option) any later version. 28 | * 29 | * Level is distributed in the hope that it will be useful, 30 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | * GNU General Public License for more details. 33 | * 34 | * You should have received a copy of the GNU General Public License 35 | * along with Level. If not, see 36 | */ 37 | public class OrientationProvider implements SensorEventListener { 38 | 39 | private static final int MIN_VALUES = 20; 40 | 41 | /** 42 | * Calibration 43 | */ 44 | private static final String SAVED_PITCH = "pitch."; 45 | private static final String SAVED_ROLL = "roll."; 46 | private static final String SAVED_BALANCE = "balance."; 47 | private static OrientationProvider provider; 48 | /** 49 | * Calibration 50 | */ 51 | private final float[] calibratedPitch = new float[5]; 52 | private final float[] calibratedRoll = new float[5]; 53 | private final float[] calibratedBalance = new float[5]; 54 | /** 55 | * Rotation Matrix 56 | */ 57 | private final float[] MAG = new float[]{1f, 1f, 1f}; 58 | private final float[] I = new float[16]; 59 | private final float[] R = new float[16]; 60 | private final float[] outR = new float[16]; 61 | private final float[] LOC = new float[3]; 62 | /** 63 | * Orientation 64 | */ 65 | private float pitch; 66 | private float roll; 67 | private final int displayOrientation; 68 | private Sensor sensor; 69 | private SensorManager sensorManager; 70 | private OrientationListener listener; 71 | /** 72 | * indicates whether or not Accelerometer Sensor is supported 73 | */ 74 | private Boolean supported; 75 | /** 76 | * indicates whether or not Accelerometer Sensor is running 77 | */ 78 | private boolean running = false; 79 | private boolean calibrating = false; 80 | private float balance; 81 | private float tmp; 82 | private float oldPitch; 83 | private float oldRoll; 84 | private float oldBalance; 85 | private float minStep = 360; 86 | private float refValues = 0; 87 | private Orientation orientation; 88 | private boolean locked; 89 | private final AppCompatActivity activity; 90 | 91 | public OrientationProvider(Context context) { 92 | activity = (AppCompatActivity) context; 93 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { 94 | this.displayOrientation = activity.getDisplay().getRotation(); 95 | } else { 96 | this.displayOrientation = activity.getWindowManager().getDefaultDisplay().getRotation(); 97 | } 98 | } 99 | 100 | public static OrientationProvider getInstance(Context context) { 101 | if (provider == null) { 102 | provider = new OrientationProvider(context); 103 | } 104 | return provider; 105 | } 106 | 107 | /** 108 | * Returns true if the manager is listening to orientation changes 109 | */ 110 | public boolean isListening() { 111 | return running; 112 | } 113 | 114 | /** 115 | * Unregisters listeners 116 | */ 117 | public void stopListening() { 118 | running = false; 119 | try { 120 | if (sensorManager != null) { 121 | sensorManager.unregisterListener(this); 122 | } 123 | } catch (Exception e) { 124 | } 125 | } 126 | 127 | private List getRequiredSensors() { 128 | return Collections.singletonList( 129 | Sensor.TYPE_ACCELEROMETER 130 | ); 131 | } 132 | 133 | /** 134 | * Returns true if at least one Accelerometer sensor is available 135 | */ 136 | public boolean isSupported() { 137 | if (supported == null) { 138 | if (activity != null) { 139 | sensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE); 140 | boolean supported = true; 141 | for (int sensorType : getRequiredSensors()) { 142 | List sensors = sensorManager.getSensorList(sensorType); 143 | supported = (sensors.size() > 0) && supported; 144 | } 145 | this.supported = supported; 146 | return supported; 147 | } 148 | } 149 | return supported; 150 | } 151 | 152 | 153 | /** 154 | * Registers a listener and start listening 155 | * callback for accelerometer events 156 | */ 157 | public void startListening(OrientationListener orientationListener) { 158 | // load calibration 159 | calibrating = false; 160 | Arrays.fill(calibratedPitch, 0); 161 | Arrays.fill(calibratedRoll, 0); 162 | Arrays.fill(calibratedBalance, 0); 163 | SharedPreferences prefs = activity.getPreferences(Context.MODE_PRIVATE); 164 | for (Orientation orientation : Orientation.values()) { 165 | calibratedPitch[orientation.ordinal()] = 166 | prefs.getFloat(SAVED_PITCH + orientation.toString(), 0); 167 | calibratedRoll[orientation.ordinal()] = 168 | prefs.getFloat(SAVED_ROLL + orientation.toString(), 0); 169 | calibratedBalance[orientation.ordinal()] = 170 | prefs.getFloat(SAVED_BALANCE + orientation.toString(), 0); 171 | } 172 | // register listener and start listening 173 | sensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE); 174 | running = true; 175 | for (int sensorType : getRequiredSensors()) { 176 | List sensors = sensorManager.getSensorList(sensorType); 177 | if (sensors.size() > 0) { 178 | sensor = sensors.get(0); 179 | running = sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL) && running; 180 | } 181 | } 182 | if (running) { 183 | listener = orientationListener; 184 | } 185 | } 186 | 187 | public void onAccuracyChanged(Sensor sensor, int accuracy) { 188 | } 189 | 190 | public void onSensorChanged(SensorEvent event) { 191 | 192 | oldPitch = pitch; 193 | oldRoll = roll; 194 | oldBalance = balance; 195 | 196 | SensorManager.getRotationMatrix(R, I, event.values, MAG); 197 | 198 | // compute pitch, roll & balance 199 | switch (displayOrientation) { 200 | case Surface.ROTATION_270: 201 | SensorManager.remapCoordinateSystem( 202 | R, 203 | SensorManager.AXIS_MINUS_Y, 204 | SensorManager.AXIS_X, 205 | outR); 206 | break; 207 | case Surface.ROTATION_180: 208 | SensorManager.remapCoordinateSystem( 209 | R, 210 | SensorManager.AXIS_MINUS_X, 211 | SensorManager.AXIS_MINUS_Y, 212 | outR); 213 | break; 214 | case Surface.ROTATION_90: 215 | SensorManager.remapCoordinateSystem( 216 | R, 217 | SensorManager.AXIS_Y, 218 | SensorManager.AXIS_MINUS_X, 219 | outR); 220 | break; 221 | case Surface.ROTATION_0: 222 | default: 223 | SensorManager.remapCoordinateSystem( 224 | R, 225 | SensorManager.AXIS_X, 226 | SensorManager.AXIS_Y, 227 | outR); 228 | break; 229 | } 230 | 231 | SensorManager.getOrientation(outR, LOC); 232 | 233 | // normalize z on ux, uy 234 | tmp = (float) Math.sqrt(outR[8] * outR[8] + outR[9] * outR[9]); 235 | tmp = (tmp == 0 ? 0 : outR[8] / tmp); 236 | 237 | // LOC[0] compass 238 | pitch = (float) Math.toDegrees(LOC[1]); 239 | roll = -(float) Math.toDegrees(LOC[2]); 240 | balance = (float) Math.toDegrees(Math.asin(tmp)); 241 | 242 | // calculating minimal sensor step 243 | if (oldRoll != roll || oldPitch != pitch || oldBalance != balance) { 244 | if (oldPitch != pitch) { 245 | minStep = Math.min(minStep, Math.abs(pitch - oldPitch)); 246 | } 247 | if (oldRoll != roll) { 248 | minStep = Math.min(minStep, Math.abs(roll - oldRoll)); 249 | } 250 | if (oldBalance != balance) { 251 | minStep = Math.min(minStep, Math.abs(balance - oldBalance)); 252 | } 253 | if (refValues < MIN_VALUES) { 254 | refValues++; 255 | } 256 | } 257 | 258 | if (!locked || orientation == null) { 259 | if (pitch < -45 && pitch > -135) { 260 | // top side up 261 | orientation = Orientation.TOP; 262 | } else if (pitch > 45 && pitch < 135) { 263 | // bottom side up 264 | orientation = Orientation.BOTTOM; 265 | } else if (roll > 45) { 266 | // right side up 267 | orientation = Orientation.RIGHT; 268 | } else if (roll < -45) { 269 | // left side up 270 | orientation = Orientation.LEFT; 271 | } else { 272 | // landing 273 | orientation = Orientation.LANDING; 274 | } 275 | } 276 | 277 | if (calibrating) { 278 | calibrating = false; 279 | Editor editor = activity.getPreferences(Context.MODE_PRIVATE).edit(); 280 | editor.putFloat(SAVED_PITCH + orientation.toString(), pitch); 281 | editor.putFloat(SAVED_ROLL + orientation.toString(), roll); 282 | editor.putFloat(SAVED_BALANCE + orientation.toString(), balance); 283 | final boolean success = editor.commit(); 284 | if (success) { 285 | calibratedPitch[orientation.ordinal()] = pitch; 286 | calibratedRoll[orientation.ordinal()] = roll; 287 | calibratedBalance[orientation.ordinal()] = balance; 288 | } 289 | listener.onCalibrationSaved(success); 290 | pitch = 0; 291 | roll = 0; 292 | balance = 0; 293 | } else { 294 | pitch -= calibratedPitch[orientation.ordinal()]; 295 | roll -= calibratedRoll[orientation.ordinal()]; 296 | balance -= calibratedBalance[orientation.ordinal()]; 297 | } 298 | 299 | // propagation of the orientation 300 | listener.onOrientationChanged(orientation, pitch, roll, balance); 301 | } 302 | 303 | /** 304 | * Tell the provider to restore the calibration 305 | * to the default factory values 306 | */ 307 | public final void resetCalibration() { 308 | boolean success = false; 309 | try { 310 | success = activity.getPreferences( 311 | Context.MODE_PRIVATE).edit().clear().commit(); 312 | } catch (Exception e) { 313 | } 314 | if (success) { 315 | Arrays.fill(calibratedPitch, 0); 316 | Arrays.fill(calibratedRoll, 0); 317 | Arrays.fill(calibratedBalance, 0); 318 | } 319 | if (listener != null) { 320 | listener.onCalibrationReset(success); 321 | } 322 | } 323 | 324 | 325 | /** 326 | * Tell the provider to save the calibration 327 | * The calibration is actually saved on the next 328 | * sensor change event 329 | */ 330 | public final void saveCalibration() { 331 | calibrating = true; 332 | } 333 | 334 | public void setLocked(boolean locked) { 335 | this.locked = locked; 336 | } 337 | 338 | /** 339 | * Return the minimal sensor step 340 | * 341 | * @return the minimal sensor step 342 | * 0 if not yet known 343 | */ 344 | public float getSensibility() { 345 | if (refValues >= MIN_VALUES) { 346 | return minStep; 347 | } else { 348 | return 0; 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /app/src/main/java/org/woheller69/level/Level.java: -------------------------------------------------------------------------------- 1 | package org.woheller69.level; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.content.res.Configuration; 8 | import android.content.res.TypedArray; 9 | import android.graphics.PorterDuff; 10 | import android.media.AudioAttributes; 11 | import android.media.AudioManager; 12 | import android.media.SoundPool; 13 | import android.net.Uri; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.util.TypedValue; 17 | import android.view.Menu; 18 | import android.view.MenuItem; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.view.WindowInsetsController; 22 | import android.view.WindowManager; 23 | import android.widget.RelativeLayout; 24 | import android.widget.SeekBar; 25 | import android.widget.Toast; 26 | 27 | import androidx.appcompat.app.AlertDialog; 28 | import androidx.appcompat.app.AppCompatActivity; 29 | import androidx.appcompat.view.menu.MenuBuilder; 30 | import androidx.appcompat.widget.AppCompatImageButton; 31 | import androidx.core.content.ContextCompat; 32 | import androidx.preference.PreferenceManager; 33 | 34 | import org.woheller69.freeDroidWarn.FreeDroidWarn; 35 | import org.woheller69.level.orientation.Orientation; 36 | import org.woheller69.level.orientation.OrientationListener; 37 | import org.woheller69.level.orientation.OrientationProvider; 38 | import org.woheller69.level.util.PreferenceHelper; 39 | import org.woheller69.level.view.LevelView; 40 | import org.woheller69.level.view.RulerView; 41 | import org.woheller69.level.view.VerticalSeekBar; 42 | 43 | /* 44 | * This file is part of Level (an Android Bubble Level). 45 | * 46 | * 47 | * Copyright (C) 2014 Antoine Vianey 48 | * 49 | * Level is free software: you can redistribute it and/or modify 50 | * it under the terms of the GNU General Public License as published by 51 | * the Free Software Foundation, either version 3 of the License, or 52 | * (at your option) any later version. 53 | * 54 | * Level is distributed in the hope that it will be useful, 55 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 56 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 57 | * GNU General Public License for more details. 58 | * 59 | * You should have received a copy of the GNU General Public License 60 | * along with Level. If not, see 61 | */ 62 | public class Level extends AppCompatActivity implements OrientationListener { 63 | 64 | private static OrientationProvider provider; 65 | 66 | private LevelView levelView; 67 | private RulerView rulerView; 68 | private VerticalSeekBar rulerCalView; 69 | private VerticalSeekBar rulerCoarseCalView; 70 | private AppCompatImageButton rulerResetButtonView; 71 | 72 | /** 73 | * Gestion du son 74 | */ 75 | private SoundPool soundPool; 76 | private boolean soundEnabled; 77 | private int bipSoundID; 78 | private int bipRate; 79 | private long lastBip; 80 | private Context context; 81 | 82 | public static OrientationProvider getProvider() { 83 | return provider; 84 | } 85 | 86 | @Override 87 | protected void onCreate(Bundle savedInstanceState) { 88 | super.onCreate(savedInstanceState); 89 | context = this; 90 | setContentView(R.layout.main); 91 | if (getSupportActionBar() != null) { 92 | getSupportActionBar().setDisplayShowHomeEnabled(true); 93 | } 94 | 95 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 96 | int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; 97 | boolean isDarkMode = (nightModeFlags == Configuration.UI_MODE_NIGHT_YES); 98 | WindowInsetsController insetsController = getWindow().getInsetsController(); 99 | if (insetsController != null) { 100 | if (isDarkMode) { 101 | // Dark mode: remove light status bar appearance (use light icons) 102 | insetsController.setSystemBarsAppearance( 103 | 0, 104 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS 105 | ); 106 | } else { 107 | // Light mode: enable light status bar appearance (dark icons) 108 | insetsController.setSystemBarsAppearance( 109 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, 110 | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS 111 | ); 112 | } 113 | } 114 | } 115 | 116 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 117 | levelView = findViewById(R.id.main_levelView); 118 | // sound 119 | 120 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 121 | soundPool = new SoundPool.Builder() 122 | .setMaxStreams(1) 123 | .setAudioAttributes( 124 | new AudioAttributes.Builder() 125 | .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) 126 | .build()) 127 | .build(); 128 | } else { 129 | soundPool = new SoundPool(1, AudioManager.STREAM_RING, 0); 130 | } 131 | 132 | bipSoundID = soundPool.load(this, R.raw.bip, 1); 133 | bipRate = getResources().getInteger(R.integer.bip_rate); 134 | FreeDroidWarn.showWarningOnUpgrade(this, BuildConfig.VERSION_CODE); 135 | } 136 | 137 | @SuppressLint("RestrictedApi") 138 | @Override 139 | public boolean onCreateOptionsMenu(Menu menu) { 140 | getMenuInflater().inflate(R.menu.main, menu); 141 | menu.findItem(R.id.menu_ruler).setChecked(rulerView!=null); 142 | menu.findItem(R.id.menu_ruler).setIcon(rulerView==null ? R.drawable.ic_ruler : R.drawable.ic_bubble); 143 | menu.findItem(R.id.menu_settings).setVisible(rulerView==null); 144 | if (menu instanceof MenuBuilder) { 145 | ((MenuBuilder) menu).setOptionalIconsVisible(true); 146 | } 147 | return true; 148 | } 149 | 150 | /* Handles item selections */ 151 | public boolean onOptionsItemSelected(MenuItem item) { 152 | if (item.getItemId() == R.id.menu_calibrate) { 153 | if (rulerView==null){ 154 | new AlertDialog.Builder(this) 155 | .setTitle(R.string.calibrate_title) 156 | .setIcon(null) 157 | .setCancelable(true) 158 | .setPositiveButton(R.string.calibrate, (dialog, id) -> provider.saveCalibration()) 159 | .setNegativeButton(android.R.string.cancel, null) 160 | .setNeutralButton(R.string.reset, (dialog, id) -> provider.resetCalibration()) 161 | .setMessage(R.string.calibrate_message) 162 | .show(); 163 | } else { 164 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); 165 | int progress = sp.getInt("pref_rulercal",100); 166 | int coarseprogress = sp.getInt("pref_rulercoarsecal",2000); 167 | RelativeLayout rulerLayout = (RelativeLayout) findViewById(R.id.main_layout); 168 | if (rulerCalView == null){ 169 | Toast.makeText(this,getString(R.string.calibrate)+" \u25b2\u25bc", Toast.LENGTH_LONG).show(); 170 | rulerCalView = new VerticalSeekBar(this); 171 | rulerCalView.setMax(200); 172 | rulerCalView.setProgress(progress); 173 | rulerCalView.setThumb(ContextCompat.getDrawable(context, R.drawable.ic_fine)); 174 | rulerCalView.getThumb().setColorFilter(getThemeColor(context,R.attr.colorAccent), PorterDuff.Mode.MULTIPLY); 175 | RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); 176 | layoutParams.setMargins(rulerLayout.getWidth()*3/8,0,0,0); 177 | rulerCalView.setLayoutParams(layoutParams); 178 | rulerCoarseCalView = new VerticalSeekBar(this); 179 | rulerCoarseCalView.setMax(6000); 180 | rulerCoarseCalView.setProgress(coarseprogress); 181 | rulerCoarseCalView.setThumb(ContextCompat.getDrawable(context, R.drawable.ic_coarse)); 182 | rulerCoarseCalView.getThumb().setColorFilter(getThemeColor(context,R.attr.colorAccent), PorterDuff.Mode.MULTIPLY); 183 | RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); 184 | layoutParams2.setMargins(0,0,rulerLayout.getWidth()*3/8,0); 185 | layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); 186 | rulerCoarseCalView.setLayoutParams(layoutParams2); 187 | 188 | rulerResetButtonView = new AppCompatImageButton(this); 189 | RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 190 | layoutParams3.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE); 191 | layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); 192 | rulerResetButtonView.setLayoutParams(layoutParams3); 193 | rulerResetButtonView.setImageResource(R.drawable.ic_reset); 194 | rulerLayout.addView(rulerResetButtonView); 195 | rulerLayout.addView(rulerCalView); 196 | rulerLayout.addView(rulerCoarseCalView); 197 | 198 | rulerCalView.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 199 | @Override 200 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 201 | rulerView.setCalib(getDpmmCal(progress,rulerCoarseCalView.getProgress()), getDpmmCal(progress,rulerCoarseCalView.getProgress())*25.4/32); 202 | SharedPreferences.Editor editor = sp.edit(); 203 | editor.putInt("pref_rulercal",progress); 204 | editor.apply(); 205 | } 206 | @Override 207 | public void onStartTrackingTouch(SeekBar seekBar) {} 208 | @Override 209 | public void onStopTrackingTouch(SeekBar seekBar) {} 210 | }); 211 | rulerCoarseCalView.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 212 | @Override 213 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 214 | rulerView.setCalib(getDpmmCal(rulerCalView.getProgress(),progress), getDpmmCal(rulerCalView.getProgress(),progress)*25.4/32); 215 | SharedPreferences.Editor editor = sp.edit(); 216 | editor.putInt("pref_rulercoarsecal",progress); 217 | editor.apply(); 218 | } 219 | @Override 220 | public void onStartTrackingTouch(SeekBar seekBar) {} 221 | @Override 222 | public void onStopTrackingTouch(SeekBar seekBar) {} 223 | }); 224 | rulerResetButtonView.setOnClickListener(view -> { 225 | rulerCalView.setProgress(100); 226 | rulerCoarseCalView.setProgress(2000); 227 | }); 228 | } else { 229 | rulerLayout.removeView(rulerCalView); 230 | rulerLayout.removeView(rulerCoarseCalView); 231 | rulerLayout.removeView(rulerResetButtonView); 232 | rulerCalView = null; 233 | rulerCoarseCalView = null; 234 | rulerResetButtonView = null; 235 | } 236 | } 237 | 238 | return true; 239 | } else if (item.getItemId() == R.id.menu_settings) { 240 | startActivity(new Intent(this, SettingsActivity.class)); 241 | return true; 242 | } else if (item.getItemId() == R.id.menu_about) { 243 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/woheller69/level"))); 244 | recreate(); //fix strange action bar position when coming from ruler 245 | return true; 246 | } else if (item.getItemId() == R.id.menu_ruler) { 247 | showRuler(!item.isChecked()); 248 | } 249 | return false; 250 | } 251 | 252 | private void showRuler(boolean ruler) { 253 | if (ruler){ 254 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); 255 | RelativeLayout rulerLayout = (RelativeLayout) findViewById(R.id.main_layout); 256 | int progress = sp.getInt("pref_rulercal",100); 257 | int coarseProgress = sp.getInt("pref_rulercoarsecal",2000); 258 | float dpmm = getDpmmCal(progress,coarseProgress); 259 | 260 | rulerView = new RulerView(this, dpmm, dpmm*25.4/32); 261 | rulerView.setBackgroundColor(ContextCompat.getColor(this,R.color.silver)); 262 | rulerLayout.addView(rulerView); 263 | levelView.setVisibility(View.INVISIBLE); 264 | invalidateOptionsMenu(); 265 | } else { 266 | levelView.setVisibility(View.VISIBLE); 267 | RelativeLayout rulerLayout = (RelativeLayout) findViewById(R.id.main_layout); 268 | if (rulerView!=null) rulerLayout.removeView(rulerView); 269 | rulerView = null; 270 | if (rulerCalView !=null) rulerLayout.removeView(rulerCalView); 271 | rulerCalView = null; 272 | if (rulerCoarseCalView !=null) rulerLayout.removeView(rulerCoarseCalView); 273 | rulerCoarseCalView = null; 274 | if (rulerResetButtonView !=null) rulerLayout.removeView(rulerResetButtonView); 275 | rulerResetButtonView = null; 276 | getWindow().getDecorView().setSystemUiVisibility(0); 277 | invalidateOptionsMenu(); 278 | } 279 | 280 | } 281 | 282 | @Override 283 | protected void onResume() { 284 | super.onResume(); 285 | provider = OrientationProvider.getInstance(this); 286 | // chargement des effets sonores 287 | soundEnabled = PreferenceHelper.getSoundEnabled(); 288 | // orientation manager 289 | if (provider.isSupported()) { 290 | provider.startListening(this); 291 | } else { 292 | Toast.makeText(this, getText(R.string.not_supported), Toast.LENGTH_LONG).show(); 293 | } 294 | } 295 | 296 | @Override 297 | protected void onPause() { 298 | super.onPause(); 299 | showRuler(false); 300 | if (provider.isListening()) { 301 | provider.stopListening(); 302 | } 303 | } 304 | 305 | @Override 306 | public void onDestroy() { 307 | if (soundPool != null) { 308 | soundPool.release(); 309 | } 310 | super.onDestroy(); 311 | } 312 | 313 | @Override 314 | public void onOrientationChanged(Orientation orientation, float pitch, float roll, float balance) { 315 | if (soundEnabled 316 | && orientation.isLevel(pitch, roll, balance, provider.getSensibility()) 317 | && System.currentTimeMillis() - lastBip > bipRate) { 318 | AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 319 | float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_RING); 320 | float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_RING); 321 | float volume = streamVolumeCurrent / streamVolumeMax; 322 | lastBip = System.currentTimeMillis(); 323 | soundPool.play(bipSoundID, volume, volume, 1, 0, 1); 324 | } 325 | levelView.onOrientationChanged(orientation, pitch, roll, balance); 326 | } 327 | 328 | @Override 329 | public void onCalibrationReset(boolean success) { 330 | Toast.makeText(this, success ? 331 | R.string.calibrate_restored : R.string.calibrate_failed, 332 | Toast.LENGTH_LONG).show(); 333 | } 334 | 335 | @Override 336 | public void onCalibrationSaved(boolean success) { 337 | Toast.makeText(this, success ? 338 | R.string.calibrate_saved : R.string.calibrate_failed, 339 | Toast.LENGTH_LONG).show(); 340 | } 341 | 342 | public float getDpmmCal(int progress, int coarseProgress){ 343 | float dpmm = (float) (getResources().getDisplayMetrics().ydpi/25.4); 344 | return dpmm*(1+(progress+coarseProgress-100f-2000f)/5000f); 345 | } 346 | 347 | public static int getThemeColor(Context context, int colorResId) { 348 | TypedValue typedValue = new TypedValue(); 349 | TypedArray typedArray = context.obtainStyledAttributes(typedValue.data, new int[] {colorResId}); 350 | int color = typedArray.getColor(0, 0); 351 | typedArray.recycle(); 352 | return color; 353 | } 354 | 355 | } 356 | --------------------------------------------------------------------------------