Compare commits

..

122 Commits

Author SHA1 Message Date
Jannik 28520bee74
changelog: minor text fixes 2 years ago
Jannik 0c6a486dd9 Merge pull request 'version 0.6.0' (#46) from develop into master 2 years ago
Jannik ec15c79b63
update fastlane screenshot 2 years ago
Jannik 4592d1984f
release version 0.6.0 2 years ago
Jannik aae74cf9db „README.md“ ändern 2 years ago
Jannik e19b0db39d
add missing licenses to license dialog 2 years ago
Jannik 23fd52b0c5
fab: use plus as icon 2 years ago
Jannik 62a62049c3
code clean up 2 years ago
Jannik 849698779b
add icon as svg 2 years ago
Jannik a42a92a3c5
update constraintlayout 2 years ago
Jannik be30e2f968
enable optimisations and minifying for release builds 2 years ago
Jannik c629b2aec2
fix login button 2 years ago
Jannik 99ba87c3f6
init fragment in onViewCreated() 2 years ago
Jannik 953b4825a9
check qispos status and show message if unavailable 2 years ago
Jannik 2807843d25
fix grades divider shown when sub-subject was added 2 years ago
Jannik 638c321798
grades ui polishing & sorting fix 2 years ago
Jannik 42d938b0bc
fix background in fragment_grades 2 years ago
Jannik 9d7504bbaf
add grades ui 2 years ago
Jannik 8ecfe8f120
QISPOSParser [Part 2] 2 years ago
Jannik ff0c4ad1a7
QISPOSParser [Part 1] 2 years ago
Jannik ea0caea91e
save email and password to encrypted preference 2 years ago
Jannik 1f660bdd37
add login dialog 2 years ago
Jannik be43d87b1a
fix more lint warnings 2 years ago
Jannik dd5c7b3fb8
fix some lint warnings 2 years ago
Jannik fb3dab6dc3
save additional subjects and load them on start 2 years ago
Jannik bcfdb83d14
rework CacheController and TCoRAPIController 2 years ago
Jannik 8c55366ec0
update libs 2 years ago
Jannik 6f68fbb73c „README.md“ ändern 2 years ago
Jannik 297dd77e79
use gradle wrapper for drone 2 years ago
Jannik 6a70f26807
update .drone.yml 2 years ago
Jannik f6b00a8d81
additional Subject are now added to the timetable 2 years ago
Jannik 69ce9fef5a
code clean up 2 years ago
Jannik 2d753851c0
more "Additional Lessons" work 2 years ago
Jannik 6c0624c793
make the app more tolerant about wrong API Data 2 years ago
Jannik 7779296345
change default accent color, code clean up 2 years ago
Jannik 3e2ef0521e
change primary color to reflect mosad.xyz design guidelines 2 years ago
Jannik bdfeb46faf
add "Manage Lessons" Dialog to Settings, minor style fixes 2 years ago
Jannik 18ca435764
update TCoRAPIController to API version 1.2.0 2 years ago
Jannik 948f330ebe
TimeTableFragment clean up 2 years ago
Jannik 72e9efb9e7
fix warings in TCoRAPIController 2 years ago
Jannik bea1b47396
complete & move AddLessonDialog to separate class 2 years ago
Jannik 34a68ff75d
add courses to addLesson Dialog, read lessonSubjects from tcor 2 years ago
Jannik 1ba3f1fa87
update gradle tool to 4.0.0 2 years ago
Jannik 48544aef2f
add a complete version of the addLesson Dialog 2 years ago
Jannik bc09e33147
update gradle, constraintlayout & coroutines 2 years ago
Jannik 6ec9b9f5e2
update commons-lang and coroutines 2 years ago
Jannik a19173b499
update kotlin 2 years ago
Jannik 85dc3181fd
fix compatibility with tcor version 1.2.2 and higher 2 years ago
Jannik e46c234b6c
fix OnboardingActivity is not closed on start of MainActivity 2 years ago
Jannik faa07966da
fix crash on first start, if tcor is not reachable 2 years ago
Jannik ccc0f0f2bc
Onboarding [Part 2] 2 years ago
Jannik e15bf95b85
update to gradle 6.2.1 and gradle android plugin 3.6.0 2 years ago
Jannik 01677c04ef
Onboarding [Part 1] 2 years ago
Jannik 6e9c63d3d4 Merge branch 'develop' of Seil0/ProjectLaogai into master [Hotfix] 2 years ago
Jannik c343735b57
fix crash on first startup 2 years ago
Jannik 9c5274dc06 Merge branch 'develop' of Seil0/ProjectLaogai into master 2 years ago
Jannik b186a2e96e
release version 0.5.1 2 years ago
Jannik 2cb4b72369
anko code removed, coroutines are now used for asynchronous functions 2 years ago
Jannik 2d497d1a96
update gradle wrapper to version 6.0.1 2 years ago
Jannik 9d2de3fcb3
don't use deprecated Gson methods 2 years ago
Jannik bed3f5d978
added Timetable and Moodle shortcut, cleaned up activity init 2 years ago
Jannik 8f5a4dd1b3
reworked preference saving 3 years ago
Jannik 9907d083e9
added a Mensa shortcut 3 years ago
Jannik d4860b2a32
updated some libs 3 years ago
Jannik 5ec2b53bce
added "Get it on Google Play" badge 3 years ago
Jannik 701a351e6e
update .drone.yml 3 years ago
Jannik 5cad924b26
removed anko dependency from PreferenceController 3 years ago
Jannik 605bf6248d release 0.5.0 3 years ago
Jannik d5adc4df51
release 0.5.0 3 years ago
Jannik c23454f081
removed versionNameSuffix, f-droid does not like that 3 years ago
Jannik b240beacc9
turns out there is a current week too 3 years ago
Jannik aa69d2242f
fixed course name not being updated on change 3 years ago
Jannik 6e6c9f71a0
reworked the way the timetable date is calculated 3 years ago
Jannik 889c673c5d Update 'README.md' 3 years ago
Jannik 522f31e077
added new screenshots 3 years ago
Jannik b9ca18044c
drone use gradle test instead of assembleRelease 3 years ago
Jannik bd49e482e2
updated some libs 3 years ago
Jannik 2f5b2a6579
added drone ci 3 years ago
Jannik 4ce37b3dcf
updated desfire legal notes 3 years ago
Jannik 7550fcfd22 Update 'README.md' 3 years ago
Jannik a23f7c9212
update to release 0.5.0 3 years ago
Jannik 75168c6688
some more logging and version 0.4.99 (RC1) 3 years ago
Jannik be916a74ab
the license dialog will now follow the theme too 3 years ago
Jannik 3e909ab68f
color the selected element in the NavigationView too 3 years ago
Jannik 5f2b3aa496
clean up the layout & polished Mensa credit dialog 3 years ago
Jannik 6cc1671a52
don't crash on devices without nfc 3 years ago
Jannik b4ed1ca927
migrated all farebot code to kotlin 3 years ago
Jannik e74c307566
cleand up the nfc part, added foreground-dispatch 3 years ago
Jannik 733b675ffa
first working implementation of the mensa card reader 3 years ago
Jannik 3a0a31f781
first bits of the nfc reader 3 years ago
Jannik f52151fbf1
the no lesson card now has the correct text color 3 years ago
Jannik e8cae7e807
update to gradle 5.5.1, update gradle plugin to 3.4.2 3 years ago
Jannik c6ac19bfae
fixed the Sunday-fix 3 years ago
Jannik a7abd48726
moved all cache checking code tho the CacheController 3 years ago
Jannik 9a22d9b737
use a ScrollView for the settings fragment 3 years ago
Jannik 2cc2ecf952
theme the dialogs and navView too, added a License dialog 3 years ago
Jannik ea70aedbd0
added the fastlane changelog for version 14 3 years ago
Jannik 98b3adbf3b
update to kotlin 1.3.40, added theme choose option 3 years ago
Jannik a055f59cc8
added a dark theme, see #15 3 years ago
Jannik 01db6bb451
don't use the faBtn in MensaFragment, use it in TimetableFragment 3 years ago
Jannik 4838c9406c
fastlane description spelling fixes 3 years ago
Jannik 23195f94e0
don't us deprecated tcor api requests 3 years ago
Jannik 5e766ec126
hide the mensa card value button 3 years ago
Jannik 9c1f95ca25
some lib updates & new sunday-bug fix 3 years ago
Jannik e99127a63a Update 'README.md' 3 years ago
Jannik f7fa96b1ae
use gradle 5.4.1 3 years ago
Jannik ac37bce145 Merge branch 'develop' of Seil0/ProjectLaogai into master 3 years ago
Jannik 6fd82254e0
updated description for f-droid 3 years ago
Jannik 0dd8ba9475
release 0.4.1 3 years ago
Jannik 770e9a255d Update 'README.md' 3 years ago
Jannik 4589badbfc
updated versionName to 0.4.1 3 years ago
Jannik 74f75bfbde update to kotlin 1.3.31, gradle 5.1.1 & gradle plugin 3.4.0 3 years ago
Jannik a00c651bfd disable layout change animation 3 years ago
Jannik 77757ad9ca fixed mensa screen crash if day has no meals 3 years ago
Jannik fe111ac56b added refresh to timetable screen 3 years ago
Jannik 2bd86ff6bb added fastlane metadata 3 years ago
Jannik dbaf496a79 minor clean up and refactoring 3 years ago
Jannik 77326a8ed6 added ability to refresh the mensa menus 3 years ago
Jannik 9fc897e194 fixed initial accent color 3 years ago
Jannik f9ac219ec6 Merge remote-tracking branch 'origin/master' into develop 3 years ago
Jannik ff1d353cae removed versionNameSuffix 3 years ago
Jannik 6e5cf29eaa removed maven repo 3 years ago
  1. 9
      .drone.yml
  2. 35
      README.md
  3. 60
      app/build.gradle
  4. 9
      app/proguard-rules.pro
  5. 6
      app/src/androidTest/java/org/mosad/seil0/projectlaogai/ExampleInstrumentedTest.kt
  6. 30
      app/src/main/AndroidManifest.xml
  7. BIN
      app/src/main/ic_laogai_icon-playstore.png
  8. BIN
      app/src/main/ic_laogai_icon-web.png
  9. 227
      app/src/main/java/com/codebutler/farebot/Utils.kt
  10. 9
      app/src/main/java/com/codebutler/farebot/card/desfire/DesfireException.kt
  11. 104
      app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFile.kt
  12. 248
      app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFileSettings.kt
  13. 181
      app/src/main/java/com/codebutler/farebot/card/desfire/DesfireManufacturingData.kt
  14. 214
      app/src/main/java/com/codebutler/farebot/card/desfire/DesfireProtocol.kt
  15. 26
      app/src/main/java/com/codebutler/farebot/card/desfire/DesfireRecord.kt
  16. 208
      app/src/main/java/org/mosad/seil0/projectlaogai/MainActivity.kt
  17. 145
      app/src/main/java/org/mosad/seil0/projectlaogai/OnboardingActivity.kt
  18. 122
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/CacheController.kt
  19. 106
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/NFCMensaCard.kt
  20. 133
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/PreferencesController.kt
  21. 236
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/QISPOSParser.kt
  22. 134
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/TCoRAPIController.kt
  23. 375
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/cache/CacheController.kt
  24. 131
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/cache/TimetableController.kt
  25. 110
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/preferences/EncryptedPreferences.kt
  26. 192
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/preferences/Preferences.kt
  27. 200
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/GradesFragment.kt
  28. 183
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/HomeFragment.kt
  29. 105
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/MensaFragment.kt
  30. 9
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/MoodleFragment.kt
  31. 236
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/SettingsFragment.kt
  32. 164
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/TimeTableFragment.kt
  33. 59
      app/src/main/java/org/mosad/seil0/projectlaogai/onboarding/ViewPagerAdapter.kt
  34. 42
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/DayCardView.kt
  35. 58
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/GradeLinearLayout.kt
  36. 7
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/LessonLinearLayout.kt
  37. 7
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/MealLinearLayout.kt
  38. 63
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/TextViewInfo.kt
  39. 164
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/dialogs/AddSubjectDialog.kt
  40. 87
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/dialogs/LoginDialog.kt
  41. 83
      app/src/main/java/org/mosad/seil0/projectlaogai/util/DataTypes.kt
  42. 64
      app/src/main/java/org/mosad/seil0/projectlaogai/util/NotRetardedCalendar.kt
  43. 8
      app/src/main/res/drawable/background_splash.xml
  44. 9
      app/src/main/res/drawable/ic_add_black_24dp.xml
  45. 9
      app/src/main/res/drawable/ic_error_outline_black_24dp.xml
  46. 9
      app/src/main/res/drawable/ic_grading_black_24dp.xml
  47. 9
      app/src/main/res/drawable/ic_menu_send.xml
  48. 9
      app/src/main/res/drawable/ic_menu_share.xml
  49. 13
      app/src/main/res/drawable/ic_settings_black_24dp.xml
  50. BIN
      app/src/main/res/drawable/ic_splash_logo.png
  51. 9
      app/src/main/res/drawable/side_nav_bar.xml
  52. 24
      app/src/main/res/layout/fragment_mensa.xml
  53. 185
      app/src/main/res/layout/fragment_settings.xml
  54. 24
      app/src/main/res/layout/fragment_timetable.xml
  55. 35
      app/src/main/res/layout/linearlayout_lesson.xml
  56. 23
      app/src/main/res/layout/linearlayout_meal.xml
  57. 3
      app/src/main/res/layouts/activities/layout/activity_main.xml
  58. 53
      app/src/main/res/layouts/activities/layout/activity_onboarding.xml
  59. 0
      app/src/main/res/layouts/activities/layout/app_bar_main.xml
  60. 6
      app/src/main/res/layouts/activities/layout/cardview_day.xml
  61. 30
      app/src/main/res/layouts/activities/layout/dialog_login.xml
  62. 70
      app/src/main/res/layouts/activities/layout/linearlayout_grade.xml
  63. 52
      app/src/main/res/layouts/activities/layout/linearlayout_lesson.xml
  64. 35
      app/src/main/res/layouts/activities/layout/linearlayout_meal.xml
  65. 30
      app/src/main/res/layouts/activities/layout/nav_header_main.xml
  66. 33
      app/src/main/res/layouts/activities/values/css_styles.xml
  67. 43
      app/src/main/res/layouts/activities/xml/shortcuts.xml
  68. 42
      app/src/main/res/layouts/dialogs/layout/dialog_add_lesson.xml
  69. 0
      app/src/main/res/layouts/dialogs/layout/dialog_loading.xml
  70. 34
      app/src/main/res/layouts/dialogs/layout/dialog_mensa_credit.xml
  71. 38
      app/src/main/res/layouts/fragments/layout/fragment_grades.xml
  72. 6
      app/src/main/res/layouts/fragments/layout/fragment_home.xml
  73. 29
      app/src/main/res/layouts/fragments/layout/fragment_mensa.xml
  74. 3
      app/src/main/res/layouts/fragments/layout/fragment_moodle.xml
  75. 72
      app/src/main/res/layouts/fragments/layout/fragment_on_course.xml
  76. 93
      app/src/main/res/layouts/fragments/layout/fragment_on_login.xml
  77. 70
      app/src/main/res/layouts/fragments/layout/fragment_on_welcome.xml
  78. 348
      app/src/main/res/layouts/fragments/layout/fragment_settings.xml
  79. 41
      app/src/main/res/layouts/fragments/layout/fragment_timetable.xml
  80. 4
      app/src/main/res/menu/activity_main_drawer.xml
  81. 4
      app/src/main/res/mipmap-anydpi-v26/ic_laogai_icon.xml
  82. BIN
      app/src/main/res/mipmap-hdpi/ic_laogai_icon.png
  83. BIN
      app/src/main/res/mipmap-hdpi/ic_laogai_icon_foreground.png
  84. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
  85. BIN
      app/src/main/res/mipmap-mdpi/ic_laogai_icon.png
  86. BIN
      app/src/main/res/mipmap-mdpi/ic_laogai_icon_foreground.png
  87. BIN
      app/src/main/res/mipmap-mdpi/ic_laogai_icon_splash.png
  88. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
  89. BIN
      app/src/main/res/mipmap-xhdpi/ic_laogai_icon.png
  90. BIN
      app/src/main/res/mipmap-xhdpi/ic_laogai_icon_foreground.png
  91. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
  92. BIN
      app/src/main/res/mipmap-xxhdpi/ic_laogai_icon.png
  93. BIN
      app/src/main/res/mipmap-xxhdpi/ic_laogai_icon_foreground.png
  94. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
  95. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_laogai_icon.png
  96. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_laogai_icon_foreground.png
  97. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
  98. BIN
      app/src/main/res/raw/notenverwaltung_hs_offenburg_de.crt
  99. 51
      app/src/main/res/raw/notices.xml
  100. 97
      app/src/main/res/values-de-rDE/strings.xml
  101. Some files were not shown because too many files have changed in this diff Show More

9
.drone.yml

@ -0,0 +1,9 @@
kind: pipeline
name: default
steps:
- name: assembleRelease
image: nextcloudci/android10:android-56
commands:
- ./gradlew assembleRelease

35
README.md

@ -1,16 +1,27 @@
# ProjectLaogai "hso App"
ProjectLaogai is a app to access the timetable and the mensa menu of Hochschule Offenburg.
[![Build Status](https://drone.mosad.xyz/api/badges/Seil0/ProjectLaogai/status.svg)](https://drone.mosad.xyz/Seil0/ProjectLaogai)
![fdroid version](https://img.shields.io/f-droid/v/org.mosad.seil0.projectlaogai.svg?style=flat-square)
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0)
# Project Laogai
Project Laogai is an app to access the timetable, grades (qispos) and the canteen menu of Hochschule Offenburg. Laogai uses the TCoR-API fot timetables and the canteen menu, wich makes acessing them super fast. To get the grades from Qispos, Laogai will ask for your login data, wich are stored encrypted on your device.
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75">](https://f-droid.org/packages/org.mosad.seil0.projectlaogai/)
[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="75">](https://play.google.com/store/apps/details?id=org.mosad.seil0.projectlaogai)
## Features
* check out the mensa menu of this and next week
* access your timetable
* open moodle
* probably some funny bugs
* have your grades displayed directly in the app
* show the timetable of your course
* take a look at the canteen menu for the current and next week
* check the current balance of your mensa card
* open moodle directly in the app
Please report bugs and issues to support@mosad.xyz
## Screenshots
[<img src="https://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/Project_Laogai/ProjectLaogai_HomeScreen.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/Project_Laogai/ProjectLaogai_HomeScreen.png)
[<img src="https://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/Project_Laogai/ProjectLaogai_Mensa.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/Project_Laogai/ProjectLaogai_Mensa.png)
[<img src="https://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/Project_Laogai/ProjectLaogai_Timetable.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/Project_Laogai/ProjectLaogai_Timetable.png)
[<img src="https://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/Project_Laogai/ProjectLaogai_Settings.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/Project_Laogai/ProjectLaogai_Settings.png)
[<img src="https://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/Project_Laogai/ProjectLaogai_NavDrawer.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/Project_Laogai/ProjectLaogai_NavDrawer.png)
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.png)
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa.png)
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Timetable.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Timetable.png)
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.png)
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa_dark.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa_dark.png)
ProjectLaogai © 2019 mosad [www.mosad.xyz](http://www.mosad.xyz), Project by [@Seil0](https://git.mosad.xyz/Seil0)
ProjectLaogai © 2019-2020 [@Seil0](https://git.mosad.xyz/Seil0), a [mosad.xyz](http://www.mosad.xyz) Project

60
app/build.gradle

@ -1,19 +1,17 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
signingConfigs {
}
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
applicationId "org.mosad.seil0.projectlaogai"
minSdkVersion 21
targetSdkVersion 28
versionCode 12
versionName "0.4.0"
minSdkVersion 23
targetSdkVersion 29
versionCode 6000 // 0006000
versionName "0.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "build_time", buildTime()
setProperty("archivesBaseName", "projectlaogai-$versionName")
@ -21,33 +19,55 @@ android {
buildTypes {
release {
minifyEnabled false
shrinkResources false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main {
res.srcDirs =
[
'src/main/res/layouts/activities',
'src/main/res/layouts/dialogs',
'src/main/res/layouts/fragments',
'src/main/res/layouts',
'src/main/res'
]
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.anko:anko-commons:0.10.8'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'
implementation 'androidx.core:core:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.security:security-crypto:1.1.0-alpha02'
implementation 'com.google.android.material:material:1.2.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.afollestad:aesthetic:1.0.0-beta05'
implementation 'com.afollestad.material-dialogs:core:2.6.0'
implementation 'com.afollestad.material-dialogs:color:2.6.0'
implementation 'com.afollestad.material-dialogs:core:3.3.0'
implementation 'com.afollestad.material-dialogs:color:3.3.0'
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
implementation 'org.apache.commons:commons-lang3:3.11'
implementation 'org.jsoup:jsoup:1.13.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
}
static def buildTime() {

9
app/proguard-rules.pro vendored

@ -15,7 +15,16 @@
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
-dontobfuscate
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class org.mosad.seil0.projectlaogai.util.** { <fields>; }
#Gson
-keepattributes Signature
-dontwarn sun.misc.**
#misc
-dontwarn java.lang.instrument.ClassFileTransformer

6
app/src/androidTest/java/org/mosad/seil0/projectlaogai/ExampleInstrumentedTest.kt

@ -1,7 +1,7 @@
package org.mosad.seil0.projectlaogai
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Test
import org.junit.runner.RunWith
@ -18,7 +18,7 @@ class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
val appContext = InstrumentationRegistry.getInstrumentation().context
assertEquals("org.mosad.seil0.projectlaogai", appContext.packageName)
}
}

30
app/src/main/AndroidManifest.xml

@ -2,31 +2,55 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.mosad.seil0.projectlaogai">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.NFC" />
<application
android:allowBackup="true"
android:fullBackupContent="@xml/backup_descriptor"
android:icon="@mipmap/ic_laogai_icon"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_laogai_icon"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme.Light">
<activity
android:name=".SplashActivity"
android:label="@string/app_name"
android:theme="@style/SplashTheme"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
<activity
android:name=".OnboardingActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.Light"
android:screenOrientation="portrait"
android:launchMode="singleTop">
</activity>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:screenOrientation="portrait">
android:theme="@style/AppTheme.Light"
android:screenOrientation="portrait"
android:launchMode="singleTop">
<!-- nfc stuff -->
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
</application>

BIN
app/src/main/ic_laogai_icon-playstore.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
app/src/main/ic_laogai_icon-web.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

227
app/src/main/java/com/codebutler/farebot/Utils.kt

@ -0,0 +1,227 @@
/**
* Utils.kt
*
* Copyright (C) 2011 Eric Butler
* Copyright (C) 2019 <seil0@mosad.xyz>
*
* Authors:
* Eric Butler <eric@codebutler.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.codebutler.farebot
import android.app.Activity
import android.app.AlertDialog
import android.util.Log
import android.view.WindowManager
import com.codebutler.farebot.card.desfire.DesfireException
import com.codebutler.farebot.card.desfire.DesfireFileSettings
import com.codebutler.farebot.card.desfire.DesfireProtocol
import org.w3c.dom.Node
import java.io.StringWriter
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import kotlin.experimental.and
class Utils {
companion object {
private val TAG = Utils::class.java.name
@Suppress("unused")
fun showError(activity: Activity, ex: Exception) {
Log.e(activity.javaClass.name, ex.message, ex)
AlertDialog.Builder(activity)
.setMessage(getErrorMessage(ex))
.show()
}
@Suppress("unused")
fun showErrorAndFinish(activity: Activity, ex: Exception) {
try {
Log.e(activity.javaClass.name, getErrorMessage(ex))
ex.printStackTrace()
AlertDialog.Builder(activity)
.setMessage(getErrorMessage(ex))
.setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ -> activity.finish() }
.show()
} catch (unused: WindowManager.BadTokenException) {
/* Ignore... happens if the activity was destroyed */
}
}
@Throws(Exception::class)
fun getHexString(b: ByteArray): String {
var result = ""
for (i in b.indices) {
result += ((b[i] and 0xff.toByte()) + 0x100).toString(16).substring(1)
}
return result
}
@Suppress("unused")
fun getHexString(b: ByteArray, defaultResult: String): String {
return try {
getHexString(b)
} catch (ex: Exception) {
defaultResult
}
}
@Suppress("unused")
fun hexStringToByteArray(s: String): ByteArray {
if (s.length % 2 != 0) {
throw IllegalArgumentException("Bad input string: $s")
}
val len = s.length
val data = ByteArray(len / 2)
var i = 0
while (i < len) {
data[i / 2] = ((Character.digit(s[i], 16) shl 4) + Character.digit(s[i + 1], 16)).toByte()
i += 2
}
return data
}
@JvmOverloads
fun byteArrayToInt(b: ByteArray, offset: Int = 0, length: Int = b.size): Int {
return byteArrayToLong(b, offset, length).toInt()
}
fun byteArrayToLong(b: ByteArray, offset: Int, length: Int): Long {
if (b.size < length)
throw IllegalArgumentException("length must be less than or equal to b.length")
var value: Long = 0
for (i in 0 until length) {
val shift = (length - 1 - i) * 8
value += ((b[i + offset].toInt() and 0x000000FF).toLong() shl shift)
}
return value
}
@Suppress("unused")
fun byteArraySlice(b: ByteArray, offset: Int, length: Int): ByteArray {
val ret = ByteArray(length)
for (i in 0 until length)
ret[i] = b[offset + i]
return ret
}
@Suppress("unused")
@Throws(Exception::class)
fun xmlNodeToString(node: Node): String {
// The amount of code required to do simple things in Java is incredible.
val source = DOMSource(node)
val stringWriter = StringWriter()
val result = StreamResult(stringWriter)
val factory = TransformerFactory.newInstance()
val transformer = factory.newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
transformer.uriResolver = null
transformer.transform(source, result)
return stringWriter.buffer.toString()
}
fun getErrorMessage(ex: Throwable): String {
var errorMessage: String? = ex.localizedMessage
if (errorMessage == null)
errorMessage = ex.message
if (errorMessage == null)
errorMessage = ex.toString()
if (ex.cause != null) {
var causeMessage: String? = ex.cause!!.localizedMessage
if (causeMessage == null)
causeMessage = ex.cause!!.message
if (causeMessage == null)
causeMessage = ex.cause.toString()
errorMessage += ": $causeMessage"
}
return errorMessage
}
@Suppress("unused")
fun <T> findInList(list: List<T>, matcher: Matcher<T>): T? {
for (item in list) {
if (matcher.matches(item)) {
return item
}
}
return null
}
interface Matcher<T> {
fun matches(t: T): Boolean
}
fun selectAppFile(tag: DesfireProtocol, appID: Int, fileID: Int): DesfireFileSettings? {
try {
tag.selectApp(appID)
} catch (e: DesfireException) {
Log.w(TAG, "App not found")
return null
}
return try {
tag.getFileSettings(fileID)
} catch (e: DesfireException) {
Log.w(TAG, "File not found")
null
}
}
fun arrayContains(arr: IntArray, item: Int): Boolean {
for (i in arr)
if (i == item)
return true
return false
}
@Suppress("unused")
fun containsAppFile(tag: DesfireProtocol, appID: Int, fileID: Int): Boolean {
try {
tag.selectApp(appID)
} catch (e: DesfireException) {
Log.w(TAG, "App not found")
Log.w(TAG, e)
return false
}
return try {
arrayContains(tag.fileList, fileID)
} catch (e: DesfireException) {
Log.w(TAG, "File not found")
Log.w(TAG, e)
false
}
}
}
}

9
app/src/main/java/com/codebutler/farebot/card/desfire/DesfireException.kt

@ -0,0 +1,9 @@
package com.codebutler.farebot.card.desfire
/**
* Created by Jakob Wenzel on 16.11.13.
*/
class DesfireException : Exception {
constructor(message: String) : super(message)
constructor(cause: Throwable) : super(cause)
}

104
app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFile.kt

@ -0,0 +1,104 @@
/**
* DesfireFile.kt
*
* Copyright (C) 2011 Eric Butler
* Copyright (C) 2019 <seil0@mosad.xyz>
*
* Authors:
* Eric Butler <eric@codebutler.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.codebutler.farebot.card.desfire
import android.os.Parcel
import android.os.Parcelable
import com.codebutler.farebot.card.desfire.DesfireFileSettings.RecordDesfireFileSettings
import org.apache.commons.lang3.ArrayUtils
open class DesfireFile private constructor(val id: Int, private val fileSettings: DesfireFileSettings?, val data: ByteArray) :
Parcelable {
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
if (this is InvalidDesfireFile) {
parcel.writeInt(1)
parcel.writeString(this.errorMessage)
} else {
parcel.writeInt(0)
parcel.writeParcelable(fileSettings, 0)
parcel.writeInt(data.size)
parcel.writeByteArray(data)
}
}
override fun describeContents(): Int {
return 0
}
class RecordDesfireFile(fileId: Int, fileSettings: DesfireFileSettings, fileData: ByteArray) :
DesfireFile(fileId, fileSettings, fileData) {
private val records: Array<DesfireRecord?>
init {
val settings = fileSettings as RecordDesfireFileSettings
val records = arrayOfNulls<DesfireRecord>(settings.curRecords)
for (i in 0 until settings.curRecords) {
val offset = settings.recordSize * i
records[i] = DesfireRecord(ArrayUtils.subarray(data, offset, offset + settings.recordSize))
}
this.records = records
}
}
class InvalidDesfireFile(fileId: Int, val errorMessage: String?) : DesfireFile(fileId, null, ByteArray(0))
companion object {
fun create(fileId: Int, fileSettings: DesfireFileSettings, fileData: ByteArray): DesfireFile {
return (fileSettings as? RecordDesfireFileSettings)?.let { RecordDesfireFile(fileId, it, fileData) }
?: DesfireFile(fileId, fileSettings, fileData)
}
@Suppress("unused")
@JvmField
val CREATOR: Parcelable.Creator<DesfireFile> = object : Parcelable.Creator<DesfireFile> {
override fun createFromParcel(source: Parcel): DesfireFile {
val fileId = source.readInt()
val isError = source.readInt() == 1
return if (!isError) {
val fileSettings =
source.readParcelable<Parcelable>(DesfireFileSettings::class.java.classLoader) as DesfireFileSettings
val dataLength = source.readInt()
val fileData = ByteArray(dataLength)
source.readByteArray(fileData)
create(fileId, fileSettings, fileData)
} else {
InvalidDesfireFile(fileId, source.readString())
}
}
override fun newArray(size: Int): Array<DesfireFile?> {
return arrayOfNulls(size)
}
}
}
}

248
app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFileSettings.kt

@ -0,0 +1,248 @@
/**
* DesfireFileSettings.kt
*
* Copyright (C) 2011 Eric Butler
* Copyright (C) 2019 <seil0@mosad.xyz>
*
* Authors:
* Eric Butler <eric@codebutler.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.codebutler.farebot.card.desfire
import android.os.Parcel
import android.os.Parcelable
import com.codebutler.farebot.Utils
import org.apache.commons.lang3.ArrayUtils
import java.io.ByteArrayInputStream
abstract class DesfireFileSettings : Parcelable {
private val fileType: Byte
private val commSetting: Byte
private val accessRights: ByteArray
@Suppress("unused")
val fileTypeName: String
get() {
return when (fileType) {
STANDARD_DATA_FILE -> "Standard"
BACKUP_DATA_FILE -> "Backup"
VALUE_FILE -> "Value"
LINEAR_RECORD_FILE -> "Linear Record"
CYCLIC_RECORD_FILE -> "Cyclic Record"
else -> "Unknown"
}
}
private constructor(stream: ByteArrayInputStream) {
fileType = stream.read().toByte()
commSetting = stream.read().toByte()
accessRights = ByteArray(2)
stream.read(accessRights, 0, accessRights.size)
}
private constructor(fileType: Byte, commSetting: Byte, accessRights: ByteArray) {
this.fileType = fileType
this.commSetting = commSetting
this.accessRights = accessRights
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeByte(fileType)
parcel.writeByte(commSetting)
parcel.writeInt(accessRights.size)
parcel.writeByteArray(accessRights)
}
override fun describeContents(): Int {
return 0
}
class StandardDesfireFileSettings : DesfireFileSettings {
private val fileSize: Int
internal constructor(stream: ByteArrayInputStream) : super(stream) {
val buf = ByteArray(3)
stream.read(buf, 0, buf.size)
ArrayUtils.reverse(buf)
fileSize = Utils.byteArrayToInt(buf)
}
internal constructor(
fileType: Byte,
commSetting: Byte,
accessRights: ByteArray,
fileSize: Int
) : super(fileType, commSetting, accessRights) {
this.fileSize = fileSize
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
super.writeToParcel(parcel, flags)
parcel.writeInt(fileSize)
}
}
class RecordDesfireFileSettings : DesfireFileSettings {
private val maxRecords: Int
val recordSize: Int
val curRecords: Int
constructor(stream: ByteArrayInputStream) : super(stream) {
var buf = ByteArray(3)
stream.read(buf, 0, buf.size)
ArrayUtils.reverse(buf)
recordSize = Utils.byteArrayToInt(buf)
buf = ByteArray(3)
stream.read(buf, 0, buf.size)
ArrayUtils.reverse(buf)
maxRecords = Utils.byteArrayToInt(buf)
buf = ByteArray(3)
stream.read(buf, 0, buf.size)
ArrayUtils.reverse(buf)
curRecords = Utils.byteArrayToInt(buf)
}
internal constructor(
fileType: Byte,
commSetting: Byte,
accessRights: ByteArray,
recordSize: Int,
maxRecords: Int,
curRecords: Int
) : super(fileType, commSetting, accessRights) {
this.recordSize = recordSize
this.maxRecords = maxRecords
this.curRecords = curRecords
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
super.writeToParcel(parcel, flags)
parcel.writeInt(recordSize)
parcel.writeInt(maxRecords)
parcel.writeInt(curRecords)
}
}
class ValueDesfireFileSettings(stream: ByteArrayInputStream) : DesfireFileSettings(stream) {
private val lowerLimit: Int
private val upperLimit: Int
val value: Int
private val limitedCreditEnabled: Byte
init {
var buf = ByteArray(4)
stream.read(buf, 0, buf.size)
ArrayUtils.reverse(buf)
lowerLimit = Utils.byteArrayToInt(buf)
buf = ByteArray(4)
stream.read(buf, 0, buf.size)
ArrayUtils.reverse(buf)
upperLimit = Utils.byteArrayToInt(buf)
buf = ByteArray(4)
stream.read(buf, 0, buf.size)
ArrayUtils.reverse(buf)
value = Utils.byteArrayToInt(buf)
buf = ByteArray(1)
stream.read(buf, 0, buf.size)
limitedCreditEnabled = buf[0]
//http://www.skyetek.com/docs/m2/desfire.pdf
//http://neteril.org/files/M075031_desfire.pdf
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
super.writeToParcel(parcel, flags)
parcel.writeInt(lowerLimit)
parcel.writeInt(upperLimit)
parcel.writeInt(value)
parcel.writeByte(limitedCreditEnabled)
}
}
class UnsupportedDesfireFileSettings(fileType: Byte) :
DesfireFileSettings(fileType, java.lang.Byte.MIN_VALUE, ByteArray(0))
companion object {
/* DesfireFile Types */
internal const val STANDARD_DATA_FILE = 0x00.toByte()
internal const val BACKUP_DATA_FILE = 0x01.toByte()
internal const val VALUE_FILE = 0x02.toByte()
internal const val LINEAR_RECORD_FILE = 0x03.toByte()
internal const val CYCLIC_RECORD_FILE = 0x04.toByte()
@Throws(DesfireException::class)
fun create(data: ByteArray): DesfireFileSettings {
val fileType = data[0]
val stream = ByteArrayInputStream(data)
return if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE)
StandardDesfireFileSettings(stream)
else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE)
RecordDesfireFileSettings(stream)
else if (fileType == VALUE_FILE)
ValueDesfireFileSettings(stream)
else
throw DesfireException("Unknown file type: " + Integer.toHexString(fileType.toInt()))
}
@Suppress("unused")
@JvmField
val CREATOR: Parcelable.Creator<DesfireFileSettings> = object : Parcelable.Creator<DesfireFileSettings> {
override fun createFromParcel(source: Parcel): DesfireFileSettings {
val fileType = source.readByte()
val commSetting = source.readByte()
val accessRights = ByteArray(source.readInt())
source.readByteArray(accessRights)
if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) {
val fileSize = source.readInt()
return StandardDesfireFileSettings(fileType, commSetting, accessRights, fileSize)
} else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) {
val recordSize = source.readInt()
val maxRecords = source.readInt()
val curRecords = source.readInt()
return RecordDesfireFileSettings(
fileType,
commSetting,
accessRights,
recordSize,
maxRecords,
curRecords
)
} else {
return UnsupportedDesfireFileSettings(fileType)
}
}
override fun newArray(size: Int): Array<DesfireFileSettings?> {
return arrayOfNulls(size)
}
}
}
}

181
app/src/main/java/com/codebutler/farebot/card/desfire/DesfireManufacturingData.kt

@ -0,0 +1,181 @@
/**
* DesfireManufacturingData.kt
*
* Copyright (C) 2011 Eric Butler
* Copyright (C) 2019 <seil0@mosad.xyz>
*
* Authors:
* Eric Butler <eric@codebutler.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the