Compare commits
80 Commits
Author | SHA1 | Date | |
---|---|---|---|
e7d733fc5b
|
|||
521045e0dd
|
|||
3e52061a20
|
|||
b76f869ef0
|
|||
175d3be882 | |||
2687a8124d | |||
5491971959
|
|||
b29392c6f6
|
|||
4b1fb47abf
|
|||
0724502831
|
|||
1162ec73e5
|
|||
f40c0503c0
|
|||
451f6082ff
|
|||
36568e9682
|
|||
6388dfe54a
|
|||
c509381ec4
|
|||
390728bfb7
|
|||
a3102bc3f2
|
|||
95ce9e14bd
|
|||
d803d4dae1
|
|||
45d37f25de
|
|||
95362d5ab8
|
|||
f12873fe00
|
|||
cb1f43ec66
|
|||
d6eb1ff1e5
|
|||
d94f38de93
|
|||
313ff4741a
|
|||
07e348989a | |||
0c6a486dd9 | |||
ec15c79b63
|
|||
4592d1984f
|
|||
aae74cf9db | |||
e19b0db39d
|
|||
23fd52b0c5
|
|||
62a62049c3
|
|||
849698779b
|
|||
a42a92a3c5
|
|||
be30e2f968
|
|||
c629b2aec2
|
|||
99ba87c3f6
|
|||
953b4825a9
|
|||
2807843d25
|
|||
638c321798
|
|||
42d938b0bc
|
|||
9d7504bbaf
|
|||
8ecfe8f120
|
|||
ff0c4ad1a7
|
|||
ea0caea91e
|
|||
1f660bdd37
|
|||
be43d87b1a
|
|||
dd5c7b3fb8
|
|||
fb3dab6dc3
|
|||
bcfdb83d14
|
|||
8c55366ec0
|
|||
6f68fbb73c | |||
297dd77e79
|
|||
6a70f26807
|
|||
f6b00a8d81
|
|||
69ce9fef5a
|
|||
2d753851c0
|
|||
6c0624c793
|
|||
7779296345
|
|||
3e2ef0521e
|
|||
bdfeb46faf
|
|||
18ca435764
|
|||
948f330ebe
|
|||
72e9efb9e7
|
|||
bea1b47396
|
|||
34a68ff75d
|
|||
1ba3f1fa87
|
|||
48544aef2f
|
|||
bc09e33147
|
|||
6ec9b9f5e2
|
|||
a19173b499
|
|||
85dc3181fd
|
|||
e46c234b6c
|
|||
faa07966da
|
|||
ccc0f0f2bc
|
|||
e15bf95b85
|
|||
01677c04ef
|
@ -2,8 +2,7 @@ kind: pipeline
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: assembleRelease
|
||||
image: nextcloudci/android:android-51
|
||||
- name: build
|
||||
image: nextcloudci/android10:android-56
|
||||
commands:
|
||||
- gradle assembleRelease
|
||||
|
||||
- ./gradlew build
|
||||
|
28
README.md
@ -1,23 +1,27 @@
|
||||
[](https://drone.mosad.xyz/Seil0/ProjectLaogai)
|
||||

|
||||
[](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.
|
||||
# Project Laogai
|
||||
Project Laogai is an app to access the timetable, grades (qispos) and the canteen menu of Hochschule Offenburg. Laogai uses the TCoR-API for timetables and the canteen menu, wich makes acessing them super fast. To get the grades from Qispos, Laogai will ask for your login data, wich are stored encrypted on your device.
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75">](https://f-droid.org/packages/org.mosad.seil0.projectlaogai/)
|
||||
[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="75">](https://play.google.com/store/apps/details?id=org.mosad.seil0.projectlaogai)
|
||||
|
||||
## Features
|
||||
* check out the mensa menu of this and next week
|
||||
* access your timetable
|
||||
* have your grades displayed directly in the app
|
||||
* show the timetable of your course
|
||||
* take a look at the canteen menu for the current and next week
|
||||
* check the current balance of your mensa card
|
||||
* open moodle
|
||||
* probably some funny bugs
|
||||
* open moodle directly in the app
|
||||
|
||||
Please report bugs and issues to support@mosad.xyz
|
||||
|
||||
## 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)
|
||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.webp" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.webp)
|
||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa.webp" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa.webp)
|
||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Timetable.webp" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Timetable.webp)
|
||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.webp" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.webp)
|
||||
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_dark_Mensa.webp" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_dark_Mensa.webp)
|
||||
|
||||
ProjectLaogai © 2019-2020 [@Seil0](https://git.mosad.xyz/Seil0), a [mosad](http://www.mosad.xyz) Project
|
||||
ProjectLaogai © 2019-2020 [@Seil0](https://git.mosad.xyz/Seil0), a [mosad.xyz](http://www.mosad.xyz) Project
|
||||
|
@ -1,33 +1,41 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain 11
|
||||
sourceSets.configureEach {
|
||||
languageSettings.optIn("kotlin.RequiresOptIn")
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
}
|
||||
compileSdkVersion 29
|
||||
|
||||
compileSdk 34
|
||||
buildToolsVersion = '34.0.0'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.mosad.seil0.projectlaogai"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 15
|
||||
versionName "0.5.1"
|
||||
targetSdkVersion 33
|
||||
versionCode 6100 // 00.06.100
|
||||
versionName "0.6.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'
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -42,28 +50,44 @@ android {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
testOptions.unitTests.all {
|
||||
useJUnitPlatform()
|
||||
|
||||
testLogging {
|
||||
events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
|
||||
}
|
||||
}
|
||||
namespace 'org.mosad.seil0.projectlaogai'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
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 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
|
||||
implementation "androidx.work:work-runtime-ktx:2.9.0"
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
implementation 'com.google.code.gson:gson:2.10'
|
||||
// implementation 'com.afollestad:aesthetic:1.0.0-beta05'
|
||||
implementation 'com.afollestad.material-dialogs:core:3.3.0'
|
||||
implementation 'com.afollestad.material-dialogs:color:3.3.0'
|
||||
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
|
||||
implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
|
||||
|
||||
implementation 'org.apache.commons:commons-lang3:3.9'
|
||||
implementation 'org.apache.commons:commons-lang3:3.11'
|
||||
implementation 'org.jsoup:jsoup:1.15.3'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
|
||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
}
|
||||
|
||||
static def buildTime() {
|
||||
|
13
app/proguard-rules.pro
vendored
@ -15,7 +15,20 @@
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
-dontobfuscate
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
-keep class org.mosad.seil0.projectlaogai.util.** { <fields>; }
|
||||
|
||||
# Gson
|
||||
-keepattributes Signature
|
||||
-dontwarn sun.misc.**
|
||||
|
||||
# com.google.crypto
|
||||
-dontwarn javax.annotation.Nullable
|
||||
-dontwarn javax.annotation.concurrent.GuardedBy
|
||||
|
||||
# misc
|
||||
-dontwarn java.lang.instrument.ClassFileTransformer
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.mosad.seil0.projectlaogai
|
||||
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@ -18,7 +18,7 @@ class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getTargetContext()
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().context
|
||||
assertEquals("org.mosad.seil0.projectlaogai", appContext.packageName)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.mosad.seil0.projectlaogai">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
<application
|
||||
@ -15,32 +15,40 @@
|
||||
|
||||
<activity
|
||||
android:name=".SplashActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/SplashTheme"
|
||||
android:screenOrientation="portrait">
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.shortcuts"
|
||||
<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.Light"
|
||||
android:name=".OnboardingActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:launchMode="singleTop">
|
||||
android:theme="@style/AppTheme.Light" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.Light">
|
||||
|
||||
<!-- nfc stuff -->
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
|
||||
<action android:name="android.nfc.action.TECH_DISCOVERED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
|
||||
<meta-data
|
||||
android:name="android.nfc.action.TECH_DISCOVERED"
|
||||
android:resource="@xml/nfc_tech_filter" />
|
||||
|
||||
</activity>
|
||||
|
BIN
app/src/main/ic_laogai_icon-playstore.png
Normal file
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 39 KiB |
@ -31,24 +31,26 @@ import android.nfc.NfcManager
|
||||
import android.nfc.tech.NfcA
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.addCallback
|
||||
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.aesthetic.Aesthetic
|
||||
import com.afollestad.aesthetic.NavigationViewMode
|
||||
import androidx.fragment.app.commit
|
||||
//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.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.controller.cache.CacheController
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.EncryptedPreferences
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.databinding.ActivityMainBinding
|
||||
import org.mosad.seil0.projectlaogai.fragments.*
|
||||
import org.mosad.seil0.projectlaogai.util.NotificationUtils
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
/**
|
||||
@ -56,8 +58,8 @@ import kotlin.system.measureTimeMillis
|
||||
*/
|
||||
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private var activeFragment: Fragment = HomeFragment() // the currently active fragment, home at the start
|
||||
private val className = "MainActivity"
|
||||
|
||||
private lateinit var adapter: NfcAdapter
|
||||
private lateinit var pendingIntent: PendingIntent
|
||||
@ -66,12 +68,12 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
private var useNFC = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
|
||||
|
||||
Aesthetic.attach(this)
|
||||
// Aesthetic.attach(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.appBar.toolbar)
|
||||
|
||||
// load mensa, timetable and color
|
||||
load()
|
||||
@ -79,24 +81,31 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
initForegroundDispatch()
|
||||
|
||||
val toggle = ActionBarDrawerToggle(
|
||||
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
||||
this, binding.drawerLayout, binding.appBar.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
||||
)
|
||||
drawer_layout.addDrawerListener(toggle)
|
||||
binding.drawerLayout.addDrawerListener(toggle)
|
||||
toggle.syncState()
|
||||
|
||||
nav_view.setNavigationItemSelectedListener(this)
|
||||
binding.navView.setNavigationItemSelectedListener(this)
|
||||
|
||||
// based on the inent we get, call readBalance or open a Fragment
|
||||
// based on the intent 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()
|
||||
getString(R.string.intent_action_mensaFragment) -> activeFragment = MensaFragment()
|
||||
getString(R.string.intent_action_timetableFragment) -> activeFragment = TimetableFragment()
|
||||
getString(R.string.intent_action_moodleFragment) -> activeFragment = MoodleFragment()
|
||||
else -> activeFragment = HomeFragment()
|
||||
}
|
||||
|
||||
// open the activeFragment, default is the HomeFragment
|
||||
fragmentTransaction.replace(R.id.fragment_container, activeFragment)
|
||||
fragmentTransaction.commit()
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.fragment_container, activeFragment, activeFragment.javaClass.simpleName)
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback {
|
||||
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
@ -109,7 +118,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
Aesthetic.resume(this)
|
||||
// Aesthetic.resume(this)
|
||||
if(useNFC)
|
||||
adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
|
||||
|
||||
@ -117,20 +126,11 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
Aesthetic.pause(this)
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.main, menu)
|
||||
@ -152,7 +152,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
activeFragment = when(item.itemId) {
|
||||
R.id.nav_home -> HomeFragment()
|
||||
R.id.nav_mensa -> MensaFragment()
|
||||
R.id.nav_timetable -> TimeTableFragment()
|
||||
R.id.nav_timetable -> TimetableFragment()
|
||||
R.id.nav_moodle -> MoodleFragment()
|
||||
R.id.nav_settings -> SettingsFragment()
|
||||
else -> HomeFragment()
|
||||
@ -162,7 +162,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
fragmentTransaction.replace(R.id.fragment_container, activeFragment)
|
||||
fragmentTransaction.commit()
|
||||
|
||||
drawer_layout.closeDrawer(GravityCompat.START)
|
||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
||||
|
||||
return true
|
||||
}
|
||||
@ -172,32 +172,43 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
*/
|
||||
private fun load() {
|
||||
val startupTime = measureTimeMillis {
|
||||
PreferencesController.load(this) // load the settings, must be finished before doing anything else
|
||||
Preferences.load(this) // load the settings, must be finished before doing anything else
|
||||
CacheController(this) // load the cache
|
||||
EncryptedPreferences.load(this)
|
||||
NotificationUtils(this)
|
||||
}
|
||||
Log.i(className, "startup completed in $startupTime ms")
|
||||
Log.i(javaClass.simpleName, "Startup completed in $startupTime ms")
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
// if (Aesthetic.isFirstTime) {
|
||||
// // set the default theme at the first app start
|
||||
// Aesthetic.config {
|
||||
// activityTheme(R.style.AppTheme_Light)
|
||||
// apply()
|
||||
// }
|
||||
// // TODO needs to be shown on first start
|
||||
// // show the onboarding activity
|
||||
// startActivity(Intent(this, OnboardingActivity::class.java))
|
||||
// finish()
|
||||
// }
|
||||
//
|
||||
// Aesthetic.config {
|
||||
// colorPrimary(Preferences.colorPrimary)
|
||||
// colorPrimaryDark(Preferences.colorPrimary)
|
||||
// colorAccent(Preferences.colorAccent)
|
||||
// navigationViewMode(NavigationViewMode.SELECTED_ACCENT)
|
||||
// apply()
|
||||
// }
|
||||
|
||||
SettingsFragment().selectCourse(this) // FIXME this is not working
|
||||
}
|
||||
|
||||
Aesthetic.config {
|
||||
colorPrimary(cColorPrimary)
|
||||
colorPrimaryDark(cColorPrimary)
|
||||
colorAccent(cColorAccent)
|
||||
navigationViewMode(NavigationViewMode.SELECTED_ACCENT)
|
||||
apply()
|
||||
}
|
||||
// set theme color values
|
||||
val out = TypedValue()
|
||||
this.theme.resolveAttribute(R.attr.themePrimary, out, true)
|
||||
Preferences.themePrimary = out.data
|
||||
|
||||
this.theme.resolveAttribute(R.attr.themeSecondary, out, true)
|
||||
Preferences.themeSecondary = out.data
|
||||
}
|
||||
|
||||
private fun initForegroundDispatch() {
|
||||
@ -211,7 +222,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
adapter = NfcAdapter.getDefaultAdapter(this)
|
||||
pendingIntent = PendingIntent.getActivity(
|
||||
this, 0,
|
||||
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0
|
||||
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import kotlinx.coroutines.*
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.CacheController
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.EncryptedPreferences
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.databinding.ActivityOnboardingBinding
|
||||
import org.mosad.seil0.projectlaogai.onboarding.ViewPagerAdapter
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.CourseSelectionDialog
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.LoadingDialog
|
||||
|
||||
class OnboardingActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityOnboardingBinding
|
||||
companion object {
|
||||
val layouts = intArrayOf(R.layout.fragment_on_welcome, R.layout.fragment_on_course, R.layout.fragment_on_login)
|
||||
}
|
||||
private lateinit var viewPager: ViewPager
|
||||
private lateinit var viewPagerAdapter: ViewPagerAdapter
|
||||
private lateinit var linLayoutDots: LinearLayout
|
||||
private lateinit var dots: Array<TextView>
|
||||
|
||||
private lateinit var editTextEmail: EditText
|
||||
private lateinit var editTextPassword: EditText
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityOnboardingBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
viewPager = findViewById(R.id.viewPager)
|
||||
linLayoutDots = findViewById(R.id.linLayout_Dots)
|
||||
|
||||
addBottomDots(0)
|
||||
|
||||
viewPagerAdapter = ViewPagerAdapter(this)
|
||||
viewPager.adapter = viewPagerAdapter
|
||||
viewPager.addOnPageChangeListener(viewPagerPageChangeListener)
|
||||
|
||||
// we don't use the skip button, instead we use the start button to skip the last fragment
|
||||
binding.btnSkip.visibility = View.GONE
|
||||
}
|
||||
|
||||
fun btnNextClick(@Suppress("UNUSED_PARAMETER")v: View) {
|
||||
if (viewPager.currentItem < layouts.size - 1) {
|
||||
viewPager.currentItem++
|
||||
} else {
|
||||
launchHomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
fun btnSkipClick(@Suppress("UNUSED_PARAMETER")v: View) {
|
||||
launchHomeScreen()
|
||||
}
|
||||
|
||||
fun btnSelectCourseClick(v: View) {
|
||||
CourseSelectionDialog(this).show {
|
||||
list = CacheController.coursesList.map { it.courseName }
|
||||
listItems {
|
||||
val loadingDialog = LoadingDialog(context)
|
||||
loadingDialog.show()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
Preferences.saveCourse(context, CacheController.coursesList[selectedIndex]) // save the course
|
||||
|
||||
// update current & next weeks timetable
|
||||
val threads = listOf(
|
||||
CacheController.updateTimetable(Preferences.course.courseName, 0, context),
|
||||
CacheController.updateTimetable(Preferences.course.courseName, 1, context)
|
||||
)
|
||||
threads.joinAll() // blocking since we want the new data
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
onDismiss {
|
||||
btnNextClick(v) // show the next fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun btnLoginClick(@Suppress("UNUSED_PARAMETER")v: View) {
|
||||
editTextEmail = findViewById(R.id.editText_email)
|
||||
editTextPassword = findViewById(R.id.editText_password)
|
||||
|
||||
// get login credentials from gui
|
||||
val email = editTextEmail.text.toString()
|
||||
val password = editTextPassword.text.toString()
|
||||
|
||||
// save the credentials
|
||||
EncryptedPreferences.saveCredentials(email, password, this)
|
||||
|
||||
launchHomeScreen()
|
||||
}
|
||||
|
||||
private fun addBottomDots(currentPage: Int) {
|
||||
dots = Array(layouts.size) { TextView(this) }
|
||||
linLayoutDots.removeAllViews()
|
||||
|
||||
dots.forEach {
|
||||
it.text = HtmlCompat.fromHtml("•", HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
it.textSize = 35f
|
||||
it.setTextColor(Color.parseColor("#cccccc"))
|
||||
linLayoutDots.addView(it)
|
||||
}
|
||||
|
||||
if (dots.isNotEmpty())
|
||||
dots[currentPage].setTextColor(Color.parseColor("#000000"))
|
||||
}
|
||||
|
||||
private fun launchHomeScreen() {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
|
||||
private var viewPagerPageChangeListener: ViewPager.OnPageChangeListener = object : ViewPager.OnPageChangeListener {
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
addBottomDots(position)
|
||||
// changing the next button text to skip for the login fragment
|
||||
if (position == layouts.size - 1) {
|
||||
binding.btnNext.text = getString(R.string.skip)
|
||||
binding.btnNext.visibility = View.VISIBLE
|
||||
binding.btnSkip.visibility = View.GONE
|
||||
} else {
|
||||
binding.btnNext.visibility = View.GONE
|
||||
binding.btnSkip.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrolled(arg0: Int, arg1: Float, arg2: Int) {}
|
||||
override fun onPageScrollStateChanged(arg0: Int) {}
|
||||
}
|
||||
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
}
|
@ -28,13 +28,14 @@ import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.util.Log
|
||||
import android.widget.TextView
|
||||
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 org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import java.lang.Exception
|
||||
|
||||
class NFCMensaCard {
|
||||
@ -84,20 +85,20 @@ class NFCMensaCard {
|
||||
val dialog = MaterialDialog(context)
|
||||
.customView(R.layout.dialog_mensa_credit)
|
||||
|
||||
val current = if (!PreferencesController.oGiants) {
|
||||
val current = if (!Preferences.oGiants) {
|
||||
String.format("%.2f €", (currentRaw.toFloat() / 1000))
|
||||
} else {
|
||||
String.format("%.4f shm", (currentRaw.toFloat() * 0.0000075))
|
||||
}
|
||||
|
||||
val last = if (!PreferencesController.oGiants) {
|
||||
val last = if (!Preferences.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)
|
||||
dialog.findViewById<TextView>(R.id.txtView_current).text = current
|
||||
dialog.findViewById<TextView>(R.id.txtView_last).text = context.resources.getString(R.string.mensa_last, last)
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
@ -1,183 +0,0 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,105 +22,95 @@
|
||||
|
||||
package org.mosad.seil0.projectlaogai.controller
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.reflect.TypeToken
|
||||
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 org.mosad.seil0.projectlaogai.util.*
|
||||
import java.net.URL
|
||||
import kotlin.Exception
|
||||
|
||||
/**
|
||||
* This Controller calls the tcor api,
|
||||
* all functions return tcor api objects
|
||||
*/
|
||||
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)
|
||||
* Get a array of all currently available courses at the tcor API.
|
||||
* Read the json object from tcor api
|
||||
*/
|
||||
fun getCoursesList(context: Context) = GlobalScope.launch {
|
||||
try {
|
||||
|
||||
fun getCourseListNEW(): CoursesList {
|
||||
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)
|
||||
}
|
||||
|
||||
return Gson().fromJson(url.readText(), CoursesList().javaClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* get the json object from tcor api and write it as file (cache)
|
||||
* Get current and next weeks mensa menus from the tcor API.
|
||||
* Read the json object from tcor api
|
||||
*/
|
||||
fun getMensa(context: Context) = GlobalScope.launch {
|
||||
try {
|
||||
fun getMensaMenu(): MensaMenu {
|
||||
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)
|
||||
}
|
||||
|
||||
return Gson().fromJson(url.readText(), MensaMenu().javaClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* get the json object from tcor api and write it as file (cache)
|
||||
* Get the timetable for "courseName" at week "week"
|
||||
* Read the json object from tcor api
|
||||
* @param courseName the course name (e.g AI1)
|
||||
* @param week the week to update (0 for the current and so on)
|
||||
*/
|
||||
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")
|
||||
fun getTimetable(courseName: String, week: Int): TimetableWeek {
|
||||
val url = URL("$tcorBaseURL/timetable?course=$courseName&week=$week")
|
||||
val timetableCW = Gson().fromJson(url.readText(), TimetableCourseWeek().javaClass)
|
||||
|
||||
// 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()
|
||||
return TimetableWeek(
|
||||
timetableCW.meta.weekIndex,
|
||||
timetableCW.meta.weekNumberYear,
|
||||
timetableCW.timetable.days
|
||||
)
|
||||
}
|
||||
|
||||
// update cache time
|
||||
timetableCacheTime = System.currentTimeMillis() / 1000
|
||||
PreferencesController.save(context)
|
||||
} catch (ex: Exception) {
|
||||
Log.e(className, "failed to get /timetable", ex)
|
||||
/**
|
||||
* Get all lessons for a course at one week (async)
|
||||
* @param courseName the course name
|
||||
* @param week the week to look up
|
||||
*/
|
||||
fun getSubjectListAsync(courseName: String, week: Int): Deferred<ArrayList<String>> {
|
||||
val url = URL("$tcorBaseURL/subjectList?course=$courseName&week=$week")
|
||||
|
||||
return CoroutineScope(Dispatchers.IO).async {
|
||||
Gson().fromJson(url.readText(), ArrayList<String>()::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all occurrences of a lesson for a course at one week
|
||||
* @param courseName the course name
|
||||
* @param subject the subject to search for
|
||||
* @param week the week to look up
|
||||
*/
|
||||
fun getLessons(courseName: String, subject: String, week: Int): ArrayList<Lesson> {
|
||||
val url = URL("$tcorBaseURL/lessons?course=$courseName&subject=$subject&week=$week")
|
||||
var array: ArrayList<Lesson>
|
||||
|
||||
runBlocking {
|
||||
withContext(Dispatchers.Default) {
|
||||
array = Gson().fromJson(
|
||||
JsonParser.parseString(url.readText()).asJsonArray,
|
||||
object : TypeToken<ArrayList<Lesson>>() {}.type
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
371
app/src/main/java/org/mosad/seil0/projectlaogai/controller/cache/CacheController.kt
vendored
Normal file
@ -0,0 +1,371 @@
|
||||
/**
|
||||
* 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.cache
|
||||
|
||||
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.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.coursesCacheTime
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.mensaCacheTime
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.timetableCacheTime
|
||||
import org.mosad.seil0.projectlaogai.util.*
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
/**
|
||||
* The cacheController reads and updates the cache files.
|
||||
* It contains the courseList and mensaMenu object, all timetable objects
|
||||
* are located in TimetableController.
|
||||
*/
|
||||
class CacheController(private val context: Context) {
|
||||
|
||||
private val className = this.javaClass.name
|
||||
|
||||
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
|
||||
cal.time = Date(mensaCacheTime * 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 - mensaCacheTime) > 86400) {
|
||||
Log.i(className, "Update mensa blocking")
|
||||
CoroutineScope(Dispatchers.Default).launch { updateMensaMenu(context).join() }
|
||||
}
|
||||
|
||||
// check if we need to update the timetable before displaying it
|
||||
cal.time = Date(timetableCacheTime * 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 - timetableCacheTime) > 86400) {
|
||||
Log.i(className, "Updating timetable after sunday!")
|
||||
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val threads = listOf(
|
||||
updateTimetable(Preferences.course.courseName, 0, context),
|
||||
updateTimetable(Preferences.course.courseName, 1, context)
|
||||
)
|
||||
threads.joinAll()
|
||||
}
|
||||
}
|
||||
|
||||
updateCourseList(context)
|
||||
|
||||
readStartCache(Preferences.course.courseName) // initially read values from cache
|
||||
|
||||
// check if an update is necessary, not blocking
|
||||
if (currentTime - coursesCacheTime > 86400)
|
||||
updateCourseList(context)
|
||||
|
||||
if (currentTime - mensaCacheTime > 10800)
|
||||
updateMensaMenu(context)
|
||||
|
||||
if (currentTime - timetableCacheTime > 10800)
|
||||
TimetableController.update(context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val className = "CacheController"
|
||||
var coursesList = ArrayList<Course>()
|
||||
var mensaMenu = MensaMenu()
|
||||
|
||||
/**
|
||||
* update the course list, async
|
||||
*/
|
||||
fun updateCourseList(context: Context): Job {
|
||||
val file = File(context.filesDir, "courses.json")
|
||||
var courseListUp = CoursesList()
|
||||
|
||||
return CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
courseListUp = TCoRAPIController.getCourseListNEW()
|
||||
} catch (ex: Exception) {
|
||||
Log.e(className, "could not load course list from tcor", ex)
|
||||
}
|
||||
|
||||
// update coursesList array list
|
||||
coursesList = courseListUp.courses
|
||||
|
||||
// save cache file and update time
|
||||
save(file, Gson().toJson(courseListUp))
|
||||
coursesCacheTime = System.currentTimeMillis() / 1000
|
||||
Preferences.save(context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update the mensa menu, async
|
||||
*/
|
||||
fun updateMensaMenu(context: Context): Job {
|
||||
val file = File(context.filesDir, "mensa.json")
|
||||
|
||||
return CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
mensaMenu = TCoRAPIController.getMensaMenu()
|
||||
} catch (ex: Exception) {
|
||||
Log.e(className, "could not load mensa menu from tcor", ex)
|
||||
}
|
||||
|
||||
// save cache file and update time
|
||||
save(file, Gson().toJson(mensaMenu))
|
||||
mensaCacheTime = System.currentTimeMillis() / 1000
|
||||
Preferences.save(context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update the timetable for a week, async
|
||||
* @param courseName the course name (e.g AI1)
|
||||
* @param week the week to update (0 for the current and so on)
|
||||
*/
|
||||
fun updateTimetable(courseName: String, week: Int, context: Context): Job {
|
||||
val file = File(context.filesDir, "timetable-$courseName-$week.json")
|
||||
var timetable = TimetableWeek()
|
||||
|
||||
// try to update timetable from tcor, async
|
||||
return CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
timetable = TCoRAPIController.getTimetable(courseName, week)
|
||||
} catch (ex: Exception) {
|
||||
Log.e(className, "could not load timetable $courseName[$week] from tcor", ex)
|
||||
}
|
||||
|
||||
// update timetable in TTC
|
||||
if (TimetableController.timetable.size > week) {
|
||||
TimetableController.timetable[week] = timetable
|
||||
} else {
|
||||
TimetableController.timetable.add(timetable)
|
||||
}
|
||||
|
||||
// save cache file and update time
|
||||
save(file, Gson().toJson(timetable))
|
||||
timetableCacheTime = System.currentTimeMillis() / 1000
|
||||
Preferences.save(context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update all additional subject lessons, async
|
||||
*/
|
||||
fun updateAdditionalLessons(context: Context): Job {
|
||||
val fileLessons = File(context.filesDir, "additional_lessons.json")
|
||||
|
||||
return CoroutineScope(Dispatchers.IO).launch {
|
||||
TimetableController.subjectMap.forEach { (courseName, subjects) ->
|
||||
// update all subjects for a course
|
||||
subjects.forEach {subject ->
|
||||
try {
|
||||
TCoRAPIController.getLessons(courseName, subject, 0).forEach { lesson ->
|
||||
TimetableController.addLesson(courseName, subject, lesson)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Log.e(className, "could not load $courseName: $subject", ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save(fileLessons, Gson().toJson(TimetableController.lessonMap))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* save changes in lessonMap and subjectMap,
|
||||
* called on addSubject or removeSubject
|
||||
*/
|
||||
fun saveAdditionalSubjects(context: Context) {
|
||||
val fileLessons = File(context.filesDir, "additional_lessons.json")
|
||||
val fileSubjects = File(context.filesDir, "additional_subjects.json")
|
||||
|
||||
save(fileLessons, Gson().toJson(TimetableController.lessonMap))
|
||||
save(fileSubjects, Gson().toJson(TimetableController.subjectMap))
|
||||
}
|
||||
|
||||
private fun save(file: File, text: String) {
|
||||
try {
|
||||
val writer = BufferedWriter(FileWriter(file))
|
||||
writer.write(text)
|
||||
writer.close()
|
||||
} catch (ex: Exception) {
|
||||
Log.e(className, "failed to write file \"${file.absoluteFile}\"", ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
try {
|
||||
readCoursesList()
|
||||
} catch (ex : Exception) {
|
||||
Log.e(className, "Error while reading the course list", ex)
|
||||
}
|
||||
|
||||
try {
|
||||
readMensa()
|
||||
} catch (ex : Exception) {
|
||||
Log.e(className, "Error while reading the mensa menu", ex)
|
||||
}
|
||||
|
||||
try {
|
||||
readTimetable(courseName, 0)
|
||||
} catch (ex : Exception) {
|
||||
Log.e(className, "Error while reading timetable week 0", ex)
|
||||
}
|
||||
|
||||
try {
|
||||
readTimetable(courseName, 1)
|
||||
} catch (ex : Exception) {
|
||||
Log.e(className, "Error while reading timetable week 1", ex)
|
||||
}
|
||||
|
||||
try {
|
||||
readAdditionalSubjects()
|
||||
} catch (ex : Exception) {
|
||||
Log.e(className, "Error while reading additional subjects", ex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read the courses list from the cached file
|
||||
* add them to the coursesList object
|
||||
*/
|
||||
private fun readCoursesList() {
|
||||
val file = File(context.filesDir, "courses.json")
|
||||
|
||||
// make sure the file exists
|
||||
if (!file.exists()) {
|
||||
runBlocking { updateCourseList(context).join() }
|
||||
return
|
||||
}
|
||||
|
||||
coursesList = FileReader(file).use {
|
||||
GsonBuilder().create().fromJson(it, CoursesList().javaClass).courses
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the MensaMenu object from the cached json,
|
||||
* if cache is empty create the cache file
|
||||
*/
|
||||
private fun readMensa() {
|
||||
val file = File(context.filesDir, "mensa.json")
|
||||
|
||||
// make sure the file exists
|
||||
if (!file.exists()) {
|
||||
runBlocking { updateMensaMenu(context).join() }
|
||||
return
|
||||
}
|
||||
|
||||
mensaMenu = FileReader(file).use {
|
||||
GsonBuilder().create().fromJson(it, 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)
|
||||
*/
|
||||
private fun readTimetable(courseName: String, week: Int) {
|
||||
val file = File(context.filesDir, "timetable-$courseName-$week.json")
|
||||
|
||||
// if the file does not exist, call updateTimetable blocking and return
|
||||
if (!file.exists()) {
|
||||
runBlocking { updateTimetable(courseName, week, context).join() }
|
||||
return
|
||||
}
|
||||
|
||||
val timetableObject = FileReader(file).use {
|
||||
JsonParser.parseString(BufferedReader(it).readLine()).asJsonObject
|
||||
}
|
||||
|
||||
// if its a TimetableCourseWeek object migrate to TimetableWeek TODO remove converting at version 0.8
|
||||
val timetable = if(timetableObject.has("meta")) {
|
||||
Log.i(Companion.className, "trying to migrate TimetableCourseWeek to TimetableWeek")
|
||||
val timetableWC = Gson().fromJson(timetableObject, TimetableCourseWeek().javaClass)
|
||||
save(file, Gson().toJson(TimetableWeek(
|
||||
timetableWC.meta.weekIndex,
|
||||
timetableWC.meta.weekNumberYear,
|
||||
timetableWC.timetable.days
|
||||
)))
|
||||
TimetableWeek(timetableWC.meta.weekIndex, timetableWC.meta.weekNumberYear, timetableWC.timetable.days)
|
||||
} else {
|
||||
Gson().fromJson(timetableObject, TimetableWeek().javaClass)
|
||||
}
|
||||
|
||||
// update timetable in TTC
|
||||
if (TimetableController.timetable.size > week) {
|
||||
TimetableController.timetable[week] = timetable
|
||||
} else {
|
||||
TimetableController.timetable.add(timetable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readAdditionalSubjects() {
|
||||
val fileLessons = File(context.filesDir, "additional_lessons.json")
|
||||
val fileSubjects = File(context.filesDir, "additional_subjects.json")
|
||||
|
||||
// make sure the file exists
|
||||
if (!fileLessons.exists() || !fileSubjects.exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
// clear the maps before loading, just to be save
|
||||
TimetableController.lessonMap.clear()
|
||||
TimetableController.subjectMap.clear()
|
||||
|
||||
// read subjects and lessons from cache file
|
||||
FileReader(fileLessons).use {
|
||||
TimetableController.lessonMap.putAll(
|
||||
GsonBuilder().create()
|
||||
.fromJson(it, object : TypeToken<HashMap<String, Lesson>>() {}.type)
|
||||
)
|
||||
}
|
||||
|
||||
FileReader(fileSubjects).use {
|
||||
TimetableController.subjectMap.putAll(
|
||||
GsonBuilder().create()
|
||||
.fromJson(it, HashMap<String, ArrayList<String>>().javaClass)
|
||||
)
|
||||
}
|
||||
|
||||
// add lessons to timetable
|
||||
TimetableController.lessonMap.forEach { (_, lesson) ->
|
||||
TimetableController.addLessonToTimetable(lesson)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
128
app/src/main/java/org/mosad/seil0/projectlaogai/controller/cache/TimetableController.kt
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 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.cache
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.coroutines.Job
|
||||
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.util.Lesson
|
||||
import org.mosad.seil0.projectlaogai.util.TimetableWeek
|
||||
|
||||
/**
|
||||
* The TimetableController contains the timetable, subjectMap
|
||||
* and lessonMap objects. It also contains the additional subjects logic.
|
||||
* All functions ro read or update cache files are located in the CacheController.
|
||||
*
|
||||
* TODO
|
||||
* * add second week
|
||||
* * add configurable week to addSubject() and removeSubject(), updateAdditionalLessons()
|
||||
*/
|
||||
object TimetableController {
|
||||
|
||||
val timetable = ArrayList<TimetableWeek>()
|
||||
val lessonMap = HashMap<String, Lesson>() // the key is courseName-subject-lessonID
|
||||
val subjectMap = HashMap<String, ArrayList<String>>() // the key is courseName
|
||||
|
||||
/**
|
||||
* update the main timetable and all additional subjects, async
|
||||
*/
|
||||
fun update(context: Context): List<Job> {
|
||||
return listOf(
|
||||
CacheController.updateTimetable(Preferences.course.courseName, 0, context),
|
||||
CacheController.updateTimetable(Preferences.course.courseName, 1, context),
|
||||
CacheController.updateAdditionalLessons(context)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* add a subject to the subjectMap and all it's lessons
|
||||
* to the lessonMap
|
||||
* @param courseName course to which the subject belongs
|
||||
* @param subject the subjects name
|
||||
*/
|
||||
fun addSubject(courseName: String, subject: String, context: Context) {
|
||||
// add subject
|
||||
if (subjectMap.containsKey(courseName)) {
|
||||
subjectMap[courseName]?.add(subject)
|
||||
} else {
|
||||
subjectMap[courseName] = arrayListOf(subject)
|
||||
}
|
||||
|
||||
// add concrete lessons
|
||||
TCoRAPIController.getLessons(courseName, subject, 0).forEach { lesson ->
|
||||
addLesson(courseName, subject, lesson)
|
||||
}
|
||||
|
||||
CacheController.saveAdditionalSubjects(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a subject from the subjectMap and all it's lessons
|
||||
* from the lessonMap
|
||||
* @param courseName course to which the subject belongs
|
||||
* @param subject the subjects name
|
||||
*/
|
||||
fun removeSubject(courseName: String, subject: String, context: Context) {
|
||||
// remove subject
|
||||
subjectMap[courseName]?.remove(subject)
|
||||
|
||||
// remove concrete lessons
|
||||
val iterator = lessonMap.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val it = iterator.next()
|
||||
if(it.key.contains("$courseName-$subject")) {
|
||||
// remove the lesson from the lessons list
|
||||
iterator.remove() // use iterator to remove, otherwise ConcurrentModificationException
|
||||
|
||||
// remove the lesson from the timetable
|
||||
val id = it.value.lessonID.split(".")
|
||||
if(id.size == 3)
|
||||
timetable[0].days[id[0].toInt()].timeslots[id[1].toInt()].remove(it.value)
|
||||
}
|
||||
}
|
||||
|
||||
CacheController.saveAdditionalSubjects(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* add a lesson to the lessonMap, also add it to the timetable
|
||||
*/
|
||||
fun addLesson(courseName: String, subject: String, lesson: Lesson) {
|
||||
//the courseName, subject and lessonID, separator: -
|
||||
val key = "$courseName-$subject-${lesson.lessonID}"
|
||||
lessonMap[key] = lesson
|
||||
|
||||
addLessonToTimetable(lesson)
|
||||
}
|
||||
|
||||
/**
|
||||
* add a lesson to the timetable
|
||||
*/
|
||||
fun addLessonToTimetable(lesson: Lesson) {
|
||||
val id = lesson.lessonID.split(".")
|
||||
if(id.size == 3)
|
||||
timetable[0].days[id[0].toInt()].timeslots[id[1].toInt()].add(lesson)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 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.preferences
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import android.util.Log
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
|
||||
object EncryptedPreferences {
|
||||
|
||||
var email = ""
|
||||
internal set
|
||||
|
||||
/**
|
||||
* save user email and password to encrypted preference
|
||||
*/
|
||||
fun saveCredentials(email: String, password: String, context: Context) {
|
||||
this.email = email
|
||||
|
||||
with (getEncryptedPreferences(context)?.edit()) {
|
||||
this?.putString(context.getString(R.string.save_key_user_email), email)
|
||||
this?.putString(context.getString(R.string.save_key_user_password), password)
|
||||
this?.apply()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* read user email and password from encrypted preference
|
||||
* @return Pair(email, password)
|
||||
*/
|
||||
fun readCredentials(context: Context): Pair<String, String> {
|
||||
return with (getEncryptedPreferences(context)) {
|
||||
email = this?.getString(context.getString(R.string.save_key_user_email), "").toString()
|
||||
|
||||
Pair(
|
||||
this?.getString(context.getString(R.string.save_key_user_email), "").toString(),
|
||||
this?.getString(context.getString(R.string.save_key_user_password), "").toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initially load the stored values
|
||||
*/
|
||||
fun load(context: Context) {
|
||||
with(getEncryptedPreferences(context)) {
|
||||
email = this?.getString(
|
||||
context.getString(R.string.save_key_user_email), ""
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a encrypted shared preference
|
||||
*/
|
||||
private fun getEncryptedPreferences(context: Context): SharedPreferences? {
|
||||
return try {
|
||||
val spec = KeyGenParameterSpec.Builder(MasterKey.DEFAULT_MASTER_KEY_ALIAS,
|
||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||
.setKeySize(MasterKey.DEFAULT_AES_GCM_MASTER_KEY_SIZE)
|
||||
.build()
|
||||
|
||||
val masterKey = MasterKey.Builder(context)
|
||||
.setKeyGenParameterSpec(spec)
|
||||
.build()
|
||||
|
||||
EncryptedSharedPreferences.create(
|
||||
context,
|
||||
context.getString(R.string.encrypted_preference_file_key),
|
||||
masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
Log.e(javaClass.name, "Could not create encrypted shared preference.", ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,219 @@
|
||||
/**
|
||||
* 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.preferences
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
import org.mosad.seil0.projectlaogai.util.Course
|
||||
|
||||
/**
|
||||
* The PreferencesController class
|
||||
* contains all preferences and global variables that exist in this app
|
||||
*/
|
||||
object Preferences {
|
||||
|
||||
var coursesCacheTime: Long = 0
|
||||
var mensaCacheTime: Long = 0
|
||||
var timetableCacheTime: Long = 0
|
||||
var gradesCacheTime: Long = 0
|
||||
var colorPrimary: Int = Color.parseColor("#009688")
|
||||
var colorAccent: Int = Color.parseColor("#0096ff")
|
||||
var gradesSyncInterval = 0
|
||||
var course = Course(
|
||||
"https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0",
|
||||
"AI3"
|
||||
)
|
||||
var showBuffet = true
|
||||
var oGiants = false
|
||||
|
||||
// TODO move!
|
||||
var themePrimary = 0
|
||||
var themeSecondary = 0
|
||||
|
||||
// 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
|
||||
)
|
||||
putLong(context.getString(R.string.save_key_gradesCacheTime),
|
||||
gradesCacheTime
|
||||
)
|
||||
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()
|
||||
}
|
||||
|
||||
this.course = 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()
|
||||
}
|
||||
|
||||
this.colorPrimary = 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()
|
||||
}
|
||||
|
||||
this.colorAccent = colorAccent
|
||||
}
|
||||
|
||||
fun saveGradesSync(context: Context, interval: 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_gradesSyncInterval),
|
||||
interval
|
||||
)
|
||||
apply()
|
||||
}
|
||||
|
||||
gradesSyncInterval = interval
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
|
||||
this.showBuffet = showBuffet
|
||||
}
|
||||
|
||||
/**
|
||||
* initially load the stored values
|
||||
*/
|
||||
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
|
||||
)
|
||||
gradesCacheTime = sharedPref.getLong(
|
||||
context.getString(R.string.save_key_gradesCacheTime), 0
|
||||
)
|
||||
|
||||
// load saved course
|
||||
course = 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
|
||||
colorPrimary = sharedPref.getInt(
|
||||
context.getString(R.string.save_key_colorPrimary), colorPrimary
|
||||
)
|
||||
colorAccent = sharedPref.getInt(
|
||||
context.getString(R.string.save_key_colorAccent), colorAccent
|
||||
)
|
||||
|
||||
// load grades sync interval
|
||||
gradesSyncInterval = sharedPref.getInt(
|
||||
context.getString(R.string.save_key_gradesSyncInterval), gradesSyncInterval
|
||||
)
|
||||
|
||||
// load showBuffet
|
||||
showBuffet = sharedPref.getBoolean(
|
||||
context.getString(R.string.save_key_showBuffet), true
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -30,16 +30,17 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.*
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
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.controller.cache.CacheController.Companion.mensaMenu
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController
|
||||
import org.mosad.seil0.projectlaogai.databinding.FragmentHomeBinding
|
||||
import org.mosad.seil0.projectlaogai.util.Meal
|
||||
import org.mosad.seil0.projectlaogai.util.TimetableDay
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.MealLinearLayout
|
||||
import org.mosad.seil0.projectlaogai.util.NotRetardedCalendar
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@ -51,36 +52,39 @@ class HomeFragment : Fragment() {
|
||||
|
||||
private val className = "HomeFragment"
|
||||
private val formatter = SimpleDateFormat("E dd.MM", Locale.getDefault())
|
||||
private lateinit var binding: FragmentHomeBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view: View = inflater.inflate(R.layout.fragment_home, container, false)
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
addMensaMenu()
|
||||
addTimeTable()
|
||||
|
||||
// Inflate the layout for this fragment
|
||||
return view
|
||||
}
|
||||
|
||||
/**
|
||||
* add the current mensa meal to the home screens
|
||||
*/
|
||||
private fun addMensaMenu() = GlobalScope.launch(Dispatchers.Default) {
|
||||
private fun addMensaMenu() = lifecycleScope.launch(Dispatchers.Default) {
|
||||
|
||||
var dayMeals: ArrayList<Meal>
|
||||
val cal = Calendar.getInstance()
|
||||
val mensaCardView = DayCardView(context!!)
|
||||
val mensaCardView = DayCardView(requireContext())
|
||||
|
||||
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)))
|
||||
mensaCardView.setDayHeading(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)))
|
||||
mensaCardView.setDayHeading(getString(R.string.tomorrow_date, formatter.format(cal.time)))
|
||||
}
|
||||
|
||||
if (dayMeals.size >= 2) {
|
||||
@ -105,7 +109,7 @@ class HomeFragment : Fragment() {
|
||||
mensaCardView.getLinLayoutDay().addView(getNoCard(resources.getString(R.string.mensa_closed)))
|
||||
}
|
||||
|
||||
linLayout_Home.addView(mensaCardView)
|
||||
binding.linLayoutHome.addView(mensaCardView)
|
||||
}
|
||||
|
||||
}
|
||||
@ -115,22 +119,17 @@ class HomeFragment : Fragment() {
|
||||
/**
|
||||
* add the current timetable to the home screen
|
||||
*/
|
||||
private fun addTimeTable() = GlobalScope.launch(Dispatchers.Default) {
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
if (isAdded && timetables.isNotEmpty()) {
|
||||
private fun addTimeTable() = lifecycleScope.launch(Dispatchers.Main) {
|
||||
if (isAdded && TimetableController.timetable.isNotEmpty()) {
|
||||
try {
|
||||
val dayCardView = findNextDay(NotRetardedCalendar.getDayOfWeekIndex())
|
||||
linLayout_Home.addView(dayCardView)
|
||||
binding.linLayoutHome.addView(dayCardView)
|
||||
} catch (ex: Exception) {
|
||||
Log.e(className, "could not load timetable", ex) // TODO send feedback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* find the next day with a lesson
|
||||
* start at week 0, startDayIndex and search every cached week until we find a) a day with a timetable
|
||||
@ -139,17 +138,18 @@ class HomeFragment : Fragment() {
|
||||
* @return a DayCardView with all lessons added
|
||||
*/
|
||||
private fun findNextDay(startDayIndex: Int): DayCardView {
|
||||
val dayCardView = DayCardView(context!!)
|
||||
val dayCardView = DayCardView(requireContext())
|
||||
var dayTimetable: TimetableDay? = null
|
||||
var dayIndexSearch = startDayIndex
|
||||
var weekIndexSearch = 0
|
||||
|
||||
while (dayTimetable == null && weekIndexSearch < timetables.size) {
|
||||
while (dayTimetable == null && weekIndexSearch < TimetableController.timetable.size) {
|
||||
for (dayIndex in dayIndexSearch..5) {
|
||||
dayTimetable = timetables[weekIndexSearch].timetable.days[dayIndex]
|
||||
dayTimetable = TimetableController.timetable[weekIndexSearch].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())
|
||||
val daysToAdd = ((TimetableController.timetable[weekIndexSearch].weekNumberYear - NotRetardedCalendar.getWeekOfYear())
|
||||
* 7 + (dayIndex - NotRetardedCalendar.getDayOfWeekIndex()))
|
||||
dayCardView.addTimetableDay(dayTimetable, daysToAdd)
|
||||
|
||||
// if there are no lessons don't show the dayCardView
|
||||
|
@ -27,20 +27,20 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlinx.android.synthetic.main.fragment_mensa.*
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
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.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.controller.cache.CacheController
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.CacheController.Companion.mensaMenu
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.databinding.FragmentMensaBinding
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.MealLinearLayout
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.TextViewInfo
|
||||
import org.mosad.seil0.projectlaogai.util.MensaWeek
|
||||
import org.mosad.seil0.projectlaogai.util.NotRetardedCalendar
|
||||
|
||||
/**
|
||||
* The mensa controller class
|
||||
@ -48,25 +48,35 @@ import org.mosad.seil0.projectlaogai.uicomponents.MealLinearLayout
|
||||
*/
|
||||
class MensaFragment : Fragment() {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
private lateinit var binding: FragmentMensaBinding
|
||||
|
||||
val view: View = inflater.inflate(R.layout.fragment_mensa, container, false)
|
||||
|
||||
// init actions
|
||||
refreshAction()
|
||||
|
||||
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)
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
binding = FragmentMensaBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// TODO should we show a info if there is no more food this & next week?
|
||||
binding.refreshLayoutMensa.setProgressBackgroundColorSchemeColor(Preferences.themeSecondary)
|
||||
|
||||
return view
|
||||
initActions() // init actions
|
||||
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
val dayCurrent = if(NotRetardedCalendar.getDayOfWeekIndex() == 6) 0 else NotRetardedCalendar.getDayOfWeekIndex()
|
||||
|
||||
// add the current and next week
|
||||
addWeek(mensaMenu.currentWeek, dayCurrent).join()
|
||||
addWeek(mensaMenu.nextWeek, 0).join()
|
||||
|
||||
// show a info if there are no more menus
|
||||
if (binding.linLayoutMensa.childCount == 0) {
|
||||
val txtViewInfo = TextViewInfo(requireContext()).set {
|
||||
txt = resources.getString(R.string.no_more_meals)
|
||||
}
|
||||
withContext(Dispatchers.Main) { binding.linLayoutMensa.addView(txtViewInfo) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,14 +84,13 @@ class MensaFragment : Fragment() {
|
||||
* @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) {
|
||||
private fun addWeek(menusWeek: MensaWeek, dayStart: Int) = lifecycleScope.launch(Dispatchers.Default) {
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
// 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!!)
|
||||
val dayCardView = DayCardView(requireContext())
|
||||
|
||||
if(menusWeek.days[dayIndex].meals.isNotEmpty())
|
||||
dayCardView.setDayHeading(menusWeek.days[dayIndex].meals[0].day)
|
||||
@ -90,7 +99,7 @@ class MensaFragment : Fragment() {
|
||||
val mealLayout = MealLinearLayout(context)
|
||||
mealLayout.setMeal(meal)
|
||||
|
||||
if(meal.heading != "Buffet" || cShowBuffet) {
|
||||
if(meal.heading != "Buffet" || Preferences.showBuffet) {
|
||||
dayCardView.getLinLayoutDay().addView(mealLayout)
|
||||
helpMeal = mealLayout
|
||||
}
|
||||
@ -98,47 +107,49 @@ class MensaFragment : Fragment() {
|
||||
|
||||
helpMeal.disableDivider()
|
||||
|
||||
if(dayCardView.getLinLayoutDay().childCount > 1)
|
||||
linLayout_Mensa.addView(dayCardView)
|
||||
if(dayCardView.getLinLayoutDay().childCount > 2)
|
||||
binding.linLayoutMensa.addView(dayCardView)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the refresh action
|
||||
* initialize the actions
|
||||
*/
|
||||
private fun refreshAction() = GlobalScope.launch(Dispatchers.Default) {
|
||||
withContext(Dispatchers.Main) {
|
||||
private fun initActions() {
|
||||
// set the refresh listener
|
||||
refreshLayout_Mensa.setOnRefreshListener {
|
||||
binding.refreshLayoutMensa.setOnRefreshListener {
|
||||
updateMensaScreen()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh the mensa cache and update the mensa screen
|
||||
*/
|
||||
private fun updateMensaScreen() = GlobalScope.launch(Dispatchers.Default) {
|
||||
// update the cache
|
||||
TCoRAPIController.getMensa(context!!).join() // blocking since we want the new data
|
||||
CacheController.readMensa(context!!)
|
||||
private fun updateMensaScreen() = lifecycleScope.launch(Dispatchers.Default) {
|
||||
CacheController.updateMensaMenu(requireContext()).join() // blocking since we want the new data
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
// remove all menus from the layout
|
||||
linLayout_Mensa.removeAllViews()
|
||||
binding.linLayoutMensa.removeAllViews()
|
||||
|
||||
// add the refreshed menus
|
||||
val dayCurrent = if (NotRetardedCalendar.getDayOfWeekIndex() == 6) 0 else NotRetardedCalendar.getDayOfWeekIndex()
|
||||
|
||||
// add the current and next week
|
||||
addWeek(mensaMenu.currentWeek, dayCurrent).join()
|
||||
addWeek(mensaMenu.nextWeek, 0).join()
|
||||
|
||||
// add the next week
|
||||
addWeek(mensaMenu.nextWeek, 0)
|
||||
binding.refreshLayoutMensa.isRefreshing = false
|
||||
|
||||
refreshLayout_Mensa.isRefreshing = false
|
||||
// show a info if there are no more menus
|
||||
if (binding.linLayoutMensa.childCount == 0) {
|
||||
val txtViewInfo = TextViewInfo(requireContext()).set {
|
||||
txt = resources.getString(R.string.no_more_meals)
|
||||
}
|
||||
binding.linLayoutMensa.addView(txtViewInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,11 @@ class MoodleFragment : Fragment() {
|
||||
private lateinit var webSettings: WebSettings
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_moodle, container, false)
|
||||
}
|
||||
|
||||
val view: View = inflater.inflate(R.layout.fragment_moodle, container, false)
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
webView = view.findViewById(R.id.webView)
|
||||
webView.loadUrl("https://elearning.hs-offenburg.de/moodle/")
|
||||
@ -52,7 +55,5 @@ class MoodleFragment : Fragment() {
|
||||
//webSettings.setJavaScriptEnabled(true) // Enable Javascript
|
||||
|
||||
webView.webViewClient = WebViewClient() // Force links and redirects to open in the WebView instead of in a browser
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
@ -22,36 +22,35 @@
|
||||
|
||||
package org.mosad.seil0.projectlaogai.fragments
|
||||
|
||||
import android.content.Context
|
||||
//import com.afollestad.aesthetic.Aesthetic
|
||||
import android.os.Bundle
|
||||
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 androidx.lifecycle.lifecycleScope
|
||||
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 com.afollestad.materialdialogs.WhichButton
|
||||
import com.afollestad.materialdialogs.actions.getActionButton
|
||||
import com.afollestad.materialdialogs.list.listItemsMultiChoice
|
||||
import de.psdev.licensesdialog.LicensesDialog
|
||||
import kotlinx.android.synthetic.main.fragment_settings.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mosad.seil0.projectlaogai.BuildConfig
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
import org.mosad.seil0.projectlaogai.controller.CacheController
|
||||
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.coursesList
|
||||
import org.mosad.seil0.projectlaogai.controller.PreferencesController
|
||||
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorAccent
|
||||
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
|
||||
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
|
||||
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cShowBuffet
|
||||
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
|
||||
import org.mosad.seil0.projectlaogai.hsoparser.DataTypes
|
||||
import java.util.*
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.CacheController
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.CacheController.Companion.coursesList
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.EncryptedPreferences
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.databinding.FragmentSettingsBinding
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.CourseSelectionDialog
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.LoadingDialog
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.LoginDialog
|
||||
import org.mosad.seil0.projectlaogai.util.DataTypes
|
||||
|
||||
/**
|
||||
* The settings controller class
|
||||
@ -59,90 +58,135 @@ import java.util.*
|
||||
*/
|
||||
class SettingsFragment : Fragment() {
|
||||
|
||||
private lateinit var linLayoutUser: LinearLayout
|
||||
private lateinit var linLayoutCourse: LinearLayout
|
||||
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
|
||||
private lateinit var binding: FragmentSettingsBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
private var selectedTheme = DataTypes.Theme.Light
|
||||
|
||||
val view: View = inflater.inflate(R.layout.fragment_settings, container, false)
|
||||
|
||||
linLayoutUser = view.findViewById(R.id.linLayout_User)
|
||||
linLayoutCourse = view.findViewById(R.id.linLayout_Course)
|
||||
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()
|
||||
|
||||
// Inflate the layout for this fragment
|
||||
return view
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the settings gui
|
||||
*/
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// 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
|
||||
initActions()
|
||||
|
||||
binding.textUser.text = EncryptedPreferences.email.ifEmpty { resources.getString(R.string.sample_user) }
|
||||
binding.textCourse.text = Preferences.course.courseName
|
||||
binding.textAboutDesc.text = resources.getString(R.string.about_version, BuildConfig.VERSION_NAME, getString(R.string.build_time))
|
||||
binding.switchBuffet.isChecked = Preferences.showBuffet // init switch
|
||||
|
||||
val outValue = TypedValue()
|
||||
activity!!.theme.resolveAttribute(R.attr.themeName, outValue, true)
|
||||
requireActivity().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)
|
||||
binding.switchBuffet.setTextColor(requireActivity().resources.getColor(R.color.textPrimaryLight, requireActivity().theme))
|
||||
selectedTheme = DataTypes.Theme.Light
|
||||
selectedTheme.string = 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)
|
||||
binding.switchBuffet.setTextColor(requireActivity().resources.getColor(R.color.textPrimaryDark, requireActivity().theme))
|
||||
selectedTheme = DataTypes.Theme.Dark
|
||||
selectedTheme.string = 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)
|
||||
binding.switchBuffet.setTextColor(requireActivity().resources.getColor(R.color.textPrimaryDark, requireActivity().theme))
|
||||
selectedTheme = DataTypes.Theme.Black
|
||||
selectedTheme.string = resources.getString(R.string.themeBlack)
|
||||
}
|
||||
}
|
||||
|
||||
binding.textThemeSelected.text = selectedTheme.string
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize some actions for SettingsFragment elements
|
||||
*/
|
||||
private fun initActions() {
|
||||
linLayoutUser.setOnClickListener {
|
||||
binding.linLayoutUser.setOnClickListener {
|
||||
// open a new dialog
|
||||
LoginDialog(requireContext())
|
||||
.positiveButton {
|
||||
EncryptedPreferences.saveCredentials(email, password, context)
|
||||
}
|
||||
.show {
|
||||
email = EncryptedPreferences.email
|
||||
password = ""
|
||||
}
|
||||
}
|
||||
|
||||
linLayoutUser.setOnLongClickListener {
|
||||
PreferencesController.oGiants = true // enable easter egg
|
||||
binding.linLayoutUser.setOnLongClickListener {
|
||||
Preferences.oGiants = true // enable easter egg
|
||||
return@setOnLongClickListener true
|
||||
}
|
||||
|
||||
linLayoutCourse.setOnClickListener {
|
||||
selectCourse(context!!)
|
||||
binding.linLayoutCourse.setOnClickListener {
|
||||
CourseSelectionDialog(requireContext()).show {
|
||||
list = coursesList.map { it.courseName }
|
||||
listItems {
|
||||
val loadingDialog = LoadingDialog(context)
|
||||
loadingDialog.show()
|
||||
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
Preferences.saveCourse(context, coursesList[selectedIndex]) // save the course
|
||||
|
||||
// update current & next weeks timetable
|
||||
val threads = listOf(
|
||||
CacheController.updateTimetable(Preferences.course.courseName, 0, context),
|
||||
CacheController.updateTimetable(Preferences.course.courseName, 1, context)
|
||||
)
|
||||
threads.joinAll() // blocking since we want the new data
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
onDismiss {
|
||||
binding.textCourse.text = Preferences.course.courseName // update txtView after the dialog is dismissed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linLayoutAbout.setOnClickListener {
|
||||
binding.linLayoutManageLessons.setOnClickListener {
|
||||
val lessons = ArrayList<String>()
|
||||
TimetableController.subjectMap.forEach { pair ->
|
||||
pair.value.forEach {
|
||||
lessons.add("${pair.key} - $it")
|
||||
}
|
||||
}
|
||||
|
||||
MaterialDialog(requireContext()).show {
|
||||
title(R.string.manage_lessons)
|
||||
positiveButton(R.string.delete)
|
||||
negativeButton(R.string.cancel)
|
||||
getActionButton(WhichButton.POSITIVE).updateTextColor(Preferences.colorAccent)
|
||||
getActionButton(WhichButton.NEGATIVE).updateTextColor(Preferences.colorAccent)
|
||||
|
||||
listItemsMultiChoice(items = lessons) { _, _, items ->
|
||||
items.forEach {
|
||||
val list = it.split(" - ")
|
||||
TimetableController.removeSubject(list[0], list[1], context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.linLayoutAbout.setOnClickListener {
|
||||
// open a new info dialog
|
||||
MaterialDialog(context!!)
|
||||
MaterialDialog(requireContext())
|
||||
.title(R.string.about_dialog_heading)
|
||||
.message(R.string.about_dialog_text)
|
||||
.show()
|
||||
}
|
||||
|
||||
linLayoutLicence.setOnClickListener {
|
||||
binding.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)
|
||||
requireContext().theme.resolveAttribute(R.attr.themeName, outValue, true)
|
||||
|
||||
val dialogCss = when (outValue.string) {
|
||||
"light" -> R.string.license_dialog_style_light
|
||||
@ -155,7 +199,7 @@ class SettingsFragment : Fragment() {
|
||||
}
|
||||
|
||||
// open a new license dialog
|
||||
LicensesDialog.Builder(context!!)
|
||||
LicensesDialog.Builder(requireContext())
|
||||
.setNotices(R.raw.notices)
|
||||
.setTitle(R.string.licenses)
|
||||
.setIncludeOwnLicense(true)
|
||||
@ -165,105 +209,72 @@ class SettingsFragment : Fragment() {
|
||||
.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
// binding.linLayoutTheme.setOnClickListener {
|
||||
// val themes = listOf(
|
||||
// resources.getString(R.string.themeLight),
|
||||
// resources.getString(R.string.themeDark),
|
||||
// resources.getString(R.string.themeBlack)
|
||||
// )
|
||||
//
|
||||
// MaterialDialog(requireContext()).show {
|
||||
// title(R.string.theme)
|
||||
// listItemsSingleChoice(items = themes, initialSelection = selectedTheme.ordinal) { _, 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()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// binding.linLayoutPrimaryColor.setOnClickListener {
|
||||
// // open a new color chooser dialog
|
||||
// MaterialDialog(requireContext())
|
||||
// .colorChooser(DataTypes().primaryColors, allowCustomArgb = true, initialSelection = Preferences.colorPrimary) { _, color ->
|
||||
// binding.viewPrimaryColor.setBackgroundColor(color)
|
||||
// Aesthetic.config {
|
||||
// colorPrimary(color)
|
||||
// colorPrimaryDark(color)
|
||||
// apply()
|
||||
// }
|
||||
// Preferences.saveColorPrimary(requireContext(), color)
|
||||
// }
|
||||
// .show {
|
||||
// title(R.string.primary_color)
|
||||
// positiveButton(R.string.select)
|
||||
// getActionButton(WhichButton.POSITIVE).updateTextColor(Preferences.colorAccent)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
// binding.linLayoutAccentColor.setOnClickListener {
|
||||
// // open a new color chooser dialog
|
||||
// MaterialDialog(requireContext())
|
||||
// .colorChooser(DataTypes().accentColors, allowCustomArgb = true, initialSelection = Preferences.colorAccent) { _, color ->
|
||||
// binding.viewAccentColor.setBackgroundColor(color)
|
||||
// Aesthetic.config {
|
||||
// colorAccent(color)
|
||||
// apply()
|
||||
// }
|
||||
//
|
||||
// Preferences.saveColorAccent(requireContext(), color)
|
||||
// }
|
||||
// .show{
|
||||
// title(R.string.accent_color)
|
||||
// positiveButton(R.string.select)
|
||||
// getActionButton(WhichButton.POSITIVE).updateTextColor(Preferences.colorAccent)
|
||||
// }
|
||||
// }
|
||||
|
||||
binding.switchBuffet.setOnClickListener {
|
||||
Preferences.saveShowBuffet(requireContext(), binding.switchBuffet.isChecked)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
PreferencesController.saveCourse(context, coursesList[index]) // save the course
|
||||
|
||||
// update current & next weeks timetable
|
||||
val threads = listOf(
|
||||
TCoRAPIController.getTimetable(cCourse.courseName, 0, context),
|
||||
TCoRAPIController.getTimetable(cCourse.courseName, 1, context)
|
||||
)
|
||||
threads.joinAll() // blocking since we want the new data
|
||||
|
||||
CacheController.readTimetable(cCourse.courseName, 0, context)
|
||||
CacheController.readTimetable(cCourse.courseName, 1, context)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
dialog.dismiss()
|
||||
txtView_Course.text = cCourse.courseName // update txtView
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,162 +0,0 @@
|
||||
/**
|
||||
* 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 android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.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.DayCardView
|
||||
|
||||
/**
|
||||
* The timetable controller class
|
||||
* contains all needed parts to display and the timetable detail screen
|
||||
*/
|
||||
class TimeTableFragment : Fragment() {
|
||||
|
||||
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_timetable, container, false)
|
||||
scrollViewTimetable = view.findViewById(R.id.scrollView_Timetable)
|
||||
faBtnAddLesson = view.findViewById(R.id.faBtnAddLesson)
|
||||
|
||||
// init actions
|
||||
initActions()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the actions
|
||||
*/
|
||||
private fun initActions() = GlobalScope.launch(Dispatchers.Default) {
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
refreshLayout_Timetable.setOnRefreshListener {
|
||||
updateTimetableScreen()
|
||||
}
|
||||
|
||||
faBtnAddLesson.setOnClickListener {
|
||||
MaterialDialog(context!!)
|
||||
.title(text = resources.getString(R.string.add_lesson))
|
||||
.message(text = "wähle einen Studiengang aus:\n\nWähle eine Vorlesung aus: \n\n Diese Funktion ist noch nicht verfügbar")
|
||||
.show()
|
||||
}
|
||||
|
||||
// hide the btnCardValue if the user is scrolling down
|
||||
scrollViewTimetable.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
if (scrollY > oldScrollY) {
|
||||
faBtnAddLesson.hide()
|
||||
} else {
|
||||
faBtnAddLesson.show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* add the current and next weeks lessons
|
||||
*/
|
||||
private fun addInitWeeks() = GlobalScope.launch(Dispatchers.Default) {
|
||||
val currentDayIndex = NotRetardedCalendar.getDayOfWeekIndex()
|
||||
|
||||
addWeek(currentDayIndex, 5, 0).join() // add current week
|
||||
addWeek(0, currentDayIndex - 1, 1) // add next week
|
||||
}
|
||||
|
||||
private fun addWeek(startDayIndex: Int, dayEndIndex: Int, weekIndex: Int) = GlobalScope.launch(Dispatchers.Default) {
|
||||
val timetable = timetables[weekIndex].timetable
|
||||
val timetableMeta = timetables[weekIndex].meta
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
for (dayIndex in startDayIndex..dayEndIndex) {
|
||||
val dayCardView = DayCardView(context!!)
|
||||
|
||||
// some wired calendar magic, calculate the correct date to be shown ((timetable week - current week * 7) + (dayIndex - dayIndex of current week)
|
||||
val daysToAdd =(timetableMeta.weekNumberYear - NotRetardedCalendar.getWeekOfYear()) * 7 + (dayIndex - NotRetardedCalendar.getDayOfWeekIndex())
|
||||
dayCardView.addTimetableDay(timetable.days[dayIndex], daysToAdd)
|
||||
|
||||
// if there are no lessons don't show the dayCardView
|
||||
if (dayCardView.getLinLayoutDay().childCount > 1)
|
||||
linLayout_Timetable.addView(dayCardView)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTimetableScreen() = GlobalScope.launch(Dispatchers.Default) {
|
||||
// update the cache
|
||||
val threads = listOf(
|
||||
TCoRAPIController.getTimetable(cCourse.courseName, 0, context!!),
|
||||
TCoRAPIController.getTimetable(cCourse.courseName, 1, context!!)
|
||||
)
|
||||
threads.joinAll() // blocking since we want the new data
|
||||
|
||||
CacheController.readTimetable(cCourse.courseName, 0, context!!)
|
||||
CacheController.readTimetable(cCourse.courseName, 1, context!!)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
// remove all menus from the layout
|
||||
linLayout_Timetable.removeAllViews()
|
||||
|
||||
// add the refreshed timetables
|
||||
val dayIndex = NotRetardedCalendar.getDayOfWeekIndex()
|
||||
|
||||
// add current week
|
||||
addWeek(dayIndex, 5, 0).join()
|
||||
|
||||
// add next week
|
||||
addWeek(0, dayIndex - 1, 1)
|
||||
|
||||
refreshLayout_Timetable.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 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 android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.*
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController.timetable
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.databinding.FragmentTimetableBinding
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.AddSubjectDialog
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView
|
||||
import org.mosad.seil0.projectlaogai.uicomponents.TextViewInfo
|
||||
import org.mosad.seil0.projectlaogai.util.NotRetardedCalendar
|
||||
|
||||
/**
|
||||
* The timetable controller class
|
||||
* contains all needed parts to display and the timetable detail screen
|
||||
*/
|
||||
class TimetableFragment : Fragment() {
|
||||
|
||||
private lateinit var binding: FragmentTimetableBinding
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
binding = FragmentTimetableBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.refreshLayoutTimetable.setProgressBackgroundColorSchemeColor(Preferences.themeSecondary)
|
||||
|
||||
initActions() // init actions
|
||||
|
||||
if (timetable.size > 1 && timetable[0].days.isNotEmpty() && timetable[1].days.isNotEmpty()) {
|
||||
initTimetable()
|
||||
} else {
|
||||
val txtViewInfo = TextViewInfo(requireContext()).set {
|
||||
txt = resources.getString(R.string.timetable_generic_error)
|
||||
}.showImage()
|
||||
binding.linLayoutTimetable.addView(txtViewInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the actions
|
||||
*/
|
||||
private fun initActions() {
|
||||
|
||||
binding.refreshLayoutTimetable.setOnRefreshListener {
|
||||
runBlocking { TimetableController.update(requireContext()).joinAll() }
|
||||
reloadTimetableUI()
|
||||
}
|
||||
|
||||
// show the AddLessonDialog if the ftaBtn is clicked
|
||||
binding.faBtnAddSubject.setOnClickListener {
|
||||
AddSubjectDialog(requireContext())
|
||||
.positiveButton {
|
||||
TimetableController.addSubject(selectedCourse, selectedSubject, context)
|
||||
runBlocking { reloadTimetableUI() }
|
||||
}.show()
|
||||
}
|
||||
|
||||
// hide the btnCardValue if the user is scrolling down
|
||||
binding.scrollViewTimetable.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
if (scrollY > oldScrollY) {
|
||||
binding.faBtnAddSubject.hide()
|
||||
} else {
|
||||
binding.faBtnAddSubject.show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* add the current and next weeks lessons
|
||||
*/
|
||||
private fun initTimetable() = lifecycleScope.launch(Dispatchers.Default) {
|
||||
val currentDayIndex = NotRetardedCalendar.getDayOfWeekIndex()
|
||||
|
||||
addTimetableWeek(currentDayIndex, 5, 0).join() // add current week
|
||||
addTimetableWeek(0, currentDayIndex - 1, 1) // add next week
|
||||
}
|
||||
|
||||
private fun addTimetableWeek(dayBegin: Int, dayEnd: Int, week: Int) = lifecycleScope.launch(Dispatchers.Main) {
|
||||
for (dayIndex in dayBegin..dayEnd) {
|
||||
val dayCardView = DayCardView(requireContext())
|
||||
|
||||
// some wired calendar magic, calculate the correct date to be shown
|
||||
// ((timetable week - current week * 7) + (dayIndex - dayIndex of current week)
|
||||
val daysToAdd = ((timetable[week].weekNumberYear - NotRetardedCalendar.getWeekOfYear())
|
||||
* 7 + (dayIndex - NotRetardedCalendar.getDayOfWeekIndex()))
|
||||
dayCardView.addTimetableDay(timetable[week].days[dayIndex], daysToAdd)
|
||||
|
||||
// if there are no lessons don't show the dayCardView
|
||||
if (dayCardView.getLinLayoutDay().childCount > 1)
|
||||
binding.linLayoutTimetable.addView(dayCardView)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clear linLayout_Timetable, add the updated timetable
|
||||
*/
|
||||
private fun reloadTimetableUI() = lifecycleScope.launch(Dispatchers.Default) {
|
||||
withContext(Dispatchers.Main) {
|
||||
// remove all lessons from the layout
|
||||
binding.linLayoutTimetable.removeAllViews()
|
||||
|
||||
// add the refreshed timetables
|
||||
val dayIndex = NotRetardedCalendar.getDayOfWeekIndex()
|
||||
|
||||
addTimetableWeek(dayIndex, 5, 0).join() // add current week
|
||||
addTimetableWeek(0, dayIndex - 1, 1) // add next week
|
||||
|
||||
binding.refreshLayoutTimetable.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.viewpager.widget.PagerAdapter
|
||||
import org.mosad.seil0.projectlaogai.OnboardingActivity.Companion.layouts
|
||||
|
||||
/**
|
||||
* a PagerAdapter
|
||||
*/
|
||||
class ViewPagerAdapter(cont: Context) : PagerAdapter() {
|
||||
|
||||
private val context = cont
|
||||
|
||||
override fun isViewFromObject(view: View, `object`: Any): Boolean {
|
||||
return view === `object`
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return layouts.size
|
||||
}
|
||||
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val view = LayoutInflater.from(context).inflate(layouts[position], container, false)
|
||||
container.addView(view)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||
val view = `object` as View
|
||||
container.removeView(view)
|
||||
}
|
||||
|
||||
}
|
@ -24,32 +24,30 @@ package org.mosad.seil0.projectlaogai.uicomponents
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
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 org.mosad.seil0.projectlaogai.databinding.CardviewDayBinding
|
||||
import org.mosad.seil0.projectlaogai.util.DataTypes
|
||||
import org.mosad.seil0.projectlaogai.util.TimetableDay
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class DayCardView(context: Context) : CardView(context) {
|
||||
|
||||
private var binding = CardviewDayBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
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)
|
||||
this.setBackgroundColor(Color.TRANSPARENT) // workaround to prevent a white border
|
||||
}
|
||||
|
||||
fun getLinLayoutDay() : LinearLayout {
|
||||
return linLayout_Day
|
||||
return binding.linearDay
|
||||
}
|
||||
|
||||
fun setDayHeading(heading: String) {
|
||||
txtView_DayHeading.text = heading
|
||||
binding.textDayHeading.text = heading
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,17 +60,16 @@ class DayCardView(context: Context) : CardView(context) {
|
||||
// set the heading
|
||||
val cal = Calendar.getInstance()
|
||||
cal.add(Calendar.DATE, daysToAdd)
|
||||
txtView_DayHeading.text = formatter.format(cal.time)
|
||||
binding.textDayHeading.text = formatter.format(cal.time)
|
||||
|
||||
// for every timeslot of that timetable
|
||||
for ((tsIndex, timeslot) in timetable.timeslots.withIndex()) {
|
||||
|
||||
for (lesson in timeslot) {
|
||||
timetable.timeslots.forEachIndexed { tsIndex, timeslot ->
|
||||
timeslot.forEach { lesson ->
|
||||
if (lesson.lessonSubject.isNotEmpty()) {
|
||||
|
||||
val lessonLayout = LessonLinearLayout(context)
|
||||
lessonLayout.setLesson(lesson, DataTypes().getTime()[tsIndex])
|
||||
linLayout_Day.addView(lessonLayout)
|
||||
lessonLayout.setLesson(lesson, DataTypes().times[tsIndex])
|
||||
binding.linearDay.addView(lessonLayout)
|
||||
|
||||
if (lesson != timeslot.last()) {
|
||||
lessonLayout.disableDivider()
|
||||
@ -81,6 +78,7 @@ class DayCardView(context: Context) : CardView(context) {
|
||||
lastLesson = lessonLayout
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lastLesson.disableDivider() // disable the divider for the last lesson of the day
|
||||
|
@ -23,28 +23,25 @@
|
||||
package org.mosad.seil0.projectlaogai.uicomponents
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
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
|
||||
import org.mosad.seil0.projectlaogai.databinding.LinearlayoutLessonBinding
|
||||
import org.mosad.seil0.projectlaogai.util.Lesson
|
||||
|
||||
class LessonLinearLayout(context: Context?) : LinearLayout(context) {
|
||||
|
||||
init {
|
||||
CardView.inflate(context, R.layout.linearlayout_lesson, this)
|
||||
}
|
||||
private val binding = LinearlayoutLessonBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
|
||||
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
|
||||
binding.textLessonTime.text = time
|
||||
binding.textLessonSubject.text = lesson.lessonSubject
|
||||
binding.textLessonTeacher.text = lesson.lessonTeacher
|
||||
binding.textLessonRoom.text = lesson.lessonRoom
|
||||
}
|
||||
|
||||
fun disableDivider() {
|
||||
divider_lesson.visibility = View.GONE
|
||||
binding.dividerLesson.visibility = View.GONE
|
||||
}
|
||||
|
||||
}
|
@ -23,31 +23,28 @@
|
||||
package org.mosad.seil0.projectlaogai.uicomponents
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
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
|
||||
import org.mosad.seil0.projectlaogai.databinding.LinearlayoutMealBinding
|
||||
import org.mosad.seil0.projectlaogai.util.Meal
|
||||
|
||||
class MealLinearLayout(context: Context?): LinearLayout(context) {
|
||||
|
||||
init {
|
||||
CardView.inflate(context, R.layout.linearlayout_meal, this)
|
||||
}
|
||||
private val binding = LinearlayoutMealBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
|
||||
fun setMeal(meal: Meal) {
|
||||
txtView_MealHeading.text = meal.heading
|
||||
binding.textMealHeading.text = meal.heading
|
||||
|
||||
meal.parts.forEachIndexed { partIndex, part ->
|
||||
txtView_Meal.append(part)
|
||||
binding.textMeal.append(part)
|
||||
if(partIndex < (meal.parts.size - 1))
|
||||
txtView_Meal.append("\n")
|
||||
binding.textMeal.append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
fun disableDivider() {
|
||||
divider_meal.visibility = View.GONE
|
||||
binding.dividerMeal.visibility = View.GONE
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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.Rect
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
|
||||
|
||||
class TextViewInfo(context: Context?): AppCompatTextView(context!!) {
|
||||
|
||||
var txt = ""
|
||||
var txtSize = 15F
|
||||
var txtAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
var drawable = R.drawable.ic_error_outline_black_24dp
|
||||
var params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
init {
|
||||
params.setMargins(0,200,0,0)
|
||||
}
|
||||
|
||||
fun set(func: TextViewInfo.() -> Unit): TextViewInfo = apply {
|
||||
func()
|
||||
|
||||
text = txt
|
||||
layoutParams = params
|
||||
textSize = txtSize
|
||||
textAlignment = txtAlignment
|
||||
}
|
||||
|
||||
fun showImage(): TextViewInfo = apply {
|
||||
val img = ResourcesCompat.getDrawable(resources, drawable, context.theme)?.apply {
|
||||
bounds = Rect(0, 0, 75, 75)
|
||||
}
|
||||
setCompoundDrawables(null, null, null, img)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Spinner
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.WhichButton
|
||||
import com.afollestad.materialdialogs.actions.getActionButton
|
||||
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||
import com.afollestad.materialdialogs.bottomsheets.setPeekHeight
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import com.afollestad.materialdialogs.customview.getCustomView
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
import org.mosad.seil0.projectlaogai.controller.cache.CacheController
|
||||
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
|
||||
import org.mosad.seil0.projectlaogai.util.Course
|
||||
import java.util.stream.Collectors
|
||||
|
||||
/**
|
||||
* This class creates a new AddLessonDialog.
|
||||
*/
|
||||
class AddSubjectDialog(val context: Context) {
|
||||
|
||||
private val dialog = MaterialDialog(context, BottomSheet())
|
||||
|
||||
private val spinnerCourses: Spinner
|
||||
private val spinnerSubjects: Spinner
|
||||
|
||||
private val subjectsList = ArrayList<String>()
|
||||
private val courseNamesList = getCourseNames()
|
||||
|
||||
var selectedCourse = ""
|
||||
var selectedSubject = ""
|
||||
|
||||
init {
|
||||
dialog.title(R.string.add_lesson)
|
||||
.message(R.string.add_lesson_desc)
|
||||
.customView(R.layout.dialog_add_lesson)
|
||||
.positiveButton(R.string.add)
|
||||
.negativeButton(R.string.cancel)
|
||||
.setPeekHeight(900)
|
||||
|
||||
spinnerCourses = dialog.getCustomView().findViewById(R.id.spinner_Courses)
|
||||
spinnerSubjects = dialog.getCustomView().findViewById(R.id.spinner_Lessons)
|
||||
|
||||
// fix not working accent color
|
||||
dialog.getActionButton(WhichButton.POSITIVE).updateTextColor(Preferences.colorAccent)
|
||||
dialog.getActionButton(WhichButton.NEGATIVE).updateTextColor(Preferences.colorAccent)
|
||||
|
||||
initSpinners()
|
||||
}
|
||||
|
||||
fun positiveButton(func: AddSubjectDialog.() -> Unit): AddSubjectDialog = apply {
|
||||
dialog.positiveButton {
|
||||
func()
|
||||
}
|
||||
}
|
||||
|
||||
fun show() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
fun show(func: AddSubjectDialog.() -> Unit): AddSubjectDialog = apply {
|
||||
func()
|
||||
this.show()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun dismiss() {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
private fun initSpinners() {
|
||||
setArrayAdapter(spinnerCourses, courseNamesList)
|
||||
val lessonsAdapter = setArrayAdapter(spinnerSubjects, subjectsList)
|
||||
|
||||
// don't call onItemSelected() on spinnerCourses.onItemSelectedListener
|
||||
spinnerCourses.setSelection(0,false)
|
||||
spinnerCourses.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
|
||||
selectedCourse = parent.getItemAtPosition(pos).toString()
|
||||
|
||||
// TODO show loading dialog while loading
|
||||
val lessonSubjects = runBlocking {
|
||||
TCoRAPIController.getSubjectListAsync(parent.getItemAtPosition(pos).toString(), 0).await()
|
||||
}
|
||||
|
||||
lessonsAdapter.clear()
|
||||
lessonsAdapter.addAll(lessonSubjects)
|
||||
lessonsAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// Another interface callback
|
||||
}
|
||||
}
|
||||
|
||||
// don't call onItemSelected() on spinnerCourses.onItemSelectedListener
|
||||
spinnerSubjects.setSelection(0,false)
|
||||
spinnerSubjects.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
|
||||
selectedSubject = parent.getItemAtPosition(pos).toString()
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
// Another interface callback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set a new ArrayAdapter for a spinner with a list
|
||||
* @param spinner the spinner you wish to set the adapter for
|
||||
* @param list the list to set the adapter to
|
||||
*/
|
||||
private fun setArrayAdapter(spinner: Spinner, list: List<String>): ArrayAdapter<String> {
|
||||
return ArrayAdapter(context, android.R.layout.simple_spinner_item, list)
|
||||
.also { adapter ->
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
spinner.adapter = adapter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get all course names from coursesList
|
||||
* @return a list, containing all course names
|
||||
*/
|
||||
private fun getCourseNames(): List<String> {
|
||||
val coursesNameList: List<String>
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
coursesNameList = CacheController.coursesList.stream().map(Course::courseName).collect(
|
||||
Collectors.toList())
|
||||
} else {
|
||||
coursesNameList = ArrayList()
|
||||
CacheController.coursesList.forEach { course ->
|
||||
coursesNameList.add(course.courseName)
|
||||
}
|
||||
}
|
||||
|
||||
return coursesNameList
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.mosad.seil0.projectlaogai.uicomponents.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.callbacks.onDismiss
|
||||
import com.afollestad.materialdialogs.list.listItems
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
|
||||
class CourseSelectionDialog(val context: Context) {
|
||||
|
||||
private val dialog = MaterialDialog(context)
|
||||
|
||||
var list: List<String> = listOf()
|
||||
var selectedIndex = 0
|
||||
|
||||
init {
|
||||
dialog.title(R.string.select_course)
|
||||
}
|
||||
|
||||
fun show() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
fun show(func: CourseSelectionDialog.() -> Unit): CourseSelectionDialog = apply {
|
||||
func()
|
||||
this.show()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun dismiss() {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
fun onDismiss(func: CourseSelectionDialog.() -> Unit): CourseSelectionDialog = apply {
|
||||
dialog.onDismiss {
|
||||
func()
|
||||
}
|
||||
}
|
||||
|
||||
fun listItems(func: CourseSelectionDialog.() -> Unit): CourseSelectionDialog = apply {
|
||||
dialog.listItems(items = list) { _, index, _ ->
|
||||
selectedIndex = index
|
||||
|
||||
func()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package org.mosad.seil0.projectlaogai.uicomponents.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
|
||||
class LoadingDialog(val context: Context) {
|
||||
|
||||
private val dialog = MaterialDialog(context)
|
||||
|
||||
init {
|
||||
dialog.cancelable(false)
|
||||
.cancelOnTouchOutside(false)
|
||||
.customView(R.layout.dialog_loading)
|
||||
}
|
||||
|
||||
fun show() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
fun show(func: LoadingDialog.() -> Unit): LoadingDialog = apply {
|
||||
func()
|
||||
this.show()
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.EditText
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.WhichButton
|
||||
import com.afollestad.materialdialogs.actions.getActionButton
|
||||
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||
import com.afollestad.materialdialogs.bottomsheets.setPeekHeight
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import com.afollestad.materialdialogs.customview.getCustomView
|
||||
import org.mosad.seil0.projectlaogai.R
|
||||
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
|
||||
|
||||
class LoginDialog(val context: Context) {
|
||||
|
||||
private val dialog = MaterialDialog(context, BottomSheet())
|
||||
|
||||
private val editTextEmail: EditText
|
||||
private val editTextPassword: EditText
|
||||
|
||||
var email = ""
|
||||
var password = ""
|
||||
|
||||
init {
|
||||
dialog.title(R.string.login_heading)
|
||||
.message(R.string.login_desc_on)
|
||||
.customView(R.layout.dialog_login)
|
||||
.positiveButton(R.string.save)
|
||||
.negativeButton(R.string.cancel)
|
||||
.setPeekHeight(900)
|
||||
|
||||
editTextEmail = dialog.getCustomView().findViewById(R.id.editText_email)
|
||||
editTextPassword = dialog.getCustomView().findViewById(R.id.editText_password)
|
||||
|
||||
// fix not working accent color
|
||||
dialog.getActionButton(WhichButton.POSITIVE).updateTextColor(Preferences.colorAccent)
|
||||
dialog.getActionButton(WhichButton.NEGATIVE).updateTextColor(Preferences.colorAccent)
|
||||
}
|
||||
|
||||
fun positiveButton(func: LoginDialog.() -> Unit): LoginDialog = apply {
|
||||
dialog.positiveButton {
|
||||
email = editTextEmail.text.toString()
|
||||
password = editTextPassword.text.toString()
|
||||
|
||||
func()
|
||||
}
|
||||
}
|
||||
|
||||
fun negativeButton(func: LoginDialog.() -> Unit): LoginDialog = apply {
|
||||
dialog.negativeButton {
|
||||
func()
|
||||
}
|
||||
}
|
||||
|
||||
fun show() {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
fun show(func: LoginDialog.() -> Unit): LoginDialog = apply {
|
||||
func()
|
||||
|
||||
editTextEmail.setText(email)
|
||||
editTextPassword.setText(password)
|
||||
|
||||
show()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun dismiss() {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
}
|
@ -20,15 +20,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.mosad.seil0.projectlaogai.hsoparser
|
||||
package org.mosad.seil0.projectlaogai.util
|
||||
|
||||
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"),
|
||||
@ -60,7 +58,7 @@ class DataTypes {
|
||||
Color.parseColor("#3F51B5"),
|
||||
Color.parseColor("#3D5AFE"),
|
||||
Color.parseColor("#2979FF"),
|
||||
Color.parseColor("#00B0FF"),
|
||||
Color.parseColor("#0096FF"),
|
||||
Color.parseColor("#00E5FF"),
|
||||
Color.parseColor("#1DE9B6"),
|
||||
Color.parseColor("#00E676"),
|
||||
@ -72,58 +70,20 @@ class DataTypes {
|
||||
Color.parseColor("#000000")
|
||||
)
|
||||
|
||||
init {
|
||||
// do something
|
||||
}
|
||||
|
||||
fun getTime(): Array<String> {
|
||||
return times
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NotRetardedCalendar {
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
enum class Theme(var string: String) {
|
||||
Light("Light"),
|
||||
Dark("Dark"),
|
||||
Black("Black")
|
||||
}
|
||||
}
|
||||
|
||||
// data classes for the course part
|
||||
data class Course(val courseLink: String, val courseName: String)
|
||||
|
||||
data class CoursesMeta(val updateTime: Long = 0, val totalCourses: Int = 0)
|
||||
|
||||
data class CoursesList(val meta: CoursesMeta = CoursesMeta(), val courses: ArrayList<Course> = ArrayList())
|
||||
|
||||
// data classes for the Mensa part
|
||||
data class Meal(val day: String, val heading: String, val parts: ArrayList<String>, val additives: String)
|
||||
|
||||
@ -137,16 +97,23 @@ data class MensaMenu(val meta: MensaMeta = MensaMeta(), val currentWeek: MensaWe
|
||||
|
||||
// data classes for the timetable part
|
||||
data class Lesson(
|
||||
val lessonID: String,
|
||||
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 TimetableDay(val timeslots: Array<ArrayList<Lesson>> = Array(6) { ArrayList() })
|
||||
|
||||
data class TimetableWeek(val days: Array<TimetableDay> = Array(6) { TimetableDay() })
|
||||
data class TimetableWeek(val weekIndex: Int = 0, val weekNumberYear: Int = 0, val days: Array<TimetableDay> = Array(6) { TimetableDay() })
|
||||
|
||||
// data classes for the qispos part
|
||||
data class GradeSubject(val id: String = "", val name: String = "", val semester: String = "", val grade: String = "", val credits: String = "")
|
||||
|
||||
// TCoR
|
||||
data class TimetableWeekTCoR(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())
|
||||
data class TimetableCourseWeek(val meta: TimetableCourseMeta = TimetableCourseMeta(), var timetable: TimetableWeekTCoR = TimetableWeekTCoR())
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.util
|
||||
|
||||
import java.util.*
|
||||
|
||||
class NotRetardedCalendar {
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.mosad.seil0.projectlaogai.util
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
|
||||
class NotificationUtils(val context: Context) {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID_GRADES = "channel_grades"
|
||||
|
||||
val id = AtomicInteger(0)
|
||||
|
||||
fun getId() = id.incrementAndGet()
|
||||
}
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannel(CHANNEL_ID_GRADES, "Grades Channel", "A Channel")
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
private fun createNotificationChannel(channelId: String, name: String, desc: String) {
|
||||
val mChannel = NotificationChannel(channelId, name, NotificationManager.IMPORTANCE_DEFAULT)
|
||||
mChannel.description = desc
|
||||
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(mChannel)
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +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="@android:color/black"/>
|
||||
|
||||
<item android:gravity="center" android:width="144dp" android:height="144dp">
|
||||
<bitmap
|
||||
android:gravity="fill_horizontal|fill_vertical"
|
||||
android:src="@mipmap/ic_laogai_icon_splash"/>
|
||||
android:src="@drawable/ic_splash_logo"/>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
9
app/src/main/res/drawable/ic_add_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"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_grading_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"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4,7h16v2H4V7zM4,13h16v-2H4V13zM4,17h7v-2H4V17zM4,21h7v-2H4V21zM15.41,18.17L14,16.75l-1.41,1.41L15.41,21L20,16.42L18.58,15L15.41,18.17zM4,3v2h16V3H4z"
|
||||
android:fillColor="#000000"/>
|
||||
</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="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
|
||||
</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="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||
</vector>
|
@ -1,9 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
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"/>
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
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" />
|
||||
</vector>
|
||||
|
BIN
app/src/main/res/drawable/ic_splash_logo.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
@ -1,9 +0,0 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="135"
|
||||
android:centerColor="?colorPrimary"
|
||||
android:endColor="?colorPrimaryDark"
|
||||
android:startColor="?colorPrimary"
|
||||
android:type="linear"/>
|
||||
</shape>
|
@ -10,9 +10,10 @@
|
||||
tools:openDrawer="start">
|
||||
|
||||
<include
|
||||
android:id="@+id/app_bar"
|
||||
layout="@layout/app_bar_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/nav_view"
|
||||
@ -22,7 +23,6 @@
|
||||
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>
|
||||
|
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout 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">
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
</androidx.viewpager.widget.ViewPager>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linLayout_Dots"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="1dp"
|
||||
android:layout_above="@id/linLayout_Dots"
|
||||
android:alpha="0.5"
|
||||
android:background="@android:color/white" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_Next"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@null"
|
||||
android:onClick="btnNextClick"
|
||||
android:text="@string/next"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_Skip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@null"
|
||||
android:onClick="btnSkipClick"
|
||||
android:text="@string/skip"
|
||||
tools:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
@ -3,19 +3,25 @@
|
||||
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"
|
||||
>
|
||||
app:cardElevation="5dp"
|
||||
app:cardUseCompatPadding="true">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/linear_day"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" android:id="@+id/linLayout_Day">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:text="@string/sample_date"
|
||||
android:id="@+id/text_day_heading"
|
||||
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"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sample_date"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?colorAccent"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
30
app/src/main/res/layouts/activities/layout/dialog_login.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/linLayout_login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editText_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:ems="10"
|
||||
android:hint="@string/email"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textEmailAddress" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editText_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:ems="10"
|
||||
android:hint="@string/password"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
</LinearLayout>
|
@ -15,30 +15,30 @@
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_lessonSubject"
|
||||
android:id="@+id/text_lesson_subject"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_lessonTime"
|
||||
android:id="@+id/text_lesson_time"
|
||||
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" />
|
||||
android:textColor="?textSecondary" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_lessonTeacher"
|
||||
android:id="@+id/text_lesson_teacher"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_lessonRoom"
|
||||
android:id="@+id/text_lesson_room"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/linLayout_Meal"
|
||||
android:id="@+id/lienar_meal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
@ -10,7 +10,7 @@
|
||||
android:paddingBottom="2dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_MealHeading"
|
||||
android:id="@+id/text_meal_heading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
@ -20,7 +20,7 @@
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_Meal"
|
||||
android:id="@+id/text_meal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
|
@ -1,38 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/nav_header_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/nav_header_height"
|
||||
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:background="@android:color/black"
|
||||
android:gravity="bottom"
|
||||
android:id="@+id/nav_header_main">
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||
app:srcCompat="@mipmap/ic_laogai_icon"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:id="@+id/imageView"/>
|
||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||
app:srcCompat="@mipmap/ic_laogai_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_nav_header_title"
|
||||
android:layout_width="match_parent"
|
||||
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:id="@+id/txtView_nav_header_title"/>
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textColor="#FFFFFF" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nav_header_subtitle"
|
||||
android:id="@+id/textView"/>
|
||||
android:textColor="#ffffff" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -21,7 +21,7 @@
|
||||
android:shortcutLongLabel="@string/shortcut_timetable_long"
|
||||
android:shortcutDisabledMessage="@string/shortcut_timetable_disabled">
|
||||
<intent
|
||||
android:action="org.mosad.seil0.projectlaogai.fragments.TimeTableFragment"
|
||||
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" />
|
||||
@ -40,4 +40,18 @@
|
||||
android:targetClass="org.mosad.seil0.projectlaogai.MainActivity" />
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
|
||||
<shortcut
|
||||
android:shortcutId="grades"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_grading_black_24dp"
|
||||
android:shortcutShortLabel="@string/shortcut_grades_short"
|
||||
android:shortcutLongLabel="@string/shortcut_grades_long"
|
||||
android:shortcutDisabledMessage="@string/shortcut_grades_disabled">
|
||||
<intent
|
||||
android:action="org.mosad.seil0.projectlaogai.fragments.GradesFragment"
|
||||
android:targetPackage="org.mosad.seil0.projectlaogai"
|
||||
android:targetClass="org.mosad.seil0.projectlaogai.MainActivity" />
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/linLayout_addlesson"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_Course"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="7dp"
|
||||
android:text="@string/courses"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_Courses"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/courses"
|
||||
android:paddingBottom="10dp"
|
||||
android:popupBackground="?themeSecondary"
|
||||
android:spinnerMode="dropdown" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_Lesson"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="7dp"
|
||||
android:text="@string/lessons"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_Lessons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/lessons"
|
||||
android:paddingBottom="10dp"
|
||||
android:popupBackground="?themeSecondary" />
|
||||
</LinearLayout>
|
@ -20,7 +20,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="7dp"
|
||||
android:layout_marginBottom="7dp"
|
||||
android:text="16,95 €"
|
||||
android:text="@string/mensa_current"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp" />
|
||||
|
||||
|
@ -7,20 +7,21 @@
|
||||
android:background="?themePrimary">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/refreshLayout_Mensa"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/refreshLayout_Mensa">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView_Mensa"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/scrollView_Mensa">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/linLayout_Mensa"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="false"
|
||||
android:id="@+id/linLayout_Mensa"/>
|
||||
android:orientation="vertical" />
|
||||
</ScrollView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
|
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgCourse"
|
||||
android:layout_width="256dp"
|
||||
android:layout_height="256dp"
|
||||
android:contentDescription="@string/timetable"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toTopOf="@+id/linearLayout2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_baseline_calendar_today_24dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="60dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_Course"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/course_heading"
|
||||
android:textAlignment="center"
|
||||
android:textSize="26sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_CourseDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:text="@string/course_desc_on"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSelectCourse"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:onClick="btnSelectCourseClick"
|
||||
android:text="@string/select_course"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgGrades"
|
||||
android:layout_width="256dp"
|
||||
android:layout_height="256dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toTopOf="@+id/linearLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_school_black_24dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="60dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_Grades"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_heading"
|
||||
android:textAlignment="center"
|
||||
android:textSize="26sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_GradesDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:text="@string/login_desc_on"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editText_email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:ems="10"
|
||||
android:hint="@string/email"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textEmailAddress" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editText_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:ems="10"
|
||||
android:hint="@string/password"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnLogin"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:onClick="btnLoginClick"
|
||||
android:text="@string/login"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logo"
|
||||
android:layout_width="256dp"
|
||||
android:layout_height="256dp"
|
||||
android:contentDescription="@string/app_name"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toTopOf="@+id/linearLayout3"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@mipmap/ic_laogai_icon_foreground" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="60dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtAppName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_name"
|
||||
android:textAlignment="center"
|
||||
android:textSize="26sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtWelcome"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:text="@string/welcome"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnGetStarted"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:onClick="btnNextClick"
|
||||
android:text="@string/get_started"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
@ -4,8 +4,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.SettingsFragment"
|
||||
android:background="?themePrimary">
|
||||
android:background="?themePrimary"
|
||||
tools:context=".fragments.SettingsFragment">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -14,267 +14,342 @@
|
||||
<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:orientation="vertical"
|
||||
android:paddingBottom="11dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cardView_Info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="9dp"
|
||||
android:id="@+id/cardView_Info"
|
||||
app:cardBackgroundColor="?themeSecondary"
|
||||
app:cardElevation="5dp"
|
||||
app:cardUseCompatPadding="true"
|
||||
app:cardBackgroundColor="?themeSecondary">
|
||||
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:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/info"
|
||||
android:textColor="?colorAccent"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:fontFamily="sans-serif"
|
||||
android:layout_margin="7dp"
|
||||
android:textColor="?colorAccent"/>
|
||||
android:typeface="sans" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linLayout_User"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="vertical"
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_user"
|
||||
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:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/user"
|
||||
android:id="@+id/text_user_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/txtView_UserDesc"/>
|
||||
android:text="@string/user_desc" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?dividerColor"
|
||||
/>
|
||||
android:background="?dividerColor" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linLayout_Course"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="vertical"
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_course"
|
||||
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"/>
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/course_desc"
|
||||
android:id="@+id/text_course_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/txtView_CourseDesc"/>
|
||||
android:text="@string/course_desc" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?dividerColor"
|
||||
/>
|
||||
android:background="?dividerColor" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linLayout_ManageLessons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="vertical"
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_manage_lessons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/manage_lessons"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtView_ManageLessonsDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/manage_lessons_desc" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider7"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?dividerColor" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linLayout_About"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="7dp"
|
||||
android:id="@+id/linLayout_About">
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="vertical"
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_about"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/about_version"
|
||||
android:id="@+id/text_about_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/txtView_AboutDesc"/>
|
||||
android:text="@string/about_version" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?dividerColor"
|
||||
/>
|
||||
android:background="?dividerColor" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linLayout_Licence"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" android:id="@+id/linLayout_Licence"
|
||||
android:layout_margin="7dp">
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:text="@string/licenses"
|
||||
android:id="@+id/textView3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/textView3"
|
||||
android:textStyle="bold" android:textSize="16sp"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/licenses"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardBackgroundColor="?themeSecondary"
|
||||
app:cardElevation="5dp"
|
||||
app:cardUseCompatPadding="true"
|
||||
app:cardBackgroundColor="?themeSecondary">
|
||||
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:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:paddingTop="3dp"
|
||||
android:text="@string/settings"
|
||||
android:textColor="?colorAccent"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingTop="3dp" android:layout_margin="7dp"/>
|
||||
android:typeface="sans" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linLayout_Theme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" android:id="@+id/linLayout_Theme"
|
||||
android:layout_margin="7dp">
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_theme"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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"/>
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/themeLight"
|
||||
android:id="@+id/text_theme_selected"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/txtView_SelectedTheme"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/themeLight" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?dividerColor"
|
||||
/>
|
||||
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">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical|end"
|
||||
android:orientation="horizontal"
|
||||
android:padding="7dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_primary_color"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/primary_color_desc"
|
||||
android:id="@+id/text_primary_color_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/txtView_PrimaryColorDesc"/>
|
||||
android:text="@string/primary_color_desc" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:gravity="end">
|
||||
android:gravity="end"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/view_primary_color"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:id="@+id/view_PrimaryColor"
|
||||
android:background="?colorPrimary"/>
|
||||
android:background="?colorPrimary" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/divider4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?dividerColor"
|
||||
/>
|
||||
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">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical|end"
|
||||
android:orientation="horizontal"
|
||||
android:padding="7dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_accent_color"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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"
|
||||
/>
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/accent_color_desc"
|
||||
android:id="@+id/text_accent_color_desc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/txtView_AccentColorDesc"/>
|
||||
android:text="@string/accent_color_desc" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="7dp"
|
||||
android:gravity="end">
|
||||
android:gravity="end"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/view_accent_color"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:id="@+id/view_AccentColor"
|
||||
android:background="?colorAccent"/>
|
||||
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:background="?android:attr/listDivider" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/switch_buffet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/switch_buffet"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:padding="7dp"
|
||||
android:text="@string/show_buffet"
|
||||
android:layout_margin="7dp"/>
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</LinearLayout>
|
||||
|
@ -1,9 +1,10 @@
|
||||
<?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.TimeTableFragment"
|
||||
tools:context=".fragments.TimetableFragment"
|
||||
android:background="?themePrimary">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
@ -22,11 +23,19 @@
|
||||
android:id="@+id/linLayout_Timetable"/>
|
||||
</ScrollView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:src="@drawable/icon_custom_black"
|
||||
android:id="@+id/faBtnAddSubject"
|
||||
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"/>
|
||||
android:layout_gravity="bottom|center|end"
|
||||
android:layout_marginEnd="11dp"
|
||||
android:layout_marginBottom="11dp"
|
||||
android:clickable="true"
|
||||
android:elevation="7dp"
|
||||
android:focusable="true"
|
||||
android:src="@drawable/ic_add_black_24dp"
|
||||
android:visibility="visible"
|
||||
app:backgroundTint="?colorAccent"
|
||||
app:fabSize="auto" />
|
||||
</FrameLayout>
|
@ -1,5 +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"/>
|
||||
<background android:drawable="@color/ic_laogai_icon_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_laogai_icon_foreground"/>
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_laogai_icon_foreground.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_laogai_icon_foreground.png
Normal file
After Width: | Height: | Size: 755 B |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_laogai_icon_foreground.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.0 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_laogai_icon_foreground.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_laogai_icon_foreground.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 8.2 KiB |
BIN
app/src/main/res/raw/notenverwaltung_hs_offenburg_de.crt
Normal file
@ -3,7 +3,7 @@
|
||||
<notice>
|
||||
<name>Material Dialogs</name>
|
||||
<url>https://github.com/afollestad/material-dialogs</url>
|
||||
<copyright>Copyright (C) Aidan Follestad</copyright>
|
||||
<copyright>Copyright Aidan Follestad</copyright>
|
||||
<license>Apache Software License 2.0</license>
|
||||
</notice>
|
||||
<notice>
|
||||
@ -13,11 +13,17 @@
|
||||
<license>Apache Software License 2.0</license>
|
||||
</notice>
|
||||
<notice>
|
||||
<name>gson</name>
|
||||
<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>Jsoup</name>
|
||||
<url>https://jsoup.org/</url>
|
||||
<copyright>Copyright 2009 - 2020 Jonathan Hedley</copyright>
|
||||
<license>MIT License</license>
|
||||
</notice>
|
||||
<notice>
|
||||
<name>kotlinx.coroutines</name>
|
||||
<url>https://github.com/Kotlin/kotlinx.coroutines</url>
|
||||
@ -25,15 +31,21 @@
|
||||
<license>Apache Software License 2.0</license>
|
||||
</notice>
|
||||
<notice>
|
||||
<name>farebot part for desfire cards</name>
|
||||
<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>
|
||||
<name>AndroidX</name>
|
||||
<url>https://developer.android.com/jetpack/androidx</url>
|
||||
<copyright>Copyright 2018 The Android Open Source Project</copyright>
|
||||
<license>Apache Software License 2.0</license>
|
||||
</notice>
|
||||
<notice>
|
||||
<name>Material design icons</name>
|
||||
<url>https://github.com/google/material-design-icons</url>
|
||||
<copyright>Copyright Google Inc.</copyright>
|
||||
<license>Apache Software License 2.0</license>
|
||||
</notice>
|
||||
</notices>
|
||||
|
@ -8,8 +8,23 @@
|
||||
<string name="mensa">Mensa</string>
|
||||
<string name="timetable">Stundenplan</string>
|
||||
<string name="moodle">Moodle</string>
|
||||
<string name="grades">Noten</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
|
||||
<!-- Onboarding -->
|
||||
<string name="skip">überspringen</string>
|
||||
<string name="next">weiter</string>
|
||||
<string name="start">fertig</string>
|
||||
<string name="welcome">Willkommen bei Project Laogai.\nBevor wir loslegen können, richte bitte die App ein.</string>
|
||||
<string name="get_started">los geht\'s</string>
|
||||
<string name="course_heading">Studiengang</string>
|
||||
<string name="course_desc_on">Wähle deinen aktuellen Studiengang.\nZusätzliche Vorlesungen können später hinzugefügt werden.</string>
|
||||
<string name="login_heading">Login</string>
|
||||
<string name="login_desc_on">Project Laogai kann auf die Notenverwaltung zugreifen. Deine Login-Daten werden verschlüsselt auf deinem Gerät gespeichert. Diese Funktion wird "as is" und ohne Garantie bereitgestellt.</string>
|
||||
<string name="email">E-Mail</string>
|
||||
<string name="password">Passwort</string>
|
||||
<string name="login">login</string>
|
||||
|
||||
<!-- fragment_home -->
|
||||
<string name="meal">Essen</string>
|
||||
<string name="today_date">Heute, %1$s</string>
|
||||
@ -18,13 +33,29 @@
|
||||
<string name="no_more_food">Diese Woche keine weitere Essensausgabe</string>
|
||||
<string name="no_lesson_today">heute keine Vorlesung!</string>
|
||||
|
||||
<!-- fragment_mensa -->
|
||||
<string name="no_more_meals">Für diese und nächste Woche ist der Speiseplan leer.</string>
|
||||
|
||||
<!-- fragment_timetable -->
|
||||
<string name="add_lesson">Eine Vorlesung hinzufügen</string>
|
||||
<string name="add_lesson_desc">Füge eine Vorlesung eines anderen Studiengangs zu deinem Stundenplan hinzu.</string>
|
||||
<string name="courses">Studiengänge:</string>
|
||||
<string name="lessons">Vorlesungen:</string>
|
||||
<string name="timetable_generic_error">Beim laden des Stundenplans ist ein Fehler aufgetreten.\n</string>
|
||||
|
||||
<!-- fragment_grades -->
|
||||
<string name="loading_from_hs">Lade Daten von den Hochschul Servern.\nDas kann eine Weile dauern.</string>
|
||||
<string name="credentials_missing">Diese Funktion benötigt deine Login-Daten. Bitte logge dich über die Einstellungen ein.</string>
|
||||
<string name="qispos_unavailable">Die Notenverwaltung ist zur Zeit nicht ereichbar.\nVersuche es später noch einmal.\n</string>
|
||||
<string name="qispos_generic_error">Error.\nVersuche es später noch einmal.\n</string>
|
||||
<string name="without_guarantee">Alle Angaben ohne Gewähr.</string>
|
||||
|
||||
<!-- fragment_settings -->
|
||||
<string name="info">Info</string>
|
||||
<string name="user">Benutzer</string>
|
||||
<string name="user_desc">Zum bearbeiten tippen</string>
|
||||
<string name="course_desc">Tippe um den Kurs zu ändern</string>
|
||||
<string name="manage_lessons">Bearbeite Zusätzliche Vorlesungen</string>
|
||||
<string name="manage_lessons_desc">Tippe um zusätzliche Vorlesungen zu bearbeiten</string>
|
||||
<string name="about_dialog_heading">Über</string>
|
||||
<string name="licenses">Lizenzen</string>
|
||||
<string name="theme">Design</string>
|
||||
@ -32,23 +63,35 @@
|
||||
<string name="themeDark">Dunkel</string>
|
||||
<string name="themeBlack">Schwarz</string>
|
||||
<string name="primary_color">Primärfarbe</string>
|
||||
<string name="primary_color_desc">Zum Ändern tippen, Standard ist Schwarz.</string>
|
||||
<string name="primary_color_desc">Zum Ändern tippen, Standard ist Blaugrün.</string>
|
||||
<string name="accent_color">Akzentfarbe</string>
|
||||
<string name="accent_color_desc">Zum Ändern tippen, Standard ist Indigo.</string>
|
||||
<string name="accent_color_desc">Zum Ändern tippen, Standard ist Hellblau.</string>
|
||||
<string name="grades_sync">Aktualisierungsintervall Noten</string>
|
||||
<string name="grades_sync_desc">%1$d Stunden</string>
|
||||
<string name="grades_sync_desc_never">nie</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="add">hinzufügen</string>
|
||||
<string name="delete">löschen</string>
|
||||
<string name="cancel">abbrechen</string>
|
||||
<string name="select">auswählen</string>
|
||||
<string name="close">schließen</string>
|
||||
<string name="save">speichern</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>
|
||||
|
||||
<!-- notifications -->
|
||||
<string name="notification_grades">Noten</string>
|
||||
<string name="notification_grades_single_desc">%1$s wurden hinzugefügt oder aktualisiert</string>
|
||||
<string name="notification_grades_multiple_desc">%1$s und %2$d weiter Vorlesungen wurden hinzugefügt oder aktualisiert</string>
|
||||
<string name="notification_grades_updating_desc">Suche nach neuen Noten …</string>
|
||||
|
||||
<!-- errors -->
|
||||
<string name="error">Fehler</string>
|
||||
<string name="timetable_error">Stundenplan konnte nicht geladen werden!</string>
|
||||
<string name="timetable_error">Der Stundenplan konnte nicht geladen werden.</string>
|
||||
|
||||
<!-- shortcuts -->
|
||||
<string name="shortcut_mensa_short">Mensa</string>
|
||||
@ -63,4 +106,16 @@
|
||||
<string name="shortcut_moodle_long">Moodle</string>
|
||||
<string name="shortcut_moodle_disabled">Moodle deaktiviert</string>
|
||||
|
||||
<string name="shortcut_grades_short">Noten</string>
|
||||
<string name="shortcut_grades_long">Noten</string>
|
||||
<string name="shortcut_grades_disabled">Noten deaktiviert</string>
|
||||
|
||||
<string-array name="syncInterval">
|
||||
<item>Manuell</item>
|
||||
<item>1 Stunde</item>
|
||||
<item>3 Stunden</item>
|
||||
<item>6 Stunden</item>
|
||||
<item>12 Stunden</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
@ -1,28 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#000000</color>
|
||||
<color name="colorPrimaryDark">#000000</color>
|
||||
<color name="colorAccent">#3f51b5</color>
|
||||
<color name="ic_launcher_background">#ffffff</color>
|
||||
<!-- base theme colors -->
|
||||
<color name="colorPrimary">#009688</color>
|
||||
<color name="colorPrimaryDark">#009688</color>
|
||||
<color name="colorAccent">#0096ff</color>
|
||||
<color name="ic_laogai_icon_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>
|
||||
<!-- light theme colors -->
|
||||
<color name="themePrimaryLight">#f5f5f5</color>
|
||||
<color name="themeSecondaryLight">#ffffff</color>
|
||||
<color name="textPrimaryLight">#000000</color>
|
||||
<color name="textSecondaryLight">#818181</color>
|
||||
<color name="dividerLight">#e0e0e0</color>
|
||||
<color name="textPrimaryLight">#de000000</color>
|
||||
<color name="textSecondaryLight">#99000000</color>
|
||||
<color name="dividerLight">#22000000</color>
|
||||
|
||||
<!-- dark theme colors -->
|
||||
<color name="themePrimaryDark">#121212</color>
|
||||
<color name="themeSecondaryDark">#202020</color>
|
||||
<color name="textPrimaryDark">#deffffff</color>
|
||||
<color name="textSecondaryDark">#99ffffff</color>
|
||||
<color name="dividerDark">#22ffffff</color>
|
||||
<color name="controlHighlightDark">#11ffffff</color>
|
||||
|
||||
<!-- black theme colors -->
|
||||
<color name="themePrimaryBlack">#000000</color>
|
||||
<color name="themeSecondaryBlack">#303030</color>
|
||||
|
||||
</resources>
|
||||
|
@ -10,8 +10,23 @@
|
||||
<string name="mensa">Mensa</string>
|
||||
<string name="timetable">Timetable</string>
|
||||
<string name="moodle">Moodle</string>
|
||||
<string name="grades">Grades</string>
|
||||
<string name="settings">Settings</string>
|
||||
|
||||
<!-- Onboarding -->
|
||||
<string name="skip">skip</string>
|
||||
<string name="next">next</string>
|
||||
<string name="start">start</string>
|
||||
<string name="welcome">Welcome to Project Laogai.\nBefore we can start, setup the App.</string>
|
||||
<string name="get_started">get started</string>
|
||||
<string name="course_heading">Course</string>
|
||||
<string name="course_desc_on">Select your current course.\nAdditional lessons can be added later.</string>
|
||||
<string name="login_heading">Login</string>
|
||||
<string name="login_desc_on">Project Laogai can connect to the Qispos Online-Portal. Your Login-Data will be stored encrypted on your device. This feature is provided "as is", without any guarantee.</string>
|
||||
<string name="email">E-Mail</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="login">login</string>
|
||||
|
||||
<!-- fragment_home -->
|
||||
<string name="meal">Meal</string>
|
||||
<string name="today_date">Today, %1$s</string>
|
||||
@ -20,47 +35,78 @@
|
||||
<string name="no_more_food">No more Food this week</string>
|
||||
<string name="no_lesson_today">"No lecture today!"</string>
|
||||
|
||||
<!-- fragment_mensa -->
|
||||
<string name="no_more_meals">The menu is empty for this and the next week.</string>
|
||||
|
||||
<!-- fragment_timetable -->
|
||||
<string name="add_lesson">Add a lesson</string>
|
||||
<string name="add_lesson_desc">Add a lesson from another course to your timetable.</string>
|
||||
<string name="courses">Courses:</string>
|
||||
<string name="lessons">Lessons:</string>
|
||||
<string name="timetable_generic_error">An error occurred while loading the timetable.\n</string>
|
||||
|
||||
<!-- fragment_grades -->
|
||||
<string name="loading_from_hs">Loading data from the university servers.\nThis may take a while.</string>
|
||||
<string name="credentials_missing">This feature needs your Login-Data to work. Please login via the settings.</string>
|
||||
<string name="qispos_unavailable">The Qispos server is currently unavailable.\nPlease try again later.\n</string>
|
||||
<string name="qispos_generic_error">Error.\nPlease try again later.\n</string>
|
||||
<string name="without_guarantee">All information is supplied without guarantee.</string>
|
||||
|
||||
<!-- fragment_settings -->
|
||||
<string name="info">Info</string>
|
||||
<string name="user">User</string>
|
||||
<string name="user_desc">Tap to edit</string>
|
||||
<string name="course_desc">Tap to change course</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="manage_lessons">Manage Additional Lessons</string>
|
||||
<string name="manage_lessons_desc">Tap to manage additional lessons</string>
|
||||
<string name="about_txtView" translatable="false">hso App by @Seil0</string>
|
||||
<string name="about_version" translatable="false">Version %1$s (%2$s)</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="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="primary_color_desc">Tap to change, default is teal blue.</string>
|
||||
<string name="accent_color">Accent color</string>
|
||||
<string name="accent_color_desc">Tap to change, default is indigo.</string>
|
||||
<string name="accent_color_desc">Tap to change, default is light blue.</string>
|
||||
<string name="grades_sync">Update interval grades</string>
|
||||
<string name="grades_sync_desc">%1$d hours</string>
|
||||
<string name="grades_sync_desc_never">never</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="add">add</string>
|
||||
<string name="delete">delete</string>
|
||||
<string name="cancel">@android:string/cancel</string>
|
||||
<string name="select">select</string>
|
||||
<string name="close">close</string>
|
||||
<string name="save">save</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>
|
||||
|
||||
<!-- notifications -->
|
||||
<string name="notification_grades">Grades</string>
|
||||
<string name="notification_grades_single_desc">%1$s was added or updated</string>
|
||||
<string name="notification_grades_multiple_desc">%1$s and %2$d other subjects have been added or updated</string>
|
||||
<string name="notification_grades_updating_desc">Checking for new grades …</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="sample_course" translatable="false">Everything</string>
|
||||
<string name="sample_subject" translatable="false">Earthbending</string>
|
||||
<string name="sample_sub_subject" translatable="false">Applied Earthbending</string>
|
||||
<string name="sample_grade" translatable="false">1,0</string>
|
||||
<string name="sample_grade_state" translatable="false">passed</string>
|
||||
<string name="sample_date" translatable="false">Monday, 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>
|
||||
<string name="timetable_error">Could not load the timetable!"</string>
|
||||
|
||||
<!-- shortcuts -->
|
||||
<string name="shortcut_mensa_short">Mensa</string>
|
||||
@ -75,15 +121,54 @@
|
||||
<string name="shortcut_moodle_long">Moodle</string>
|
||||
<string name="shortcut_moodle_disabled">Moodle disabled</string>
|
||||
|
||||
<string name="shortcut_grades_short">Grades</string>
|
||||
<string name="shortcut_grades_long">Grades</string>
|
||||
<string name="shortcut_grades_disabled">Grades disabled</string>
|
||||
|
||||
<!-- save keys -->
|
||||
<string name="preference_file_key" translatable="false">org.mosad.seil0.projectlaogai_preferences</string>
|
||||
<string name="encrypted_preference_file_key" translatable="false">org.mosad.seil0.projectlaogai_encrypted_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_gradesSyncInterval" translatable="false">org.mosad.seil0.projectlaogai.gradesSyncInterval</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>
|
||||
<string name="save_key_gradesCacheTime" translatable="false">org.mosad.seil0.projectlaogai.gradesCacheTime</string>
|
||||
<string name="save_key_user_email" translatable="false">org.mosad.seil0.projectlaogai.user_email</string>
|
||||
<string name="save_key_user_password" translatable="false">org.mosad.seil0.projectlaogai.user_password</string>
|
||||
|
||||
<!-- intent actions -->
|
||||
<string name="intent_action_mensaFragment" translatable="false">org.mosad.seil0.projectlaogai.fragments.MensaFragment</string>
|
||||
<string name="intent_action_timetableFragment" translatable="false">org.mosad.seil0.projectlaogai.fragments.TimetableFragment</string>
|
||||
<string name="intent_action_moodleFragment" translatable="false">org.mosad.seil0.projectlaogai.fragments.MoodleFragment</string>
|
||||
<string name="intent_action_gradesFragment" translatable="false">org.mosad.seil0.projectlaogai.fragments.GradesFragment</string>
|
||||
|
||||
<string-array name="syncInterval">
|
||||
<item>Manually</item>
|
||||
<item>1 Hour</item>
|
||||
<item>3 Hours</item>
|
||||
<item>6 Hours</item>
|
||||
<item>12 Hours</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="courses">
|
||||
<item>AI-1</item>
|
||||
<item>AI-2</item>
|
||||
<item>AI-3</item>
|
||||
<item>AI-4</item>
|
||||
<item>AI-5</item>
|
||||
<item>AI-6</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="lessons">
|
||||
<item>Mathematik</item>
|
||||
<item>Verteilte Systemr</item>
|
||||
<item>EA 1</item>
|
||||
<item>IT Security</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
@ -12,12 +12,13 @@
|
||||
<item name="themeName">light</item>
|
||||
<item name="themePrimary">@color/themePrimaryLight</item>
|
||||
<item name="themeSecondary">@color/themeSecondaryLight</item>
|
||||
<item name="textPrimary">@color/textPrimaryLight</item>
|
||||
<item name="textSecondary">@color/textSecondaryLight</item>
|
||||
<item name="android:textColor">@color/textPrimaryLight</item>
|
||||
<item name="android:textColorPrimary">@color/textPrimaryLight</item>
|
||||
<item name="textPrimary">@color/textPrimaryLight</item>
|
||||
<item name="android:textColorHint">@color/textSecondaryLight</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>
|
||||
|
||||
@ -25,32 +26,25 @@
|
||||
<item name="themeName">dark</item>
|
||||
<item name="themePrimary">@color/themePrimaryDark</item>
|
||||
<item name="themeSecondary">@color/themeSecondaryDark</item>
|
||||
<item name="textPrimary">@color/textPrimaryDark</item>
|
||||
<item name="textSecondary">@color/textSecondaryDark</item>
|
||||
<item name="android:textColor">@color/textPrimaryDark</item>
|
||||
<item name="android:textColorPrimary">@color/textPrimaryDark</item>
|
||||
<item name="textPrimary">@color/textPrimaryDark</item>
|
||||
<item name="android:textColorHint">@color/textSecondaryDark</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>
|
||||
|
||||
<!-- change on click indicator color for manually set components -->
|
||||
<item name="colorControlHighlight">@color/controlHighlightDark</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Black" parent="AppTheme">
|
||||
<style name="AppTheme.Black" parent="AppTheme.Dark">
|
||||
<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>
|
||||
<item name="themePrimary">@color/themePrimaryBlack</item>
|
||||
<item name="themeSecondary">@color/themePrimaryBlack</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
package org.mosad.seil0.projectlaogai
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
18
app/src/test/resources/grades_diff_semester.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"WiSe 17/18":[
|
||||
{"credits":"4,0","grade":"1,0","id":"AI-1020","name":"Grundlagen der Elektronik","semester":"WiSe 17/18"},
|
||||
{"credits":"2,0","grade":"2,3","id":"AI-1040","name":"Prozedurale Programmierung","semester":"WiSe 17/18"}
|
||||
],
|
||||
"SoSe 18":[
|
||||
{"credits":"4,0","grade":"1,7","id":"AI-2010","name":"Technische Informatik","semester":"SoSe 18"},
|
||||
{"credits":"1,0","grade":"bestanden","id":"AI-2015","name":"Praktikum Technische Informatik","semester":"SoSe 18"}
|
||||
],
|
||||
"WiSe 18/19":[
|
||||
{"credits":"2,0","grade":"2,7","id":"AI-3010","name":"Computernetze","semester":"WiSe 18/19"},
|
||||
{"credits":"3,0","grade":"bestanden","id":"AI-3015","name":"Praktikum Computernetze","semester":"WiSe 18/19"}
|
||||
],
|
||||
"SoSe 19":[
|
||||
{"credits":"4,0","grade":"1,7","id":"AI-2010","name":"Mathemaik 7","semester":"SoSe 19"},
|
||||
{"credits":"1,0","grade":"bestanden","id":"AI-2015","name":"Praktikum Mathemaik 7","semester":"SoSe 19"}
|
||||
]
|
||||
}
|
16
app/src/test/resources/grades_diff_subject.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"WiSe 17/18":[
|
||||
{"credits":"4,0","grade":"1,0","id":"AI-1020","name":"Grundlagen der Elektronik","semester":"WiSe 17/18"},
|
||||
{"credits":"2,0","grade":"2,3","id":"AI-1040","name":"Prozedurale Programmierung","semester":"WiSe 17/18"}
|
||||
],
|
||||
"SoSe 18":[
|
||||
{"credits":"4,0","grade":"1,7","id":"AI-2010","name":"Technische Informatik","semester":"SoSe 18"},
|
||||
{"credits":"1,0","grade":"bestanden","id":"AI-2015","name":"Praktikum Technische Informatik","semester":"SoSe 18"}
|
||||
],
|
||||
"WiSe 18/19":[
|
||||
{"credits":"2,0","grade":"0,7","id":"AI-3010","name":"Computernetze","semester":"WiSe 18/19"},
|
||||
{"credits":"3,0","grade":"bestanden","id":"AI-3015","name":"Praktikum Computernetze","semester":"WiSe 18/19"},
|
||||
{"credits":"2,0","grade":"1,7","id":"AI-3020","name":"Datenbanksysteme 1","semester":"WiSe 18/19"},
|
||||
{"credits":"3,0","grade":"bestanden","id":"AI-3025","name":"Praktikum Datenbanksysteme","semester":"WiSe 18/19"}
|
||||
]
|
||||
}
|
14
app/src/test/resources/grades_orig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"WiSe 17/18":[
|
||||
{"credits":"4,0","grade":"1,0","id":"AI-1020","name":"Grundlagen der Elektronik","semester":"WiSe 17/18"},
|
||||
{"credits":"2,0","grade":"2,3","id":"AI-1040","name":"Prozedurale Programmierung","semester":"WiSe 17/18"}
|
||||
],
|
||||
"SoSe 18":[
|
||||
{"credits":"4,0","grade":"1,7","id":"AI-2010","name":"Technische Informatik","semester":"SoSe 18"},
|
||||
{"credits":"1,0","grade":"bestanden","id":"AI-2015","name":"Praktikum Technische Informatik","semester":"SoSe 18"}
|
||||
],
|
||||
"WiSe 18/19":[
|
||||
{"credits":"2,0","grade":"2,7","id":"AI-3010","name":"Computernetze","semester":"WiSe 18/19"},
|
||||
{"credits":"3,0","grade":"bestanden","id":"AI-3015","name":"Praktikum Computernetze","semester":"WiSe 18/19"}
|
||||
]
|
||||
}
|
10
build.gradle
@ -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.61'
|
||||
ext.kotlin_version = '1.9.22'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.android.tools.build:gradle:8.2.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
@ -18,10 +18,10 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register('clean', Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
46
etc/drawable_resources/laogai_icon.svg
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.0"
|
||||
width="121"
|
||||
height="219"
|
||||
id="svg92"
|
||||
viewBox="0 0 121.0 219"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs6">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipEmfPath1">
|
||||
<path
|
||||
d="M -0.33333333,0 H 1087.3333 V 232 H -0.33333333 Z"
|
||||
id="path2" />
|
||||
</clipPath>
|
||||
<pattern
|
||||
id="EMFhbasepattern"
|
||||
patternUnits="userSpaceOnUse"
|
||||
width="6"
|
||||
height="6"
|
||||
x="0"
|
||||
y="0" />
|
||||
</defs>
|
||||
<g
|
||||
id="g60"
|
||||
transform="matrix(1.0027625,0,0,1.0015244,-19.720996,-9.0137196)">
|
||||
<path
|
||||
style="opacity:0.993;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;image-rendering:auto"
|
||||
clip-path="url(#clipEmfPath1)"
|
||||
d="m 103.66667,16 v 0 0 40.333333 c 12,6.666667 21.66666,19 21.66666,46.999997 0,28 -14.33333,45 -21.66666,50.33334 v 36 l 36.66666,-12.33334 V 9 Z"
|
||||
id="path8" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
clip-path="url(#clipEmfPath1)"
|
||||
d="M 64.666667,56.666667 V 15.333333 l -45,8 v 0 0 204.333337 l 45,-15.66667 V 169 C 52,162.66667 38.333333,147.33333 38.333333,116.33333 38.333333,85.333333 53.666667,65 64.666667,56.666667 Z"
|
||||
id="path10" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
clip-path="url(#clipEmfPath1)"
|
||||
d="M 75.333333,68.666667 V 158 c 11.333334,-4.33333 25.333337,-20 25.333337,-49 0,-32.909003 -12.564687,-42.347858 -25.328763,-44.875741 0.01241,1.916829 -0.005,1.98343 -0.0046,4.542408 z"
|
||||
id="path90" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -1,8 +1,10 @@
|
||||
ProjectLaogai ist eine App um den Vorlesungsplan und den Mensa-Speiseplan der Hochschule Offenburg anzuzeigen.
|
||||
ProjectLaogai ist eine App um den Vorlesungsplan, die Notenverwaltung 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
|
||||
* lass dir deine Noten direkt in der App anzeigen
|
||||
* zeige den Stundenplan deines Studiengangs an
|
||||
* schau dir den Mensa Speiseplan der aktuellen und der nächsten Woche an
|
||||
* schaue dir das aktuelle Guthaben deiner Mensakarte an
|
||||
* öffne moodle direkt in der App
|
||||
* viele lustige Bugs
|
||||
|
||||
Bitte melde Fehler und Probleme an support@mosad.xyz
|
||||
|
9
fastlane/metadata/android/en-US/changelogs/6000.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Here comes version 0.6.0 "anthropomorphised Athena"
|
||||
|
||||
* new Onboarding Process
|
||||
* it's now possible to access the grades from qispos
|
||||
* you can now add additional lessons which are not part of your course timetable
|
||||
* new primary and accent colors to match the mosad.xyz design
|
||||
* updated some libraries
|
||||
* better compatibility with tcor version 1.2.2 and higher
|
||||
* fixed a crash on first start, if tcor is not reachable
|
5
fastlane/metadata/android/en-US/changelogs/6100.txt
Normal file
@ -0,0 +1,5 @@
|
||||
Version 0.6.1 "anthropomorphised Athena"
|
||||
|
||||
* Grades can be synced in the background
|
||||
* Grades will be cached for offline use or if qispos is not reachable
|
||||
* The message "Menu empty" is no longer displayed if there is a menu in the next week
|
@ -1,8 +1,10 @@
|
||||
ProjectLaogai is an app to access the timetable and the mensa menu of Hochschule Offenburg.
|
||||
ProjectLaogai is an app to access the timetable, grades (qispos) and the canteen menu of Hochschule Offenburg.
|
||||
|
||||
Features:
|
||||
* check out the mensa menu of this and next week
|
||||
* access your timetable
|
||||
* have your grades displayed directly in the app
|
||||
* show the timetable of your course
|
||||
* take a look at the canteen menu for the current and next week
|
||||
* check the current balance of your mensa card
|
||||
* open moodle
|
||||
* probably some funny bugs
|
||||
* open moodle directly in the app
|
||||
|
||||
Please report bugs and issues to support@mosad.xyz
|
||||
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 124 KiB |
@ -15,3 +15,6 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
kotlin.code.style=official
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
294
gradlew
vendored
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -17,78 +17,110 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@ -97,92 +129,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|