Compare commits
	
		
			118 Commits
		
	
	
		
			0.2.3
			...
			6e9c63d3d4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6e9c63d3d4 | |||
| 
						
						
							
						
						c343735b57
	
				 | 
					
					
						|||
| 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 | |||
| 5a1a07cd42 | |||
| dbfdaffe99 | |||
| 58eb217ab7 | |||
| 24f920c05f | |||
| 6301308d76 | |||
| 8e205fa889 | |||
| e9bdcee443 | |||
| b214cfccb2 | |||
| 404ddd58b8 | |||
| 137ff7df0c | |||
| b4071d7456 | |||
| 5e6e6cfde6 | |||
| ffeb09a37f | |||
| 75a457312d | |||
| 87bf614d28 | |||
| e69354af96 | |||
| ec74a8e4f8 | |||
| b49d16b1a1 | |||
| 70059b4b0c | |||
| 3d7f6f961a | |||
| f97e8b2b14 | |||
| deaf139b70 | |||
| bf48bec16b | |||
| e6c4096787 | |||
| b083e44711 | |||
| 2df58311cb | 
							
								
								
									
										9
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
			
		||||
kind: pipeline
 | 
			
		||||
name: default
 | 
			
		||||
 | 
			
		||||
steps:
 | 
			
		||||
- name: assembleRelease
 | 
			
		||||
  image: nextcloudci/android:android-51
 | 
			
		||||
  commands:
 | 
			
		||||
  - gradle assembleRelease
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@ -1,12 +1,23 @@
 | 
			
		||||
# ProjectLaogai "hso App"
 | 
			
		||||
Some info about the app ...
 | 
			
		||||

 | 
			
		||||
[](https://www.gnu.org/licenses/gpl-3.0)
 | 
			
		||||
 | 
			
		||||
# ProjectLaogai "hso App"
 | 
			
		||||
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
 | 
			
		||||
* look up what you can eat in the mensa
 | 
			
		||||
* check your timetable
 | 
			
		||||
* probably many many bugs
 | 
			
		||||
* 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
 | 
			
		||||
 | 
			
		||||
## Screenshots
 | 
			
		||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.png)
 | 
			
		||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa.png)
 | 
			
		||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Timetable.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Timetable.png)
 | 
			
		||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.png)
 | 
			
		||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa_dark.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa_dark.png)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ProjectLaogai © 2018 mosad 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,43 +1,71 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
 | 
			
		||||
apply plugin: 'kotlin-android-extensions'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    signingConfigs {
 | 
			
		||||
    }
 | 
			
		||||
    compileSdkVersion 28
 | 
			
		||||
    compileSdkVersion 29
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "org.mosad.seil0.projectlaogai"
 | 
			
		||||
        minSdkVersion 21
 | 
			
		||||
        targetSdkVersion 28
 | 
			
		||||
        versionCode 7
 | 
			
		||||
        versionName "0.2.3"
 | 
			
		||||
        minSdkVersion 23
 | 
			
		||||
        targetSdkVersion 29
 | 
			
		||||
        versionCode 15
 | 
			
		||||
        versionName "0.5.1"
 | 
			
		||||
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 | 
			
		||||
        resValue "string", "build_time", buildTime()
 | 
			
		||||
        setProperty("archivesBaseName", "projectlaogai-$versionName")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled false
 | 
			
		||||
            shrinkResources false
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    compileOptions {
 | 
			
		||||
        sourceCompatibility JavaVersion.VERSION_1_8
 | 
			
		||||
        targetCompatibility JavaVersion.VERSION_1_8
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sourceSets {
 | 
			
		||||
        main {
 | 
			
		||||
            res.srcDirs =
 | 
			
		||||
                    [
 | 
			
		||||
                            'src/main/res/layouts/activities',
 | 
			
		||||
                            'src/main/res/layouts/dialogs',
 | 
			
		||||
                            'src/main/res/layouts/fragments',
 | 
			
		||||
                            'src/main/res/layouts',
 | 
			
		||||
                            'src/main/res'
 | 
			
		||||
                    ]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation fileTree(include: ['*.jar'], dir: 'libs')
 | 
			
		||||
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
 | 
			
		||||
    implementation '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 'com.google.android.material:material:1.0.0'
 | 
			
		||||
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
 | 
			
		||||
    implementation 'org.jsoup:jsoup:1.11.3'
 | 
			
		||||
    implementation 'org.jetbrains.anko:anko-commons:0.10.7'
 | 
			
		||||
    implementation 'com.afollestad.material-dialogs:core:2.0.0-beta5'
 | 
			
		||||
    implementation 'com.afollestad.material-dialogs:color:2.0.0-beta5'
 | 
			
		||||
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
 | 
			
		||||
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
 | 
			
		||||
    implementation 'com.google.android.material:material:1.1.0'
 | 
			
		||||
    implementation 'com.google.code.gson:gson:2.8.6'
 | 
			
		||||
    implementation 'com.afollestad:aesthetic:1.0.0-beta05'
 | 
			
		||||
    implementation 'com.afollestad.material-dialogs:core:3.1.1'
 | 
			
		||||
    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'
 | 
			
		||||
    androidTestImplementation 'androidx.test:runner:1.1.0'
 | 
			
		||||
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
 | 
			
		||||
    androidTestImplementation 'androidx.test:runner:1.2.0'
 | 
			
		||||
    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"
 | 
			
		||||
          package="org.mosad.seil0.projectlaogai">
 | 
			
		||||
    <uses-permission android:name="android.permission.INTERNET"/>
 | 
			
		||||
    <uses-permission android:name="android.permission.NFC" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
            android:allowBackup="true"
 | 
			
		||||
            android:icon="@mipmap/ic_launcher"
 | 
			
		||||
            android:fullBackupContent="@xml/backup_descriptor"
 | 
			
		||||
            android:icon="@mipmap/ic_laogai_icon"
 | 
			
		||||
            android:label="@string/app_name"
 | 
			
		||||
            android:roundIcon="@mipmap/ic_launcher_round"
 | 
			
		||||
            android:roundIcon="@mipmap/ic_laogai_icon"
 | 
			
		||||
            android:supportsRtl="true"
 | 
			
		||||
            android:theme="@style/AppTheme">
 | 
			
		||||
            android:theme="@style/AppTheme.Light">
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
                android:name=".SplashActivity"
 | 
			
		||||
                android:label="@string/app_name"
 | 
			
		||||
                android:theme="@style/SplashTheme"
 | 
			
		||||
                android:screenOrientation="portrait">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER" />
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN"/>
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER"/>
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data android:name="android.app.shortcuts"
 | 
			
		||||
                    android:resource="@xml/shortcuts" />
 | 
			
		||||
        </activity>
 | 
			
		||||
 | 
			
		||||
        <activity
 | 
			
		||||
                android:name=".MainActivity"
 | 
			
		||||
                android:label="@string/app_name"
 | 
			
		||||
                android:theme="@style/AppTheme.NoActionBar"
 | 
			
		||||
                android:screenOrientation="portrait">
 | 
			
		||||
                android:theme="@style/AppTheme.Light"
 | 
			
		||||
                android:screenOrientation="portrait"
 | 
			
		||||
                android:launchMode="singleTop">
 | 
			
		||||
 | 
			
		||||
            <!-- nfc stuff -->
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.nfc.action.TECH_DISCOVERED"/>
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
 | 
			
		||||
                       android:resource="@xml/nfc_tech_filter" />
 | 
			
		||||
 | 
			
		||||
        </activity>
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/ic_laogai_icon-web.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 39 KiB  | 
							
								
								
									
										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
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -22,53 +22,61 @@
 | 
			
		||||
 | 
			
		||||
package org.mosad.seil0.projectlaogai
 | 
			
		||||
 | 
			
		||||
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.util.Log
 | 
			
		||||
import android.view.Menu
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
import androidx.appcompat.app.ActionBarDrawerToggle
 | 
			
		||||
import androidx.appcompat.app.AppCompatActivity
 | 
			
		||||
import androidx.core.view.GravityCompat
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import androidx.fragment.app.FragmentTransaction
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.afollestad.aesthetic.Aesthetic
 | 
			
		||||
import com.afollestad.aesthetic.NavigationViewMode
 | 
			
		||||
import com.google.android.material.navigation.NavigationView
 | 
			
		||||
import kotlinx.android.synthetic.main.activity_main.*
 | 
			
		||||
import kotlinx.android.synthetic.main.app_bar_main.*
 | 
			
		||||
import org.jetbrains.anko.doAsync
 | 
			
		||||
import org.jetbrains.anko.uiThread
 | 
			
		||||
import org.mosad.seil0.projectlaogai.fragments.HomeFragment
 | 
			
		||||
import org.mosad.seil0.projectlaogai.fragments.MensaFragment
 | 
			
		||||
import org.mosad.seil0.projectlaogai.fragments.SettingsFragment
 | 
			
		||||
import org.mosad.seil0.projectlaogai.fragments.TimeTableFragment
 | 
			
		||||
import org.mosad.seil0.projectlaogai.hsoparser.*
 | 
			
		||||
import org.mosad.seil0.projectlaogai.controller.CacheController
 | 
			
		||||
import org.mosad.seil0.projectlaogai.controller.NFCMensaCard
 | 
			
		||||
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.fragments.*
 | 
			
		||||
import kotlin.system.measureTimeMillis
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODO save the current fragment to show it when the app is restarted
 | 
			
		||||
 */
 | 
			
		||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
 | 
			
		||||
 | 
			
		||||
    //TODO make toolbar and navbar global
 | 
			
		||||
    private val mensaParser = MensaParser()
 | 
			
		||||
    private val timeTableParser = TimeTableParser()
 | 
			
		||||
    private var activeFragment: Fragment = HomeFragment() // the currently active fragment, home at the start
 | 
			
		||||
    private val className = "MainActivity"
 | 
			
		||||
 | 
			
		||||
    private var weekMenus = ArrayList<Meal>()
 | 
			
		||||
    private var courseTTLinkList = ArrayList<CourseTTLink>()
 | 
			
		||||
    private var timeTableCurrentWeek = arrayOf<Array<Lesson>>()
 | 
			
		||||
    private var timeTableNextWeek = arrayOf<Array<Lesson>>()
 | 
			
		||||
 | 
			
		||||
    private lateinit var course: CourseTTLink
 | 
			
		||||
    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?) {
 | 
			
		||||
        val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
 | 
			
		||||
 | 
			
		||||
        Aesthetic.attach(this)
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
        setContentView(R.layout.activity_main)
 | 
			
		||||
        setSupportActionBar(toolbar)
 | 
			
		||||
 | 
			
		||||
        // load mensa and timetable
 | 
			
		||||
        // load mensa, timetable and color
 | 
			
		||||
        load()
 | 
			
		||||
 | 
			
		||||
        //init home fragment TODO make a abstract fragment class
 | 
			
		||||
        val homeFragment = HomeFragment()
 | 
			
		||||
        homeFragment.setMainActivity(this)
 | 
			
		||||
        val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
 | 
			
		||||
        fragmentTransaction.replace(R.id.fragment_container, homeFragment)
 | 
			
		||||
        fragmentTransaction.commit()
 | 
			
		||||
        initAesthetic()
 | 
			
		||||
        initForegroundDispatch()
 | 
			
		||||
 | 
			
		||||
        val toggle = ActionBarDrawerToggle(
 | 
			
		||||
            this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
 | 
			
		||||
@ -77,12 +85,48 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
        toggle.syncState()
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
        super.onResume()
 | 
			
		||||
        Aesthetic.resume(this)
 | 
			
		||||
        if(useNFC)
 | 
			
		||||
            adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPause() {
 | 
			
		||||
        super.onPause()
 | 
			
		||||
        Aesthetic.pause(this)
 | 
			
		||||
        if(useNFC)
 | 
			
		||||
            adapter.disableForegroundDispatch(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onBackPressed() {
 | 
			
		||||
        if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
 | 
			
		||||
            drawer_layout.closeDrawer(GravityCompat.START)
 | 
			
		||||
        } else {
 | 
			
		||||
            // TODO only call on double tap
 | 
			
		||||
            super.onBackPressed()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -105,147 +149,71 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
 | 
			
		||||
 | 
			
		||||
    override fun onNavigationItemSelected(item: MenuItem): Boolean {
 | 
			
		||||
        // Handle navigation view item clicks here.
 | 
			
		||||
        when (item.itemId) {
 | 
			
		||||
            R.id.nav_home -> {
 | 
			
		||||
                val homeFragment = HomeFragment()
 | 
			
		||||
                homeFragment.setMainActivity(this)
 | 
			
		||||
                val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
 | 
			
		||||
                fragmentTransaction.replace(R.id.fragment_container, homeFragment)
 | 
			
		||||
                fragmentTransaction.commit()
 | 
			
		||||
            }
 | 
			
		||||
            R.id.nav_mensa -> {
 | 
			
		||||
                val mensaFragment = MensaFragment()
 | 
			
		||||
                mensaFragment.setMainActivity(this)
 | 
			
		||||
                val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
 | 
			
		||||
                fragmentTransaction.replace(R.id.fragment_container, mensaFragment)
 | 
			
		||||
                fragmentTransaction.commit()
 | 
			
		||||
            }
 | 
			
		||||
            R.id.nav_timetable -> {
 | 
			
		||||
                val timeTableFragment = TimeTableFragment()
 | 
			
		||||
                timeTableFragment.setMainActivity(this)
 | 
			
		||||
                val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
 | 
			
		||||
                fragmentTransaction.replace(R.id.fragment_container, timeTableFragment)
 | 
			
		||||
                fragmentTransaction.commit()
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            R.id.nav_moodle -> {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            R.id.nav_email -> {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            R.id.nav_settings -> {
 | 
			
		||||
                val settingsFragment = SettingsFragment()
 | 
			
		||||
                settingsFragment.setMainActivity(this)
 | 
			
		||||
                val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
 | 
			
		||||
                fragmentTransaction.replace(R.id.fragment_container, settingsFragment)
 | 
			
		||||
                fragmentTransaction.commit()
 | 
			
		||||
            }
 | 
			
		||||
        activeFragment = when(item.itemId) {
 | 
			
		||||
            R.id.nav_home -> HomeFragment()
 | 
			
		||||
            R.id.nav_mensa -> MensaFragment()
 | 
			
		||||
            R.id.nav_timetable -> TimeTableFragment()
 | 
			
		||||
            R.id.nav_moodle -> MoodleFragment()
 | 
			
		||||
            R.id.nav_settings -> SettingsFragment()
 | 
			
		||||
            else -> HomeFragment()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
 | 
			
		||||
        fragmentTransaction.replace(R.id.fragment_container, activeFragment)
 | 
			
		||||
        fragmentTransaction.commit()
 | 
			
		||||
 | 
			
		||||
        drawer_layout.closeDrawer(GravityCompat.START)
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * update the gui with the data of the new selected course
 | 
			
		||||
     * save selected course and courseTTLink
 | 
			
		||||
     * load the mensa menus of the current week
 | 
			
		||||
     */
 | 
			
		||||
    fun updateCourse(course: CourseTTLink) {
 | 
			
		||||
        println(course.course)
 | 
			
		||||
        println(course.courseTTLink)
 | 
			
		||||
    private fun load() {
 | 
			
		||||
        val startupTime = measureTimeMillis {
 | 
			
		||||
            PreferencesController.load(this) // load the settings, must be finished before doing anything else
 | 
			
		||||
            CacheController(this) // load the cache
 | 
			
		||||
        }
 | 
			
		||||
        Log.i(className, "startup completed in $startupTime ms")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        this.course = course
 | 
			
		||||
        // save new selected course
 | 
			
		||||
        val sharedPref = getPreferences(MODE_PRIVATE) ?: return
 | 
			
		||||
        with (sharedPref.edit()) {
 | 
			
		||||
            putString(getString(R.string.save_key_course), course.course)
 | 
			
		||||
            putString(getString(R.string.save_key_courseTTLink), course.courseTTLink.replace("http", "https"))
 | 
			
		||||
    private fun initAesthetic() {
 | 
			
		||||
        // If we haven't set any defaults, do that now
 | 
			
		||||
        if (Aesthetic.isFirstTime) {
 | 
			
		||||
            // this is executed on the first app start, use this to show tutorial etc.
 | 
			
		||||
            Aesthetic.config {
 | 
			
		||||
                activityTheme(R.style.AppTheme_Light)
 | 
			
		||||
                apply()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SettingsFragment().selectCourse(this) // FIXME this is not working
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Aesthetic.config {
 | 
			
		||||
            colorPrimary(cColorPrimary)
 | 
			
		||||
            colorPrimaryDark(cColorPrimary)
 | 
			
		||||
            colorAccent(cColorAccent)
 | 
			
		||||
            navigationViewMode(NavigationViewMode.SELECTED_ACCENT)
 | 
			
		||||
            apply()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        timeTableCurrentWeek = timeTableParser.getTimeTable(course.courseTTLink.replace("http", "https"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * load the mensa menus of the current week
 | 
			
		||||
     * TODO evaluate if we should use a timeout here
 | 
			
		||||
     */
 | 
			
		||||
    private fun load() {
 | 
			
		||||
    private fun initForegroundDispatch() {
 | 
			
		||||
        val nfcManager = this.getSystemService(Context.NFC_SERVICE) as NfcManager
 | 
			
		||||
        val nfcAdapter = nfcManager.defaultAdapter
 | 
			
		||||
 | 
			
		||||
        // load saved course
 | 
			
		||||
        val sharedPref = getPreferences(MODE_PRIVATE) ?: return
 | 
			
		||||
        course = CourseTTLink(
 | 
			
		||||
            sharedPref.getString(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(getString(R.string.save_key_course), "AI3")!!
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * load mensa, course timetable and courselist from the swfr/hso website
 | 
			
		||||
         * TODO make an API see https://git.mosad.xyz/Seil0/TheCitadelofRicks
 | 
			
		||||
         */
 | 
			
		||||
        val time = measureTimeMillis {
 | 
			
		||||
            /* getting the course list should be faster than the timetable,
 | 
			
		||||
             * we need have time until the user opens the dialog
 | 
			
		||||
             */
 | 
			
		||||
            doAsync {
 | 
			
		||||
                courseTTLinkList = timeTableParser.getCourseTTLinks()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            doAsync {
 | 
			
		||||
                try {
 | 
			
		||||
                    timeTableNextWeek = timeTableParser.getTimeTable(course.courseTTLink.replace("week=0","week=1"))
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
                    e.stackTrace
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val t1 = doAsync {
 | 
			
		||||
                weekMenus = mensaParser.getMensaMenu()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val t2 = doAsync {
 | 
			
		||||
                try {
 | 
			
		||||
                    timeTableCurrentWeek = timeTableParser.getTimeTable(course.courseTTLink)
 | 
			
		||||
                } catch (e: Exception) {
 | 
			
		||||
 | 
			
		||||
                    uiThread {
 | 
			
		||||
                        MaterialDialog(this@MainActivity)
 | 
			
		||||
                            .title(R.string.error)
 | 
			
		||||
                            .message(R.string.no_tt_error)
 | 
			
		||||
                            .show()
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    e.stackTrace
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            t1.get()
 | 
			
		||||
            t2.get()
 | 
			
		||||
        if (nfcAdapter != null) {
 | 
			
		||||
            useNFC = true
 | 
			
		||||
            intentFiltersArray = arrayOf(IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply { addDataType("*/*") })
 | 
			
		||||
            techListsArray = arrayOf(arrayOf(NfcA::class.java.name))
 | 
			
		||||
            adapter = NfcAdapter.getDefaultAdapter(this)
 | 
			
		||||
            pendingIntent = PendingIntent.getActivity(
 | 
			
		||||
                this, 0,
 | 
			
		||||
                Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        println("Completed in $time ms")
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getCourseTTLinkList(): ArrayList<CourseTTLink>{
 | 
			
		||||
        return  courseTTLinkList
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getTimeTableCurrentWeek(): Array<Array<Lesson>> {
 | 
			
		||||
        return timeTableCurrentWeek
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getTimeTableNextWeek(): Array<Array<Lesson>> {
 | 
			
		||||
        return timeTableNextWeek
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getWeekMenu(): ArrayList<Meal>{
 | 
			
		||||
        return weekMenus
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getCourse(): CourseTTLink {
 | 
			
		||||
        return course
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,177 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.*
 | 
			
		||||
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())
 | 
			
		||||
                runBlocking { 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())
 | 
			
		||||
                runBlocking { 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())
 | 
			
		||||
                runBlocking { 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
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -22,20 +22,25 @@
 | 
			
		||||
 | 
			
		||||
package org.mosad.seil0.projectlaogai.fragments
 | 
			
		||||
 | 
			
		||||
import android.graphics.Typeface
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.LinearLayout
 | 
			
		||||
import android.widget.TextView
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_home.*
 | 
			
		||||
import org.jetbrains.anko.doAsync
 | 
			
		||||
import org.jetbrains.anko.uiThread
 | 
			
		||||
import org.mosad.seil0.projectlaogai.MainActivity
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import org.mosad.seil0.projectlaogai.R
 | 
			
		||||
import org.mosad.seil0.projectlaogai.hsoparser.*
 | 
			
		||||
import org.mosad.seil0.projectlaogai.uicomponents.LessonCardView
 | 
			
		||||
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.mensaMenu
 | 
			
		||||
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.timetables
 | 
			
		||||
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.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -44,19 +49,14 @@ import java.util.*
 | 
			
		||||
 */
 | 
			
		||||
class HomeFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var linLayoutTimeTable: LinearLayout
 | 
			
		||||
    private var mainActivity = MainActivity()
 | 
			
		||||
    private val className = "HomeFragment"
 | 
			
		||||
    private val formatter = SimpleDateFormat("E dd.MM", Locale.getDefault())
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 | 
			
		||||
 | 
			
		||||
        val view: View = inflater.inflate(R.layout.fragment_home, container, false)
 | 
			
		||||
 | 
			
		||||
        // init UI elements
 | 
			
		||||
        linLayoutTimeTable = view.findViewById(R.id.linLayoutTimeTable)
 | 
			
		||||
 | 
			
		||||
        //setText()
 | 
			
		||||
        addCurrentMensaMenu()
 | 
			
		||||
        addCurrentTimeTable()
 | 
			
		||||
        addMensaMenu()
 | 
			
		||||
        addTimeTable()
 | 
			
		||||
 | 
			
		||||
        // Inflate the layout for this fragment
 | 
			
		||||
        return view
 | 
			
		||||
@ -65,46 +65,49 @@ class HomeFragment : Fragment() {
 | 
			
		||||
    /**
 | 
			
		||||
     * add the current mensa meal to the home screens
 | 
			
		||||
     */
 | 
			
		||||
    private fun addCurrentMensaMenu() {
 | 
			
		||||
        doAsync {
 | 
			
		||||
            val dayMenus: ArrayList<Meal>
 | 
			
		||||
            val cal = Calendar.getInstance()
 | 
			
		||||
    private fun addMensaMenu() = GlobalScope.launch(Dispatchers.Default) {
 | 
			
		||||
 | 
			
		||||
            // TODO needs testing
 | 
			
		||||
            if (cal.get(Calendar.HOUR_OF_DAY) < 15) {
 | 
			
		||||
                dayMenus = MensaParser().getMensaMenuDay(mainActivity.getWeekMenu(), cal.get(Calendar.DAY_OF_WEEK))
 | 
			
		||||
            } else {
 | 
			
		||||
                dayMenus = MensaParser().getMensaMenuDay(mainActivity.getWeekMenu(), cal.get(Calendar.DAY_OF_WEEK) + 1)
 | 
			
		||||
                uiThread {
 | 
			
		||||
                    // TODO Mensa closed today is showing
 | 
			
		||||
                    txtView_Menu1Heading.text = resources.getString(R.string.meal_1_tomorrow)
 | 
			
		||||
                    txtView_Menu2Heading.text = resources.getString(R.string.meal_2_tomorrow)
 | 
			
		||||
        var dayMeals: ArrayList<Meal>
 | 
			
		||||
        val cal = Calendar.getInstance()
 | 
			
		||||
        val mensaCardView = DayCardView(context!!)
 | 
			
		||||
 | 
			
		||||
        withContext(Dispatchers.Main) {
 | 
			
		||||
 | 
			
		||||
            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 (dayMenus.size >= 2) {
 | 
			
		||||
                if (dayMeals.size >= 2) {
 | 
			
		||||
                    // get the index of the first meal, not a "Schneller Teller"
 | 
			
		||||
                    loop@ for ((i, meal) in dayMenus.withIndex()) {
 | 
			
		||||
                        if(meal.heading.contains("Essen")) {
 | 
			
		||||
                            for (part in dayMenus[i].parts) {
 | 
			
		||||
                                txtViewMenu1.append(part)
 | 
			
		||||
                            }
 | 
			
		||||
                    loop@ for ((i, meal) in dayMeals.withIndex()) {
 | 
			
		||||
                        if (meal.heading.contains("Essen")) {
 | 
			
		||||
 | 
			
		||||
                            for (part in dayMenus[i + 1].parts) {
 | 
			
		||||
                                txtViewMenu2.append(part)
 | 
			
		||||
                            }
 | 
			
		||||
                            val meal1Layout = MealLinearLayout(context)
 | 
			
		||||
                            meal1Layout.setMeal(dayMeals[i])
 | 
			
		||||
                            mensaCardView.getLinLayoutDay().addView(meal1Layout)
 | 
			
		||||
 | 
			
		||||
                            val meal2Layout = MealLinearLayout(context)
 | 
			
		||||
                            meal2Layout.setMeal(dayMeals[i + 1])
 | 
			
		||||
                            meal2Layout.disableDivider()
 | 
			
		||||
                            mensaCardView.getLinLayoutDay().addView(meal2Layout)
 | 
			
		||||
 | 
			
		||||
                            break@loop
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                } else {
 | 
			
		||||
                    txtViewMenu1.text = resources.getString(R.string.no_meal_today)
 | 
			
		||||
                    txtViewMenu2.text = resources.getString(R.string.no_meal_today)
 | 
			
		||||
                    mensaCardView.getLinLayoutDay().addView(getNoCard(resources.getString(R.string.mensa_closed)))
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                linLayout_Home.addView(mensaCardView)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@ -112,51 +115,71 @@ class HomeFragment : Fragment() {
 | 
			
		||||
    /**
 | 
			
		||||
     * add the current timetable to the home screen
 | 
			
		||||
     */
 | 
			
		||||
    private fun addCurrentTimeTable() {
 | 
			
		||||
        val dayIndex = NotRetardedCalendar().getDayOfWeekIndex()
 | 
			
		||||
    private fun addTimeTable() = GlobalScope.launch(Dispatchers.Default) {
 | 
			
		||||
 | 
			
		||||
        if (mainActivity.getTimeTableCurrentWeek().isNotEmpty() && dayIndex < 6) {
 | 
			
		||||
        withContext(Dispatchers.Main) {
 | 
			
		||||
 | 
			
		||||
            val timeTableDay = mainActivity.getTimeTableCurrentWeek()[dayIndex]
 | 
			
		||||
 | 
			
		||||
            for (i in 0..5) {
 | 
			
		||||
                val lessonCardView = LessonCardView(context!!, null)
 | 
			
		||||
 | 
			
		||||
                lessonCardView.getTxtViewLesson().text = resources.getString(R.string.string_new_line, timeTableDay[i].lessonSubject)
 | 
			
		||||
                lessonCardView.getTxtViewLesson().append(timeTableDay[i].lessonTeacher + "\n")
 | 
			
		||||
                lessonCardView.getTxtViewLesson().append(timeTableDay[i].lessonRoom)
 | 
			
		||||
                lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]
 | 
			
		||||
 | 
			
		||||
                if(lessonCardView.getTxtViewLesson().text.length > 2)
 | 
			
		||||
                    linLayoutTimeTable.addView(lessonCardView)
 | 
			
		||||
            if (isAdded && timetables.isNotEmpty()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    val dayCardView = findNextDay(NotRetardedCalendar.getDayOfWeekIndex())
 | 
			
		||||
                    linLayout_Home.addView(dayCardView)
 | 
			
		||||
                } catch (ex: Exception) {
 | 
			
		||||
                    Log.e(className, "could not load timetable", ex) // TODO send feedback
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // add a card if there is no lesson today
 | 
			
		||||
            if (linLayoutTimeTable.childCount == 0) {
 | 
			
		||||
                // TODO we could display the next day with a lecture
 | 
			
		||||
                val noLessonCardView = LessonCardView(context!!, null)
 | 
			
		||||
                noLessonCardView.getTxtViewLesson().text = resources.getString(R.string.no_lesson_today)
 | 
			
		||||
                linLayoutTimeTable.addView(noLessonCardView)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (dayIndex == 6) {
 | 
			
		||||
                // if that's the case it's sunday
 | 
			
		||||
                val noLessonCardView = LessonCardView(context!!, null)
 | 
			
		||||
                noLessonCardView.getTxtViewLesson().text = resources.getString(R.string.no_lesson_today)
 | 
			
		||||
                linLayoutTimeTable.addView(noLessonCardView)
 | 
			
		||||
            } else {
 | 
			
		||||
                MaterialDialog(context!!)
 | 
			
		||||
                    .title(R.string.error)
 | 
			
		||||
                    .message(R.string.gen_tt_error)
 | 
			
		||||
                    .show()
 | 
			
		||||
                // TODO log the error and send feedback
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setMainActivity(mainActivity: MainActivity) {
 | 
			
		||||
        this.mainActivity = mainActivity
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -23,18 +23,24 @@
 | 
			
		||||
package org.mosad.seil0.projectlaogai.fragments
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.LinearLayout
 | 
			
		||||
import org.jetbrains.anko.doAsync
 | 
			
		||||
import org.jetbrains.anko.uiThread
 | 
			
		||||
import org.mosad.seil0.projectlaogai.MainActivity
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_mensa.*
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.GlobalScope
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
import org.mosad.seil0.projectlaogai.R
 | 
			
		||||
import org.mosad.seil0.projectlaogai.uicomponents.MensaDayCardView
 | 
			
		||||
import org.mosad.seil0.projectlaogai.uicomponents.MenuCardView
 | 
			
		||||
import java.util.*
 | 
			
		||||
import org.mosad.seil0.projectlaogai.controller.CacheController
 | 
			
		||||
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.mensaMenu
 | 
			
		||||
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
 | 
			
		||||
@ -42,76 +48,98 @@ import java.util.*
 | 
			
		||||
 */
 | 
			
		||||
class MensaFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var linLayoutMensaFragment: LinearLayout
 | 
			
		||||
    private var mainActivity = MainActivity()
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
                        Calendar.MONDAY -> "Mon"
 | 
			
		||||
                        Calendar.TUESDAY -> "Die"
 | 
			
		||||
                        Calendar.WEDNESDAY -> "Mit"
 | 
			
		||||
                        Calendar.THURSDAY -> "Don"
 | 
			
		||||
                        Calendar.FRIDAY -> "Fre"
 | 
			
		||||
                        Calendar.SATURDAY -> "Sam"
 | 
			
		||||
                        else -> "TODAY" // the app will likely crash here
 | 
			
		||||
                for (meal in menusWeek.days[dayIndex].meals) {
 | 
			
		||||
                    val mealLayout = MealLinearLayout(context)
 | 
			
		||||
                    mealLayout.setMeal(meal)
 | 
			
		||||
 | 
			
		||||
                    if(meal.heading != "Buffet" || cShowBuffet) {
 | 
			
		||||
                        dayCardView.getLinLayoutDay().addView(mealLayout)
 | 
			
		||||
                        helpMeal = mealLayout
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    val cardViewMensaDay = MensaDayCardView(context!!, null)
 | 
			
		||||
                    var add = false
 | 
			
		||||
 | 
			
		||||
                    for (meal in mainActivity.getWeekMenu()) {
 | 
			
		||||
                        //println("Day: " + meal.day)
 | 
			
		||||
                        if (meal.day.contains(strDay)) {
 | 
			
		||||
 | 
			
		||||
                            val menuViewMenu = MenuCardView(context!!, null)
 | 
			
		||||
                            menuViewMenu.setMenuHeading(meal.heading)
 | 
			
		||||
 | 
			
		||||
                            for(part in meal.parts) {
 | 
			
		||||
                                menuViewMenu.getTxtViewMenu().append(part)
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            cardViewMensaDay.setDayHeading(meal.day) //TODO move this out of the first for loop, performance!!
 | 
			
		||||
                            cardViewMensaDay.getLinLayoutMensaDay().addView(menuViewMenu)
 | 
			
		||||
                            add = true
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    if(add)
 | 
			
		||||
                        linLayoutMensaFragment.addView(cardViewMensaDay)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // add a card if there are no more meals in this week
 | 
			
		||||
                if(linLayoutMensaFragment.childCount == 0) {
 | 
			
		||||
                    val cardViewNoMoreFood = MensaDayCardView(context!!, null)
 | 
			
		||||
                    cardViewNoMoreFood.setDayHeading(resources.getString(R.string.no_more_food))
 | 
			
		||||
                    linLayoutMensaFragment.addView(cardViewNoMoreFood)
 | 
			
		||||
                }
 | 
			
		||||
                helpMeal.disableDivider()
 | 
			
		||||
 | 
			
		||||
                if(dayCardView.getLinLayoutDay().childCount > 1)
 | 
			
		||||
                    linLayout_Mensa.addView(dayCardView)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * initialize the refresh action
 | 
			
		||||
     */
 | 
			
		||||
    private fun refreshAction() = GlobalScope.launch(Dispatchers.Default) {
 | 
			
		||||
        withContext(Dispatchers.Main) {
 | 
			
		||||
            // set the refresh listener
 | 
			
		||||
            refreshLayout_Mensa.setOnRefreshListener {
 | 
			
		||||
                updateMensaScreen()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setMainActivity(mainActivity: MainActivity) {
 | 
			
		||||
        this.mainActivity = mainActivity
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,58 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.fragments
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.webkit.WebSettings
 | 
			
		||||
import android.webkit.WebView
 | 
			
		||||
import org.mosad.seil0.projectlaogai.R
 | 
			
		||||
import android.webkit.WebViewClient
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The moodle screen controller class
 | 
			
		||||
 * contains all needed parts to display and the moodle screen
 | 
			
		||||
 */
 | 
			
		||||
class MoodleFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var webView: WebView
 | 
			
		||||
    private lateinit var webSettings: WebSettings
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 | 
			
		||||
 | 
			
		||||
        val view: View = inflater.inflate(R.layout.fragment_moodle, container, false)
 | 
			
		||||
 | 
			
		||||
        webView = view.findViewById(R.id.webView)
 | 
			
		||||
        webView.loadUrl("https://elearning.hs-offenburg.de/moodle/")
 | 
			
		||||
 | 
			
		||||
        webSettings = webView.settings
 | 
			
		||||
        //webSettings.setJavaScriptEnabled(true) // Enable Javascript
 | 
			
		||||
 | 
			
		||||
        webView.webViewClient = WebViewClient() // Force links and redirects to open in the WebView instead of in a browser
 | 
			
		||||
 | 
			
		||||
        return view
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
/**
 | 
			
		||||
 * ProjectLaogai
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -22,24 +22,36 @@
 | 
			
		||||
 | 
			
		||||
package org.mosad.seil0.projectlaogai.fragments
 | 
			
		||||
 | 
			
		||||
import android.graphics.Color
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import android.util.TypedValue
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.LinearLayout
 | 
			
		||||
import android.widget.Switch
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import com.afollestad.aesthetic.Aesthetic
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.afollestad.materialdialogs.color.colorChooser
 | 
			
		||||
import com.afollestad.materialdialogs.customview.customView
 | 
			
		||||
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 org.jetbrains.anko.doAsync
 | 
			
		||||
import org.jetbrains.anko.uiThread
 | 
			
		||||
import org.mosad.seil0.projectlaogai.MainActivity
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import org.mosad.seil0.projectlaogai.BuildConfig
 | 
			
		||||
import org.mosad.seil0.projectlaogai.R
 | 
			
		||||
import org.mosad.seil0.projectlaogai.hsoparser.CourseTTLink
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
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.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The settings controller class
 | 
			
		||||
@ -49,12 +61,12 @@ class SettingsFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var linLayoutUser: LinearLayout
 | 
			
		||||
    private lateinit var linLayoutCourse: LinearLayout
 | 
			
		||||
    private lateinit var linLayoutInfo: LinearLayout
 | 
			
		||||
    private lateinit var linLayoutMainColor: LinearLayout
 | 
			
		||||
    private lateinit var viewPrimaryColor: View
 | 
			
		||||
    private lateinit var courseTTLinkList: ArrayList<CourseTTLink>
 | 
			
		||||
    private var mainActivity = MainActivity()
 | 
			
		||||
 | 
			
		||||
    private lateinit var linLayoutAbout: LinearLayout
 | 
			
		||||
    private lateinit var linLayoutLicence: LinearLayout
 | 
			
		||||
    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? {
 | 
			
		||||
 | 
			
		||||
@ -62,9 +74,12 @@ class SettingsFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
        linLayoutUser = view.findViewById(R.id.linLayout_User)
 | 
			
		||||
        linLayoutCourse = view.findViewById(R.id.linLayout_Course)
 | 
			
		||||
        linLayoutInfo = view.findViewById(R.id.linLayout_Info)
 | 
			
		||||
        linLayoutMainColor = view.findViewById(R.id.linLayout_MainColor)
 | 
			
		||||
        viewPrimaryColor = view.findViewById(R.id.view_PrimaryColor)
 | 
			
		||||
        linLayoutAbout = view.findViewById(R.id.linLayout_About)
 | 
			
		||||
        linLayoutLicence = view.findViewById(R.id.linLayout_Licence)
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
@ -75,7 +90,28 @@ class SettingsFragment : Fragment() {
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState)
 | 
			
		||||
 | 
			
		||||
        txtView_Course.text = mainActivity.getCourse().course
 | 
			
		||||
        // initialize the settings gui
 | 
			
		||||
        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)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -86,59 +122,148 @@ class SettingsFragment : Fragment() {
 | 
			
		||||
            // open a new dialog
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        linLayoutCourse.setOnClickListener {
 | 
			
		||||
            // open a new dialog
 | 
			
		||||
            val courseList = ArrayList<String>()
 | 
			
		||||
        linLayoutUser.setOnLongClickListener {
 | 
			
		||||
            PreferencesController.oGiants = true // enable easter egg
 | 
			
		||||
            return@setOnLongClickListener true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            courseTTLinkList = mainActivity.getCourseTTLinkList()
 | 
			
		||||
            courseTTLinkList.forEach { (_, course) ->
 | 
			
		||||
                courseList.add(course)
 | 
			
		||||
        linLayoutCourse.setOnClickListener {
 | 
			
		||||
            selectCourse(context!!)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        linLayoutAbout.setOnClickListener {
 | 
			
		||||
            // open a new info dialog
 | 
			
		||||
            MaterialDialog(context!!)
 | 
			
		||||
                .title(R.string.about_dialog_heading)
 | 
			
		||||
                .message(R.string.about_dialog_text)
 | 
			
		||||
                .show()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        linLayoutLicence.setOnClickListener {
 | 
			
		||||
            // do the theme magic, as the lib's theme support is broken
 | 
			
		||||
            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
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MaterialDialog(context!!).listItems(items = courseList){ _, index, text ->
 | 
			
		||||
                txtView_Course.text = text // update txtView
 | 
			
		||||
            val themeId = when (outValue.string) {
 | 
			
		||||
                "light" -> R.style.AppTheme_Light
 | 
			
		||||
                else -> R.style.LicensesDialogTheme_Dark
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                val dialog = MaterialDialog(context!!).cancelable(false)
 | 
			
		||||
            // 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!!)
 | 
			
		||||
                .title(R.string.primary_color)
 | 
			
		||||
                .colorChooser(DataTypes().primaryColors, allowCustomArgb = true, initialSelection = cColorPrimary) { _, color ->
 | 
			
		||||
                    view_PrimaryColor.setBackgroundColor(color)
 | 
			
		||||
                    Aesthetic.config {
 | 
			
		||||
                        colorPrimary(color)
 | 
			
		||||
                        colorPrimaryDark(color)
 | 
			
		||||
                        apply()
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    PreferencesController.saveColorPrimary(context!!, color)
 | 
			
		||||
                }
 | 
			
		||||
                .positiveButton(R.string.select)
 | 
			
		||||
                .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()
 | 
			
		||||
 | 
			
		||||
                doAsync {
 | 
			
		||||
                    mainActivity.updateCourse(courseTTLinkList[index])
 | 
			
		||||
                GlobalScope.launch(Dispatchers.Default) {
 | 
			
		||||
                    PreferencesController.saveCourse(context, coursesList[index]) // save the course
 | 
			
		||||
 | 
			
		||||
                    uiThread {
 | 
			
		||||
                    // 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()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        linLayoutInfo.setOnClickListener {
 | 
			
		||||
            // open a new info dialog
 | 
			
		||||
            MaterialDialog(context!!)
 | 
			
		||||
                .title(R.string.about)
 | 
			
		||||
                .message(R.string.about_text)
 | 
			
		||||
                .show()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        linLayoutMainColor.setOnClickListener {
 | 
			
		||||
            // open a new color chooser dialog
 | 
			
		||||
            val colors = intArrayOf(Color.parseColor("#3F51B5"), Color.RED, Color.GREEN, Color.BLUE)
 | 
			
		||||
 | 
			
		||||
            MaterialDialog(context!!)
 | 
			
		||||
                .title(R.string.primary_color)
 | 
			
		||||
                .colorChooser(colors, initialSelection = Color.parseColor("#3F51B5")) { _, color ->
 | 
			
		||||
                    viewPrimaryColor.setBackgroundColor(color)
 | 
			
		||||
                }
 | 
			
		||||
                .positiveButton(R.string.select)
 | 
			
		||||
                .show()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setMainActivity(mainActivity: MainActivity) {
 | 
			
		||||
        this.mainActivity = mainActivity
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
/**
 | 
			
		||||
 * ProjectLaogai
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -22,23 +22,23 @@
 | 
			
		||||
 | 
			
		||||
package org.mosad.seil0.projectlaogai.fragments
 | 
			
		||||
 | 
			
		||||
import android.graphics.Color
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.LinearLayout
 | 
			
		||||
import org.jetbrains.anko.doAsync
 | 
			
		||||
import org.jetbrains.anko.uiThread
 | 
			
		||||
import org.mosad.seil0.projectlaogai.MainActivity
 | 
			
		||||
import android.widget.ScrollView
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_timetable.*
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
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.uicomponents.LessonCardView
 | 
			
		||||
import org.mosad.seil0.projectlaogai.uicomponents.MensaDayCardView
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The timetable controller class
 | 
			
		||||
@ -46,91 +46,117 @@ import java.util.*
 | 
			
		||||
 */
 | 
			
		||||
class TimeTableFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var linLayoutTTFragment: LinearLayout
 | 
			
		||||
    private var mainActivity = MainActivity()
 | 
			
		||||
    private lateinit var scrollViewTimetable: ScrollView
 | 
			
		||||
    private lateinit var faBtnAddLesson: FloatingActionButton
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
 | 
			
		||||
        addCurrentWeek()
 | 
			
		||||
        if (timetables[0].timetable.days.isNotEmpty() && timetables[1].timetable.days.isNotEmpty()) {
 | 
			
		||||
            addInitWeeks()
 | 
			
		||||
        } else {
 | 
			
		||||
            MaterialDialog(context!!)
 | 
			
		||||
                .title(R.string.error)
 | 
			
		||||
                .message(R.string.timetable_error)
 | 
			
		||||
                .show()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return view
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * add the remaining days of the current week to the timetable screen
 | 
			
		||||
     * initialize the actions
 | 
			
		||||
     */
 | 
			
		||||
    private fun addCurrentWeek() {
 | 
			
		||||
        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()
 | 
			
		||||
    private fun initActions() = GlobalScope.launch(Dispatchers.Default) {
 | 
			
		||||
 | 
			
		||||
        doAsync {
 | 
			
		||||
        withContext(Dispatchers.Main) {
 | 
			
		||||
            refreshLayout_Timetable.setOnRefreshListener {
 | 
			
		||||
                updateTimetableScreen()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uiThread {
 | 
			
		||||
            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()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                // add current weeks days
 | 
			
		||||
                for(day in dayIndex..5) {
 | 
			
		||||
                    val cardViewTimeTableDay = MensaDayCardView(context!!, null)
 | 
			
		||||
                    cardViewTimeTableDay.setDayHeading(formatter.format(calendar.time))
 | 
			
		||||
 | 
			
		||||
                    // for each lessen of the day
 | 
			
		||||
                    for((i, lesson) in mainActivity.getTimeTableCurrentWeek()[day].withIndex()) {
 | 
			
		||||
                        val lessonCardView = LessonCardView(context!!, null)
 | 
			
		||||
                        lessonCardView.setBackgroundColor(Color.TRANSPARENT)
 | 
			
		||||
 | 
			
		||||
                        lessonCardView.getTxtViewLesson().text = resources.getString(R.string.string_new_line, lesson.lessonSubject)
 | 
			
		||||
                        lessonCardView.getTxtViewLesson().append(lesson.lessonTeacher + "\n")
 | 
			
		||||
                        lessonCardView.getTxtViewLesson().append(lesson.lessonRoom)
 | 
			
		||||
                        lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]
 | 
			
		||||
 | 
			
		||||
                        if(lessonCardView.getTxtViewLesson().text.length > 2)
 | 
			
		||||
                            cardViewTimeTableDay.getLinLayoutMensaDay().addView(lessonCardView)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    calendar.add(Calendar.DATE,1)
 | 
			
		||||
                    linLayoutTTFragment.addView(cardViewTimeTableDay)
 | 
			
		||||
            // hide the btnCardValue if the user is scrolling down
 | 
			
		||||
            scrollViewTimetable.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
 | 
			
		||||
                if (scrollY > oldScrollY) {
 | 
			
		||||
                    faBtnAddLesson.hide()
 | 
			
		||||
                } else {
 | 
			
		||||
                    faBtnAddLesson.show()
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // add next weeks days, max number  = dayIndex, if timetable was loaded
 | 
			
		||||
                if (mainActivity.getTimeTableNextWeek().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 lessen of the day
 | 
			
		||||
                        for((i, lesson) in mainActivity.getTimeTableNextWeek()[day].withIndex()) {
 | 
			
		||||
                            val lessonCardView = LessonCardView(context!!, null)
 | 
			
		||||
                            lessonCardView.setBackgroundColor(Color.TRANSPARENT)
 | 
			
		||||
 | 
			
		||||
                            lessonCardView.getTxtViewLesson().text = resources.getString(R.string.string_new_line, lesson.lessonSubject)
 | 
			
		||||
                            lessonCardView.getTxtViewLesson().append(lesson.lessonTeacher + "\n")
 | 
			
		||||
                            lessonCardView.getTxtViewLesson().append(lesson.lessonRoom)
 | 
			
		||||
                            lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]
 | 
			
		||||
 | 
			
		||||
                            if(lessonCardView.getTxtViewLesson().text.length > 2)
 | 
			
		||||
                                cardViewTimeTableDay.getLinLayoutMensaDay().addView(lessonCardView)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        calendar.add(Calendar.DATE,1)
 | 
			
		||||
                        linLayoutTTFragment.addView(cardViewTimeTableDay)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // TODO if there is no lesson at one day , show a no lesson card
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setMainActivity(mainActivity: MainActivity) {
 | 
			
		||||
        this.mainActivity = mainActivity
 | 
			
		||||
    /**
 | 
			
		||||
     * 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
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -22,10 +22,55 @@
 | 
			
		||||
 | 
			
		||||
package org.mosad.seil0.projectlaogai.hsoparser
 | 
			
		||||
 | 
			
		||||
import android.graphics.Color
 | 
			
		||||
import java.util.*
 | 
			
		||||
import kotlin.collections.ArrayList
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
        // do something
 | 
			
		||||
@ -38,26 +83,70 @@ class DataTypes {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class NotRetardedCalendar {
 | 
			
		||||
    private val calendar = Calendar.getInstance()!!
 | 
			
		||||
    companion object {
 | 
			
		||||
        private val calendar = Calendar.getInstance()
 | 
			
		||||
 | 
			
		||||
    fun getDayOfWeekIndex(): Int {
 | 
			
		||||
        return when(calendar.get(Calendar.DAY_OF_WEEK)) {
 | 
			
		||||
            Calendar.MONDAY -> 0
 | 
			
		||||
            Calendar.TUESDAY -> 1
 | 
			
		||||
            Calendar.WEDNESDAY -> 2
 | 
			
		||||
            Calendar.THURSDAY -> 3
 | 
			
		||||
            Calendar.FRIDAY -> 4
 | 
			
		||||
            Calendar.SATURDAY -> 5
 | 
			
		||||
            Calendar.SUNDAY -> 6
 | 
			
		||||
            else -> 7
 | 
			
		||||
        fun getDayOfWeekIndex(): Int {
 | 
			
		||||
            return when (calendar.get(Calendar.DAY_OF_WEEK)) {
 | 
			
		||||
                Calendar.MONDAY -> 0
 | 
			
		||||
                Calendar.TUESDAY -> 1
 | 
			
		||||
                Calendar.WEDNESDAY -> 2
 | 
			
		||||
                Calendar.THURSDAY -> 3
 | 
			
		||||
                Calendar.FRIDAY -> 4
 | 
			
		||||
                Calendar.SATURDAY -> 5
 | 
			
		||||
                Calendar.SUNDAY -> 6
 | 
			
		||||
                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 class Lesson(val lessonSubject: String, val lessonTeacher: String, val lessonRoom:String, val lessonRemark: String)
 | 
			
		||||
// data classes for the course part
 | 
			
		||||
data class Course(val courseLink: String, val courseName: String)
 | 
			
		||||
 | 
			
		||||
data class CourseTTLink(val courseTTLink: String, val course: 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 Meals(val meals: ArrayList<Meal>)
 | 
			
		||||
 | 
			
		||||
data class MensaWeek(val days: Array<Meals> = Array(7) { Meals(ArrayList()) })
 | 
			
		||||
 | 
			
		||||
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,79 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * ProjectLaogai
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <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 {
 | 
			
		||||
    private var mealList = ArrayList<Meal>()
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        // do something
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * returns the mensa menu for the whole week
 | 
			
		||||
     */
 | 
			
		||||
    fun getMensaMenu(): 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("|"))
 | 
			
		||||
                    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,94 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * ProjectLaogai
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <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<CourseTTLink>()
 | 
			
		||||
    private var timeTableWeek = arrayOf<Array<Lesson>>()
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        // create the timetable array
 | 
			
		||||
        for (i in 0..5) {
 | 
			
		||||
            var timeTableDay = arrayOf<Lesson>()
 | 
			
		||||
            for (j in 0..5) {
 | 
			
		||||
                timeTableDay += Lesson("", "","","")
 | 
			
		||||
            }
 | 
			
		||||
            timeTableWeek += timeTableDay
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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): Array<Array<Lesson>> {
 | 
			
		||||
        val scheduleHTML = Jsoup.connect(courseTTURL).get()
 | 
			
		||||
 | 
			
		||||
        //val week = scheduleHTML.select("h1.timetable-caption").text()
 | 
			
		||||
        //println("$week successful!\n")
 | 
			
		||||
 | 
			
		||||
        scheduleHTML.select("table.timetable").select("td.lastcol").forEachIndexed { index, element ->
 | 
			
		||||
            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<CourseTTLink> {
 | 
			
		||||
        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(CourseTTLink(element.attr("href"),element.text()))
 | 
			
		||||
        }
 | 
			
		||||
        return courseTTLinkList
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun printTimeTableWeek (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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -23,31 +23,28 @@
 | 
			
		||||
package org.mosad.seil0.projectlaogai.uicomponents
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.graphics.Color
 | 
			
		||||
import android.util.AttributeSet
 | 
			
		||||
import android.widget.TextView
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.LinearLayout
 | 
			
		||||
import androidx.cardview.widget.CardView
 | 
			
		||||
import kotlinx.android.synthetic.main.linearlayout_lesson.view.*
 | 
			
		||||
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){
 | 
			
		||||
 | 
			
		||||
    private var txtViewLesson: TextView
 | 
			
		||||
    private var txtViewTime: TextView
 | 
			
		||||
class LessonLinearLayout(context: Context?) : LinearLayout(context) {
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        inflate(context, R.layout.lesson_cardview,this)
 | 
			
		||||
 | 
			
		||||
        txtViewLesson = findViewById(R.id.txtView_Lesson)
 | 
			
		||||
        txtViewTime = findViewById(R.id.txtView_Time)
 | 
			
		||||
 | 
			
		||||
        // workaround to prevent a white border
 | 
			
		||||
        this.setBackgroundColor(Color.parseColor("#3F51B5"))
 | 
			
		||||
        CardView.inflate(context, R.layout.linearlayout_lesson, this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getTxtViewLesson(): TextView {
 | 
			
		||||
        return txtViewLesson
 | 
			
		||||
    fun setLesson(lesson: Lesson, time: String) {
 | 
			
		||||
        txtView_lessonTime.text = time
 | 
			
		||||
        txtView_lessonSubject.text = lesson.lessonSubject
 | 
			
		||||
        txtView_lessonTeacher.text = lesson.lessonTeacher
 | 
			
		||||
        txtView_lessonRoom.text = lesson.lessonRoom
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getTxtViewTime(): TextView {
 | 
			
		||||
        return txtViewTime
 | 
			
		||||
    fun disableDivider() {
 | 
			
		||||
        divider_lesson.visibility = View.GONE
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
/**
 | 
			
		||||
 * ProjectLaogai
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2018  <seil0@mosad.xyz>
 | 
			
		||||
 * 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
 | 
			
		||||
@ -23,32 +23,31 @@
 | 
			
		||||
package org.mosad.seil0.projectlaogai.uicomponents
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.util.AttributeSet
 | 
			
		||||
import android.view.View
 | 
			
		||||
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.hsoparser.Meal
 | 
			
		||||
 | 
			
		||||
class MensaDayCardView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : androidx.cardview.widget.CardView(context, attrs){
 | 
			
		||||
 | 
			
		||||
    private var linLayoutMensaDay: LinearLayout
 | 
			
		||||
    private var txtViewDayHeading: TextView
 | 
			
		||||
class MealLinearLayout(context: Context?): LinearLayout(context) {
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        inflate(context, R.layout.mensaday_cardview,this)
 | 
			
		||||
 | 
			
		||||
        linLayoutMensaDay = findViewById(R.id.linLayout_MensaDay)
 | 
			
		||||
        txtViewDayHeading = findViewById(R.id.txtView_DayHeading)
 | 
			
		||||
 | 
			
		||||
        // workaround to prevent a white border
 | 
			
		||||
        //this.setBackgroundColor(Color.TRANSPARENT)
 | 
			
		||||
        CardView.inflate(context, R.layout.linearlayout_meal, this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getLinLayoutMensaDay(): LinearLayout {
 | 
			
		||||
        return linLayoutMensaDay
 | 
			
		||||
    fun setMeal(meal: Meal) {
 | 
			
		||||
        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) {
 | 
			
		||||
        txtViewDayHeading.text = heading
 | 
			
		||||
    fun disableDivider() {
 | 
			
		||||
        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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 6.2 KiB  | 
@ -1,34 +0,0 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        xmlns:aapt="http://schemas.android.com/aapt"
 | 
			
		||||
        android:width="108dp"
 | 
			
		||||
        android:height="108dp"
 | 
			
		||||
        android:viewportHeight="108"
 | 
			
		||||
        android:viewportWidth="108">
 | 
			
		||||
    <path
 | 
			
		||||
            android:fillType="evenOdd"
 | 
			
		||||
            android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
 | 
			
		||||
            android:strokeColor="#00000000"
 | 
			
		||||
            android:strokeWidth="1">
 | 
			
		||||
        <aapt:attr name="android:fillColor">
 | 
			
		||||
            <gradient
 | 
			
		||||
                    android:endX="78.5885"
 | 
			
		||||
                    android:endY="90.9159"
 | 
			
		||||
                    android:startX="48.7653"
 | 
			
		||||
                    android:startY="61.0927"
 | 
			
		||||
                    android:type="linear">
 | 
			
		||||
                <item
 | 
			
		||||
                        android:color="#44000000"
 | 
			
		||||
                        android:offset="0.0"/>
 | 
			
		||||
                <item
 | 
			
		||||
                        android:color="#00000000"
 | 
			
		||||
                        android:offset="1.0"/>
 | 
			
		||||
            </gradient>
 | 
			
		||||
        </aapt:attr>
 | 
			
		||||
    </path>
 | 
			
		||||
    <path
 | 
			
		||||
            android:fillColor="#FFFFFF"
 | 
			
		||||
            android:fillType="nonZero"
 | 
			
		||||
            android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
 | 
			
		||||
            android:strokeColor="#00000000"
 | 
			
		||||
            android:strokeWidth="1"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@ -1,13 +1,12 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
 | 
			
		||||
    <item
 | 
			
		||||
            android:drawable="@color/colorPrimary"/>
 | 
			
		||||
    <item android:drawable="@color/colorPrimary"/>
 | 
			
		||||
 | 
			
		||||
    <item>
 | 
			
		||||
    <item android:gravity="center" android:width="144dp" android:height="144dp">
 | 
			
		||||
        <bitmap
 | 
			
		||||
                android:gravity="center"
 | 
			
		||||
                android:src="@drawable/ic_launcher"/>
 | 
			
		||||
                android:gravity="fill_horizontal|fill_vertical"
 | 
			
		||||
                android:src="@mipmap/ic_laogai_icon_splash"/>
 | 
			
		||||
    </item>
 | 
			
		||||
 | 
			
		||||
</layer-list>
 | 
			
		||||
@ -1,74 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<vector
 | 
			
		||||
        xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:height="108dp"
 | 
			
		||||
        android:width="108dp"
 | 
			
		||||
        android:viewportHeight="108"
 | 
			
		||||
        android:viewportWidth="108">
 | 
			
		||||
    <path android:fillColor="#008577"
 | 
			
		||||
          android:pathData="M0,0h108v108h-108z"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
    <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
 | 
			
		||||
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
 | 
			
		||||
</vector>
 | 
			
		||||
							
								
								
									
										9
									
								
								app/src/main/res/drawable/ic_local_dining_black_24dp.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,9 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:height="24dp"
 | 
			
		||||
        android:viewportWidth="24.0"
 | 
			
		||||
        android:viewportHeight="24.0">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF000000"
 | 
			
		||||
        android:pathData="M8.1,13.34l2.83,-2.83L3.91,3.5c-1.56,1.56 -1.56,4.09 0,5.66l4.19,4.18zM14.88,11.53c1.53,0.71 3.68,0.21 5.27,-1.38 1.91,-1.91 2.28,-4.65 0.81,-6.12 -1.46,-1.46 -4.2,-1.1 -6.12,0.81 -1.59,1.59 -2.09,3.74 -1.38,5.27L3.7,19.87l1.41,1.41L12,14.41l6.88,6.88 1.41,-1.41L13.41,13l1.47,-1.47z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@ -1,9 +0,0 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:height="24dp"
 | 
			
		||||
        android:viewportWidth="24.0"
 | 
			
		||||
        android:viewportHeight="24.0">
 | 
			
		||||
    <path
 | 
			
		||||
            android:fillColor="#FF000000"
 | 
			
		||||
            android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-8,12.5v-9l6,4.5 -6,4.5z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@ -5,5 +5,5 @@
 | 
			
		||||
        android:viewportHeight="24.0">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF000000"
 | 
			
		||||
        android:pathData="M20,3L4,3v10c0,2.21 1.79,4 4,4h6c2.21,0 4,-1.79 4,-4v-3h2c1.11,0 2,-0.9 2,-2L22,5c0,-1.11 -0.89,-2 -2,-2zM20,8h-2L18,5h2v3zM4,19h16v2L4,21z"/>
 | 
			
		||||
        android:pathData="M5,13.18v4L12,21l7,-3.82v-4L12,17l-7,-3.82zM12,3L1,9l11,6 9,-4.91V17h2V9L12,3z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:autoMirrored="true"
 | 
			
		||||
        android:height="24dp"
 | 
			
		||||
        android:viewportWidth="24.0"
 | 
			
		||||
        android:viewportHeight="24.0">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FF000000"
 | 
			
		||||
        android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:viewportHeight="24.0"
 | 
			
		||||
        android:viewportWidth="24.0">
 | 
			
		||||
    <path android:fillColor="#FF000000"
 | 
			
		||||
          android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,8 @@
 | 
			
		||||
       android:shape="rectangle">
 | 
			
		||||
    <gradient
 | 
			
		||||
            android:angle="135"
 | 
			
		||||
            android:centerColor="@color/colorPrimary"
 | 
			
		||||
            android:endColor="@color/colorPrimaryDark"
 | 
			
		||||
            android:startColor="@color/colorPrimary"
 | 
			
		||||
            android:centerColor="?colorPrimary"
 | 
			
		||||
            android:endColor="?colorPrimaryDark"
 | 
			
		||||
            android:startColor="?colorPrimary"
 | 
			
		||||
            android:type="linear"/>
 | 
			
		||||
</shape>
 | 
			
		||||
@ -1,108 +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="125dp"
 | 
			
		||||
                android:layout_marginStart="5dp"
 | 
			
		||||
                android:layout_marginTop="5dp"
 | 
			
		||||
                android:layout_marginEnd="5dp"
 | 
			
		||||
                android:clickable="false"
 | 
			
		||||
                android:maxHeight="125dp"
 | 
			
		||||
                app:cardCornerRadius="15dp"
 | 
			
		||||
                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_Menu1Heading"
 | 
			
		||||
                        android:textStyle="bold" android:textAlignment="center" android:textSize="16sp"
 | 
			
		||||
                        android:typeface="sans" android:fontFamily="sans-serif" android:paddingBottom="5dp"/>
 | 
			
		||||
                <TextView
 | 
			
		||||
                        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:id="@+id/txtViewMenu1"/>
 | 
			
		||||
            </LinearLayout>
 | 
			
		||||
        </androidx.cardview.widget.CardView>
 | 
			
		||||
 | 
			
		||||
        <androidx.cardview.widget.CardView
 | 
			
		||||
                android:id="@+id/cardView2"
 | 
			
		||||
                android:layout_width="0dp"
 | 
			
		||||
                android:layout_height="125dp"
 | 
			
		||||
                android:layout_marginStart="5dp"
 | 
			
		||||
                android:layout_marginEnd="5dp"
 | 
			
		||||
                app:cardCornerRadius="15dp"
 | 
			
		||||
                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">
 | 
			
		||||
 | 
			
		||||
            <LinearLayout
 | 
			
		||||
                    android:orientation="vertical"
 | 
			
		||||
                    android:layout_width="match_parent"
 | 
			
		||||
                    android:layout_height="match_parent">
 | 
			
		||||
                <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"/>
 | 
			
		||||
            </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/colorPrimary" android:paddingTop="6dp">
 | 
			
		||||
 | 
			
		||||
            <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="@color/colorPrimary"/>
 | 
			
		||||
                        </LinearLayout>
 | 
			
		||||
                    </LinearLayout>
 | 
			
		||||
                </LinearLayout>
 | 
			
		||||
            </androidx.cardview.widget.CardView>
 | 
			
		||||
        </LinearLayout>
 | 
			
		||||
    </androidx.constraintlayout.widget.ConstraintLayout>
 | 
			
		||||
</FrameLayout>
 | 
			
		||||
@ -1,34 +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:contentPadding="5dp" app:cardElevation="5dp"
 | 
			
		||||
        app:cardUseCompatPadding="true" app:cardPreventCornerOverlap="false" app:cardCornerRadius="12dp">
 | 
			
		||||
 | 
			
		||||
    <LinearLayout
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:orientation="horizontal">
 | 
			
		||||
 | 
			
		||||
        <TextView
 | 
			
		||||
                android:id="@+id/txtView_Lesson"
 | 
			
		||||
                android:layout_width="wrap_content"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:text="@string/a_lesson"
 | 
			
		||||
                android:textColor="@android:color/primary_text_light"
 | 
			
		||||
                android:textSize="14sp"
 | 
			
		||||
                android:textStyle="bold"
 | 
			
		||||
                android:typeface="sans"/>
 | 
			
		||||
 | 
			
		||||
        <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">
 | 
			
		||||
        <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:cardCornerRadius="15dp"
 | 
			
		||||
        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"/>
 | 
			
		||||
    </LinearLayout>
 | 
			
		||||
 | 
			
		||||
</androidx.cardview.widget.CardView>
 | 
			
		||||
@ -20,7 +20,9 @@
 | 
			
		||||
            android:layout_height="match_parent"
 | 
			
		||||
            android:layout_gravity="start"
 | 
			
		||||
            android:fitsSystemWindows="true"
 | 
			
		||||
            android:background="?themeSecondary"
 | 
			
		||||
            app:headerLayout="@layout/nav_header_main"
 | 
			
		||||
            app:itemTextColor="?colorAccent"
 | 
			
		||||
            app:menu="@menu/activity_main_drawer"/>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
</androidx.drawerlayout.widget.DrawerLayout>
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
                    android:id="@+id/toolbar"
 | 
			
		||||
                    android:layout_width="match_parent"
 | 
			
		||||
                    android:layout_height="?attr/actionBarSize"
 | 
			
		||||
                    android:background="@color/design_default_color_primary"
 | 
			
		||||
                    android:background="?colorPrimary"
 | 
			
		||||
                    app:popupTheme="@style/AppTheme.PopupOverlay"/>
 | 
			
		||||
 | 
			
		||||
        </com.google.android.material.appbar.AppBarLayout>
 | 
			
		||||
							
								
								
									
										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>
 | 
			
		||||
@ -4,21 +4,22 @@
 | 
			
		||||
        xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="@dimen/nav_header_height"
 | 
			
		||||
        android:background="@drawable/side_nav_bar"
 | 
			
		||||
        android:background="@color/colorPrimary"
 | 
			
		||||
        android:paddingBottom="@dimen/activity_vertical_margin"
 | 
			
		||||
        android:paddingLeft="@dimen/activity_horizontal_margin"
 | 
			
		||||
        android:paddingRight="@dimen/activity_horizontal_margin"
 | 
			
		||||
        android:paddingTop="@dimen/activity_vertical_margin"
 | 
			
		||||
        android:theme="@style/ThemeOverlay.AppCompat.Dark"
 | 
			
		||||
        android:orientation="vertical"
 | 
			
		||||
        android:gravity="bottom">
 | 
			
		||||
        android:gravity="bottom"
 | 
			
		||||
        android:id="@+id/nav_header_main">
 | 
			
		||||
 | 
			
		||||
    <ImageView
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:paddingTop="@dimen/nav_header_vertical_spacing"
 | 
			
		||||
            app:srcCompat="@mipmap/ic_launcher_round"
 | 
			
		||||
            android:contentDescription="@string/nav_header_desc"
 | 
			
		||||
            app:srcCompat="@mipmap/ic_laogai_icon"
 | 
			
		||||
            android:contentDescription="@string/app_name"
 | 
			
		||||
            android:id="@+id/imageView"/>
 | 
			
		||||
 | 
			
		||||
    <TextView
 | 
			
		||||
@ -26,7 +27,7 @@
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:paddingTop="@dimen/nav_header_vertical_spacing"
 | 
			
		||||
            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
 | 
			
		||||
            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"
 | 
			
		||||
             android:layout_width="match_parent"
 | 
			
		||||
             android:layout_height="match_parent"
 | 
			
		||||
             tools:context=".fragments.TimeTableFragment">
 | 
			
		||||
             tools:context=".fragments.HomeFragment"
 | 
			
		||||
             android:background="?themePrimary">
 | 
			
		||||
 | 
			
		||||
    <androidx.constraintlayout.widget.ConstraintLayout
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
@ -15,9 +16,11 @@
 | 
			
		||||
            <LinearLayout
 | 
			
		||||
                    android:orientation="vertical"
 | 
			
		||||
                    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>
 | 
			
		||||
        </ScrollView>
 | 
			
		||||
 | 
			
		||||
    </androidx.constraintlayout.widget.ConstraintLayout>
 | 
			
		||||
 | 
			
		||||
</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>
 | 
			
		||||
@ -0,0 +1,14 @@
 | 
			
		||||
<?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.MoodleFragment">
 | 
			
		||||
 | 
			
		||||
    <!-- TODO: Update blank fragment layout -->
 | 
			
		||||
    <WebView
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="match_parent"
 | 
			
		||||
            android:id="@+id/webView"/>
 | 
			
		||||
 | 
			
		||||
</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,10 +4,13 @@
 | 
			
		||||
      tools:showIn="navigation_view">
 | 
			
		||||
 | 
			
		||||
    <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
 | 
			
		||||
                android:id="@+id/nav_mensa"
 | 
			
		||||
                android:icon="@drawable/ic_free_breakfast_black_24dp"
 | 
			
		||||
                android:icon="@drawable/ic_local_dining_black_24dp"
 | 
			
		||||
                android:title="@string/mensa"/>
 | 
			
		||||
        <item
 | 
			
		||||
                android:id="@+id/nav_timetable"
 | 
			
		||||
@ -15,12 +18,8 @@
 | 
			
		||||
                android:title="@string/timetable"/>
 | 
			
		||||
        <item
 | 
			
		||||
                android:id="@+id/nav_moodle"
 | 
			
		||||
                android:icon="@drawable/ic_menu_slideshow"
 | 
			
		||||
                android:icon="@drawable/ic_school_black_24dp"
 | 
			
		||||
                android:title="@string/moodle"/>
 | 
			
		||||
        <item
 | 
			
		||||
                android:id="@+id/nav_email"
 | 
			
		||||
                android:icon="@android:drawable/ic_dialog_email"
 | 
			
		||||
                android:title="@string/e_mail"/>
 | 
			
		||||
        <item
 | 
			
		||||
                android:id="@+id/nav_settings"
 | 
			
		||||
                android:icon="@drawable/ic_settings_black_24dp"
 | 
			
		||||
 | 
			
		||||
@ -4,5 +4,5 @@
 | 
			
		||||
    <item android:id="@+id/action_settings"
 | 
			
		||||
          android:title="@string/settings"
 | 
			
		||||
          android:orderInCategory="100"
 | 
			
		||||
          app:showAsAction="never"/>
 | 
			
		||||
          app:showAsAction="never" android:visible="false"/>
 | 
			
		||||
</menu>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								app/src/main/res/mipmap-anydpi-v26/ic_laogai_icon.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <background android:drawable="@color/ic_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
@ -1,5 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <background android:drawable="@drawable/ic_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
@ -1,5 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <background android:drawable="@drawable/ic_launcher_background"/>
 | 
			
		||||
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
 | 
			
		||||
</adaptive-icon>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_laogai_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.3 KiB  | 
| 
		 Before Width: | Height: | Size: 2.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 4.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_laogai_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_laogai_icon_splash.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.7 KiB  | 
| 
		 Before Width: | Height: | Size: 2.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 2.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_laogai_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.7 KiB  | 
| 
		 Before Width: | Height: | Size: 4.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.7 KiB  | 
| 
		 Before Width: | Height: | Size: 6.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_laogai_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 7.7 KiB  | 
| 
		 Before Width: | Height: | Size: 6.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.2 KiB  | 
| 
		 Before Width: | Height: | Size: 10 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_laogai_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
		 Before Width: | Height: | Size: 8.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 8.2 KiB  | 
| 
		 Before Width: | Height: | Size: 15 KiB  | 
							
								
								
									
										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,30 +1,66 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<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="mensa">Mensa</string>
 | 
			
		||||
    <string name="timetable">Stundenplan</string>
 | 
			
		||||
    <string name="moodle">Moodle</string>
 | 
			
		||||
    <string name="e_mail">E-Mail</string>
 | 
			
		||||
    <string name="settings">Einstellungen</string>
 | 
			
		||||
    <string name="meal_1">Essen 1</string>
 | 
			
		||||
    <string name="meal_2">Essen 2</string>
 | 
			
		||||
    <string name="meal_1_tomorrow">Essen 1, Morgen</string>
 | 
			
		||||
    <string name="meal_2_tomorrow">Essen 1, Morgen</string>
 | 
			
		||||
    <string name="no_meal_today">heute keine Essensausgabe</string>
 | 
			
		||||
 | 
			
		||||
    <!-- fragment_home -->
 | 
			
		||||
    <string name="meal">Essen</string>
 | 
			
		||||
    <string name="today_date">Heute, %1$s</string>
 | 
			
		||||
    <string name="tomorrow_date">Morgen, %1$s</string>
 | 
			
		||||
    <string name="mensa_closed">keine Essensausgabe</string>
 | 
			
		||||
    <string name="no_more_food">Diese Woche keine weitere Essensausgabe</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>
 | 
			
		||||
    <string name="gen_tt_error">Allgemeiner Stundenplan Fehler!"</string>
 | 
			
		||||
    <string name="no_lesson_today">heute keine Vorlesung!</string>
 | 
			
		||||
 | 
			
		||||
    <!-- fragment_timetable -->
 | 
			
		||||
    <string name="add_lesson">Eine Vorlesung hinzufügen</string>
 | 
			
		||||
 | 
			
		||||
    <!-- fragment_settings -->
 | 
			
		||||
    <string name="info">Info</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="main_color_desc">Die Primärfarbe, standard ist Indigo</string>
 | 
			
		||||
    <string name="select">auswählen</string>
 | 
			
		||||
    <string name="about">über</string>
 | 
			
		||||
    <string name="primary_color_desc">Zum Ändern tippen, Standard ist Schwarz.</string>
 | 
			
		||||
    <string name="accent_color">Akzentfarbe</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="hello_blank_fragment">bald ™</string>
 | 
			
		||||
    <string name="navigation_drawer_close">Navigationsleiste schließen</string>
 | 
			
		||||
    <string name="navigation_drawer_open">Navigationsleiste öffnen</string>
 | 
			
		||||
</resources>
 | 
			
		||||
    <string name="select">auswählen</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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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>
 | 
			
		||||
@ -1,7 +1,28 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <color name="colorPrimary">@color/design_default_color_primary</color>
 | 
			
		||||
    <color name="colorPrimaryDark">@color/design_default_color_primary_dark</color>
 | 
			
		||||
    <color name="colorAccent">#d81b60</color>
 | 
			
		||||
    <color name="colorMensaDay">#ebe8e9</color>
 | 
			
		||||
    <color name="colorPrimary">#000000</color>
 | 
			
		||||
    <color name="colorPrimaryDark">#000000</color>
 | 
			
		||||
    <color name="colorAccent">#3f51b5</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>
 | 
			
		||||
 | 
			
		||||
@ -4,5 +4,4 @@
 | 
			
		||||
    <dimen name="activity_vertical_margin">16dp</dimen>
 | 
			
		||||
    <dimen name="nav_header_vertical_spacing">8dp</dimen>
 | 
			
		||||
    <dimen name="nav_header_height">176dp</dimen>
 | 
			
		||||
    <dimen name="fab_margin">16dp</dimen>
 | 
			
		||||
</resources>
 | 
			
		||||
@ -2,51 +2,88 @@
 | 
			
		||||
    <string name="app_name" translatable="false">Project Laogai</string>
 | 
			
		||||
    <string name="navigation_drawer_open">Open navigation drawer</string>
 | 
			
		||||
    <string name="navigation_drawer_close">Close navigation drawer</string>
 | 
			
		||||
    <string name="nav_header_title" translatable="false">hso App 0.2.2</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_desc" translatable="false">Project Laogai</string>
 | 
			
		||||
 | 
			
		||||
    <!-- nav-view -->
 | 
			
		||||
    <string name="home">Home</string>
 | 
			
		||||
    <string name="mensa">Mensa</string>
 | 
			
		||||
    <string name="timetable">Timetable</string>
 | 
			
		||||
    <string name="moodle">Moodle</string>
 | 
			
		||||
    <string name="e_mail">E-Mail</string>
 | 
			
		||||
    <string name="settings">Settings</string>
 | 
			
		||||
 | 
			
		||||
    <string name="meal_1">Meal 1</string>
 | 
			
		||||
    <string name="meal_2">Meal 2</string>
 | 
			
		||||
    <string name="meal_1_tomorrow">Meal 1, tomorrow</string>
 | 
			
		||||
    <string name="meal_2_tomorrow">Meal 2, tomorrow</string>
 | 
			
		||||
    <string name="no_meal_today">Mensa closed today</string>
 | 
			
		||||
    <!-- fragment_home -->
 | 
			
		||||
    <string name="meal">Meal</string>
 | 
			
		||||
    <string name="today_date">Today, %1$s</string>
 | 
			
		||||
    <string name="tomorrow_date">Tomorrow, %1$s</string>
 | 
			
		||||
    <string name="mensa_closed">the Mensa is closed</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>
 | 
			
		||||
    <string name="sample_course" translatable="false">SampleCourse 3</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_timetable -->
 | 
			
		||||
    <string name="add_lesson">Add a lesson</string>
 | 
			
		||||
 | 
			
		||||
    <!-- fragment_settings -->
 | 
			
		||||
    <string name="info">Info</string>
 | 
			
		||||
    <string name="user">User</string>
 | 
			
		||||
    <string name="course_desc">Tap to change course</string>
 | 
			
		||||
    <string name="primary_color">primary color</string>
 | 
			
		||||
    <string name="main_color_desc">The primary color, default is indigo</string>
 | 
			
		||||
    <string name="select">select</string>
 | 
			
		||||
    <string name="version" translatable="false">version 0.2.2</string>
 | 
			
		||||
    <string name="about">about</string>
 | 
			
		||||
    <string name="about_dialog_heading">About</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="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 conditions of GPL 3. For further information visit \ngit.mosad.xyz/Seil0/ProjectLaogai \n\n© 2018 seil0@mosad.xyz "</string>
 | 
			
		||||
    <string name="about_version" translatable="false">Version %1$s (%2$s)</string>
 | 
			
		||||
    <string name="licenses">Licenses</string>
 | 
			
		||||
    <string name="theme">Theme</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="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="hello_blank_fragment">soon ™</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_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_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>
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,50 @@
 | 
			
		||||
<resources>
 | 
			
		||||
 | 
			
		||||
    <!-- Base application theme. -->
 | 
			
		||||
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
 | 
			
		||||
        <!-- Customize your theme here. -->
 | 
			
		||||
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
 | 
			
		||||
        <item name="colorPrimary">@color/colorPrimary</item>
 | 
			
		||||
        <item name="colorPrimaryDark">@color/colorPrimaryDark</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 name="AppTheme.NoActionBar">
 | 
			
		||||
@ -19,4 +58,8 @@
 | 
			
		||||
        <item name="android:windowBackground">@drawable/background_splash</item>
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <style name="LicensesDialogTheme.Dark" parent="Theme.AppCompat.Dialog">
 | 
			
		||||
        <item name="android:windowBackground">@color/themeSecondaryDark</item>
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
</resources>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<full-backup-content>
 | 
			
		||||
    <!-- Exclude specific shared preferences that contain GCM registration Id -->
 | 
			
		||||
    <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.
 | 
			
		||||
 | 
			
		||||
buildscript {
 | 
			
		||||
    ext.kotlin_version = '1.3.10'
 | 
			
		||||
    ext.kotlin_version = '1.3.61'
 | 
			
		||||
    repositories {
 | 
			
		||||
        google()
 | 
			
		||||
        jcenter()
 | 
			
		||||
    }
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:3.2.1'
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:3.5.3'
 | 
			
		||||
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 | 
			
		||||
 | 
			
		||||
        // NOTE: Do not place your application dependencies here; they belong
 | 
			
		||||
@ -19,7 +19,6 @@ allprojects {
 | 
			
		||||
    repositories {
 | 
			
		||||
        google()
 | 
			
		||||
        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 
 | 
			
		||||