Compare commits
90 Commits
0.3.3
...
9c5274dc06
Author | SHA1 | Date | |
---|---|---|---|
9c5274dc06 | |||
b186a2e96e
|
|||
2cb4b72369
|
|||
2d497d1a96
|
|||
9d2de3fcb3
|
|||
bed3f5d978
|
|||
8f5a4dd1b3
|
|||
9907d083e9
|
|||
d4860b2a32
|
|||
5ec2b53bce
|
|||
701a351e6e
|
|||
5cad924b26
|
|||
605bf6248d | |||
d5adc4df51
|
|||
c23454f081
|
|||
b240beacc9
|
|||
aa69d2242f
|
|||
6e6c9f71a0
|
|||
889c673c5d | |||
522f31e077
|
|||
b9ca18044c
|
|||
bd49e482e2
|
|||
2f5b2a6579
|
|||
4ce37b3dcf
|
|||
7550fcfd22 | |||
a23f7c9212
|
|||
75168c6688
|
|||
be916a74ab
|
|||
3e909ab68f
|
|||
5f2b3aa496
|
|||
6cc1671a52
|
|||
b4ed1ca927
|
|||
e74c307566
|
|||
733b675ffa
|
|||
3a0a31f781
|
|||
f52151fbf1
|
|||
e8cae7e807
|
|||
c6ac19bfae
|
|||
a7abd48726
|
|||
9a22d9b737
|
|||
2cc2ecf952
|
|||
ea70aedbd0
|
|||
98b3adbf3b
|
|||
a055f59cc8
|
|||
01db6bb451
|
|||
4838c9406c
|
|||
23195f94e0
|
|||
5e766ec126
|
|||
9c1f95ca25
|
|||
e99127a63a | |||
f7fa96b1ae
|
|||
ac37bce145 | |||
6fd82254e0
|
|||
0dd8ba9475
|
|||
770e9a255d | |||
4589badbfc
|
|||
74f75bfbde | |||
a00c651bfd | |||
77757ad9ca | |||
fe111ac56b | |||
4971d0b1b8 | |||
2bd86ff6bb | |||
dbaf496a79 | |||
77326a8ed6 | |||
9fc897e194 | |||
f9ac219ec6 | |||
a87e57c80e | |||
ff1d353cae | |||
6e5cf29eaa | |||
ee388bf5a5 | |||
8aaf8e3647 | |||
d645d75bbf | |||
f97491addd | |||
e08790aaa4 | |||
750a808fbe | |||
e51a80b78d | |||
a4eaea2918 | |||
cd3136715f | |||
15f1386b6e | |||
3bace6c155 | |||
61716f8eb4 | |||
a1410f7b80 | |||
953185425b | |||
a6d14044c2 | |||
f3b7ff066d | |||
040eb26dcf | |||
1a5d2b6561 | |||
92b60d660c | |||
a4eceeddc9 | |||
3e3a80442e |
9
.drone.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
kind: pipeline
|
||||||
|
name: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: assembleRelease
|
||||||
|
image: nextcloudci/android:android-51
|
||||||
|
commands:
|
||||||
|
- gradle assembleRelease
|
||||||
|
|
24
README.md
@ -1,13 +1,23 @@
|
|||||||
|

|
||||||
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|
|
||||||
# ProjectLaogai "hso App"
|
# ProjectLaogai "hso App"
|
||||||
ProjectLaogai is a app to access the timetable for any course of the Hochschule Offenburg. You can also check the current weeks mensa menus.
|
ProjectLaogai is a app to access the timetable and the mensa menu of Hochschule Offenburg.
|
||||||
|
|
||||||
|
[<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
|
## Features
|
||||||
* look up what you can eat in the mensa
|
* check out the mensa menu of this and next week
|
||||||
* check your timetable
|
* access your timetable
|
||||||
|
* check the current balance of your mensa card
|
||||||
|
* open moodle
|
||||||
* probably some funny bugs
|
* probably some funny bugs
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
[<img src="https://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/ProjectLaogai_HomeScreen.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/ProjectLaogai_HomeScreen.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://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/ProjectLaogai_Mensa.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/ProjectLaogai_Mensa.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://raw.githubusercontent.com/Seil0/Seil0.github.io/master/images/ProjectLaogai_NavDrawer.png" width=180>](https://github.com/Seil0/Seil0.github.io/blob/master/images/ProjectLaogai_NavDrawer.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](http://www.mosad.xyz) Project
|
||||||
|
@ -1,45 +1,71 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
}
|
}
|
||||||
compileSdkVersion 28
|
compileSdkVersion 29
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.mosad.seil0.projectlaogai"
|
applicationId "org.mosad.seil0.projectlaogai"
|
||||||
minSdkVersion 21
|
minSdkVersion 23
|
||||||
targetSdkVersion 28
|
targetSdkVersion 29
|
||||||
versionCode 11
|
versionCode 15
|
||||||
versionName "0.3.3"
|
versionName "0.5.1"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
resValue "string", "build_time", buildTime()
|
||||||
|
setProperty("archivesBaseName", "projectlaogai-$versionName")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
|
shrinkResources false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
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 {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
||||||
implementation 'org.jsoup:jsoup:1.11.3'
|
implementation 'com.google.android.material:material:1.1.0'
|
||||||
implementation 'org.jetbrains.anko:anko-commons:0.10.8'
|
implementation 'com.google.code.gson:gson:2.8.6'
|
||||||
implementation 'com.afollestad:aesthetic:1.0.0-beta05'
|
implementation 'com.afollestad:aesthetic:1.0.0-beta05'
|
||||||
implementation 'com.afollestad.material-dialogs:core:2.0.3'
|
implementation 'com.afollestad.material-dialogs:core:3.1.1'
|
||||||
implementation 'com.afollestad.material-dialogs:color:2.0.3'
|
implementation 'com.afollestad.material-dialogs:color:3.1.1'
|
||||||
|
implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
|
||||||
|
|
||||||
|
implementation 'org.apache.commons:commons-lang3:3.9'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static def buildTime() {
|
||||||
|
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
|
||||||
|
}
|
@ -2,31 +2,47 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.mosad.seil0.projectlaogai">
|
package="org.mosad.seil0.projectlaogai">
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:fullBackupContent="@xml/backup_descriptor"
|
||||||
android:icon="@mipmap/ic_laogai_icon"
|
android:icon="@mipmap/ic_laogai_icon"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_laogai_icon"
|
android:roundIcon="@mipmap/ic_laogai_icon"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme.Light">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".SplashActivity"
|
android:name=".SplashActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/SplashTheme"
|
android:theme="@style/SplashTheme"
|
||||||
android:screenOrientation="portrait">
|
android:screenOrientation="portrait">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcuts" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.Light"
|
||||||
android:screenOrientation="portrait">
|
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>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
227
app/src/main/java/com/codebutler/farebot/Utils.kt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
* 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.w3c.dom.Element
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
|
class DesfireManufacturingData : Parcelable {
|
||||||
|
private val hwVendorID: Int
|
||||||
|
private val hwType: Int
|
||||||
|
private val hwSubType: Int
|
||||||
|
private val hwMajorVersion: Int
|
||||||
|
private val hwMinorVersion: Int
|
||||||
|
private val hwStorageSize: Int
|
||||||
|
private val hwProtocol: Int
|
||||||
|
|
||||||
|
private val swVendorID: Int
|
||||||
|
private val swType: Int
|
||||||
|
private val swSubType: Int
|
||||||
|
private val swMajorVersion: Int
|
||||||
|
private val swMinorVersion: Int
|
||||||
|
private val swStorageSize: Int
|
||||||
|
private val swProtocol: Int
|
||||||
|
|
||||||
|
private val uid: Int
|
||||||
|
private val batchNo: Int
|
||||||
|
private val weekProd: Int
|
||||||
|
private val yearProd: Int
|
||||||
|
|
||||||
|
constructor(data: ByteArray) {
|
||||||
|
val stream = ByteArrayInputStream(data)
|
||||||
|
hwVendorID = stream.read()
|
||||||
|
hwType = stream.read()
|
||||||
|
hwSubType = stream.read()
|
||||||
|
hwMajorVersion = stream.read()
|
||||||
|
hwMinorVersion = stream.read()
|
||||||
|
hwStorageSize = stream.read()
|
||||||
|
hwProtocol = stream.read()
|
||||||
|
|
||||||
|
swVendorID = stream.read()
|
||||||
|
swType = stream.read()
|
||||||
|
swSubType = stream.read()
|
||||||
|
swMajorVersion = stream.read()
|
||||||
|
swMinorVersion = stream.read()
|
||||||
|
swStorageSize = stream.read()
|
||||||
|
swProtocol = stream.read()
|
||||||
|
|
||||||
|
// FIXME: This has fewer digits than what's contained in EXTRA_ID, why?
|
||||||
|
var buf = ByteArray(7)
|
||||||
|
stream.read(buf, 0, buf.size)
|
||||||
|
uid = Utils.byteArrayToInt(buf)
|
||||||
|
|
||||||
|
// FIXME: This is returning a negative number. Probably is unsigned.
|
||||||
|
buf = ByteArray(5)
|
||||||
|
stream.read(buf, 0, buf.size)
|
||||||
|
batchNo = Utils.byteArrayToInt(buf)
|
||||||
|
|
||||||
|
// FIXME: These numbers aren't making sense.
|
||||||
|
weekProd = stream.read()
|
||||||
|
yearProd = stream.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(element: Element) {
|
||||||
|
hwVendorID = Integer.parseInt(element.getElementsByTagName("hw-vendor-id").item(0).textContent)
|
||||||
|
hwType = Integer.parseInt(element.getElementsByTagName("hw-type").item(0).textContent)
|
||||||
|
hwSubType = Integer.parseInt(element.getElementsByTagName("hw-sub-type").item(0).textContent)
|
||||||
|
hwMajorVersion = Integer.parseInt(element.getElementsByTagName("hw-major-version").item(0).textContent)
|
||||||
|
hwMinorVersion = Integer.parseInt(element.getElementsByTagName("hw-minor-version").item(0).textContent)
|
||||||
|
hwStorageSize = Integer.parseInt(element.getElementsByTagName("hw-storage-size").item(0).textContent)
|
||||||
|
hwProtocol = Integer.parseInt(element.getElementsByTagName("hw-protocol").item(0).textContent)
|
||||||
|
|
||||||
|
swVendorID = Integer.parseInt(element.getElementsByTagName("sw-vendor-id").item(0).textContent)
|
||||||
|
swType = Integer.parseInt(element.getElementsByTagName("sw-type").item(0).textContent)
|
||||||
|
swSubType = Integer.parseInt(element.getElementsByTagName("sw-sub-type").item(0).textContent)
|
||||||
|
swMajorVersion = Integer.parseInt(element.getElementsByTagName("sw-major-version").item(0).textContent)
|
||||||
|
swMinorVersion = Integer.parseInt(element.getElementsByTagName("sw-minor-version").item(0).textContent)
|
||||||
|
swStorageSize = Integer.parseInt(element.getElementsByTagName("sw-storage-size").item(0).textContent)
|
||||||
|
swProtocol = Integer.parseInt(element.getElementsByTagName("sw-protocol").item(0).textContent)
|
||||||
|
|
||||||
|
uid = Integer.parseInt(element.getElementsByTagName("uid").item(0).textContent)
|
||||||
|
batchNo = Integer.parseInt(element.getElementsByTagName("batch-no").item(0).textContent)
|
||||||
|
weekProd = Integer.parseInt(element.getElementsByTagName("week-prod").item(0).textContent)
|
||||||
|
yearProd = Integer.parseInt(element.getElementsByTagName("year-prod").item(0).textContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(parcel: Parcel) {
|
||||||
|
hwVendorID = parcel.readInt()
|
||||||
|
hwType = parcel.readInt()
|
||||||
|
hwSubType = parcel.readInt()
|
||||||
|
hwMajorVersion = parcel.readInt()
|
||||||
|
hwMinorVersion = parcel.readInt()
|
||||||
|
hwStorageSize = parcel.readInt()
|
||||||
|
hwProtocol = parcel.readInt()
|
||||||
|
|
||||||
|
swVendorID = parcel.readInt()
|
||||||
|
swType = parcel.readInt()
|
||||||
|
swSubType = parcel.readInt()
|
||||||
|
swMajorVersion = parcel.readInt()
|
||||||
|
swMinorVersion = parcel.readInt()
|
||||||
|
swStorageSize = parcel.readInt()
|
||||||
|
swProtocol = parcel.readInt()
|
||||||
|
|
||||||
|
uid = parcel.readInt()
|
||||||
|
batchNo = parcel.readInt()
|
||||||
|
weekProd = parcel.readInt()
|
||||||
|
yearProd = parcel.readInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
parcel.writeInt(hwVendorID)
|
||||||
|
parcel.writeInt(hwType)
|
||||||
|
parcel.writeInt(hwSubType)
|
||||||
|
parcel.writeInt(hwMajorVersion)
|
||||||
|
parcel.writeInt(hwMinorVersion)
|
||||||
|
parcel.writeInt(hwStorageSize)
|
||||||
|
parcel.writeInt(hwProtocol)
|
||||||
|
|
||||||
|
parcel.writeInt(swVendorID)
|
||||||
|
parcel.writeInt(swType)
|
||||||
|
parcel.writeInt(swSubType)
|
||||||
|
parcel.writeInt(swMajorVersion)
|
||||||
|
parcel.writeInt(swMinorVersion)
|
||||||
|
parcel.writeInt(swStorageSize)
|
||||||
|
parcel.writeInt(swProtocol)
|
||||||
|
|
||||||
|
parcel.writeInt(uid)
|
||||||
|
parcel.writeInt(batchNo)
|
||||||
|
parcel.writeInt(weekProd)
|
||||||
|
parcel.writeInt(yearProd)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun describeContents(): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun fromXml(element: Element): DesfireManufacturingData {
|
||||||
|
return DesfireManufacturingData(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@JvmField
|
||||||
|
val CREATOR: Parcelable.Creator<DesfireManufacturingData> =
|
||||||
|
object : Parcelable.Creator<DesfireManufacturingData> {
|
||||||
|
override fun createFromParcel(source: Parcel): DesfireManufacturingData {
|
||||||
|
return DesfireManufacturingData(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<DesfireManufacturingData?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,214 @@
|
|||||||
|
/**
|
||||||
|
* DesfireProtocol.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.nfc.tech.IsoDep
|
||||||
|
import com.codebutler.farebot.Utils
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils
|
||||||
|
import kotlin.experimental.and
|
||||||
|
|
||||||
|
class DesfireProtocol(private val mTagTech: IsoDep) {
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val manufacturingData: DesfireManufacturingData
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
get() {
|
||||||
|
val respBuffer = sendRequest(GET_MANUFACTURING_DATA)
|
||||||
|
|
||||||
|
if (respBuffer.size != 28)
|
||||||
|
throw DesfireException("Invalid response")
|
||||||
|
|
||||||
|
return DesfireManufacturingData(respBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val appList: IntArray
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
get() {
|
||||||
|
val appDirBuf = sendRequest(GET_APPLICATION_DIRECTORY)
|
||||||
|
|
||||||
|
val appIds = IntArray(appDirBuf.size / 3)
|
||||||
|
|
||||||
|
var app = 0
|
||||||
|
while (app < appDirBuf.size) {
|
||||||
|
val appId = ByteArray(3)
|
||||||
|
System.arraycopy(appDirBuf, app, appId, 0, 3)
|
||||||
|
|
||||||
|
appIds[app / 3] = Utils.byteArrayToInt(appId)
|
||||||
|
app += 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return appIds
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileList: IntArray
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
get() {
|
||||||
|
val buf = sendRequest(GET_FILES)
|
||||||
|
val fileIds = IntArray(buf.size)
|
||||||
|
for (x in buf.indices) {
|
||||||
|
fileIds[x] = buf[x].toInt()
|
||||||
|
}
|
||||||
|
return fileIds
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
fun selectApp(appId: Int) {
|
||||||
|
val appIdBuff = ByteArray(3)
|
||||||
|
appIdBuff[0] = (appId and 0xFF0000 shr 16).toByte()
|
||||||
|
appIdBuff[1] = (appId and 0xFF00 shr 8).toByte()
|
||||||
|
appIdBuff[2] = (appId and 0xFF).toByte()
|
||||||
|
|
||||||
|
sendRequest(SELECT_APPLICATION, appIdBuff)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
fun getFileSettings(fileNo: Int): DesfireFileSettings {
|
||||||
|
val data = sendRequest(GET_FILE_SETTINGS, byteArrayOf(fileNo.toByte()))
|
||||||
|
return DesfireFileSettings.create(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
fun readFile(fileNo: Int): ByteArray {
|
||||||
|
return sendRequest(
|
||||||
|
READ_DATA,
|
||||||
|
byteArrayOf(
|
||||||
|
fileNo.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
fun readRecord(fileNum: Int): ByteArray {
|
||||||
|
return sendRequest(
|
||||||
|
READ_RECORD,
|
||||||
|
byteArrayOf(
|
||||||
|
fileNum.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte(),
|
||||||
|
0x0.toByte()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
fun readValue(fileNum: Int): Int {
|
||||||
|
val buf = sendRequest(READ_VALUE, byteArrayOf(fileNum.toByte()))
|
||||||
|
ArrayUtils.reverse(buf)
|
||||||
|
return Utils.byteArrayToInt(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
private fun sendRequest(command: Byte, parameters: ByteArray? = null): ByteArray {
|
||||||
|
val output = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
var recvBuffer: ByteArray
|
||||||
|
try {
|
||||||
|
recvBuffer = mTagTech.transceive(wrapMessage(command, parameters))
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw DesfireException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (recvBuffer[recvBuffer.size - 2] != 0x91.toByte())
|
||||||
|
throw DesfireException("Invalid response")
|
||||||
|
|
||||||
|
output.write(recvBuffer, 0, recvBuffer.size - 2)
|
||||||
|
|
||||||
|
val status = recvBuffer[recvBuffer.size - 1]
|
||||||
|
if (status == OPERATION_OK) {
|
||||||
|
break
|
||||||
|
} else if (status == ADDITIONAL_FRAME) {
|
||||||
|
try {
|
||||||
|
recvBuffer = mTagTech.transceive(wrapMessage(GET_ADDITIONAL_FRAME, null))
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw DesfireException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (status == PERMISSION_DENIED) {
|
||||||
|
throw DesfireException("Permission denied")
|
||||||
|
} else {
|
||||||
|
throw DesfireException("Unknown status code: " + Integer.toHexString((status and 0xFF.toByte()).toInt()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(DesfireException::class)
|
||||||
|
private fun wrapMessage(command: Byte, parameters: ByteArray?): ByteArray {
|
||||||
|
val stream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
stream.write(0x90.toByte().toInt())
|
||||||
|
stream.write(command.toInt())
|
||||||
|
stream.write(0x00.toByte().toInt())
|
||||||
|
stream.write(0x00.toByte().toInt())
|
||||||
|
if (parameters != null) {
|
||||||
|
stream.write(parameters.size.toByte().toInt())
|
||||||
|
try {
|
||||||
|
stream.write(parameters)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw DesfireException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
stream.write(0x00.toByte().toInt())
|
||||||
|
|
||||||
|
return stream.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/* Commands */
|
||||||
|
internal const val GET_MANUFACTURING_DATA = 0x60.toByte()
|
||||||
|
internal const val GET_APPLICATION_DIRECTORY = 0x6A.toByte()
|
||||||
|
internal const val GET_ADDITIONAL_FRAME = 0xAF.toByte()
|
||||||
|
internal const val SELECT_APPLICATION = 0x5A.toByte()
|
||||||
|
internal const val READ_DATA = 0xBD.toByte()
|
||||||
|
internal const val READ_RECORD = 0xBB.toByte()
|
||||||
|
internal const val READ_VALUE = 0x6C.toByte()
|
||||||
|
internal const val GET_FILES = 0x6F.toByte()
|
||||||
|
internal const val GET_FILE_SETTINGS = 0xF5.toByte()
|
||||||
|
|
||||||
|
/* Status codes */
|
||||||
|
internal const val OPERATION_OK = 0x00.toByte()
|
||||||
|
internal const val PERMISSION_DENIED = 0x9D.toByte()
|
||||||
|
internal const val ADDITIONAL_FRAME = 0xAF.toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* DesfireRecord.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
|
||||||
|
|
||||||
|
class DesfireRecord(val data: ByteArray)
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -22,8 +22,15 @@
|
|||||||
|
|
||||||
package org.mosad.seil0.projectlaogai
|
package org.mosad.seil0.projectlaogai
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.nfc.NfcAdapter
|
||||||
|
import android.nfc.NfcManager
|
||||||
|
import android.nfc.tech.NfcA
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||||
@ -32,30 +39,35 @@ import androidx.core.view.GravityCompat
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.fragment.app.FragmentTransaction
|
||||||
import com.afollestad.aesthetic.Aesthetic
|
import com.afollestad.aesthetic.Aesthetic
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.aesthetic.NavigationViewMode
|
||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.android.synthetic.main.app_bar_main.*
|
import kotlinx.android.synthetic.main.app_bar_main.*
|
||||||
import org.jetbrains.anko.doAsync
|
import org.mosad.seil0.projectlaogai.controller.CacheController
|
||||||
import org.jetbrains.anko.uiThread
|
import org.mosad.seil0.projectlaogai.controller.NFCMensaCard
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cColor
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourse
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorAccent
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourseTTLinkList
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek
|
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableNextWeek
|
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cWeekMenus
|
|
||||||
import org.mosad.seil0.projectlaogai.fragments.*
|
import org.mosad.seil0.projectlaogai.fragments.*
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.MensaParser
|
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.TimeTableParser
|
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO save the current fragment to show it when the app is restarted
|
||||||
|
*/
|
||||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||||
|
|
||||||
private val mensaParser = MensaParser()
|
|
||||||
private val timeTableParser = TimeTableParser()
|
|
||||||
private var activeFragment: Fragment = HomeFragment() // the currently active fragment, home at the start
|
private var activeFragment: Fragment = HomeFragment() // the currently active fragment, home at the start
|
||||||
|
private val className = "MainActivity"
|
||||||
|
|
||||||
|
private lateinit var adapter: NfcAdapter
|
||||||
|
private lateinit var pendingIntent: PendingIntent
|
||||||
|
private lateinit var intentFiltersArray: Array<IntentFilter>
|
||||||
|
private lateinit var techListsArray: Array<Array<String>>
|
||||||
|
private var useNFC = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
|
||||||
|
|
||||||
Aesthetic.attach(this)
|
Aesthetic.attach(this)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
@ -63,27 +75,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||||||
|
|
||||||
// load mensa, timetable and color
|
// load mensa, timetable and color
|
||||||
load()
|
load()
|
||||||
|
initAesthetic()
|
||||||
// If we haven't set any defaults, do that now
|
initForegroundDispatch()
|
||||||
if (Aesthetic.isFirstTime) {
|
|
||||||
// this is executed on the first app start, use this to show tutorial etc.
|
|
||||||
Aesthetic.config {
|
|
||||||
colorPrimary(Color.BLACK)
|
|
||||||
colorPrimaryDark(Color.BLACK)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Aesthetic.config {
|
|
||||||
colorPrimary(cColor)
|
|
||||||
colorPrimaryDark(cColor)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//init home fragment
|
|
||||||
val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
|
|
||||||
fragmentTransaction.replace(R.id.fragment_container, activeFragment)
|
|
||||||
fragmentTransaction.commit()
|
|
||||||
|
|
||||||
val toggle = ActionBarDrawerToggle(
|
val toggle = ActionBarDrawerToggle(
|
||||||
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
||||||
@ -92,22 +85,48 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||||||
toggle.syncState()
|
toggle.syncState()
|
||||||
|
|
||||||
nav_view.setNavigationItemSelectedListener(this)
|
nav_view.setNavigationItemSelectedListener(this)
|
||||||
|
|
||||||
|
// based on the inent we get, call readBalance or open a Fragment
|
||||||
|
when (intent.action) {
|
||||||
|
NfcAdapter.ACTION_TECH_DISCOVERED -> NFCMensaCard.readBalance(intent, this)
|
||||||
|
"org.mosad.seil0.projectlaogai.fragments.MensaFragment" -> activeFragment = MensaFragment()
|
||||||
|
"org.mosad.seil0.projectlaogai.fragments.TimeTableFragment" -> activeFragment = TimeTableFragment()
|
||||||
|
"org.mosad.seil0.projectlaogai.fragments.MoodleFragment" -> activeFragment = MoodleFragment()
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the activeFragment, default is the HomeFragment
|
||||||
|
fragmentTransaction.replace(R.id.fragment_container, activeFragment)
|
||||||
|
fragmentTransaction.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
|
||||||
|
if (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action)
|
||||||
|
NFCMensaCard.readBalance(intent, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
Aesthetic.resume(this)
|
Aesthetic.resume(this)
|
||||||
|
if(useNFC)
|
||||||
|
adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
Aesthetic.pause(this)
|
Aesthetic.pause(this)
|
||||||
|
if(useNFC)
|
||||||
|
adapter.disableForegroundDispatch(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
|
if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
|
||||||
drawer_layout.closeDrawer(GravityCompat.START)
|
drawer_layout.closeDrawer(GravityCompat.START)
|
||||||
} else {
|
} else {
|
||||||
|
// TODO only call on double tap
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,22 +149,13 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||||||
|
|
||||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||||
// Handle navigation view item clicks here.
|
// Handle navigation view item clicks here.
|
||||||
when (item.itemId) {
|
activeFragment = when(item.itemId) {
|
||||||
R.id.nav_home -> {
|
R.id.nav_home -> HomeFragment()
|
||||||
activeFragment = HomeFragment()
|
R.id.nav_mensa -> MensaFragment()
|
||||||
}
|
R.id.nav_timetable -> TimeTableFragment()
|
||||||
R.id.nav_mensa -> {
|
R.id.nav_moodle -> MoodleFragment()
|
||||||
activeFragment = MensaFragment()
|
R.id.nav_settings -> SettingsFragment()
|
||||||
}
|
else -> HomeFragment()
|
||||||
R.id.nav_timetable -> {
|
|
||||||
activeFragment = TimeTableFragment()
|
|
||||||
}
|
|
||||||
R.id.nav_moodle -> {
|
|
||||||
activeFragment = MoodleFragment()
|
|
||||||
}
|
|
||||||
R.id.nav_settings -> {
|
|
||||||
activeFragment = SettingsFragment()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
|
val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
|
||||||
@ -153,6 +163,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||||||
fragmentTransaction.commit()
|
fragmentTransaction.commit()
|
||||||
|
|
||||||
drawer_layout.closeDrawer(GravityCompat.START)
|
drawer_layout.closeDrawer(GravityCompat.START)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,56 +171,49 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
|||||||
* load the mensa menus of the current week
|
* load the mensa menus of the current week
|
||||||
*/
|
*/
|
||||||
private fun load() {
|
private fun load() {
|
||||||
// load the settings
|
val startupTime = measureTimeMillis {
|
||||||
PreferencesController.load(this) // this must be finished before doing anything else
|
PreferencesController.load(this) // load the settings, must be finished before doing anything else
|
||||||
|
CacheController(this) // load the cache
|
||||||
/**
|
}
|
||||||
* load mensa, course timetable and courselist from the swfr/hso website
|
Log.i(className, "startup completed in $startupTime ms")
|
||||||
* TODO make an API see https://git.mosad.xyz/Seil0/TheCitadelofRicks
|
}
|
||||||
*/
|
|
||||||
val time = measureTimeMillis {
|
private fun initAesthetic() {
|
||||||
|
// If we haven't set any defaults, do that now
|
||||||
/* getting the course list should be faster than the timetable,
|
if (Aesthetic.isFirstTime) {
|
||||||
* we need have time until the user opens the dialog
|
// this is executed on the first app start, use this to show tutorial etc.
|
||||||
*/
|
Aesthetic.config {
|
||||||
doAsync {
|
activityTheme(R.style.AppTheme_Light)
|
||||||
cCourseTTLinkList = timeTableParser.getCourseTTLinks()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
val jobMenus = doAsync {
|
SettingsFragment().selectCourse(this) // FIXME this is not working
|
||||||
cWeekMenus = mensaParser.getMensaMenu()
|
}
|
||||||
}
|
|
||||||
|
Aesthetic.config {
|
||||||
val jobTTCurrentWeek = doAsync {
|
colorPrimary(cColorPrimary)
|
||||||
try {
|
colorPrimaryDark(cColorPrimary)
|
||||||
cTimeTableCurrentWeek = timeTableParser.getTimeTable(cCourse.courseLink)
|
colorAccent(cColorAccent)
|
||||||
timeTableParser.printTimeTableWeek(cTimeTableCurrentWeek)
|
navigationViewMode(NavigationViewMode.SELECTED_ACCENT)
|
||||||
} catch (e: Exception) {
|
apply()
|
||||||
|
}
|
||||||
uiThread {
|
|
||||||
MaterialDialog(this@MainActivity)
|
}
|
||||||
.title(R.string.error)
|
|
||||||
.message(R.string.no_tt_error)
|
private fun initForegroundDispatch() {
|
||||||
.show()
|
val nfcManager = this.getSystemService(Context.NFC_SERVICE) as NfcManager
|
||||||
}
|
val nfcAdapter = nfcManager.defaultAdapter
|
||||||
|
|
||||||
e.stackTrace
|
if (nfcAdapter != null) {
|
||||||
}
|
useNFC = true
|
||||||
}
|
intentFiltersArray = arrayOf(IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply { addDataType("*/*") })
|
||||||
|
techListsArray = arrayOf(arrayOf(NfcA::class.java.name))
|
||||||
val jobTTNextWeek = doAsync {
|
adapter = NfcAdapter.getDefaultAdapter(this)
|
||||||
try {
|
pendingIntent = PendingIntent.getActivity(
|
||||||
cTimeTableNextWeek = timeTableParser.getTimeTable(cCourse.courseLink.replace("week=0","week=1"))
|
this, 0,
|
||||||
} catch (e: Exception) {
|
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0
|
||||||
e.stackTrace
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jobMenus.get()
|
|
||||||
jobTTCurrentWeek.get()
|
|
||||||
jobTTNextWeek.get()
|
|
||||||
}
|
}
|
||||||
println("Completed in $time ms")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
/**
|
|
||||||
* ProjectLaogai
|
|
||||||
*
|
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
|
||||||
*
|
|
||||||
* 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, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.mosad.seil0.projectlaogai
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Color
|
|
||||||
import org.jetbrains.anko.defaultSharedPreferences
|
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.Course
|
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.Meal
|
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.TimeTable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The PreferencesController class
|
|
||||||
* contains all preferences and global variables that exist in this app
|
|
||||||
*/
|
|
||||||
class PreferencesController {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
var cCourseTTLinkList = ArrayList<Course>()
|
|
||||||
var cWeekMenus = ArrayList<Meal>()
|
|
||||||
var cTimeTableCurrentWeek = TimeTable()
|
|
||||||
var cTimeTableNextWeek = TimeTable()
|
|
||||||
var cColor: Int = Color.BLACK
|
|
||||||
var cCourse = Course("https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0", "AI3")
|
|
||||||
|
|
||||||
// the save function
|
|
||||||
fun save(context: Context) {
|
|
||||||
println(cCourse.courseLink)
|
|
||||||
// save the course
|
|
||||||
val sharedPref = context.defaultSharedPreferences
|
|
||||||
with (sharedPref.edit()) {
|
|
||||||
putString(context.getString(R.string.save_key_course), cCourse.courseName)
|
|
||||||
putString(context.getString(R.string.save_key_courseTTLink), cCourse.courseLink)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the primary color
|
|
||||||
with (sharedPref.edit()) {
|
|
||||||
putInt(context.getString(R.string.save_key_colorPrimary), cColor)
|
|
||||||
apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// the load function
|
|
||||||
fun load(context: Context) {
|
|
||||||
// load saved course
|
|
||||||
val sharedPref = context.defaultSharedPreferences
|
|
||||||
cCourse = Course(
|
|
||||||
sharedPref.getString(context.getString(R.string.save_key_courseTTLink), "https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0")!!,
|
|
||||||
sharedPref.getString(context.getString(R.string.save_key_course), "AI3")!!
|
|
||||||
)
|
|
||||||
|
|
||||||
// load saved color
|
|
||||||
cColor = sharedPref.getInt(context.getString(R.string.save_key_colorPrimary), Color.BLACK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* ProjectLaogai
|
||||||
|
*
|
||||||
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mosad.seil0.projectlaogai.controller
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.joinAll
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.Course
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.MensaMenu
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.TimetableCourseWeek
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileReader
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
class CacheController(cont: Context) {
|
||||||
|
|
||||||
|
private val className = "CacheController"
|
||||||
|
private val context = cont
|
||||||
|
|
||||||
|
init {
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
val currentDay = Calendar.getInstance().get(Calendar.DAY_OF_WEEK)
|
||||||
|
val currentTime = System.currentTimeMillis() / 1000
|
||||||
|
|
||||||
|
// check if we need to update the mensa data before displaying it
|
||||||
|
readMensa(context)
|
||||||
|
cal.time = Date(mensaMenu.meta.updateTime * 1000)
|
||||||
|
|
||||||
|
// if a) it's monday and the last cache update was on sunday or b) the cache is older than 24hr, update blocking
|
||||||
|
if ((currentDay == Calendar.MONDAY && cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) || (currentTime - mensaMenu.meta.updateTime) > 86400) {
|
||||||
|
Log.i(className, "update mensa blocking")
|
||||||
|
GlobalScope.launch(Dispatchers.Default) { TCoRAPIController.getMensa(context).join() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we need to update the timetables before displaying them
|
||||||
|
readTimetable(cCourse.courseName, 0, context)
|
||||||
|
cal.time = Date(timetables[0].meta.updateTime * 1000)
|
||||||
|
|
||||||
|
// if a) it`s monday and the last cache update was not on a sunday or b) the cache is older than 24hr, update blocking
|
||||||
|
if ((currentDay == Calendar.MONDAY && cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) || (currentTime - timetables[0].meta.updateTime) > 86400) {
|
||||||
|
Log.i(className, "updating timetable after sunday!")
|
||||||
|
|
||||||
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
val threads = listOf(
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 0, context),
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 1, context)
|
||||||
|
)
|
||||||
|
threads.joinAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 0, context)
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 1, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if an update is necessary, not blocking
|
||||||
|
if (currentTime - PreferencesController.coursesCacheTime > 86400)
|
||||||
|
TCoRAPIController.getCoursesList(context)
|
||||||
|
|
||||||
|
if (currentTime - PreferencesController.mensaCacheTime > 10800)
|
||||||
|
TCoRAPIController.getMensa(context)
|
||||||
|
|
||||||
|
if (currentTime - PreferencesController.timetableCacheTime > 10800) {
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 0, context)
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 1, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
readStartCache(cCourse.courseName)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var coursesList = ArrayList<Course>()
|
||||||
|
var timetables = ArrayList<TimetableCourseWeek>()
|
||||||
|
var mensaMenu = MensaMenu()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read the courses list from the cached file
|
||||||
|
* add them to the coursesList object
|
||||||
|
*/
|
||||||
|
private fun readCoursesList(context: Context) {
|
||||||
|
val file = File(context.filesDir, "courses.json")
|
||||||
|
|
||||||
|
// make sure the file exists
|
||||||
|
if (!file.exists())
|
||||||
|
GlobalScope.launch(Dispatchers.Default) { TCoRAPIController.getCoursesList(context).join() }
|
||||||
|
|
||||||
|
val fileReader = FileReader(file)
|
||||||
|
val bufferedReader = BufferedReader(fileReader)
|
||||||
|
val coursesObject = JsonParser.parseString(bufferedReader.readLine()).asJsonObject
|
||||||
|
|
||||||
|
coursesList = Gson().fromJson(
|
||||||
|
coursesObject.getAsJsonArray("courses"),
|
||||||
|
object : TypeToken<List<Course>>() {}.type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the MensaMenu object from the cached json,
|
||||||
|
* if cache is empty create the cache file
|
||||||
|
*/
|
||||||
|
fun readMensa(context: Context) {
|
||||||
|
val file = File(context.filesDir, "mensa.json")
|
||||||
|
|
||||||
|
// make sure the file exists
|
||||||
|
if (!file.exists())
|
||||||
|
GlobalScope.launch(Dispatchers.Default) { TCoRAPIController.getMensa(context).join() }
|
||||||
|
|
||||||
|
val fileReader = FileReader(file)
|
||||||
|
val bufferedReader = BufferedReader(fileReader)
|
||||||
|
val mensaObject = JsonParser.parseString(bufferedReader.readLine()).asJsonObject
|
||||||
|
|
||||||
|
mensaMenu = GsonBuilder().create().fromJson(mensaObject, MensaMenu().javaClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read the weeks timetable from the cached file
|
||||||
|
* @param courseName the course name (e.g AI1)
|
||||||
|
* @param week the week to read (0 for the current and so on)
|
||||||
|
*/
|
||||||
|
fun readTimetable(courseName: String, week: Int, context: Context) {
|
||||||
|
val file = File(context.filesDir, "timetable-$courseName-$week.json")
|
||||||
|
|
||||||
|
// make sure the file exists
|
||||||
|
if (!file.exists()) {
|
||||||
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
TCoRAPIController.getTimetable(courseName, week, context).join()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileReader = FileReader(file)
|
||||||
|
val bufferedReader = BufferedReader(fileReader)
|
||||||
|
val timetableObject = JsonParser.parseString(bufferedReader.readLine()).asJsonObject
|
||||||
|
|
||||||
|
// make sure you add the single weeks in the exact order!
|
||||||
|
if (timetables.size > week) {
|
||||||
|
timetables[week] = (Gson().fromJson(timetableObject, TimetableCourseWeek().javaClass))
|
||||||
|
} else {
|
||||||
|
timetables.add(Gson().fromJson(timetableObject, TimetableCourseWeek().javaClass))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read coursesList, mensa (current and next week), timetable (current and next week)
|
||||||
|
* @param courseName the course name (e.g AI1)
|
||||||
|
*/
|
||||||
|
private fun readStartCache(courseName: String) {
|
||||||
|
readCoursesList(context)
|
||||||
|
readMensa(context)
|
||||||
|
readTimetable(courseName, 0, context)
|
||||||
|
readTimetable(courseName, 1, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* ProjectLaogai
|
||||||
|
*
|
||||||
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mosad.seil0.projectlaogai.controller
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.nfc.NfcAdapter
|
||||||
|
import android.nfc.Tag
|
||||||
|
import android.nfc.tech.IsoDep
|
||||||
|
import android.util.Log
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.customview.customView
|
||||||
|
import com.codebutler.farebot.Utils
|
||||||
|
import com.codebutler.farebot.card.desfire.DesfireFileSettings
|
||||||
|
import com.codebutler.farebot.card.desfire.DesfireProtocol
|
||||||
|
import kotlinx.android.synthetic.main.dialog_mensa_credit.*
|
||||||
|
import org.mosad.seil0.projectlaogai.R
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
|
class NFCMensaCard {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val className = "NFCMensaCard"
|
||||||
|
private const val appId = 0x5F8415
|
||||||
|
private const val fileId = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read the current balance and last payment from the mensa card
|
||||||
|
* @param intent a nfc intent
|
||||||
|
* @param context the context to show the dialog in
|
||||||
|
*/
|
||||||
|
fun readBalance(intent: Intent, context: Context) {
|
||||||
|
val tag: Tag? = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
|
||||||
|
val isoDep = IsoDep.get(tag)
|
||||||
|
try {
|
||||||
|
isoDep.connect()
|
||||||
|
|
||||||
|
val card = DesfireProtocol(isoDep)
|
||||||
|
val settings = Utils.selectAppFile(card, appId, fileId)
|
||||||
|
|
||||||
|
if (settings is DesfireFileSettings.ValueDesfireFileSettings) {
|
||||||
|
val data = try {
|
||||||
|
card.readValue(fileId)
|
||||||
|
} catch (ex: Exception) { 0 }
|
||||||
|
|
||||||
|
lookAtMe(context, data, settings.value).show()
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Log.w(className,"could not connect to tag", ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generate the values for current balance and last payment
|
||||||
|
* if the easter egg is active use schmeckles as currency
|
||||||
|
* 0.0000075 = 1.11 / 148 / 1000 (dollar / shm / card multiplier)
|
||||||
|
* @param context the context to access resources
|
||||||
|
* @param currentRaw the raw card value of the current balance
|
||||||
|
* @param lastRaw the raw card value of the last payment
|
||||||
|
* @return the message containing all values
|
||||||
|
*/
|
||||||
|
private fun lookAtMe(context: Context, currentRaw: Int, lastRaw: Int): MaterialDialog {
|
||||||
|
val dialog = MaterialDialog(context)
|
||||||
|
.customView(R.layout.dialog_mensa_credit)
|
||||||
|
|
||||||
|
val current = if (!PreferencesController.oGiants) {
|
||||||
|
String.format("%.2f €", (currentRaw.toFloat() / 1000))
|
||||||
|
} else {
|
||||||
|
String.format("%.4f shm", (currentRaw.toFloat() * 0.0000075))
|
||||||
|
}
|
||||||
|
|
||||||
|
val last = if (!PreferencesController.oGiants) {
|
||||||
|
String.format("%.2f €", (lastRaw.toFloat() / 1000))
|
||||||
|
} else {
|
||||||
|
String.format("%.4f shm", (lastRaw.toFloat() * 0.0000075))
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.txtView_current.text = current
|
||||||
|
dialog.txtView_last.text = context.resources.getString(R.string.mensa_last, last)
|
||||||
|
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* ProjectLaogai
|
||||||
|
*
|
||||||
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mosad.seil0.projectlaogai.controller
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import org.mosad.seil0.projectlaogai.R
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.Course
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PreferencesController class
|
||||||
|
* contains all preferences and global variables that exist in this app
|
||||||
|
*/
|
||||||
|
class PreferencesController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var coursesCacheTime: Long = 0
|
||||||
|
var mensaCacheTime: Long = 0
|
||||||
|
var timetableCacheTime: Long = 0
|
||||||
|
var cColorPrimary: Int = Color.BLACK
|
||||||
|
var cColorAccent: Int = Color.parseColor("#3F51B5")
|
||||||
|
var cCourse = Course("https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0", "AI3")
|
||||||
|
var cShowBuffet = true
|
||||||
|
var oGiants = false
|
||||||
|
|
||||||
|
// the save function
|
||||||
|
fun save(context: Context) {
|
||||||
|
val sharedPref = context.getSharedPreferences(
|
||||||
|
context.getString(R.string.preference_file_key),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
// save the update times (cache)
|
||||||
|
with (sharedPref.edit()) {
|
||||||
|
putLong(context.getString(R.string.save_key_coursesCacheTime),
|
||||||
|
coursesCacheTime
|
||||||
|
)
|
||||||
|
putLong(context.getString(R.string.save_key_mensaCacheTime),
|
||||||
|
mensaCacheTime
|
||||||
|
)
|
||||||
|
putLong(context.getString(R.string.save_key_timetableCacheTime),
|
||||||
|
timetableCacheTime
|
||||||
|
)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save the course locally
|
||||||
|
*/
|
||||||
|
fun saveCourse(context: Context, course: Course) {
|
||||||
|
val sharedPref = context.getSharedPreferences(
|
||||||
|
context.getString(R.string.preference_file_key),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
with (sharedPref.edit()) {
|
||||||
|
putString(context.getString(R.string.save_key_course), course.courseName)
|
||||||
|
putString(context.getString(R.string.save_key_courseTTLink), course.courseLink)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
cCourse = course
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save the primary color
|
||||||
|
*/
|
||||||
|
fun saveColorPrimary(context: Context, colorPrimary: Int) {
|
||||||
|
val sharedPref = context.getSharedPreferences(
|
||||||
|
context.getString(R.string.preference_file_key),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
with (sharedPref.edit()) {
|
||||||
|
putInt(context.getString(R.string.save_key_colorPrimary),
|
||||||
|
colorPrimary
|
||||||
|
)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
cColorPrimary = colorPrimary
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save the accent color
|
||||||
|
*/
|
||||||
|
fun saveColorAccent(context: Context, colorAccent: Int) {
|
||||||
|
val sharedPref = context.getSharedPreferences(
|
||||||
|
context.getString(R.string.preference_file_key),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
with (sharedPref.edit()) {
|
||||||
|
putInt(context.getString(R.string.save_key_colorAccent),
|
||||||
|
colorAccent
|
||||||
|
)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
cColorAccent = colorAccent
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save showBuffet
|
||||||
|
*/
|
||||||
|
fun saveShowBuffet(context: Context, showBuffet: Boolean) {
|
||||||
|
val sharedPref = context.getSharedPreferences(
|
||||||
|
context.getString(R.string.preference_file_key),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
with (sharedPref.edit()) {
|
||||||
|
putBoolean(context.getString(R.string.save_key_showBuffet),
|
||||||
|
showBuffet
|
||||||
|
)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
cShowBuffet = showBuffet
|
||||||
|
}
|
||||||
|
|
||||||
|
// the load function
|
||||||
|
fun load(context: Context) {
|
||||||
|
val sharedPref = context.getSharedPreferences(
|
||||||
|
context.getString(R.string.preference_file_key),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
// load the update times (cache)
|
||||||
|
coursesCacheTime = sharedPref.getLong(context.getString(
|
||||||
|
R.string.save_key_coursesCacheTime
|
||||||
|
), 0)
|
||||||
|
mensaCacheTime = sharedPref.getLong(context.getString(
|
||||||
|
R.string.save_key_mensaCacheTime
|
||||||
|
), 0)
|
||||||
|
timetableCacheTime = sharedPref.getLong(context.getString(
|
||||||
|
R.string.save_key_timetableCacheTime
|
||||||
|
), 0)
|
||||||
|
|
||||||
|
// load saved course
|
||||||
|
cCourse = Course(
|
||||||
|
sharedPref.getString(context.getString(R.string.save_key_courseTTLink), "https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0")!!,
|
||||||
|
sharedPref.getString(context.getString(R.string.save_key_course), "AI3")!!
|
||||||
|
)
|
||||||
|
|
||||||
|
// load saved colors
|
||||||
|
cColorPrimary = sharedPref.getInt(context.getString(
|
||||||
|
R.string.save_key_colorPrimary
|
||||||
|
), Color.BLACK)
|
||||||
|
cColorAccent = sharedPref.getInt(context.getString(
|
||||||
|
R.string.save_key_colorAccent
|
||||||
|
), Color.parseColor("#3F51B5"))
|
||||||
|
|
||||||
|
// load showBuffet
|
||||||
|
cShowBuffet = sharedPref.getBoolean(context.getString(
|
||||||
|
R.string.save_key_showBuffet
|
||||||
|
), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* ProjectLaogai
|
||||||
|
*
|
||||||
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mosad.seil0.projectlaogai.controller
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.coursesCacheTime
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.mensaCacheTime
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.timetableCacheTime
|
||||||
|
import java.io.BufferedWriter
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileWriter
|
||||||
|
import java.net.URL
|
||||||
|
import kotlin.Exception
|
||||||
|
|
||||||
|
class TCoRAPIController {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val className = "TCoRAPIController"
|
||||||
|
private const val tcorBaseURL = "https://tcor.mosad.xyz"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the json object from tcor api and write it as file (cache)
|
||||||
|
*/
|
||||||
|
fun getCoursesList(context: Context) = GlobalScope.launch {
|
||||||
|
try {
|
||||||
|
val url = URL("$tcorBaseURL/courseList")
|
||||||
|
val file = File(context.filesDir, "courses.json")
|
||||||
|
|
||||||
|
// read data from the API
|
||||||
|
val coursesObject = JSONObject(url.readText()) //JSONObject(inReader.readLine())
|
||||||
|
|
||||||
|
// write the json object to a file
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val writer = BufferedWriter(FileWriter(file))
|
||||||
|
writer.write(coursesObject.toString())
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// update cache time
|
||||||
|
coursesCacheTime = System.currentTimeMillis() / 1000
|
||||||
|
PreferencesController.save(context)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Log.e(className, "failed to get /courseList", ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the json object from tcor api and write it as file (cache)
|
||||||
|
*/
|
||||||
|
fun getMensa(context: Context) = GlobalScope.launch {
|
||||||
|
try {
|
||||||
|
val url = URL("$tcorBaseURL/mensamenu")
|
||||||
|
val file = File(context.filesDir, "mensa.json")
|
||||||
|
|
||||||
|
// read data from the API
|
||||||
|
val mensaObject = JSONObject(url.readText()) //JSONObject(inReader.readLine())
|
||||||
|
|
||||||
|
// write the json object to a file
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val writer = BufferedWriter(FileWriter(file))
|
||||||
|
writer.write(mensaObject.toString())
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// update cache time
|
||||||
|
mensaCacheTime = System.currentTimeMillis() / 1000
|
||||||
|
PreferencesController.save(context)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Log.e(className, "failed to get /mensamenu", ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the json object from tcor api and write it as file (cache)
|
||||||
|
*/
|
||||||
|
fun getTimetable(courseName: String, week: Int, context: Context) = GlobalScope.launch {
|
||||||
|
try {
|
||||||
|
val url = URL("$tcorBaseURL/timetable?courseName=$courseName&week=$week")
|
||||||
|
val file = File(context.filesDir, "timetable-$courseName-$week.json")
|
||||||
|
|
||||||
|
// read data from the API
|
||||||
|
val timetableObject = JSONObject(url.readText()) //JSONObject(inReader.readLine())
|
||||||
|
|
||||||
|
// write the json object to a file
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val writer = BufferedWriter(FileWriter(file))
|
||||||
|
writer.write(timetableObject.toString())
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// update cache time
|
||||||
|
timetableCacheTime = System.currentTimeMillis() / 1000
|
||||||
|
PreferencesController.save(context)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Log.e(className, "failed to get /timetable", ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -22,22 +22,28 @@
|
|||||||
|
|
||||||
package org.mosad.seil0.projectlaogai.fragments
|
package org.mosad.seil0.projectlaogai.fragments
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.TextView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
|
||||||
import kotlinx.android.synthetic.main.fragment_home.*
|
import kotlinx.android.synthetic.main.fragment_home.*
|
||||||
import org.jetbrains.anko.doAsync
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.jetbrains.anko.uiThread
|
import kotlinx.coroutines.GlobalScope
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek
|
import kotlinx.coroutines.launch
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cWeekMenus
|
import kotlinx.coroutines.withContext
|
||||||
import org.mosad.seil0.projectlaogai.R
|
import org.mosad.seil0.projectlaogai.R
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.*
|
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.mensaMenu
|
||||||
import org.mosad.seil0.projectlaogai.uicomponents.LessonCardView
|
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.timetables
|
||||||
import org.mosad.seil0.projectlaogai.uicomponents.LessonTextView
|
import org.mosad.seil0.projectlaogai.hsoparser.Meal
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.TimetableDay
|
||||||
|
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
|
||||||
|
import org.mosad.seil0.projectlaogai.uicomponents.MealLinearLayout
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,18 +52,14 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class HomeFragment : Fragment() {
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var linLayoutTimeTable: LinearLayout
|
private val className = "HomeFragment"
|
||||||
|
private val formatter = SimpleDateFormat("E dd.MM", Locale.getDefault())
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
|
||||||
val view: View = inflater.inflate(R.layout.fragment_home, container, false)
|
val view: View = inflater.inflate(R.layout.fragment_home, container, false)
|
||||||
|
|
||||||
// init UI elements
|
addMensaMenu()
|
||||||
linLayoutTimeTable = view.findViewById(R.id.linLayoutTimeTable)
|
addTimeTable()
|
||||||
|
|
||||||
//setText()
|
|
||||||
addCurrentMensaMenu()
|
|
||||||
addCurrentTimeTable()
|
|
||||||
|
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
return view
|
return view
|
||||||
@ -66,53 +68,49 @@ class HomeFragment : Fragment() {
|
|||||||
/**
|
/**
|
||||||
* add the current mensa meal to the home screens
|
* add the current mensa meal to the home screens
|
||||||
*/
|
*/
|
||||||
private fun addCurrentMensaMenu() {
|
private fun addMensaMenu() = GlobalScope.launch(Dispatchers.Default) {
|
||||||
doAsync {
|
|
||||||
val dayMenus: ArrayList<Meal>
|
|
||||||
val cal = Calendar.getInstance()
|
|
||||||
|
|
||||||
if (cal.get(Calendar.HOUR_OF_DAY) < 15) {
|
var dayMeals: ArrayList<Meal>
|
||||||
dayMenus = MensaParser().getMensaMenuDay(cWeekMenus, cal.get(Calendar.DAY_OF_WEEK))
|
val cal = Calendar.getInstance()
|
||||||
} else {
|
val mensaCardView = DayCardView(context!!)
|
||||||
dayMenus = MensaParser().getMensaMenuDay(cWeekMenus, cal.get(Calendar.DAY_OF_WEEK) + 1)
|
|
||||||
uiThread {
|
withContext(Dispatchers.Main) {
|
||||||
txtView_Menu1Heading.text = resources.getString(R.string.meal_1_tomorrow)
|
|
||||||
txtView_Menu2Heading.text = resources.getString(R.string.meal_2_tomorrow)
|
if (isAdded) {
|
||||||
|
if (cal.get(Calendar.HOUR_OF_DAY) < 15) {
|
||||||
|
dayMeals = mensaMenu.currentWeek.days[NotRetardedCalendar.getDayOfWeekIndex()].meals
|
||||||
|
mensaCardView.setDayHeading(activity!!.resources.getString(R.string.today_date, formatter.format(cal.time)))
|
||||||
|
} else {
|
||||||
|
dayMeals = mensaMenu.currentWeek.days[NotRetardedCalendar.getTomorrowWeekIndex()].meals
|
||||||
|
cal.add(Calendar.DATE, 1)
|
||||||
|
mensaCardView.setDayHeading(activity!!.resources.getString(R.string.tomorrow_date, formatter.format(cal.time)))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
uiThread {
|
if (dayMeals.size >= 2) {
|
||||||
|
|
||||||
if (dayMenus.size >= 2) {
|
|
||||||
// get the index of the first meal, not a "Schneller Teller"
|
// get the index of the first meal, not a "Schneller Teller"
|
||||||
loop@ for ((i, meal) in dayMenus.withIndex()) {
|
loop@ for ((i, meal) in dayMeals.withIndex()) {
|
||||||
if (meal.heading.contains("Essen")) {
|
if (meal.heading.contains("Essen")) {
|
||||||
for ((j, part) in dayMenus[i].parts.withIndex()) {
|
|
||||||
txtViewMenu1.append(part)
|
|
||||||
if (j < (dayMenus[i].parts.size - 2))
|
|
||||||
txtViewMenu1.append("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((j, part) in dayMenus[i + 1].parts.withIndex()) {
|
val meal1Layout = MealLinearLayout(context)
|
||||||
txtViewMenu2.append(part)
|
meal1Layout.setMeal(dayMeals[i])
|
||||||
if (j < (dayMenus[i + 1].parts.size - 2))
|
mensaCardView.getLinLayoutDay().addView(meal1Layout)
|
||||||
txtViewMenu2.append("\n")
|
|
||||||
}
|
val meal2Layout = MealLinearLayout(context)
|
||||||
|
meal2Layout.setMeal(dayMeals[i + 1])
|
||||||
|
meal2Layout.disableDivider()
|
||||||
|
mensaCardView.getLinLayoutDay().addView(meal2Layout)
|
||||||
|
|
||||||
break@loop
|
break@loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (txtView_Menu1Heading.text == resources.getString(R.string.meal_1_tomorrow)) {
|
mensaCardView.getLinLayoutDay().addView(getNoCard(resources.getString(R.string.mensa_closed)))
|
||||||
txtViewMenu1.text = resources.getString(R.string.no_meal_tomorrow)
|
|
||||||
txtViewMenu2.text = resources.getString(R.string.no_meal_tomorrow)
|
|
||||||
} else {
|
|
||||||
txtViewMenu1.text = resources.getString(R.string.no_meal_today)
|
|
||||||
txtViewMenu2.text = resources.getString(R.string.no_meal_today)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linLayout_Home.addView(mensaCardView)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -120,61 +118,71 @@ class HomeFragment : Fragment() {
|
|||||||
/**
|
/**
|
||||||
* add the current timetable to the home screen
|
* add the current timetable to the home screen
|
||||||
*/
|
*/
|
||||||
private fun addCurrentTimeTable() {
|
private fun addTimeTable() = GlobalScope.launch(Dispatchers.Default) {
|
||||||
val dayIndex = NotRetardedCalendar().getDayOfWeekIndex()
|
|
||||||
|
|
||||||
if (cTimeTableCurrentWeek.days.isNotEmpty() && dayIndex < 6) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
||||||
val timeTableDay = cTimeTableCurrentWeek.days[dayIndex]
|
if (isAdded && timetables.isNotEmpty()) {
|
||||||
|
try {
|
||||||
// for all timeslots of the day
|
val dayCardView = findNextDay(NotRetardedCalendar.getDayOfWeekIndex())
|
||||||
for ((i, timeslot) in timeTableDay.timeslots.withIndex()) {
|
linLayout_Home.addView(dayCardView)
|
||||||
val lessonCardView = LessonCardView(context!!, null)
|
} catch (ex: Exception) {
|
||||||
lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]
|
Log.e(className, "could not load timetable", ex) // TODO send feedback
|
||||||
|
|
||||||
for (lesson in timeslot) {
|
|
||||||
val lessonTxtView = LessonTextView(context!!)
|
|
||||||
lessonTxtView.setLesson(lesson)
|
|
||||||
|
|
||||||
// TODO why does this exist
|
|
||||||
if (lessonTxtView.text.length > 3)
|
|
||||||
lessonCardView.getLinLayoutLesson().addView(lessonTxtView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lessonCardView.getLinLayoutLesson().childCount > 2)
|
|
||||||
linLayoutTimeTable.addView(lessonCardView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a card if there is no lesson today
|
|
||||||
if (linLayoutTimeTable.childCount == 0) {
|
|
||||||
// TODO we could display the next day with a lecture
|
|
||||||
val lessonTxtView = LessonTextView(context!!)
|
|
||||||
lessonTxtView.setText(resources.getString(R.string.no_lesson_today))
|
|
||||||
|
|
||||||
val noLessonCardView = LessonCardView(context!!, null)
|
|
||||||
noLessonCardView.getLinLayoutLesson().addView(lessonTxtView)
|
|
||||||
|
|
||||||
linLayoutTimeTable.addView(noLessonCardView)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (dayIndex == 6) {
|
|
||||||
// if that's the case it's sunday
|
|
||||||
val lessonTxtView = LessonTextView(context!!)
|
|
||||||
lessonTxtView.setText(resources.getString(R.string.no_lesson_today))
|
|
||||||
|
|
||||||
val noLessonCardView = LessonCardView(context!!, null)
|
|
||||||
noLessonCardView.getLinLayoutLesson().addView(lessonTxtView)
|
|
||||||
|
|
||||||
linLayoutTimeTable.addView(noLessonCardView)
|
|
||||||
} else {
|
|
||||||
MaterialDialog(context!!)
|
|
||||||
.title(R.string.error)
|
|
||||||
.message(R.string.gen_tt_error)
|
|
||||||
.show()
|
|
||||||
// TODO log the error and send feedback
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find the next day with a lesson
|
||||||
|
* start at week 0, startDayIndex and search every cached week until we find a) a day with a timetable
|
||||||
|
* or b) we find no timetable and add a no lesson card
|
||||||
|
* @param startDayIndex the day index you want to start searching
|
||||||
|
* @return a DayCardView with all lessons added
|
||||||
|
*/
|
||||||
|
private fun findNextDay(startDayIndex: Int): DayCardView {
|
||||||
|
val dayCardView = DayCardView(context!!)
|
||||||
|
var dayTimetable: TimetableDay? = null
|
||||||
|
var dayIndexSearch = startDayIndex
|
||||||
|
var weekIndexSearch = 0
|
||||||
|
|
||||||
|
while (dayTimetable == null && weekIndexSearch < timetables.size) {
|
||||||
|
for (dayIndex in dayIndexSearch..5) {
|
||||||
|
dayTimetable = timetables[weekIndexSearch].timetable.days[dayIndex]
|
||||||
|
|
||||||
|
// some wired calendar magic, calculate the correct date to be shown ((timetable week - current week * 7) + (dayIndex - dayIndex of current week)
|
||||||
|
val daysToAdd = (timetables[weekIndexSearch].meta.weekNumberYear - NotRetardedCalendar.getWeekOfYear()) * 7 + (dayIndex - NotRetardedCalendar.getDayOfWeekIndex())
|
||||||
|
dayCardView.addTimetableDay(dayTimetable, daysToAdd)
|
||||||
|
|
||||||
|
// if there are no lessons don't show the dayCardView
|
||||||
|
if (dayCardView.getLinLayoutDay().childCount > 1)
|
||||||
|
return dayCardView
|
||||||
|
}
|
||||||
|
|
||||||
|
weekIndexSearch++
|
||||||
|
dayIndexSearch = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// there was no day found in the cached weeks, add no lesson card
|
||||||
|
dayCardView.setDayHeading(formatter.format(Calendar.getInstance().time))
|
||||||
|
dayCardView.getLinLayoutDay().addView(getNoCard(resources.getString(R.string.no_lesson_today))) // if there is no lecture at all show the no lesson card
|
||||||
|
return dayCardView
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param text the text to show on the card
|
||||||
|
* @return a TextView with the text and all needed parameters
|
||||||
|
*/
|
||||||
|
private fun getNoCard(text: String): TextView {
|
||||||
|
val noLesson = TextView(context)
|
||||||
|
noLesson.text = text
|
||||||
|
noLesson.textSize = 18.0F
|
||||||
|
noLesson.setTypeface(null, Typeface.BOLD)
|
||||||
|
noLesson.textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||||
|
noLesson.setPadding(7, 7, 7, 7)
|
||||||
|
|
||||||
|
return noLesson
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -23,18 +23,24 @@
|
|||||||
package org.mosad.seil0.projectlaogai.fragments
|
package org.mosad.seil0.projectlaogai.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import androidx.fragment.app.Fragment
|
||||||
import org.jetbrains.anko.doAsync
|
import kotlinx.android.synthetic.main.fragment_mensa.*
|
||||||
import org.jetbrains.anko.uiThread
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cWeekMenus
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.mosad.seil0.projectlaogai.R
|
import org.mosad.seil0.projectlaogai.R
|
||||||
import org.mosad.seil0.projectlaogai.uicomponents.MensaDayCardView
|
import org.mosad.seil0.projectlaogai.controller.CacheController
|
||||||
import org.mosad.seil0.projectlaogai.uicomponents.MenuCardView
|
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.mensaMenu
|
||||||
import java.util.*
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cShowBuffet
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.MensaWeek
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar
|
||||||
|
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
|
||||||
|
import org.mosad.seil0.projectlaogai.uicomponents.MealLinearLayout
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mensa controller class
|
* The mensa controller class
|
||||||
@ -42,73 +48,98 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class MensaFragment : Fragment() {
|
class MensaFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var linLayoutMensaFragment: LinearLayout
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
|
||||||
val view: View = inflater.inflate(R.layout.fragment_mensa, container, false)
|
val view: View = inflater.inflate(R.layout.fragment_mensa, container, false)
|
||||||
|
|
||||||
linLayoutMensaFragment = view.findViewById(R.id.linLayout_MensaFragment)
|
// init actions
|
||||||
|
refreshAction()
|
||||||
|
|
||||||
addCurrentWeek()
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
val dayCurrent = if(NotRetardedCalendar.getDayOfWeekIndex() == 6) 0 else NotRetardedCalendar.getDayOfWeekIndex()
|
||||||
|
addWeek(mensaMenu.currentWeek, dayCurrent).join()
|
||||||
|
|
||||||
|
// add the next week
|
||||||
|
addWeek(mensaMenu.nextWeek, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO should we show a info if there is no more food this & next week?
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addCurrentWeek() {
|
/**
|
||||||
|
* add all menus from dayStart to Friday for a given week
|
||||||
|
* @param menusWeek menu of type MensaWeek you want to add
|
||||||
|
* @param dayStart the first day of the week to add
|
||||||
|
*/
|
||||||
|
private fun addWeek(menusWeek: MensaWeek, dayStart: Int) = GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
|
||||||
doAsync {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
||||||
uiThread {
|
// only add the days dayStart to Fri since the mensa is closed on Sat/Sun
|
||||||
|
for (dayIndex in dayStart..4) {
|
||||||
|
var helpMeal = MealLinearLayout(context)
|
||||||
|
val dayCardView = DayCardView(context!!)
|
||||||
|
|
||||||
for(day in Calendar.getInstance().get(Calendar.DAY_OF_WEEK)..7) {
|
if(menusWeek.days[dayIndex].meals.isNotEmpty())
|
||||||
|
dayCardView.setDayHeading(menusWeek.days[dayIndex].meals[0].day)
|
||||||
|
|
||||||
val strDay: String = when(day) {
|
for (meal in menusWeek.days[dayIndex].meals) {
|
||||||
Calendar.MONDAY -> "Mon"
|
val mealLayout = MealLinearLayout(context)
|
||||||
Calendar.TUESDAY -> "Die"
|
mealLayout.setMeal(meal)
|
||||||
Calendar.WEDNESDAY -> "Mit"
|
|
||||||
Calendar.THURSDAY -> "Don"
|
if(meal.heading != "Buffet" || cShowBuffet) {
|
||||||
Calendar.FRIDAY -> "Fre"
|
dayCardView.getLinLayoutDay().addView(mealLayout)
|
||||||
Calendar.SATURDAY -> "Sam"
|
helpMeal = mealLayout
|
||||||
else -> "TODAY" // the app will likely crash here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val cardViewMensaDay = MensaDayCardView(context!!, null)
|
|
||||||
var add = false
|
|
||||||
|
|
||||||
for (meal in cWeekMenus) {
|
|
||||||
//println("Day: " + meal.day)
|
|
||||||
if (meal.day.contains(strDay)) {
|
|
||||||
|
|
||||||
val menuViewMenu = MenuCardView(context!!, null)
|
|
||||||
menuViewMenu.setMenuHeading(meal.heading)
|
|
||||||
|
|
||||||
for ((i, part) in meal.parts.withIndex()) {
|
|
||||||
menuViewMenu.getTxtViewMenu().append(part)
|
|
||||||
if(i < (meal.parts.size - 2))
|
|
||||||
menuViewMenu.getTxtViewMenu().append("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
cardViewMensaDay.setDayHeading(meal.day)
|
|
||||||
cardViewMensaDay.getLinLayoutMensaDay().addView(menuViewMenu)
|
|
||||||
add = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(add)
|
|
||||||
linLayoutMensaFragment.addView(cardViewMensaDay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a card if there are no more meals in this week
|
helpMeal.disableDivider()
|
||||||
if(linLayoutMensaFragment.childCount == 0) {
|
|
||||||
val cardViewNoMoreFood = MensaDayCardView(context!!, null)
|
if(dayCardView.getLinLayoutDay().childCount > 1)
|
||||||
cardViewNoMoreFood.setDayHeading(resources.getString(R.string.no_more_food))
|
linLayout_Mensa.addView(dayCardView)
|
||||||
linLayoutMensaFragment.addView(cardViewNoMoreFood)
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize the refresh action
|
||||||
|
*/
|
||||||
|
private fun refreshAction() = GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
// set the refresh listener
|
||||||
|
refreshLayout_Mensa.setOnRefreshListener {
|
||||||
|
updateMensaScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refresh the mensa cache and update the mensa screen
|
||||||
|
*/
|
||||||
|
private fun updateMensaScreen() = GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
// update the cache
|
||||||
|
TCoRAPIController.getMensa(context!!).join() // blocking since we want the new data
|
||||||
|
CacheController.readMensa(context!!)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
// remove all menus from the layout
|
||||||
|
linLayout_Mensa.removeAllViews()
|
||||||
|
|
||||||
|
// add the refreshed menus
|
||||||
|
val dayCurrent = if (NotRetardedCalendar.getDayOfWeekIndex() == 6) 0 else NotRetardedCalendar.getDayOfWeekIndex()
|
||||||
|
addWeek(mensaMenu.currentWeek, dayCurrent).join()
|
||||||
|
|
||||||
|
// add the next week
|
||||||
|
addWeek(mensaMenu.nextWeek, 0)
|
||||||
|
|
||||||
|
refreshLayout_Mensa.isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -48,10 +48,10 @@ class MoodleFragment : Fragment() {
|
|||||||
webView = view.findViewById(R.id.webView)
|
webView = view.findViewById(R.id.webView)
|
||||||
webView.loadUrl("https://elearning.hs-offenburg.de/moodle/")
|
webView.loadUrl("https://elearning.hs-offenburg.de/moodle/")
|
||||||
|
|
||||||
webSettings = webView.getSettings()
|
webSettings = webView.settings
|
||||||
//webSettings.setJavaScriptEnabled(true) // Enable Javascript
|
//webSettings.setJavaScriptEnabled(true) // Enable Javascript
|
||||||
|
|
||||||
webView.setWebViewClient(WebViewClient()) // Force links and redirects to open in the WebView instead of in a browser
|
webView.webViewClient = WebViewClient() // Force links and redirects to open in the WebView instead of in a browser
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -22,29 +22,35 @@
|
|||||||
|
|
||||||
package org.mosad.seil0.projectlaogai.fragments
|
package org.mosad.seil0.projectlaogai.fragments
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.Switch
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.afollestad.aesthetic.Aesthetic
|
import com.afollestad.aesthetic.Aesthetic
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.color.colorChooser
|
import com.afollestad.materialdialogs.color.colorChooser
|
||||||
import com.afollestad.materialdialogs.customview.customView
|
import com.afollestad.materialdialogs.customview.customView
|
||||||
import com.afollestad.materialdialogs.list.listItems
|
import com.afollestad.materialdialogs.list.listItems
|
||||||
|
import com.afollestad.materialdialogs.list.listItemsSingleChoice
|
||||||
|
import de.psdev.licensesdialog.LicensesDialog
|
||||||
import kotlinx.android.synthetic.main.fragment_settings.*
|
import kotlinx.android.synthetic.main.fragment_settings.*
|
||||||
import org.jetbrains.anko.doAsync
|
import kotlinx.coroutines.*
|
||||||
import org.jetbrains.anko.uiThread
|
import org.mosad.seil0.projectlaogai.BuildConfig
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController
|
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cColor
|
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourse
|
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourseTTLinkList
|
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek
|
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableNextWeek
|
|
||||||
import org.mosad.seil0.projectlaogai.R
|
import org.mosad.seil0.projectlaogai.R
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.TimeTableParser
|
import org.mosad.seil0.projectlaogai.controller.CacheController
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.coursesList
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorAccent
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cShowBuffet
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.DataTypes
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,9 +61,12 @@ class SettingsFragment : Fragment() {
|
|||||||
|
|
||||||
private lateinit var linLayoutUser: LinearLayout
|
private lateinit var linLayoutUser: LinearLayout
|
||||||
private lateinit var linLayoutCourse: LinearLayout
|
private lateinit var linLayoutCourse: LinearLayout
|
||||||
private lateinit var linLayoutInfo: LinearLayout
|
private lateinit var linLayoutAbout: LinearLayout
|
||||||
private lateinit var linLayoutMainColor: LinearLayout
|
private lateinit var linLayoutLicence: LinearLayout
|
||||||
private lateinit var viewPrimaryColor: View
|
private lateinit var linLayoutTheme: LinearLayout
|
||||||
|
private lateinit var linLayoutPrimaryColor: LinearLayout
|
||||||
|
private lateinit var linLayoutAccentColor: LinearLayout
|
||||||
|
private lateinit var switchBuffet: Switch
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
|
||||||
@ -65,9 +74,12 @@ class SettingsFragment : Fragment() {
|
|||||||
|
|
||||||
linLayoutUser = view.findViewById(R.id.linLayout_User)
|
linLayoutUser = view.findViewById(R.id.linLayout_User)
|
||||||
linLayoutCourse = view.findViewById(R.id.linLayout_Course)
|
linLayoutCourse = view.findViewById(R.id.linLayout_Course)
|
||||||
linLayoutInfo = view.findViewById(R.id.linLayout_Info)
|
linLayoutAbout = view.findViewById(R.id.linLayout_About)
|
||||||
linLayoutMainColor = view.findViewById(R.id.linLayout_MainColor)
|
linLayoutLicence = view.findViewById(R.id.linLayout_Licence)
|
||||||
viewPrimaryColor = view.findViewById(R.id.view_PrimaryColor)
|
linLayoutTheme = view.findViewById(R.id.linLayout_Theme)
|
||||||
|
linLayoutPrimaryColor = view.findViewById(R.id.linLayout_PrimaryColor)
|
||||||
|
linLayoutAccentColor = view.findViewById(R.id.linLayout_AccentColor)
|
||||||
|
switchBuffet = view.findViewById(R.id.switch_buffet)
|
||||||
|
|
||||||
initActions()
|
initActions()
|
||||||
|
|
||||||
@ -78,7 +90,28 @@ class SettingsFragment : Fragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// initialize the settings gui
|
||||||
txtView_Course.text = cCourse.courseName
|
txtView_Course.text = cCourse.courseName
|
||||||
|
txtView_AboutDesc.text = resources.getString(R.string.about_version, BuildConfig.VERSION_NAME, getString(R.string.build_time))
|
||||||
|
switch_buffet.isChecked = cShowBuffet // init switch
|
||||||
|
|
||||||
|
val outValue = TypedValue()
|
||||||
|
activity!!.theme.resolveAttribute(R.attr.themeName, outValue, true)
|
||||||
|
when(outValue.string) {
|
||||||
|
"light" -> {
|
||||||
|
switch_buffet.setTextColor(activity!!.resources.getColor(R.color.textPrimaryLight, activity!!.theme))
|
||||||
|
txtView_SelectedTheme.text = resources.getString(R.string.themeLight)
|
||||||
|
}
|
||||||
|
"dark" -> {
|
||||||
|
switch_buffet.setTextColor(activity!!.resources.getColor(R.color.textPrimaryDark, activity!!.theme))
|
||||||
|
txtView_SelectedTheme.text = resources.getString(R.string.themeDark)
|
||||||
|
}
|
||||||
|
"black" -> {
|
||||||
|
switch_buffet.setTextColor(activity!!.resources.getColor(R.color.textPrimaryDark, activity!!.theme))
|
||||||
|
txtView_SelectedTheme.text = resources.getString(R.string.themeBlack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,67 +122,148 @@ class SettingsFragment : Fragment() {
|
|||||||
// open a new dialog
|
// open a new dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
linLayoutCourse.setOnClickListener {
|
linLayoutUser.setOnLongClickListener {
|
||||||
// open a new dialog
|
PreferencesController.oGiants = true // enable easter egg
|
||||||
val courseList = ArrayList<String>()
|
return@setOnLongClickListener true
|
||||||
|
|
||||||
cCourseTTLinkList.forEach { (_, course) ->
|
|
||||||
courseList.add(course)
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialDialog(context!!).listItems(items = courseList) { _, index, text ->
|
|
||||||
txtView_Course.text = text // update txtView
|
|
||||||
|
|
||||||
val dialog = MaterialDialog(context!!).cancelable(false)
|
|
||||||
.cancelOnTouchOutside(false)
|
|
||||||
.customView(R.layout.dialog_loading)
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
doAsync {
|
|
||||||
cCourse = cCourseTTLinkList[index] // set the course
|
|
||||||
PreferencesController.save(context!!)
|
|
||||||
|
|
||||||
// update current & next weeks timetable
|
|
||||||
cTimeTableCurrentWeek = TimeTableParser().getTimeTable(cCourse.courseLink)
|
|
||||||
cTimeTableNextWeek = TimeTableParser().getTimeTable(cCourse.courseLink.replace("week=0","week=1"))
|
|
||||||
|
|
||||||
uiThread {
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
linLayoutInfo.setOnClickListener {
|
linLayoutCourse.setOnClickListener {
|
||||||
|
selectCourse(context!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
linLayoutAbout.setOnClickListener {
|
||||||
// open a new info dialog
|
// open a new info dialog
|
||||||
MaterialDialog(context!!)
|
MaterialDialog(context!!)
|
||||||
.title(R.string.about)
|
.title(R.string.about_dialog_heading)
|
||||||
.message(R.string.about_text)
|
.message(R.string.about_dialog_text)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
linLayoutMainColor.setOnClickListener {
|
linLayoutLicence.setOnClickListener {
|
||||||
// open a new color chooser dialog
|
// do the theme magic, as the lib's theme support is broken
|
||||||
val colors = intArrayOf(Color.BLACK, Color.DKGRAY, Color.RED, Color.GREEN, Color.YELLOW)
|
val outValue = TypedValue()
|
||||||
|
context!!.theme.resolveAttribute(R.attr.themeName, outValue, true)
|
||||||
|
|
||||||
|
val dialogCss = when (outValue.string) {
|
||||||
|
"light" -> R.string.license_dialog_style_light
|
||||||
|
else -> R.string.license_dialog_style_dark
|
||||||
|
}
|
||||||
|
|
||||||
|
val themeId = when (outValue.string) {
|
||||||
|
"light" -> R.style.AppTheme_Light
|
||||||
|
else -> R.style.LicensesDialogTheme_Dark
|
||||||
|
}
|
||||||
|
|
||||||
|
// open a new license dialog
|
||||||
|
LicensesDialog.Builder(context!!)
|
||||||
|
.setNotices(R.raw.notices)
|
||||||
|
.setTitle(R.string.licenses)
|
||||||
|
.setIncludeOwnLicense(true)
|
||||||
|
.setThemeResourceId(themeId)
|
||||||
|
.setNoticesCssStyle(dialogCss)
|
||||||
|
.build()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
linLayoutTheme.setOnClickListener {
|
||||||
|
val themes = listOf(
|
||||||
|
resources.getString(R.string.themeLight),
|
||||||
|
resources.getString(R.string.themeDark),
|
||||||
|
resources.getString(R.string.themeBlack)
|
||||||
|
)
|
||||||
|
MaterialDialog(context!!).show {
|
||||||
|
listItemsSingleChoice(items = themes) { _, index, _ ->
|
||||||
|
Aesthetic.config {
|
||||||
|
when (index) {
|
||||||
|
0 -> activityTheme(R.style.AppTheme_Light)
|
||||||
|
1 -> activityTheme(R.style.AppTheme_Dark)
|
||||||
|
2 -> activityTheme(R.style.AppTheme_Black)
|
||||||
|
else -> activityTheme(R.style.AppTheme_Light)
|
||||||
|
}
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linLayoutPrimaryColor.setOnClickListener {
|
||||||
|
// open a new color chooser dialog
|
||||||
MaterialDialog(context!!)
|
MaterialDialog(context!!)
|
||||||
.title(R.string.primary_color)
|
.title(R.string.primary_color)
|
||||||
.colorChooser(colors, allowCustomArgb = true, initialSelection = cColor) { _, color ->
|
.colorChooser(DataTypes().primaryColors, allowCustomArgb = true, initialSelection = cColorPrimary) { _, color ->
|
||||||
viewPrimaryColor.setBackgroundColor(color)
|
view_PrimaryColor.setBackgroundColor(color)
|
||||||
Aesthetic.config {
|
Aesthetic.config {
|
||||||
colorPrimary(color)
|
colorPrimary(color)
|
||||||
colorPrimaryDark(color)
|
colorPrimaryDark(color)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
cColor = color
|
PreferencesController.saveColorPrimary(context!!, color)
|
||||||
PreferencesController.save(context!!)
|
|
||||||
}
|
}
|
||||||
.positiveButton(R.string.select)
|
.positiveButton(R.string.select)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linLayoutAccentColor.setOnClickListener {
|
||||||
|
// open a new color chooser dialog
|
||||||
|
MaterialDialog(context!!)
|
||||||
|
.title(R.string.accent_color)
|
||||||
|
.colorChooser(DataTypes().accentColors, allowCustomArgb = true, initialSelection = cColorAccent) { _, color ->
|
||||||
|
view_AccentColor.setBackgroundColor(color)
|
||||||
|
Aesthetic.config {
|
||||||
|
colorAccent(color)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
PreferencesController.saveColorAccent(context!!, color)
|
||||||
|
}
|
||||||
|
.positiveButton(R.string.select)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
switchBuffet.setOnClickListener {
|
||||||
|
PreferencesController.saveShowBuffet(context!!, switchBuffet.isChecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectCourse(context: Context) {
|
||||||
|
val courseNameList = ArrayList<String>()
|
||||||
|
coursesList.forEach { (_, courseName) ->
|
||||||
|
courseNameList.add(courseName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// open a new dialog
|
||||||
|
MaterialDialog(context)
|
||||||
|
.title(R.string.select_course)
|
||||||
|
.listItems(items = courseNameList) { _, index, _ ->
|
||||||
|
|
||||||
|
val dialog = MaterialDialog(context).cancelable(false)
|
||||||
|
.cancelOnTouchOutside(false)
|
||||||
|
.customView(R.layout.dialog_loading)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
PreferencesController.saveCourse(context, coursesList[index]) // save the course
|
||||||
|
|
||||||
|
// update current & next weeks timetable
|
||||||
|
val threads = listOf(
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 0, context),
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 1, context)
|
||||||
|
)
|
||||||
|
threads.joinAll() // blocking since we want the new data
|
||||||
|
|
||||||
|
CacheController.readTimetable(cCourse.courseName, 0, context)
|
||||||
|
CacheController.readTimetable(cCourse.courseName, 1, context)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
dialog.dismiss()
|
||||||
|
txtView_Course.text = cCourse.courseName // update txtView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -26,20 +26,19 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
import android.widget.ScrollView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import org.jetbrains.anko.doAsync
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import org.jetbrains.anko.uiThread
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek
|
import kotlinx.android.synthetic.main.fragment_timetable.*
|
||||||
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableNextWeek
|
import kotlinx.coroutines.*
|
||||||
import org.mosad.seil0.projectlaogai.R
|
import org.mosad.seil0.projectlaogai.R
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.DataTypes
|
import org.mosad.seil0.projectlaogai.controller.CacheController
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.timetables
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
|
||||||
|
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar
|
import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar
|
||||||
import org.mosad.seil0.projectlaogai.uicomponents.LessonCardView
|
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
|
||||||
import org.mosad.seil0.projectlaogai.uicomponents.LessonTextView
|
|
||||||
import org.mosad.seil0.projectlaogai.uicomponents.MensaDayCardView
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timetable controller class
|
* The timetable controller class
|
||||||
@ -47,124 +46,116 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class TimeTableFragment : Fragment() {
|
class TimeTableFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var linLayoutTTFragment: LinearLayout
|
private lateinit var scrollViewTimetable: ScrollView
|
||||||
|
private lateinit var faBtnAddLesson: FloatingActionButton
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
|
||||||
val view: View = inflater.inflate(R.layout.fragment_time_table, container, false)
|
val view: View = inflater.inflate(R.layout.fragment_timetable, container, false)
|
||||||
|
scrollViewTimetable = view.findViewById(R.id.scrollView_Timetable)
|
||||||
|
faBtnAddLesson = view.findViewById(R.id.faBtnAddLesson)
|
||||||
|
|
||||||
linLayoutTTFragment = view.findViewById(R.id.linLayout_TTFragment)
|
// init actions
|
||||||
|
initActions()
|
||||||
|
|
||||||
if (cTimeTableCurrentWeek.days.isNotEmpty()) {
|
if (timetables[0].timetable.days.isNotEmpty() && timetables[1].timetable.days.isNotEmpty()) {
|
||||||
addCurrentWeek()
|
addInitWeeks()
|
||||||
} else {
|
} else {
|
||||||
// TODO show card with error msg
|
MaterialDialog(context!!)
|
||||||
|
.title(R.string.error)
|
||||||
|
.message(R.string.timetable_error)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add the remaining days of the current week to the timetable screen
|
* initialize the actions
|
||||||
*/
|
*/
|
||||||
private fun addCurrentWeek() {
|
private fun initActions() = GlobalScope.launch(Dispatchers.Default) {
|
||||||
val dayIndex = NotRetardedCalendar().getDayOfWeekIndex()
|
|
||||||
val formatter = SimpleDateFormat("E dd.MM", Locale.GERMANY) // TODO change to android call when min api is 24
|
|
||||||
val calendar = Calendar.getInstance()
|
|
||||||
|
|
||||||
doAsync {
|
|
||||||
|
|
||||||
uiThread {
|
|
||||||
|
|
||||||
// add current weeks days
|
|
||||||
for (day in dayIndex..5) {
|
|
||||||
val cardViewTimeTableDay = MensaDayCardView(context!!, null)
|
|
||||||
cardViewTimeTableDay.setDayHeading(formatter.format(calendar.time))
|
|
||||||
|
|
||||||
// for each timeslot of the day
|
|
||||||
for ((i, timeslot) in cTimeTableCurrentWeek.days[day].timeslots.withIndex()) {
|
|
||||||
val lessonCardView = LessonCardView(context!!, null)
|
|
||||||
lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]
|
|
||||||
|
|
||||||
//println(timeslot)
|
|
||||||
|
|
||||||
for (lesson in timeslot) {
|
|
||||||
val lessonTxtView = LessonTextView(context!!)
|
|
||||||
lessonTxtView.setLesson(lesson)
|
|
||||||
|
|
||||||
// TODO why does this exist
|
|
||||||
if (lessonTxtView.text.length > 3)
|
|
||||||
lessonCardView.getLinLayoutLesson().addView(lessonTxtView)
|
|
||||||
}
|
|
||||||
|
|
||||||
// only add the lesson if it contains data
|
|
||||||
if (lessonCardView.getLinLayoutLesson().childCount > 1)
|
|
||||||
cardViewTimeTableDay.getLinLayoutMensaDay().addView(lessonCardView)
|
|
||||||
}
|
|
||||||
|
|
||||||
calendar.add(Calendar.DATE, 1)
|
|
||||||
|
|
||||||
// if the day contains no lessons add a text "No lesson today"
|
|
||||||
if (cardViewTimeTableDay.getLinLayoutMensaDay().childCount <= 1) {
|
|
||||||
val lessonTxtView = LessonTextView(context!!)
|
|
||||||
lessonTxtView.setText(resources.getString(R.string.no_lesson_today))
|
|
||||||
|
|
||||||
val lessonCardView = LessonCardView(context!!, null)
|
|
||||||
lessonCardView.getLinLayoutLesson().addView(lessonTxtView)
|
|
||||||
|
|
||||||
cardViewTimeTableDay.getLinLayoutMensaDay().addView(lessonCardView)
|
|
||||||
}
|
|
||||||
|
|
||||||
linLayoutTTFragment.addView(cardViewTimeTableDay)
|
|
||||||
}
|
|
||||||
|
|
||||||
// add next weeks days, max number = dayIndex, if timetable was loaded
|
|
||||||
if (cTimeTableNextWeek.days.isNotEmpty()) {
|
|
||||||
calendar.add(Calendar.DATE, 1) // before this we are at a sunday (no lecture on sundays!)
|
|
||||||
|
|
||||||
for (day in 0..(dayIndex - 1)) {
|
|
||||||
|
|
||||||
val cardViewTimeTableDay = MensaDayCardView(context!!, null)
|
|
||||||
cardViewTimeTableDay.setDayHeading(formatter.format(calendar.time))
|
|
||||||
|
|
||||||
// for each timeslot of the day
|
|
||||||
for ((i, timeslot) in cTimeTableNextWeek.days[day].timeslots.withIndex()) {
|
|
||||||
val lessonCardView = LessonCardView(context!!, null)
|
|
||||||
lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]
|
|
||||||
|
|
||||||
|
|
||||||
for (lesson in timeslot) {
|
|
||||||
val lessonTxtView = LessonTextView(context!!)
|
|
||||||
lessonTxtView.setLesson(lesson)
|
|
||||||
|
|
||||||
if (lessonTxtView.text.length > 3)
|
|
||||||
lessonCardView.getLinLayoutLesson().addView(lessonTxtView)
|
|
||||||
}
|
|
||||||
|
|
||||||
// only add the lesson if it contains data
|
|
||||||
if (lessonCardView.getLinLayoutLesson().childCount > 1)
|
|
||||||
cardViewTimeTableDay.getLinLayoutMensaDay().addView(lessonCardView)
|
|
||||||
}
|
|
||||||
|
|
||||||
calendar.add(Calendar.DATE, 1)
|
|
||||||
|
|
||||||
// if the day contains no lessons add a text "No lesson today"
|
|
||||||
if (cardViewTimeTableDay.getLinLayoutMensaDay().childCount <= 1) {
|
|
||||||
val lessonTxtView = LessonTextView(context!!)
|
|
||||||
lessonTxtView.setText(resources.getString(R.string.no_lesson_today))
|
|
||||||
|
|
||||||
val lessonCardView = LessonCardView(context!!, null)
|
|
||||||
lessonCardView.getLinLayoutLesson().addView(lessonTxtView)
|
|
||||||
|
|
||||||
cardViewTimeTableDay.getLinLayoutMensaDay().addView(lessonCardView)
|
|
||||||
}
|
|
||||||
|
|
||||||
linLayoutTTFragment.addView(cardViewTimeTableDay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
refreshLayout_Timetable.setOnRefreshListener {
|
||||||
|
updateTimetableScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
faBtnAddLesson.setOnClickListener {
|
||||||
|
MaterialDialog(context!!)
|
||||||
|
.title(text = resources.getString(R.string.add_lesson))
|
||||||
|
.message(text = "wähle einen Studiengang aus:\n\nWähle eine Vorlesung aus: \n\n Diese Funktion ist noch nicht verfügbar")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide the btnCardValue if the user is scrolling down
|
||||||
|
scrollViewTimetable.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||||
|
if (scrollY > oldScrollY) {
|
||||||
|
faBtnAddLesson.hide()
|
||||||
|
} else {
|
||||||
|
faBtnAddLesson.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add the current and next weeks lessons
|
||||||
|
*/
|
||||||
|
private fun addInitWeeks() = GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
val currentDayIndex = NotRetardedCalendar.getDayOfWeekIndex()
|
||||||
|
|
||||||
|
addWeek(currentDayIndex, 5, 0).join() // add current week
|
||||||
|
addWeek(0, currentDayIndex - 1, 1) // add next week
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addWeek(startDayIndex: Int, dayEndIndex: Int, weekIndex: Int) = GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
val timetable = timetables[weekIndex].timetable
|
||||||
|
val timetableMeta = timetables[weekIndex].meta
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
|
||||||
|
for (dayIndex in startDayIndex..dayEndIndex) {
|
||||||
|
val dayCardView = DayCardView(context!!)
|
||||||
|
|
||||||
|
// some wired calendar magic, calculate the correct date to be shown ((timetable week - current week * 7) + (dayIndex - dayIndex of current week)
|
||||||
|
val daysToAdd =(timetableMeta.weekNumberYear - NotRetardedCalendar.getWeekOfYear()) * 7 + (dayIndex - NotRetardedCalendar.getDayOfWeekIndex())
|
||||||
|
dayCardView.addTimetableDay(timetable.days[dayIndex], daysToAdd)
|
||||||
|
|
||||||
|
// if there are no lessons don't show the dayCardView
|
||||||
|
if (dayCardView.getLinLayoutDay().childCount > 1)
|
||||||
|
linLayout_Timetable.addView(dayCardView)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateTimetableScreen() = GlobalScope.launch(Dispatchers.Default) {
|
||||||
|
// update the cache
|
||||||
|
val threads = listOf(
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 0, context!!),
|
||||||
|
TCoRAPIController.getTimetable(cCourse.courseName, 1, context!!)
|
||||||
|
)
|
||||||
|
threads.joinAll() // blocking since we want the new data
|
||||||
|
|
||||||
|
CacheController.readTimetable(cCourse.courseName, 0, context!!)
|
||||||
|
CacheController.readTimetable(cCourse.courseName, 1, context!!)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
// remove all menus from the layout
|
||||||
|
linLayout_Timetable.removeAllViews()
|
||||||
|
|
||||||
|
// add the refreshed timetables
|
||||||
|
val dayIndex = NotRetardedCalendar.getDayOfWeekIndex()
|
||||||
|
|
||||||
|
// add current week
|
||||||
|
addWeek(dayIndex, 5, 0).join()
|
||||||
|
|
||||||
|
// add next week
|
||||||
|
addWeek(0, dayIndex - 1, 1)
|
||||||
|
|
||||||
|
refreshLayout_Timetable.isRefreshing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -22,10 +22,55 @@
|
|||||||
|
|
||||||
package org.mosad.seil0.projectlaogai.hsoparser
|
package org.mosad.seil0.projectlaogai.hsoparser
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class DataTypes {
|
class DataTypes {
|
||||||
val times = arrayOf("8.00 - 9.30", "9.45 - 11.15" ,"11.35 - 13.05", "14.00 -15.30", "15.45 - 17.15", "17.30 - 19.00")
|
val times =
|
||||||
|
arrayOf("8.00 - 9.30", "9.45 - 11.15", "11.35 - 13.05", "14.00 -15.30", "15.45 - 17.15", "17.30 - 19.00")
|
||||||
|
|
||||||
|
val primaryColors = intArrayOf(
|
||||||
|
Color.parseColor("#E53935"),
|
||||||
|
Color.parseColor("#E91E63"),
|
||||||
|
Color.parseColor("#9C27B0"),
|
||||||
|
Color.parseColor("#673AB7"),
|
||||||
|
Color.parseColor("#3F51B5"),
|
||||||
|
Color.parseColor("#2196F3"),
|
||||||
|
Color.parseColor("#03A9F4"),
|
||||||
|
Color.parseColor("#00BCD4"),
|
||||||
|
Color.parseColor("#009688"),
|
||||||
|
Color.parseColor("#4CAF50"),
|
||||||
|
Color.parseColor("#8BC34A"),
|
||||||
|
Color.parseColor("#CDDC39"),
|
||||||
|
Color.parseColor("#FDD835"),
|
||||||
|
Color.parseColor("#FFB300"),
|
||||||
|
Color.parseColor("#FB8C00"),
|
||||||
|
Color.parseColor("#FF5722"),
|
||||||
|
Color.parseColor("#795548"),
|
||||||
|
Color.parseColor("#9E9E9E"),
|
||||||
|
Color.parseColor("#607B8B"),
|
||||||
|
Color.parseColor("#000000")
|
||||||
|
)
|
||||||
|
|
||||||
|
val accentColors = intArrayOf(
|
||||||
|
Color.parseColor("#FF1744"),
|
||||||
|
Color.parseColor("#F50057"),
|
||||||
|
Color.parseColor("#D500F9"),
|
||||||
|
Color.parseColor("#3F51B5"),
|
||||||
|
Color.parseColor("#3D5AFE"),
|
||||||
|
Color.parseColor("#2979FF"),
|
||||||
|
Color.parseColor("#00B0FF"),
|
||||||
|
Color.parseColor("#00E5FF"),
|
||||||
|
Color.parseColor("#1DE9B6"),
|
||||||
|
Color.parseColor("#00E676"),
|
||||||
|
Color.parseColor("#C6FF00"),
|
||||||
|
Color.parseColor("#FFD600"),
|
||||||
|
Color.parseColor("#FFC400"),
|
||||||
|
Color.parseColor("#FF9100"),
|
||||||
|
Color.parseColor("#FF3D00"),
|
||||||
|
Color.parseColor("#000000")
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// do something
|
// do something
|
||||||
@ -38,30 +83,70 @@ class DataTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NotRetardedCalendar {
|
class NotRetardedCalendar {
|
||||||
private val calendar = Calendar.getInstance()!!
|
companion object {
|
||||||
|
private val calendar = Calendar.getInstance()
|
||||||
|
|
||||||
fun getDayOfWeekIndex(): Int {
|
fun getDayOfWeekIndex(): Int {
|
||||||
return when(calendar.get(Calendar.DAY_OF_WEEK)) {
|
return when (calendar.get(Calendar.DAY_OF_WEEK)) {
|
||||||
Calendar.MONDAY -> 0
|
Calendar.MONDAY -> 0
|
||||||
Calendar.TUESDAY -> 1
|
Calendar.TUESDAY -> 1
|
||||||
Calendar.WEDNESDAY -> 2
|
Calendar.WEDNESDAY -> 2
|
||||||
Calendar.THURSDAY -> 3
|
Calendar.THURSDAY -> 3
|
||||||
Calendar.FRIDAY -> 4
|
Calendar.FRIDAY -> 4
|
||||||
Calendar.SATURDAY -> 5
|
Calendar.SATURDAY -> 5
|
||||||
Calendar.SUNDAY -> 6
|
Calendar.SUNDAY -> 6
|
||||||
else -> 7
|
else -> 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTomorrowWeekIndex(): Int {
|
||||||
|
return when (calendar.get(Calendar.DAY_OF_WEEK)) {
|
||||||
|
Calendar.MONDAY -> 1
|
||||||
|
Calendar.TUESDAY -> 2
|
||||||
|
Calendar.WEDNESDAY -> 3
|
||||||
|
Calendar.THURSDAY -> 4
|
||||||
|
Calendar.FRIDAY -> 5
|
||||||
|
Calendar.SATURDAY -> 6
|
||||||
|
Calendar.SUNDAY -> 0
|
||||||
|
else -> 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWeekOfYear(): Int {
|
||||||
|
return when (calendar.get(Calendar.DAY_OF_WEEK)) {
|
||||||
|
Calendar.SUNDAY -> Calendar.getInstance().get(Calendar.WEEK_OF_YEAR) - 1
|
||||||
|
else -> Calendar.getInstance().get(Calendar.WEEK_OF_YEAR)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// data classes for the course part
|
||||||
data class Course(val courseLink: String, val courseName: String)
|
data class Course(val courseLink: String, val courseName: String)
|
||||||
|
|
||||||
|
// data classes for the Mensa part
|
||||||
data class Meal(val day: String, val heading: String, val parts: ArrayList<String>, val additives: String)
|
data class Meal(val day: String, val heading: String, val parts: ArrayList<String>, val additives: String)
|
||||||
|
|
||||||
data class Lesson(val lessonSubject: String, val lessonTeacher: String, val lessonRoom:String, val lessonRemark: String)
|
data class Meals(val meals: ArrayList<Meal>)
|
||||||
|
|
||||||
data class TimeTableDay( val timeslots: Array<ArrayList<Lesson>> = Array(6) { ArrayList<Lesson>()})
|
data class MensaWeek(val days: Array<Meals> = Array(7) { Meals(ArrayList()) })
|
||||||
|
|
||||||
data class TimeTable(val days: Array<TimeTableDay> = Array(6) { TimeTableDay() })
|
data class MensaMeta(val updateTime: Long = 0, val mensaName: String = "")
|
||||||
|
|
||||||
|
data class MensaMenu(val meta: MensaMeta = MensaMeta(), val currentWeek: MensaWeek = MensaWeek(), val nextWeek: MensaWeek = MensaWeek())
|
||||||
|
|
||||||
|
// data classes for the timetable part
|
||||||
|
data class Lesson(
|
||||||
|
val lessonSubject: String,
|
||||||
|
val lessonTeacher: String,
|
||||||
|
val lessonRoom: String,
|
||||||
|
val lessonRemark: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class TimetableDay(val timeslots: Array<ArrayList<Lesson>> = Array(6) { ArrayList<Lesson>() })
|
||||||
|
|
||||||
|
data class TimetableWeek(val days: Array<TimetableDay> = Array(6) { TimetableDay() })
|
||||||
|
|
||||||
|
data class TimetableCourseMeta(val updateTime: Long = 0, val courseName: String = "", val weekIndex: Int = 0, val weekNumberYear: Int = 0, val link: String = "")
|
||||||
|
|
||||||
|
data class TimetableCourseWeek(val meta: TimetableCourseMeta = TimetableCourseMeta(), var timetable: TimetableWeek = TimetableWeek())
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
/**
|
|
||||||
* ProjectLaogai
|
|
||||||
*
|
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
|
||||||
*
|
|
||||||
* 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, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.mosad.seil0.projectlaogai.hsoparser
|
|
||||||
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class MensaParser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the mensa menu for the whole week
|
|
||||||
*/
|
|
||||||
fun getMensaMenu(): ArrayList<Meal> {
|
|
||||||
val mealList = ArrayList<Meal>()
|
|
||||||
val menuHTML = Jsoup.connect("https://www.swfr.de/de/essen-trinken/speiseplaene/mensa-offenburg/").get()
|
|
||||||
|
|
||||||
menuHTML.select("#speiseplan-tabs").select("div.tab-content").select("div.menu-tagesplan").forEachIndexed { _, element ->
|
|
||||||
val day = element.select("h3").text()
|
|
||||||
for (i in 0 .. (element.select("div.row h4").size - 1)) {
|
|
||||||
try {
|
|
||||||
val heading = element.select("div.row h4")[i].text()
|
|
||||||
val parts = ArrayList<String>(element.select("div.row").select("div.menu-info")[i].html().substringBefore("<span").replace("<br>", " ").split("\n"))
|
|
||||||
val additives = element.select("div.row").select("div.menu-info")[i].select("span.show-with-allergenes").text()
|
|
||||||
|
|
||||||
mealList.add(Meal(day, heading, parts, additives))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// catch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mealList
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return the mensa menu of a given day (Mon - Sat)
|
|
||||||
*/
|
|
||||||
fun getMensaMenuDay(mealList: ArrayList<Meal>, day: Int): ArrayList<Meal> {
|
|
||||||
val dayMenus = ArrayList<Meal>()
|
|
||||||
val strDay: String = when (day) {
|
|
||||||
Calendar.MONDAY -> "Mon"
|
|
||||||
Calendar.TUESDAY -> "Die"
|
|
||||||
Calendar.WEDNESDAY -> "Mit"
|
|
||||||
Calendar.THURSDAY -> "Don"
|
|
||||||
Calendar.FRIDAY -> "Fre"
|
|
||||||
Calendar.SATURDAY -> "Sam"
|
|
||||||
else -> "TODAY"
|
|
||||||
}
|
|
||||||
|
|
||||||
for (meal in mealList) {
|
|
||||||
if (meal.day.contains(strDay))
|
|
||||||
dayMenus.add(meal)
|
|
||||||
}
|
|
||||||
return dayMenus
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
/**
|
|
||||||
* ProjectLaogai
|
|
||||||
*
|
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
|
||||||
*
|
|
||||||
* 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, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.mosad.seil0.projectlaogai.hsoparser
|
|
||||||
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
|
|
||||||
class TimeTableParser {
|
|
||||||
private val days = arrayOf("Monday", "Tuesday" ,"Wednesday", "Thursday", "Friday", "Saturday")
|
|
||||||
private var courseTTLinkList = ArrayList<Course>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the timetable from the given url
|
|
||||||
* the timetable is organised per row not per column;
|
|
||||||
* Mon 1, Tue 1, Wed 1, Thur 1, Fri 1, Sat 1, Mon 2 and so on
|
|
||||||
*/
|
|
||||||
fun getTimeTable(courseTTURL: String): TimeTable {
|
|
||||||
var timeTableWeek = TimeTable() // this must be a var!
|
|
||||||
val scheduleHTML = Jsoup.connect(courseTTURL).get()
|
|
||||||
|
|
||||||
// create the timetable array
|
|
||||||
/* for (i in 0..5) {
|
|
||||||
var timeTableDay = arrayOf<Lesson>()
|
|
||||||
for (j in 0..5) {
|
|
||||||
timeTableDay += Lesson("", "","","")
|
|
||||||
}
|
|
||||||
timeTableWeek += timeTableDay
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//val week = scheduleHTML.select("h1.timetable-caption").text()
|
|
||||||
//println("$week successful!\n")
|
|
||||||
|
|
||||||
val rows= scheduleHTML.select("table.timetable").select("tr[scope=\"row\"]")
|
|
||||||
var sDay = -1
|
|
||||||
var sRow = -1
|
|
||||||
var sLesson = Lesson("", "", "", "")
|
|
||||||
|
|
||||||
// get each row with index, reflects 1 timeslot per day
|
|
||||||
for ((rowIndex, row) in rows.withIndex()) {
|
|
||||||
var day = 0
|
|
||||||
|
|
||||||
row.select("td.lastcol, td[style]").forEach { element ->
|
|
||||||
// elements are now all lessons, including empty ones
|
|
||||||
|
|
||||||
//println("-------------> Element (Day.Timeslot: " + day + "." + rowIndex + "): " + element)
|
|
||||||
//println("Day.Timeslot: " + day + "." + rowIndex + " lesson: " + element.select("div.lesson-subject").text() + ", " + element.select("div.lesson-teacher").text())
|
|
||||||
|
|
||||||
// if there is a lecture with rowspan="2", we need to shift everything by one to the left. This is stupid and ugly there needs to bee an API
|
|
||||||
if ((sDay > -1 && sRow > -1) && (sDay == day && ((sRow + 1) == rowIndex))) {
|
|
||||||
// we found a lecture that is longer than 1 lesson
|
|
||||||
timeTableWeek.days[day].timeslots[rowIndex].add(sLesson) // this just works if there is one lecture per slot
|
|
||||||
|
|
||||||
// adjust the following slot
|
|
||||||
sDay++
|
|
||||||
sLesson = Lesson(element.select("div.lesson-subject").text(), element.select("div.lesson-teacher").text(), element.select("div.lesson-room").text(), element.select("div.lesson-remark").text())
|
|
||||||
|
|
||||||
// adjust the slot directly as we don't get there anymore
|
|
||||||
if(sDay == 5) {
|
|
||||||
timeTableWeek.days[day + 1].timeslots[rowIndex].add(sLesson)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
timeTableWeek.days[day].timeslots[rowIndex].add(Lesson(element.select("div.lesson-subject").text(), element.select("div.lesson-teacher").text(), element.select("div.lesson-room").text(), element.select("div.lesson-remark").text()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// we found a lecture with rowspan="2", save day, row and lesson for later adjustment
|
|
||||||
if(element.toString().contains("rowspan=\"2\"")) {
|
|
||||||
sDay = day
|
|
||||||
sRow = rowIndex
|
|
||||||
sLesson = timeTableWeek.days[day].timeslots[rowIndex].get(index = 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(element.hasClass("lastcol")) day++
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
printTimeTableWeek(timeTableWeek)
|
|
||||||
|
|
||||||
/*scheduleHTML.select("table.timetable").select("td.lastcol").forEachIndexed { index, element ->
|
|
||||||
//println("Index: " + index + " lesson: " + element.select("div.lesson-subject").text() + ", " + element.select("div.lesson-teacher").text())
|
|
||||||
timeTableWeek[index % 6][index / 6] = Lesson(element.select("div.lesson-subject").text(), element.select("div.lesson-teacher").text(), element.select("div.lesson-room").text(), element.select("div.lesson-remark").text())
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return timeTableWeek
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse all courses from the courses site at https://www.hs-offenburg.de/studium/vorlesungsplaene/
|
|
||||||
*/
|
|
||||||
fun getCourseTTLinks(): ArrayList<Course> {
|
|
||||||
val courseHTML = Jsoup.connect("https://www.hs-offenburg.de/studium/vorlesungsplaene/").get()
|
|
||||||
|
|
||||||
courseHTML.select("ul.index-group").select("li.Class").select("a[href]").forEachIndexed { _, element ->
|
|
||||||
courseTTLinkList.add(Course(element.attr("href").replace("http", "https"),element.text()))
|
|
||||||
}
|
|
||||||
return courseTTLinkList
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun printTimeTableWeekOLD (timeTableWeek: Array<Array<Lesson>>) {
|
|
||||||
|
|
||||||
for (j in 0..5) print(days[j].padEnd(25 ,' ') + " | ")
|
|
||||||
println()
|
|
||||||
for (j in 0..5) print("-".padEnd(26 + (j.toFloat().div(j).toInt()), '-') + "+")
|
|
||||||
println()
|
|
||||||
|
|
||||||
for (i in 0..5) {
|
|
||||||
for (j in 0..5) print(timeTableWeek[j][i].lessonSubject.padEnd(25 ,' ').substring(0,25) + " | ")
|
|
||||||
println()
|
|
||||||
for (j in 0..5) print(timeTableWeek[j][i].lessonTeacher.padEnd(25 ,' ').substring(0,25) + " | ")
|
|
||||||
println()
|
|
||||||
for (j in 0..5) print(timeTableWeek[j][i].lessonRoom.padEnd(25 ,' ').substring(0,25) + " | ")
|
|
||||||
println()
|
|
||||||
for (j in 0..5) print("-".padEnd(26 + (j.toFloat().div(j).toInt()), '-') + "+")
|
|
||||||
println()
|
|
||||||
}
|
|
||||||
println(" \n")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun printTimeTableWeek(timetable: TimeTable) {
|
|
||||||
for (j in 0..5) print(days[j].padEnd(75 ,' ') + " | ")
|
|
||||||
println()
|
|
||||||
for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+")
|
|
||||||
println()
|
|
||||||
|
|
||||||
// the timeslot
|
|
||||||
for (i in 0..5) {
|
|
||||||
|
|
||||||
for (j in 0..5) {
|
|
||||||
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
|
|
||||||
|
|
||||||
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonSubject.padEnd(75/ldiff ,' '))
|
|
||||||
if (ldiff == 2) print(" ")
|
|
||||||
print(" | ")
|
|
||||||
}
|
|
||||||
println()
|
|
||||||
|
|
||||||
for (j in 0..5) {
|
|
||||||
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
|
|
||||||
|
|
||||||
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonTeacher.padEnd(75/ldiff ,' '))
|
|
||||||
if (ldiff == 2) print(" ")
|
|
||||||
print(" | ")
|
|
||||||
}
|
|
||||||
println()
|
|
||||||
|
|
||||||
for (j in 0..5) {
|
|
||||||
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
|
|
||||||
|
|
||||||
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonRoom.padEnd(75/ldiff ,' '))
|
|
||||||
if (ldiff == 2) print(" ")
|
|
||||||
print(" | ")
|
|
||||||
}
|
|
||||||
println()
|
|
||||||
|
|
||||||
for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+")
|
|
||||||
println()
|
|
||||||
}
|
|
||||||
|
|
||||||
println(" \n")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* ProjectLaogai
|
||||||
|
*
|
||||||
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mosad.seil0.projectlaogai.uicomponents
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.cardview.widget.CardView
|
||||||
|
import kotlinx.android.synthetic.main.cardview_day.view.*
|
||||||
|
import org.mosad.seil0.projectlaogai.R
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.DataTypes
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.TimetableDay
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class DayCardView(context: Context) : CardView(context) {
|
||||||
|
|
||||||
|
private val formatter = SimpleDateFormat("E dd.MM", Locale.getDefault())
|
||||||
|
|
||||||
|
init {
|
||||||
|
inflate(context, R.layout.cardview_day,this)
|
||||||
|
|
||||||
|
// workaround to prevent a white border
|
||||||
|
this.setBackgroundColor(Color.TRANSPARENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLinLayoutDay() : LinearLayout {
|
||||||
|
return linLayout_Day
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDayHeading(heading: String) {
|
||||||
|
txtView_DayHeading.text = heading
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add the lessons of one day to the dayCardView
|
||||||
|
* @param timetable a timetable containing the day (and it's lessons) to be added
|
||||||
|
*/
|
||||||
|
fun addTimetableDay(timetable: TimetableDay, daysToAdd: Int) {
|
||||||
|
var lastLesson = LessonLinearLayout(context)
|
||||||
|
|
||||||
|
// set the heading
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
cal.add(Calendar.DATE, daysToAdd)
|
||||||
|
txtView_DayHeading.text = formatter.format(cal.time)
|
||||||
|
|
||||||
|
// for every timeslot of that timetable
|
||||||
|
for ((tsIndex, timeslot) in timetable.timeslots.withIndex()) {
|
||||||
|
|
||||||
|
for (lesson in timeslot) {
|
||||||
|
if (lesson.lessonSubject.isNotEmpty()) {
|
||||||
|
|
||||||
|
val lessonLayout = LessonLinearLayout(context)
|
||||||
|
lessonLayout.setLesson(lesson, DataTypes().getTime()[tsIndex])
|
||||||
|
linLayout_Day.addView(lessonLayout)
|
||||||
|
|
||||||
|
if (lesson != timeslot.last()) {
|
||||||
|
lessonLayout.disableDivider()
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLesson = lessonLayout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLesson.disableDivider() // disable the divider for the last lesson of the day
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -23,32 +23,28 @@
|
|||||||
package org.mosad.seil0.projectlaogai.uicomponents
|
package org.mosad.seil0.projectlaogai.uicomponents
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.view.View
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import androidx.cardview.widget.CardView
|
||||||
|
import kotlinx.android.synthetic.main.linearlayout_lesson.view.*
|
||||||
import org.mosad.seil0.projectlaogai.R
|
import org.mosad.seil0.projectlaogai.R
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.Lesson
|
||||||
|
|
||||||
class LessonCardView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : androidx.cardview.widget.CardView(context, attrs){
|
class LessonLinearLayout(context: Context?) : LinearLayout(context) {
|
||||||
|
|
||||||
private var linLayoutLesson: LinearLayout
|
|
||||||
private var txtViewTime: TextView
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
inflate(context, R.layout.lesson_cardview,this)
|
CardView.inflate(context, R.layout.linearlayout_lesson, this)
|
||||||
|
|
||||||
linLayoutLesson = findViewById(R.id.linLayout_Lesson)
|
|
||||||
txtViewTime = findViewById(R.id.txtView_Time)
|
|
||||||
|
|
||||||
// workaround to prevent a white border
|
|
||||||
this.setBackgroundColor(Color.TRANSPARENT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLinLayoutLesson(): LinearLayout {
|
fun setLesson(lesson: Lesson, time: String) {
|
||||||
return linLayoutLesson
|
txtView_lessonTime.text = time
|
||||||
|
txtView_lessonSubject.text = lesson.lessonSubject
|
||||||
|
txtView_lessonTeacher.text = lesson.lessonTeacher
|
||||||
|
txtView_lessonRoom.text = lesson.lessonRoom
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTxtViewTime(): TextView {
|
fun disableDivider() {
|
||||||
return txtViewTime
|
divider_lesson.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,26 +0,0 @@
|
|||||||
package org.mosad.seil0.projectlaogai.uicomponents
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.widget.TextView
|
|
||||||
import org.mosad.seil0.projectlaogai.hsoparser.Lesson
|
|
||||||
|
|
||||||
class LessonTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : TextView(context, attrs) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
this.setTextColor(Color.BLACK)
|
|
||||||
this.textSize = 16F
|
|
||||||
this.setPadding(0,6,0,0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setLesson(lesson: Lesson) {
|
|
||||||
this.text = resources.getString(org.mosad.seil0.projectlaogai.R.string.string_new_line, lesson.lessonSubject)
|
|
||||||
this.append(lesson.lessonTeacher + "\n")
|
|
||||||
this.append(lesson.lessonRoom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setText(text: String) {
|
|
||||||
this.text = text
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProjectLaogai
|
* ProjectLaogai
|
||||||
*
|
*
|
||||||
* Copyright 2019 <seil0@mosad.xyz>
|
* Copyright 2019-2020 <seil0@mosad.xyz>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -23,32 +23,31 @@
|
|||||||
package org.mosad.seil0.projectlaogai.uicomponents
|
package org.mosad.seil0.projectlaogai.uicomponents
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import androidx.cardview.widget.CardView
|
||||||
|
import kotlinx.android.synthetic.main.linearlayout_meal.view.*
|
||||||
import org.mosad.seil0.projectlaogai.R
|
import org.mosad.seil0.projectlaogai.R
|
||||||
|
import org.mosad.seil0.projectlaogai.hsoparser.Meal
|
||||||
|
|
||||||
class MensaDayCardView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : androidx.cardview.widget.CardView(context, attrs){
|
class MealLinearLayout(context: Context?): LinearLayout(context) {
|
||||||
|
|
||||||
private var linLayoutMensaDay: LinearLayout
|
|
||||||
private var txtViewDayHeading: TextView
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
inflate(context, R.layout.mensaday_cardview,this)
|
CardView.inflate(context, R.layout.linearlayout_meal, this)
|
||||||
|
|
||||||
linLayoutMensaDay = findViewById(R.id.linLayout_MensaDay)
|
|
||||||
txtViewDayHeading = findViewById(R.id.txtView_DayHeading)
|
|
||||||
|
|
||||||
// workaround to prevent a white border
|
|
||||||
//this.setBackgroundColor(Color.TRANSPARENT)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLinLayoutMensaDay(): LinearLayout {
|
fun setMeal(meal: Meal) {
|
||||||
return linLayoutMensaDay
|
txtView_MealHeading.text = meal.heading
|
||||||
|
|
||||||
|
meal.parts.forEachIndexed { partIndex, part ->
|
||||||
|
txtView_Meal.append(part)
|
||||||
|
if(partIndex < (meal.parts.size - 1))
|
||||||
|
txtView_Meal.append("\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDayHeading(heading: String) {
|
fun disableDivider() {
|
||||||
txtViewDayHeading.text = heading
|
divider_meal.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package org.mosad.seil0.projectlaogai.uicomponents
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.widget.TextView
|
|
||||||
import org.mosad.seil0.projectlaogai.R
|
|
||||||
|
|
||||||
class MenuCardView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : androidx.cardview.widget.CardView(context, attrs){
|
|
||||||
|
|
||||||
private var txtViewMenuHeading: TextView
|
|
||||||
private var txtViewMenu: TextView
|
|
||||||
|
|
||||||
init {
|
|
||||||
inflate(context, R.layout.menu_cardview,this)
|
|
||||||
|
|
||||||
txtViewMenuHeading = findViewById(R.id.txtView_MenuHeading)
|
|
||||||
txtViewMenu = findViewById(R.id.txtView_Menu)
|
|
||||||
|
|
||||||
// workaround to prevent a white border
|
|
||||||
this.setBackgroundColor(Color.TRANSPARENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setMenuHeading(heading: String) {
|
|
||||||
txtViewMenuHeading.text = heading
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getTxtViewMenu(): TextView {
|
|
||||||
return txtViewMenu
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<item
|
<item android:drawable="@color/colorPrimary"/>
|
||||||
android:drawable="@color/colorPrimary"/>
|
|
||||||
|
|
||||||
<!--TODO when minAPI is 23 use this-->
|
<item android:gravity="center" android:width="144dp" android:height="144dp">
|
||||||
<!--<item android:gravity="center" android:width="144dp" android:height="144dp">-->
|
|
||||||
<item android:gravity="center">
|
|
||||||
<bitmap
|
<bitmap
|
||||||
android:gravity="fill_horizontal|fill_vertical"
|
android:gravity="fill_horizontal|fill_vertical"
|
||||||
android:src="@mipmap/ic_laogai_icon_splash"/>
|
android:src="@mipmap/ic_laogai_icon_splash"/>
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".fragments.HomeFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/cardView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:clickable="false"
|
|
||||||
android:maxHeight="125dp"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" android:elevation="5dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" android:layout_marginBottom="2dp">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/meal_1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_Menu1Heading"
|
|
||||||
android:textStyle="bold" android:textAlignment="center" android:textSize="16sp"
|
|
||||||
android:typeface="sans" android:fontFamily="sans-serif" android:paddingBottom="5dp"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtViewMenu1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:typeface="sans"
|
|
||||||
android:textIsSelectable="true"/>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/cardView2"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/cardView" android:elevation="5dp"
|
|
||||||
android:layout_marginEnd="5dp" android:layout_marginStart="5dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" android:layout_marginBottom="2dp">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/meal_2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_Menu2Heading"
|
|
||||||
android:textAlignment="center" android:textStyle="bold" android:textSize="16sp"
|
|
||||||
android:typeface="sans" android:fontFamily="sans-serif" android:paddingBottom="5dp"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtViewMenu2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:typeface="sans"
|
|
||||||
android:textIsSelectable="true"/>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/cardView2" android:id="@+id/scrollViewTimeTable"
|
|
||||||
android:background="@color/md_divider_dark_theme" android:paddingTop="6dp"
|
|
||||||
android:layout_marginTop="5dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/linLayoutTimeTable"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/cardView2">
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".fragments.MensaFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/linLayout_MensaFragment"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
>
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
@ -1,147 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".fragments.SettingsFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
>
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="9dp" android:id="@+id/cardView_Info" app:cardElevation="5dp"
|
|
||||||
app:cardUseCompatPadding="true">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/info"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_Info"
|
|
||||||
android:typeface="sans"
|
|
||||||
android:textSize="18sp" android:textStyle="bold" android:fontFamily="sans-serif"
|
|
||||||
android:layout_margin="7dp" android:textColor="@color/colorPrimary"/>
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:padding="7dp" android:id="@+id/linLayout_User">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/sample_user"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_User"
|
|
||||||
android:textSize="16sp" android:textStyle="bold"
|
|
||||||
android:textColor="@android:color/primary_text_light"/>
|
|
||||||
<TextView
|
|
||||||
android:text="@string/user"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_UserDesc"/>
|
|
||||||
</LinearLayout>
|
|
||||||
<View
|
|
||||||
android:id="@+id/divider2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:background="?android:attr/listDivider"
|
|
||||||
/>
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:layout_margin="7dp"
|
|
||||||
android:id="@+id/linLayout_Course">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/sample_course"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_Course"
|
|
||||||
android:textSize="16sp" android:textColor="@android:color/primary_text_light"
|
|
||||||
android:textStyle="bold"/>
|
|
||||||
<TextView
|
|
||||||
android:text="@string/course_desc"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_CourseDesc"/>
|
|
||||||
</LinearLayout>
|
|
||||||
<View
|
|
||||||
android:id="@+id/divider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:background="?android:attr/listDivider"
|
|
||||||
/>
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" android:layout_margin="7dp"
|
|
||||||
android:id="@+id/linLayout_Info">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/about_txtView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_About"
|
|
||||||
android:textStyle="bold" android:textSize="16sp"
|
|
||||||
android:textColor="@android:color/primary_text_light"/>
|
|
||||||
<TextView
|
|
||||||
android:text="@string/version"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_Version"/>
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/cardView_Settings" app:cardElevation="5dp"
|
|
||||||
app:cardUseCompatPadding="true">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/linLayout_Settings">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/settings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_Settings"
|
|
||||||
android:typeface="sans"
|
|
||||||
android:textSize="18sp" android:textColor="@color/design_default_color_primary"
|
|
||||||
android:textStyle="bold" android:layout_margin="7dp" android:paddingTop="3dp"/>
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:layout_margin="7dp"
|
|
||||||
android:gravity="center_vertical|end"
|
|
||||||
android:clickable="true" android:id="@+id/linLayout_MainColor" android:focusable="true">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/primary_color"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_PrimaryColor"
|
|
||||||
android:textSize="16sp" android:textStyle="bold"
|
|
||||||
android:textColor="@android:color/primary_text_light"/>
|
|
||||||
<TextView
|
|
||||||
android:text="@string/main_color_desc"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_PrimaryColorDesc"/>
|
|
||||||
</LinearLayout>
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:layout_margin="7dp"
|
|
||||||
android:gravity="end">
|
|
||||||
<View
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp" android:id="@+id/view_PrimaryColor"
|
|
||||||
android:background="?colorPrimary"/>
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</FrameLayout>
|
|
@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardBackgroundColor="@android:color/background_light"
|
|
||||||
app:cardElevation="5dp"
|
|
||||||
app:cardUseCompatPadding="true" app:cardPreventCornerOverlap="false" app:contentPadding="5dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" android:id="@+id/linLayout_Lesson">
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtView_Time"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="end"
|
|
||||||
android:text="@string/a_time"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="9dp"
|
|
||||||
app:cardElevation="5dp"
|
|
||||||
app:cardBackgroundColor="@color/colorMensaDay"
|
|
||||||
app:cardUseCompatPadding="true">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/linLayout_MensaDay" android:paddingBottom="5dp">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/sample_date"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_DayHeading" android:textAlignment="center"
|
|
||||||
android:textSize="20sp" android:textStyle="bold" android:typeface="sans" android:paddingBottom="5dp"/>
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
@ -1,42 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/cardView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="5dp"
|
|
||||||
android:layout_marginTop="5dp"
|
|
||||||
android:layout_marginEnd="5dp"
|
|
||||||
android:clickable="false"
|
|
||||||
android:maxHeight="125dp"
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
<TextView
|
|
||||||
android:text="@string/meal_1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" android:id="@+id/txtView_MenuHeading"
|
|
||||||
android:textStyle="bold" android:textAlignment="center" android:textSize="16sp"
|
|
||||||
android:typeface="sans" android:fontFamily="sans-serif" android:paddingBottom="5dp"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtView_Menu"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:typeface="sans"
|
|
||||||
android:textIsSelectable="true"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
@ -20,7 +20,9 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
|
android:background="?themeSecondary"
|
||||||
app:headerLayout="@layout/nav_header_main"
|
app:headerLayout="@layout/nav_header_main"
|
||||||
|
app:itemTextColor="?colorAccent"
|
||||||
app:menu="@menu/activity_main_drawer"/>
|
app:menu="@menu/activity_main_drawer"/>
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
</androidx.drawerlayout.widget.DrawerLayout>
|
21
app/src/main/res/layouts/activities/layout/cardview_day.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:cardElevation="5dp"
|
||||||
|
app:cardBackgroundColor="?themeSecondary"
|
||||||
|
>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" android:id="@+id/linLayout_Day">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/sample_date"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/txtView_DayHeading" android:textSize="18sp"
|
||||||
|
android:textAlignment="center" android:textStyle="bold" android:textColor="?colorAccent"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/linLayout_lesson"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="7dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingRight="7dp"
|
||||||
|
android:paddingBottom="3dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_lessonSubject"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_lessonTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text="@string/a_time"
|
||||||
|
android:textColor="@color/textSecondary" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_lessonTeacher"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_lessonRoom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider_lesson"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="?dividerColor" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/linLayout_Meal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="7dp"
|
||||||
|
android:paddingTop="2dp"
|
||||||
|
android:paddingRight="7dp"
|
||||||
|
android:paddingBottom="2dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_MealHeading"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:text="@string/meal"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_Meal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider_meal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:background="?dividerColor" />
|
||||||
|
</LinearLayout>
|
@ -19,7 +19,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||||
app:srcCompat="@mipmap/ic_laogai_icon"
|
app:srcCompat="@mipmap/ic_laogai_icon"
|
||||||
android:contentDescription="@string/nav_header_desc"
|
android:contentDescription="@string/app_name"
|
||||||
android:id="@+id/imageView"/>
|
android:id="@+id/imageView"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -27,7 +27,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||||
android:text="@string/nav_header_title"
|
android:text="@string/nav_header_title"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:id="@+id/txtView_nav_header_title"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
33
app/src/main/res/layouts/activities/values/css_styles.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="license_dialog_style_light" translatable="false">
|
||||||
|
body {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
font-family: sans-serif;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
padding: 1em;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
</string>
|
||||||
|
|
||||||
|
<string name="license_dialog_style_dark" translatable="false">
|
||||||
|
body {
|
||||||
|
background-color: #303030;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: sans-serif;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background-color: #424242;
|
||||||
|
padding: 1em;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
li a {
|
||||||
|
color: #21a3df;
|
||||||
|
}
|
||||||
|
</string>
|
||||||
|
</resources>
|
43
app/src/main/res/layouts/activities/xml/shortcuts.xml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="mensa"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_local_dining_black_24dp"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_mensa_short"
|
||||||
|
android:shortcutLongLabel="@string/shortcut_mensa_long"
|
||||||
|
android:shortcutDisabledMessage="@string/shortcut_mensa_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="org.mosad.seil0.projectlaogai.fragments.MensaFragment"
|
||||||
|
android:targetPackage="org.mosad.seil0.projectlaogai"
|
||||||
|
android:targetClass="org.mosad.seil0.projectlaogai.MainActivity" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="timetable"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_baseline_calendar_today_24dp"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_timetable_short"
|
||||||
|
android:shortcutLongLabel="@string/shortcut_timetable_long"
|
||||||
|
android:shortcutDisabledMessage="@string/shortcut_timetable_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="org.mosad.seil0.projectlaogai.fragments.TimeTableFragment"
|
||||||
|
android:targetPackage="org.mosad.seil0.projectlaogai"
|
||||||
|
android:targetClass="org.mosad.seil0.projectlaogai.MainActivity" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
|
||||||
|
<shortcut
|
||||||
|
android:shortcutId="moodle"
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_school_black_24dp"
|
||||||
|
android:shortcutShortLabel="@string/shortcut_moodle_short"
|
||||||
|
android:shortcutLongLabel="@string/shortcut_moodle_long"
|
||||||
|
android:shortcutDisabledMessage="@string/shortcut_moodle_disabled">
|
||||||
|
<intent
|
||||||
|
android:action="org.mosad.seil0.projectlaogai.fragments.MoodleFragment"
|
||||||
|
android:targetPackage="org.mosad.seil0.projectlaogai"
|
||||||
|
android:targetClass="org.mosad.seil0.projectlaogai.MainActivity" />
|
||||||
|
<categories android:name="android.shortcut.conversation" />
|
||||||
|
</shortcut>
|
||||||
|
</shortcuts>
|
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/linLayout_mensacredit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_Heading"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/mensa_credit"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_current"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="7dp"
|
||||||
|
android:layout_marginBottom="7dp"
|
||||||
|
android:text="16,95 €"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtView_last"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/mensa_last"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
@ -3,7 +3,8 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".fragments.TimeTableFragment">
|
tools:context=".fragments.HomeFragment"
|
||||||
|
android:background="?themePrimary">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -15,9 +16,11 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" android:id="@+id/linLayout_TTFragment">
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/linLayout_Home">
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
28
app/src/main/res/layouts/fragments/layout/fragment_mensa.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".fragments.MensaFragment"
|
||||||
|
android:background="?themePrimary">
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/refreshLayout_Mensa">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/scrollView_Mensa">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="false"
|
||||||
|
android:id="@+id/linLayout_Mensa"/>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</FrameLayout>
|
@ -8,6 +8,7 @@
|
|||||||
<!-- TODO: Update blank fragment layout -->
|
<!-- TODO: Update blank fragment layout -->
|
||||||
<WebView
|
<WebView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" android:id="@+id/webView"/>
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/webView"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
283
app/src/main/res/layouts/fragments/layout/fragment_settings.xml
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".fragments.SettingsFragment"
|
||||||
|
android:background="?themePrimary">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="9dp"
|
||||||
|
android:id="@+id/cardView_Info"
|
||||||
|
app:cardElevation="5dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:cardBackgroundColor="?themeSecondary">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_Info"
|
||||||
|
android:typeface="sans"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
android:layout_margin="7dp"
|
||||||
|
android:textColor="?colorAccent"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="7dp"
|
||||||
|
android:id="@+id/linLayout_User">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/sample_user"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_User"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/user"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_UserDesc"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?dividerColor"
|
||||||
|
/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="7dp"
|
||||||
|
android:id="@+id/linLayout_Course">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/sample_course"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_Course"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/course_desc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_CourseDesc"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?dividerColor"
|
||||||
|
/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_margin="7dp"
|
||||||
|
android:id="@+id/linLayout_About">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/about_txtView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_About"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="16sp"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/about_version"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_AboutDesc"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider6"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?dividerColor"
|
||||||
|
/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" android:id="@+id/linLayout_Licence"
|
||||||
|
android:layout_margin="7dp">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/licenses"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/textView3"
|
||||||
|
android:textStyle="bold" android:textSize="16sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/cardView_Settings"
|
||||||
|
app:cardElevation="5dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:cardBackgroundColor="?themeSecondary">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/linLayout_Settings"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_Settings"
|
||||||
|
android:typeface="sans"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textColor="?colorAccent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingTop="3dp" android:layout_margin="7dp"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" android:id="@+id/linLayout_Theme"
|
||||||
|
android:layout_margin="7dp">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/theme"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/txtView_Theme"
|
||||||
|
android:textSize="16sp" android:textStyle="bold"/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/themeLight"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" android:id="@+id/txtView_SelectedTheme"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?dividerColor"
|
||||||
|
/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical|end"
|
||||||
|
android:clickable="true"
|
||||||
|
android:id="@+id/linLayout_PrimaryColor"
|
||||||
|
android:focusable="true" android:layout_margin="7dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/primary_color"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_PrimaryColor"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/primary_color_desc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_PrimaryColorDesc"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="7dp"
|
||||||
|
android:gravity="end">
|
||||||
|
<View
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:id="@+id/view_PrimaryColor"
|
||||||
|
android:background="?colorPrimary"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider4"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?dividerColor"
|
||||||
|
/>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical|end"
|
||||||
|
android:clickable="true"
|
||||||
|
android:id="@+id/linLayout_AccentColor"
|
||||||
|
android:focusable="true" android:layout_margin="7dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/accent_color"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_AccentColor"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:text="@string/accent_color_desc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/txtView_AccentColorDesc"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="7dp"
|
||||||
|
android:gravity="end">
|
||||||
|
<View
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:id="@+id/view_AccentColor"
|
||||||
|
android:background="?colorAccent"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider5"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?android:attr/listDivider"
|
||||||
|
/>
|
||||||
|
<Switch
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/switch_buffet"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/show_buffet"
|
||||||
|
android:layout_margin="7dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</FrameLayout>
|
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".fragments.TimeTableFragment"
|
||||||
|
android:background="?themePrimary">
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/refreshLayout_Timetable">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" android:id="@+id/scrollView_Timetable">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="false"
|
||||||
|
android:id="@+id/linLayout_Timetable"/>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:src="@drawable/icon_custom_black"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="true" android:id="@+id/faBtnAddLesson" android:elevation="7dp"
|
||||||
|
android:visibility="gone" android:layout_gravity="bottom|center|end" android:layout_marginEnd="11dp"
|
||||||
|
android:layout_marginBottom="11dp" android:focusable="true"/>
|
||||||
|
</FrameLayout>
|
@ -4,7 +4,10 @@
|
|||||||
tools:showIn="navigation_view">
|
tools:showIn="navigation_view">
|
||||||
|
|
||||||
<group android:checkableBehavior="single">
|
<group android:checkableBehavior="single">
|
||||||
<item android:id="@+id/nav_home" android:title="@string/home" android:icon="@drawable/ic_baseline_home_24dp"/>
|
<item
|
||||||
|
android:id="@+id/nav_home"
|
||||||
|
android:title="@string/home"
|
||||||
|
android:icon="@drawable/ic_baseline_home_24dp"/>
|
||||||
<item
|
<item
|
||||||
android:id="@+id/nav_mensa"
|
android:id="@+id/nav_mensa"
|
||||||
android:icon="@drawable/ic_local_dining_black_24dp"
|
android:icon="@drawable/ic_local_dining_black_24dp"
|
||||||
|
39
app/src/main/res/raw/notices.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<notices>
|
||||||
|
<notice>
|
||||||
|
<name>Material Dialogs</name>
|
||||||
|
<url>https://github.com/afollestad/material-dialogs</url>
|
||||||
|
<copyright>Copyright (C) Aidan Follestad</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>Aesthetic</name>
|
||||||
|
<url>https://github.com/afollestad/aesthetic</url>
|
||||||
|
<copyright>Copyright Aidan Follestad</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>gson</name>
|
||||||
|
<url>https://github.com/google/gson</url>
|
||||||
|
<copyright>Copyright 2008 Google Inc.</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>kotlinx.coroutines</name>
|
||||||
|
<url>https://github.com/Kotlin/kotlinx.coroutines</url>
|
||||||
|
<copyright>Copyright JetBrains</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>farebot part for desfire cards</name>
|
||||||
|
<url>https://github.com/codebutler/farebot</url>
|
||||||
|
<copyright>Copyright 2011-2012, 2014, 2016 Eric Butler</copyright>
|
||||||
|
<license>GNU General Public License 3.0</license>
|
||||||
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>Android Support Libraries</name>
|
||||||
|
<url>http://developer.android.com/tools/support-library/index.html</url>
|
||||||
|
<copyright>Copyright (C) 2016 The Android Open Source Project</copyright>
|
||||||
|
<license>Apache Software License 2.0</license>
|
||||||
|
</notice>
|
||||||
|
</notices>
|
@ -1,29 +1,66 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="navigation_drawer_close">Navigationsleiste schließen</string>
|
||||||
|
<string name="navigation_drawer_open">Navigationsleiste öffnen</string>
|
||||||
|
|
||||||
|
<!-- nav-view -->
|
||||||
<string name="home">Home</string>
|
<string name="home">Home</string>
|
||||||
<string name="mensa">Mensa</string>
|
<string name="mensa">Mensa</string>
|
||||||
<string name="timetable">Stundenplan</string>
|
<string name="timetable">Stundenplan</string>
|
||||||
<string name="moodle">Moodle</string>
|
<string name="moodle">Moodle</string>
|
||||||
<string name="settings">Einstellungen</string>
|
<string name="settings">Einstellungen</string>
|
||||||
<string name="meal_1">Essen 1</string>
|
|
||||||
<string name="meal_2">Essen 2</string>
|
<!-- fragment_home -->
|
||||||
<string name="meal_1_tomorrow">Essen 1, Morgen</string>
|
<string name="meal">Essen</string>
|
||||||
<string name="meal_2_tomorrow">Essen 1, Morgen</string>
|
<string name="today_date">Heute, %1$s</string>
|
||||||
<string name="no_meal_today">heute keine Essensausgabe</string>
|
<string name="tomorrow_date">Morgen, %1$s</string>
|
||||||
<string name="no_meal_tomorrow">morgen keine Essensausgabe</string>
|
<string name="mensa_closed">keine Essensausgabe</string>
|
||||||
<string name="no_more_food">Diese Woche keine weitere Essensausgabe</string>
|
<string name="no_more_food">Diese Woche keine weitere Essensausgabe</string>
|
||||||
<string name="no_lesson_today">heute keine Vorlesung!</string>
|
<string name="no_lesson_today">heute keine Vorlesung!</string>
|
||||||
<string name="error">Fehler</string>
|
|
||||||
<string name="no_tt_error">Stundenplan konnte nicht geladen werden!</string>
|
<!-- fragment_timetable -->
|
||||||
<string name="gen_tt_error">Allgemeiner Stundenplan Fehler!"</string>
|
<string name="add_lesson">Eine Vorlesung hinzufügen</string>
|
||||||
|
|
||||||
|
<!-- fragment_settings -->
|
||||||
<string name="info">Info</string>
|
<string name="info">Info</string>
|
||||||
<string name="user">Benutzer</string>
|
<string name="user">Benutzer</string>
|
||||||
<string name="course_desc">Tippen, um den Kurs zu ändern</string>
|
<string name="course_desc">Tippe um den Kurs zu ändern</string>
|
||||||
|
<string name="about_dialog_heading">Über</string>
|
||||||
|
<string name="licenses">Lizenzen</string>
|
||||||
|
<string name="theme">Design</string>
|
||||||
|
<string name="themeLight">Hell</string>
|
||||||
|
<string name="themeDark">Dunkel</string>
|
||||||
|
<string name="themeBlack">Schwarz</string>
|
||||||
<string name="primary_color">Primärfarbe</string>
|
<string name="primary_color">Primärfarbe</string>
|
||||||
<string name="main_color_desc">Die Primärfarbe, standard ist Schwarz</string>
|
<string name="primary_color_desc">Zum Ändern tippen, Standard ist Schwarz.</string>
|
||||||
<string name="select">auswählen</string>
|
<string name="accent_color">Akzentfarbe</string>
|
||||||
<string name="about">über</string>
|
<string name="accent_color_desc">Zum Ändern tippen, Standard ist Indigo.</string>
|
||||||
|
<string name="show_buffet">Buffet immer anzeigen</string>
|
||||||
|
|
||||||
|
<!-- dialogs -->
|
||||||
|
<string name="select_course">Wähle deinen Studiengang</string>
|
||||||
<string name="loading_timetable">lade Stundenplan …</string>
|
<string name="loading_timetable">lade Stundenplan …</string>
|
||||||
<string name="navigation_drawer_close">Navigationsleiste schließen</string>
|
<string name="select">auswählen</string>
|
||||||
<string name="navigation_drawer_open">Navigationsleiste öffnen</string>
|
<string name="close">schließen</string>
|
||||||
|
<string name="mensa_credit">Mensa-Guthaben</string>
|
||||||
|
<string name="mensa_current">aktuell: %1$s\n</string>
|
||||||
|
<string name="mensa_last">letzte Abbuchung: %1$s</string>
|
||||||
|
|
||||||
|
<!-- errors -->
|
||||||
|
<string name="error">Fehler</string>
|
||||||
|
<string name="timetable_error">Stundenplan konnte nicht geladen werden!</string>
|
||||||
|
|
||||||
|
<!-- shortcuts -->
|
||||||
|
<string name="shortcut_mensa_short">Mensa</string>
|
||||||
|
<string name="shortcut_mensa_long">Mensa</string>
|
||||||
|
<string name="shortcut_mensa_disabled">Mensa deaktiviert</string>
|
||||||
|
|
||||||
|
<string name="shortcut_timetable_short">Stundenplan</string>
|
||||||
|
<string name="shortcut_timetable_long">Stundenplan</string>
|
||||||
|
<string name="shortcut_timetable_disabled">Stundenplan deaktiviert</string>
|
||||||
|
|
||||||
|
<string name="shortcut_moodle_short">Moodle</string>
|
||||||
|
<string name="shortcut_moodle_long">Moodle</string>
|
||||||
|
<string name="shortcut_moodle_disabled">Moodle deaktiviert</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
9
app/src/main/res/values/attrs.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<attr format="string" name="themeName"/>
|
||||||
|
<attr format="color" name="themePrimary"/>
|
||||||
|
<attr format="color" name="themeSecondary"/>
|
||||||
|
<attr format="color" name="textPrimary"/>
|
||||||
|
<attr format="color" name="textSecondary"/>
|
||||||
|
<attr format="color" name="dividerColor"/>
|
||||||
|
</resources>
|
@ -2,7 +2,27 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#000000</color>
|
<color name="colorPrimary">#000000</color>
|
||||||
<color name="colorPrimaryDark">#000000</color>
|
<color name="colorPrimaryDark">#000000</color>
|
||||||
<color name="colorAccent">#d81b60</color>
|
<color name="colorAccent">#3f51b5</color>
|
||||||
<color name="colorMensaDay">#ebe8e9</color>
|
<color name="ic_launcher_background">#ffffff</color>
|
||||||
<color name="ic_launcher_background">#FFFFFF</color>
|
|
||||||
|
<color name="themePrimary">#ffffff</color>
|
||||||
|
<color name="themeSecondary">#ffffff</color>
|
||||||
|
<color name="textPrimary">#000000</color>
|
||||||
|
<color name="textSecondary">#818181</color>
|
||||||
|
|
||||||
|
<!--theme color section, not the working colors-->
|
||||||
|
<color name="themePrimaryDark">#000000</color>
|
||||||
|
<color name="themeSecondaryDark">#303030</color>
|
||||||
|
<color name="themeTertiaryDark">#424242</color>
|
||||||
|
<color name="textPrimaryDark">#ffffff</color>
|
||||||
|
<color name="textSecondaryDark">#e0e0e0</color>
|
||||||
|
<color name="dividerDark">#232323</color>
|
||||||
|
|
||||||
|
<color name="themePrimaryLight">#ffffff</color>
|
||||||
|
<color name="themeSecondaryLight">#ffffff</color>
|
||||||
|
<color name="textPrimaryLight">#000000</color>
|
||||||
|
<color name="textSecondaryLight">#818181</color>
|
||||||
|
<color name="dividerLight">#e0e0e0</color>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -2,54 +2,88 @@
|
|||||||
<string name="app_name" translatable="false">Project Laogai</string>
|
<string name="app_name" translatable="false">Project Laogai</string>
|
||||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||||
<string name="nav_header_title" translatable="false">hso App 0.3.3</string>
|
<string name="nav_header_title" translatable="false">Project Laogai</string>
|
||||||
<string name="nav_header_subtitle" translatable="false">seil0@mosad.xyz</string>
|
<string name="nav_header_subtitle" translatable="false">seil0@mosad.xyz</string>
|
||||||
<string name="nav_header_desc" translatable="false">Project Laogai</string>
|
|
||||||
|
|
||||||
|
<!-- nav-view -->
|
||||||
<string name="home">Home</string>
|
<string name="home">Home</string>
|
||||||
<string name="mensa">Mensa</string>
|
<string name="mensa">Mensa</string>
|
||||||
<string name="timetable">Timetable</string>
|
<string name="timetable">Timetable</string>
|
||||||
<string name="moodle">Moodle</string>
|
<string name="moodle">Moodle</string>
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
|
|
||||||
<string name="meal_1">Meal 1</string>
|
<!-- fragment_home -->
|
||||||
<string name="meal_2">Meal 2</string>
|
<string name="meal">Meal</string>
|
||||||
<string name="meal_1_tomorrow">Meal 1, tomorrow</string>
|
<string name="today_date">Today, %1$s</string>
|
||||||
<string name="meal_2_tomorrow">Meal 2, tomorrow</string>
|
<string name="tomorrow_date">Tomorrow, %1$s</string>
|
||||||
<string name="no_meal_today">Mensa closed today</string>
|
<string name="mensa_closed">the Mensa is closed</string>
|
||||||
<string name="no_meal_tomorrow">Mensa closed tomorrow</string>
|
|
||||||
<string name="no_more_food">No more Food this week</string>
|
<string name="no_more_food">No more Food this week</string>
|
||||||
|
|
||||||
<string name="no_lesson_today">"No lecture today!"</string>
|
<string name="no_lesson_today">"No lecture today!"</string>
|
||||||
<string name="error">Error</string>
|
|
||||||
<string name="no_tt_error">Could not load timetable!"</string>
|
|
||||||
<string name="gen_tt_error">There was an error with the timetable!"</string>
|
|
||||||
|
|
||||||
<string name="sample_user" translatable="false">SampleUser@stud.hs-offenburg.de</string>
|
<!-- fragment_timetable -->
|
||||||
<string name="sample_course" translatable="false">SampleCourse 3</string>
|
<string name="add_lesson">Add a lesson</string>
|
||||||
<string name="sample_date" translatable="false">Montag, 30.02</string>
|
|
||||||
<string name="a_lesson" translatable="false">a lesson\na teacher\na room</string>
|
|
||||||
<string name="a_time" translatable="false">0.00 – 23.59</string>
|
|
||||||
|
|
||||||
|
<!-- fragment_settings -->
|
||||||
<string name="info">Info</string>
|
<string name="info">Info</string>
|
||||||
<string name="user">User</string>
|
<string name="user">User</string>
|
||||||
<string name="course_desc">Tap to change course</string>
|
<string name="course_desc">Tap to change course</string>
|
||||||
<string name="primary_color">primary color</string>
|
<string name="about_dialog_heading">About</string>
|
||||||
<string name="main_color_desc">The primary color, default is black</string>
|
<string name="about_dialog_text" translatable="false">"This software is made by @Seil0 and is published under the terms and conditions of GPL 3. For further information visit \ngit.mosad.xyz/Seil0/ProjectLaogai \n\n© 2018-2020 seil0@mosad.xyz "</string>
|
||||||
<string name="select">select</string>
|
|
||||||
<string name="version" translatable="false">version 0.3.3</string>
|
|
||||||
<string name="about">about</string>
|
|
||||||
<string name="about_txtView" translatable="false">hso App by @Seil0</string>
|
<string name="about_txtView" translatable="false">hso App by @Seil0</string>
|
||||||
<string name="about_text" translatable="false">"This software is made by @Seil0 and is published under the terms and
|
<string name="about_version" translatable="false">Version %1$s (%2$s)</string>
|
||||||
conditions of GPL 3. For further information visit \ngit.mosad.xyz/Seil0/ProjectLaogai \n\n© 2018-2019
|
<string name="licenses">Licenses</string>
|
||||||
seil0@mosad.xyz "
|
<string name="theme">Theme</string>
|
||||||
</string>
|
<string name="themeLight">Light</string>
|
||||||
|
<string name="themeDark">Dark</string>
|
||||||
|
<string name="themeBlack">Black</string>
|
||||||
|
<string name="primary_color">Primary color</string>
|
||||||
|
<string name="primary_color_desc">Tap to change, default is black.</string>
|
||||||
|
<string name="accent_color">Accent color</string>
|
||||||
|
<string name="accent_color_desc">Tap to change, default is indigo.</string>
|
||||||
|
<string name="show_buffet">Always show buffet</string>
|
||||||
|
|
||||||
|
<!-- dialogs -->
|
||||||
|
<string name="select_course">Select your course</string>
|
||||||
<string name="loading_timetable">loading timetable …</string>
|
<string name="loading_timetable">loading timetable …</string>
|
||||||
|
<string name="select">select</string>
|
||||||
|
<string name="close">close</string>
|
||||||
|
<string name="mensa_credit">Mensa credit</string>
|
||||||
|
<string name="mensa_current">current: %1$s\n</string>
|
||||||
|
<string name="mensa_last">last: %1$s</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="string_new_line" translatable="false">%1$s\n</string>
|
<!-- sample strings -->
|
||||||
|
<string name="sample_user" translatable="false">spinefield@stud.hs-offenburg.de</string>
|
||||||
|
<string name="sample_course" translatable="false">SampleCourse 3</string>
|
||||||
|
<string name="sample_date" translatable="false">Montag, 30.02</string>
|
||||||
|
<string name="a_time" translatable="false">0.00 – 23.59</string>
|
||||||
|
|
||||||
|
<!-- errors -->
|
||||||
|
<string name="error">Error</string>
|
||||||
|
<string name="timetable_error">Could not load timetable!"</string>
|
||||||
|
|
||||||
|
<!-- shortcuts -->
|
||||||
|
<string name="shortcut_mensa_short">Mensa</string>
|
||||||
|
<string name="shortcut_mensa_long">Mensa</string>
|
||||||
|
<string name="shortcut_mensa_disabled">Mensa disabled</string>
|
||||||
|
|
||||||
|
<string name="shortcut_timetable_short">Timetable</string>
|
||||||
|
<string name="shortcut_timetable_long">Timetable</string>
|
||||||
|
<string name="shortcut_timetable_disabled">Timetable disabled</string>
|
||||||
|
|
||||||
|
<string name="shortcut_moodle_short">Moodle</string>
|
||||||
|
<string name="shortcut_moodle_long">Moodle</string>
|
||||||
|
<string name="shortcut_moodle_disabled">Moodle disabled</string>
|
||||||
|
|
||||||
|
<!-- save keys -->
|
||||||
|
<string name="preference_file_key" translatable="false">org.mosad.seil0.projectlaogai_preferences</string>
|
||||||
<string name="save_key_course" translatable="false">org.mosad.seil0.projectlaogai.course</string>
|
<string name="save_key_course" translatable="false">org.mosad.seil0.projectlaogai.course</string>
|
||||||
<string name="save_key_courseTTLink" translatable="false">org.mosad.seil0.projectlaogai.courseTTLink</string>
|
<string name="save_key_courseTTLink" translatable="false">org.mosad.seil0.projectlaogai.courseTTLink</string>
|
||||||
<string name="save_key_colorPrimary" translatable="false">org.mosad.seil0.projectlaogai.colorPrimary</string>
|
<string name="save_key_colorPrimary" translatable="false">org.mosad.seil0.projectlaogai.colorPrimary</string>
|
||||||
|
<string name="save_key_colorAccent" translatable="false">org.mosad.seil0.projectlaogai.colorAccent</string>
|
||||||
|
<string name="save_key_showBuffet" translatable="false">org.mosad.seil0.projectlaogai.showBuffet</string>
|
||||||
|
<string name="save_key_coursesCacheTime" translatable="false">org.mosad.seil0.projectlaogai.coursesCacheTime</string>
|
||||||
|
<string name="save_key_mensaCacheTime" translatable="false">org.mosad.seil0.projectlaogai.mensaCacheTime</string>
|
||||||
|
<string name="save_key_timetableCacheTime" translatable="false">org.mosad.seil0.projectlaogai.timetableCacheTime</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,11 +1,50 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
<item name="md_color_button_text">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.Light" parent="AppTheme">
|
||||||
|
<item name="themeName">light</item>
|
||||||
|
<item name="themePrimary">@color/themePrimaryLight</item>
|
||||||
|
<item name="themeSecondary">@color/themeSecondaryLight</item>
|
||||||
|
<item name="android:textColor">@color/textPrimaryLight</item>
|
||||||
|
<item name="android:textColorPrimary">@color/textPrimaryLight</item>
|
||||||
|
<item name="textPrimary">@color/textPrimaryLight</item>
|
||||||
|
<item name="dividerColor">@color/dividerLight</item>
|
||||||
|
<item name="md_background_color">@color/themeSecondaryLight</item>
|
||||||
|
<item name="md_color_title">@color/textPrimaryLight</item>
|
||||||
|
<item name="md_color_content">@color/textPrimaryLight</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.Dark" parent="AppTheme">
|
||||||
|
<item name="themeName">dark</item>
|
||||||
|
<item name="themePrimary">@color/themePrimaryDark</item>
|
||||||
|
<item name="themeSecondary">@color/themeSecondaryDark</item>
|
||||||
|
<item name="android:textColor">@color/textPrimaryDark</item>
|
||||||
|
<item name="android:textColorPrimary">@color/textPrimaryDark</item>
|
||||||
|
<item name="textPrimary">@color/textPrimaryDark</item>
|
||||||
|
<item name="dividerColor">@color/dividerDark</item>
|
||||||
|
<item name="md_background_color">@color/themeSecondaryDark</item>
|
||||||
|
<item name="md_color_title">@color/textPrimaryDark</item>
|
||||||
|
<item name="md_color_content">@color/textPrimaryDark</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.Black" parent="AppTheme">
|
||||||
|
<item name="themeName">black</item>
|
||||||
|
<item name="themePrimary">@color/themePrimaryDark</item>
|
||||||
|
<item name="themeSecondary">@color/themePrimaryDark</item>
|
||||||
|
<item name="android:textColor">@color/textPrimaryDark</item>
|
||||||
|
<item name="android:textColorPrimary">@color/textPrimaryDark</item>
|
||||||
|
<item name="textPrimary">@color/textPrimaryDark</item>
|
||||||
|
<item name="dividerColor">@color/dividerDark</item>
|
||||||
|
<item name="md_background_color">@color/themeSecondaryDark</item>
|
||||||
|
<item name="md_color_title">@color/textPrimaryDark</item>
|
||||||
|
<item name="md_color_content">@color/textPrimaryDark</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.NoActionBar">
|
<style name="AppTheme.NoActionBar">
|
||||||
@ -19,4 +58,8 @@
|
|||||||
<item name="android:windowBackground">@drawable/background_splash</item>
|
<item name="android:windowBackground">@drawable/background_splash</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="LicensesDialogTheme.Dark" parent="Theme.AppCompat.Dialog">
|
||||||
|
<item name="android:windowBackground">@color/themeSecondaryDark</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
5
app/src/main/res/xml/backup_descriptor.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<full-backup-content>
|
||||||
|
<exclude domain="file" path="mensa.json"/>
|
||||||
|
<exclude domain="file" path="courses.json"/>
|
||||||
|
</full-backup-content>
|
6
app/src/main/res/xml/nfc_tech_filter.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<tech-list>
|
||||||
|
<tech>android.nfc.tech.NfcA</tech>
|
||||||
|
</tech-list>
|
||||||
|
</resources>
|
@ -1,13 +1,13 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.21'
|
ext.kotlin_version = '1.3.61'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.3.1'
|
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
@ -19,7 +19,6 @@ allprojects {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven { url "https://dl.bintray.com/drummer-aidan/maven/" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
fastlane/metadata/android/de-DE/full_description.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
ProjectLaogai ist eine App um den Vorlesungsplan und den Mensa-Speiseplan der Hochschule Offenburg anzuzeigen.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* schaue was es diese und nächste Woche in der Mensa zu Essen gibt
|
||||||
|
* schaue dir deinen Vorlesungsplan an
|
||||||
|
* lass dir das aktuelle Guthaben der Mensakarte anzeigen
|
||||||
|
* öffne moodle direkt in der App
|
||||||
|
* viele lustige Bugs
|
1
fastlane/metadata/android/de-DE/short_description.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
eine App für die Hochschule Offenburg
|
8
fastlane/metadata/android/en-US/changelogs/14.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
This release 0.5.0 is called "artistical Apollon".
|
||||||
|
|
||||||
|
* new: it's now possible to choose a theme (light, dark or black)
|
||||||
|
* new: you can now check your current mensa card balance
|
||||||
|
* change: updated some libs, updated kotlin to 1.3.41
|
||||||
|
* change: added a license dialog for all used libraries
|
||||||
|
* fix: the mensa should now show the correct meals on sunday/monday
|
||||||
|
* fix: the timetable should now show the correct on the sunday/monday change
|
5
fastlane/metadata/android/en-US/changelogs/15.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
This release 0.5.1 is called "artistical Apollon".
|
||||||
|
|
||||||
|
* new: it's now possible to use App shortcuts for the timetable, mensa and moodle screen
|
||||||
|
* change: updated some libs, updated kotlin to 1.3.61
|
||||||
|
* change: the app was updated to use kotlin coroutines instead of anko for asynchronous workloads
|
8
fastlane/metadata/android/en-US/full_description.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
ProjectLaogai is an app to access the timetable and the mensa menu of Hochschule Offenburg.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* check out the mensa menu of this and next week
|
||||||
|
* access your timetable
|
||||||
|
* check the current balance of your mensa card
|
||||||
|
* open moodle
|
||||||
|
* probably some funny bugs
|
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 148 KiB |
1
fastlane/metadata/android/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
an app for the Hochschule Offenburg
|
1
fastlane/metadata/android/en-US/title.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
ProjectLaogai
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
22
gradlew
vendored
@ -1,5 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
|||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS=""
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
@ -109,8 +125,8 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
18
gradlew.bat
vendored
@ -1,3 +1,19 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
|||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS=
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|