spiced up the theme a bit (still needs work though), started to work on offline stuff, also fixed some bugs and done some polishing
@@ -37,18 +37,19 @@ android {
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("androidx.core:core-ktx:1.10.1")
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
implementation("com.google.android.material:material:1.11.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.4.0")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
implementation("androidx.viewpager2:viewpager2:1.0.0")
|
||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||
implementation("com.google.android.material:material:1.11.0")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -19,7 +20,7 @@
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Solen.NoActionBar">
|
||||
android:theme="@style/Theme.Solen">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class Account(val username: String, val password: String, private val error: Int) {
|
||||
companion object {
|
||||
private fun parseAccountDataFile(file: File): Account {
|
||||
val lines: List<String> = file.readLines()
|
||||
|
||||
if (lines.size >= 2) {
|
||||
if (lines[0] != "" && lines[1] != "") {
|
||||
return Account(lines[0], lines[1], 0)
|
||||
}
|
||||
}
|
||||
|
||||
return Account("", "", 1)
|
||||
}
|
||||
|
||||
fun clearAccountFile() {
|
||||
val file = File(Vars.fileDir, Vars.accountDataFilePath)
|
||||
file.delete()
|
||||
file.createNewFile()
|
||||
}
|
||||
|
||||
fun haveSaved(): Boolean {
|
||||
/* check if a file exists
|
||||
* no -> create it and return false
|
||||
* yes -> read the contents and use them as the username and password
|
||||
*/
|
||||
|
||||
// check if file exists
|
||||
val file = File(Vars.fileDir, Vars.accountDataFilePath)
|
||||
|
||||
return if (!file.exists()) {
|
||||
// it does not exist
|
||||
file.createNewFile()
|
||||
false
|
||||
} else {
|
||||
// it exists, so it shall be parsed
|
||||
val acc: Account = parseAccountDataFile(file)
|
||||
Vars.username = acc.username
|
||||
Vars.password = acc.password
|
||||
acc.error == 0
|
||||
}
|
||||
}
|
||||
|
||||
fun save(acc: Account): Boolean {
|
||||
return try {
|
||||
val file = File(Vars.fileDir, Vars.accountDataFilePath)
|
||||
file.writeText("${acc.username}\n${acc.password}\n", Charset.forName("UTF-8"))
|
||||
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun isValid(acc: Account): Boolean {
|
||||
return withContext(Dispatchers.IO) {
|
||||
Vars.username = acc.username
|
||||
Vars.password = acc.password
|
||||
|
||||
val token = Network.getToken()
|
||||
!JSONObject(token).has("error")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.content.res.Resources
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@@ -23,14 +24,7 @@ import androidx.lifecycle.Lifecycle
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
|
||||
interface MainActivityListener {
|
||||
fun onLaunch(view: View)
|
||||
}
|
||||
|
||||
@Suppress("INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING")
|
||||
class CalWeekFragment : Fragment() {
|
||||
//private var mainActivityListener: MainActivityListener? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@@ -41,12 +35,7 @@ class CalWeekFragment : Fragment() {
|
||||
val viewPager: ViewPager2 = view.findViewById(R.id.dayPager)
|
||||
val adapter = DayPagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
|
||||
|
||||
for (i in 0 until adapter.itemCount) {
|
||||
childFragmentManager.beginTransaction().remove(requireActivity().findViewById(adapter.getItemId(i)
|
||||
.toInt())).commit()
|
||||
}
|
||||
|
||||
for (i in 0 until MainActivity.days.size) {
|
||||
for (i in 0 until Vars.days.size) {
|
||||
adapter.addFragment(DayFragment(i))
|
||||
}
|
||||
|
||||
@@ -84,7 +73,7 @@ class DayFragment(private val position: Int) : Fragment(R.layout.fragment_day) {
|
||||
val view = inflater.inflate(R.layout.fragment_day, container, false)
|
||||
|
||||
val dayLayout: LinearLayout = view.findViewById(R.id.layoutDayMain)
|
||||
MainActivity.dayMap[position]?.let { createDayView(it, dayLayout, resources) }
|
||||
Vars.dayMap[position]?.let { createDayView(it, dayLayout, resources) }
|
||||
|
||||
return view
|
||||
}
|
||||
@@ -98,16 +87,34 @@ class DayFragment(private val position: Int) : Fragment(R.layout.fragment_day) {
|
||||
val nameDateString = day.name + " " + day.date
|
||||
nameDateTextView.text = nameDateString
|
||||
nameDateTextView.textSize = 30f
|
||||
nameDateTextView.setPadding(10, 10, 10, 40)
|
||||
nameDateTextView.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
R.color.white
|
||||
)
|
||||
)
|
||||
nameDateTextView.setPadding(10, 40, 10, 40)
|
||||
nameDateTextView.gravity = Gravity.CENTER_HORIZONTAL
|
||||
|
||||
rootLayout.addView(nameDateTextView)
|
||||
|
||||
for (subj in day.subjects) {
|
||||
val subjectBackground = when (subj.substitute) {
|
||||
"0" -> if (subj.place != "Volno") { R.color.cal_week_subject } else { R.color.cal_week_free }
|
||||
"1" -> if (subj.place != "Volno") { R.color.cal_week_substitute } else { R.color.cal_week_free }
|
||||
"2" -> if (subj.place != "Volno") { R.color.cal_week_event } else { R.color.cal_week_free }
|
||||
"0" -> if (subj.place != "Volno") {
|
||||
R.color.cal_week_subject
|
||||
} else {
|
||||
R.color.cal_week_free
|
||||
}
|
||||
"1" -> if (subj.place != "Volno") {
|
||||
R.color.cal_week_substitute
|
||||
} else {
|
||||
R.color.cal_week_free
|
||||
}
|
||||
"2" -> if (subj.place != "Volno") {
|
||||
R.color.cal_week_event
|
||||
} else {
|
||||
R.color.cal_week_free
|
||||
}
|
||||
else -> R.color.cal_week_subject
|
||||
}
|
||||
|
||||
@@ -187,7 +194,7 @@ class DayFragment(private val position: Int) : Fragment(R.layout.fragment_day) {
|
||||
)
|
||||
)
|
||||
|
||||
if (subj.name == "Volno") {
|
||||
if (subj.free) {
|
||||
subjectNameTextView.setPadding(260, 35, 20, 35)
|
||||
}
|
||||
|
||||
@@ -195,7 +202,7 @@ class DayFragment(private val position: Int) : Fragment(R.layout.fragment_day) {
|
||||
|
||||
val subjectPlaceTextView = TextView(requireContext())
|
||||
subjectPlaceTextView.text = subj.place
|
||||
if (subj.name == "Volno") {
|
||||
if (subj.free) {
|
||||
// underline
|
||||
subjectPlaceTextView.setTypeface(null, Typeface.ITALIC)
|
||||
}
|
||||
@@ -291,15 +298,6 @@ class DayFragment(private val position: Int) : Fragment(R.layout.fragment_day) {
|
||||
// Remove the listener to avoid multiple callbacks
|
||||
rootLayout.viewTreeObserver.removeOnPreDrawListener(this)
|
||||
|
||||
if (dayLayout.height < rootLayout.height) {
|
||||
val sepr = Space(requireContext())
|
||||
sepr.layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
rootLayout.height - dayLayout.height
|
||||
)
|
||||
rootLayout.addView(sepr)
|
||||
}
|
||||
|
||||
// Call your function or perform an action here
|
||||
if (day.subjects[0].substitute != "2") {
|
||||
subjectPlaceTextView.setPadding(
|
||||
@@ -315,15 +313,6 @@ class DayFragment(private val position: Int) : Fragment(R.layout.fragment_day) {
|
||||
subjectNameTextView.gravity = Gravity.CENTER_VERTICAL
|
||||
}
|
||||
|
||||
// check for substitute hours and change the background
|
||||
/*for (s in day.subjects) {
|
||||
when (s.substitute) {
|
||||
"1" -> subjectLayout.background = substituteDrawable
|
||||
"2" -> subjectLayout.background = eventDrawable
|
||||
else -> break
|
||||
}
|
||||
}*/
|
||||
|
||||
// Return true to continue with the drawing, or false to cancel
|
||||
return true
|
||||
}
|
||||
@@ -387,17 +376,27 @@ class DayFragment(private val position: Int) : Fragment(R.layout.fragment_day) {
|
||||
scrollView.addView(dayLayout)
|
||||
|
||||
rootLayout.addView(scrollView)
|
||||
|
||||
// fill in the rest with empty space
|
||||
val sepr = Space(requireContext())
|
||||
sepr.layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
Vars.height - rootLayout.height
|
||||
)
|
||||
|
||||
rootLayout.addView(sepr)
|
||||
}
|
||||
}
|
||||
|
||||
class Subject {
|
||||
var name: String = ""
|
||||
var place: String = ""
|
||||
var teacher: String = ""
|
||||
var substitute: String = ""
|
||||
var number: String = ""
|
||||
var start: String = ""
|
||||
var end: String = ""
|
||||
lateinit var name: String
|
||||
lateinit var place: String
|
||||
lateinit var teacher: String
|
||||
lateinit var substitute: String
|
||||
lateinit var number: String
|
||||
lateinit var start: String
|
||||
lateinit var end: String
|
||||
var free: Boolean = false
|
||||
|
||||
companion object {
|
||||
fun freeSubject(number: String, start: String, end: String): Subject {
|
||||
@@ -410,6 +409,7 @@ class Subject {
|
||||
s.number = number
|
||||
s.start = start
|
||||
s.end = end
|
||||
s.free = true
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
||||
class Dialogs {
|
||||
class SignOut(context: Context) : AlertDialog(context) {
|
||||
init {
|
||||
// Initialize your dialog properties
|
||||
setTitle("Upozornění")
|
||||
setMessage("Opravdu se chcete odhlásit? Tato akce vymaže uložené přihlašovací údaje a zavře aplikaci. Budete se poté muset znovu přihlásit.")
|
||||
setCancelable(true) // Set whether the dialog can be canceled by tapping outside
|
||||
|
||||
// Set positive button (e.g., "OK" button)
|
||||
setButton(
|
||||
BUTTON_POSITIVE,
|
||||
"Ano"
|
||||
) { _, _ ->
|
||||
Account.clearAccountFile()
|
||||
MainActivity.quit(context)
|
||||
}
|
||||
|
||||
// Set negative button (e.g., "Cancel" button)
|
||||
setButton(
|
||||
BUTTON_NEGATIVE,
|
||||
"Ne"
|
||||
) { _, _ ->
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LanguageChange(context: Context) : AlertDialog(context) {
|
||||
init {
|
||||
// Initialize your dialog properties
|
||||
setTitle("Změnit jazyk")
|
||||
setCancelable(true)
|
||||
// Inflate the layout for the dialog
|
||||
val view = LayoutInflater.from(context).inflate(R.layout.radio_group_language, null)
|
||||
setView(view)
|
||||
|
||||
// Set positive button (e.g., "OK" button)
|
||||
setButton(
|
||||
BUTTON_POSITIVE,
|
||||
"OK"
|
||||
) { _, _ ->
|
||||
// functionality here
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
// Set negative button (e.g., "Cancel" button)
|
||||
setButton(
|
||||
BUTTON_NEGATIVE,
|
||||
"Zrušit"
|
||||
) { _, _ ->
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,54 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import android.R.attr.maxHeight
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.ImageDecoder
|
||||
import android.graphics.drawable.AnimatedImageDrawable
|
||||
import android.media.Image
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.MeasureSpec
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.charset.Charset
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity(), MainActivityListener {
|
||||
class MainActivity : AppCompatActivity() {
|
||||
// @section COMPANION
|
||||
companion object {
|
||||
val dayMap = mutableMapOf<Int, Day>()
|
||||
var days = listOf<Day>()
|
||||
}
|
||||
fun quit(context: Context) {
|
||||
// Create an Intent to launch the home screen
|
||||
val homeIntent = Intent(Intent.ACTION_MAIN)
|
||||
homeIntent.addCategory(Intent.CATEGORY_HOME)
|
||||
homeIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
||||
private val accountDataFilePath: String = "account_data.txt"
|
||||
// Start the home screen activity
|
||||
context.startActivity(homeIntent)
|
||||
|
||||
private val client = OkHttpClient()
|
||||
|
||||
private lateinit var username: String
|
||||
private lateinit var password: String
|
||||
private val apiBaseUrl = "https://aplikace.skolaonline.cz/solapi/api"
|
||||
private val tokenUrl = "$apiBaseUrl/connect/token"
|
||||
private lateinit var token: String
|
||||
private val userDataUrl = "$apiBaseUrl/v1/user"
|
||||
private lateinit var userData: String
|
||||
private lateinit var studentId: String
|
||||
private val timeTableUrl = "$apiBaseUrl/v1/timeTable"
|
||||
//private val marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list"
|
||||
|
||||
private suspend fun setupAPI() {
|
||||
return withContext(Dispatchers.IO) {
|
||||
token = JSONObject(getToken()).get("access_token").toString()
|
||||
if (token == "") {
|
||||
return@withContext
|
||||
// If the context is an Activity, finish it to close the current activity
|
||||
if (context is AppCompatActivity) {
|
||||
context.finish()
|
||||
}
|
||||
userData = getUserDataJSON()
|
||||
studentId = getStudentId()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// @section LISTENERS
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
showSplashScreen()
|
||||
|
||||
// set variables
|
||||
Vars.fileDir = applicationContext.filesDir
|
||||
Vars.width = Util.getDisplayWidth(this)
|
||||
Vars.height = Util.getDisplayHeight(this)
|
||||
|
||||
// start the app (coroutine because splashscreen)
|
||||
lifecycleScope.launch {
|
||||
launch()
|
||||
}
|
||||
@@ -76,24 +60,6 @@ class MainActivity : AppCompatActivity(), MainActivityListener {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun showSignInScreen() {
|
||||
setContentView(R.layout.activity_sign_in)
|
||||
setSignInButtonOnClick()
|
||||
}
|
||||
|
||||
private fun showSettingsScreen() {
|
||||
setContentView(R.layout.activity_settings)
|
||||
findViewById<Button>(R.id.settingsBackButton).setOnClickListener {
|
||||
launchPhase1()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSignInButtonOnClick() {
|
||||
findViewById<Button>(R.id.signInButton).setOnClickListener {
|
||||
onSignIn()
|
||||
}
|
||||
}
|
||||
|
||||
// Override onOptionsItemSelected to handle menu item clicks
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
@@ -103,17 +69,20 @@ class MainActivity : AppCompatActivity(), MainActivityListener {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.item_sign_in -> {
|
||||
showSignInScreen()
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.black)
|
||||
SignInFragment().show(supportFragmentManager, "fragment_sign_in")
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.item_settings -> {
|
||||
showSettingsScreen()
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.item_sign_out -> {
|
||||
clearAccountFile()
|
||||
showSignInScreen()
|
||||
Dialogs.SignOut(this).show()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -121,26 +90,10 @@ class MainActivity : AppCompatActivity(), MainActivityListener {
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun clearAccountFile() {
|
||||
val file = File(applicationContext.filesDir, accountDataFilePath)
|
||||
file.delete()
|
||||
file.createNewFile()
|
||||
}
|
||||
|
||||
override fun onLaunch(view: View) {
|
||||
launch()
|
||||
}
|
||||
|
||||
private suspend fun accountIsValid(acc: Account): Boolean {
|
||||
return withContext(Dispatchers.IO) {
|
||||
username = acc.username
|
||||
password = acc.password
|
||||
|
||||
val token = getToken()
|
||||
!JSONObject(token).has("error")
|
||||
}
|
||||
}
|
||||
|
||||
// @section SIGN_IN
|
||||
private fun onSignIn() {
|
||||
val acc = Account(
|
||||
findViewById<EditText>(R.id.usernameField).text.toString(),
|
||||
@@ -150,10 +103,10 @@ class MainActivity : AppCompatActivity(), MainActivityListener {
|
||||
|
||||
showSplashScreen()
|
||||
|
||||
saveAccount(acc)
|
||||
Account.save(acc)
|
||||
|
||||
lifecycleScope.launch {
|
||||
val valid = accountIsValid(acc)
|
||||
val valid = Account.isValid(acc)
|
||||
|
||||
if (valid) {
|
||||
launch()
|
||||
@@ -168,7 +121,97 @@ class MainActivity : AppCompatActivity(), MainActivityListener {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSignInButtonOnClick() {
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.black)
|
||||
findViewById<Button>(R.id.signInButton).setOnClickListener {
|
||||
onSignIn()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// @section LAUNCH
|
||||
private fun launch() {
|
||||
// show the splash/loading screen
|
||||
showSplashScreen()
|
||||
|
||||
// start
|
||||
lifecycleScope.launch {
|
||||
/* check if there is a saved user account
|
||||
* if yes, use that
|
||||
* otherwise show the login activity
|
||||
*/
|
||||
|
||||
val accSaved: Boolean = Account.haveSaved()
|
||||
|
||||
if (!accSaved) {
|
||||
// show the sign in screen
|
||||
showSignInScreen()
|
||||
} else {
|
||||
// launch!
|
||||
launchPhase1(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchPhase1(showSplash: Boolean) {
|
||||
var week: List<Day>
|
||||
|
||||
if (showSplash) {
|
||||
showSplashScreen()
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
Network.setupAPI()
|
||||
|
||||
if (Vars.token != "NETWORK_UNREACHABLE") {
|
||||
week = Parsing.parseTimeTableJSON(Network.getTimeTableJSON())
|
||||
launchPhase2(week)
|
||||
} else {
|
||||
// show some sort of warning message and/or load offline files
|
||||
//week =
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchPhase2(week: List<Day>) {
|
||||
Vars.days = week
|
||||
for ((i, day) in week.withIndex()) {
|
||||
Vars.dayMap[i] = day
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val toolbar = findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
toolbar.title = ""
|
||||
toolbar.inflateMenu(R.menu.menu_main)
|
||||
|
||||
val mainFragment = CalWeekFragment()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, mainFragment)
|
||||
.commit()
|
||||
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.primary)
|
||||
}
|
||||
|
||||
|
||||
// @section SPLASH_SCREENS
|
||||
private fun showSignInScreen() {
|
||||
setContentView(R.layout.activity_sign_in)
|
||||
setSignInButtonOnClick()
|
||||
}
|
||||
|
||||
private fun showSettingsScreen() {
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.primary)
|
||||
val settingsFragment = SettingsFragment()
|
||||
settingsFragment.show(supportFragmentManager, "Nastavení")
|
||||
}
|
||||
|
||||
private fun showSplashScreen() {
|
||||
window.statusBarColor = ContextCompat.getColor(this, R.color.black)
|
||||
|
||||
lifecycleScope.launch {
|
||||
setContentView(R.layout.activity_splash_screen)
|
||||
|
||||
@@ -187,411 +230,4 @@ class MainActivity : AppCompatActivity(), MainActivityListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Account(val username: String, val password: String, val error: Int)
|
||||
|
||||
private fun parseAccountDataFile(file: File): Account {
|
||||
val lines: List<String> = file.readLines()
|
||||
|
||||
if (lines.size >= 2) {
|
||||
if (lines[0] != "" && lines[1] != "") {
|
||||
return Account(lines[0], lines[1], 0)
|
||||
}
|
||||
}
|
||||
|
||||
return Account("", "", 1)
|
||||
}
|
||||
|
||||
private fun haveSavedAccount(): Boolean {
|
||||
/* check if a file exists
|
||||
* no -> create it and return false
|
||||
* yes -> read the contents and use them as the username and password
|
||||
*/
|
||||
|
||||
// check if file exists
|
||||
val file = File(applicationContext.filesDir, accountDataFilePath)
|
||||
|
||||
return if (!file.exists()) {
|
||||
// it does not exist
|
||||
file.createNewFile()
|
||||
false
|
||||
} else {
|
||||
// it exists, so it shall be parsed
|
||||
val acc: Account = parseAccountDataFile(file)
|
||||
username = acc.username
|
||||
password = acc.password
|
||||
acc.error == 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveAccount(acc: Account): Boolean {
|
||||
return try {
|
||||
val file = File(applicationContext.filesDir, accountDataFilePath)
|
||||
file.writeText("${acc.username}\n${acc.password}\n", Charset.forName("UTF-8"))
|
||||
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchPhase1() {
|
||||
var week: List<Day>
|
||||
showSplashScreen()
|
||||
lifecycleScope.launch {
|
||||
setupAPI()
|
||||
week = parseJSON(getTimeTableJSON())
|
||||
|
||||
launchPhase2(week)
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchPhase2(week: List<Day>) {
|
||||
days = week
|
||||
for ((i, day) in week.withIndex()) {
|
||||
dayMap[i] = day
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val toolbar = findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
toolbar.title = ""
|
||||
toolbar.inflateMenu(R.menu.menu_main)
|
||||
|
||||
val mainFragment = CalWeekFragment()
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, mainFragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
private fun launch() {
|
||||
lifecycleScope.launch {
|
||||
/* check if there is a saved user account
|
||||
* if yes, use that
|
||||
* otherwise show the login activity
|
||||
*/
|
||||
|
||||
val accSaved: Boolean = haveSavedAccount()
|
||||
|
||||
if (!accSaved) {
|
||||
// show the sign in screen
|
||||
showSignInScreen()
|
||||
} else {
|
||||
// launch!
|
||||
launchPhase1()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getToken(): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val request = Request.Builder()
|
||||
.url(tokenUrl)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.post(
|
||||
FormBody.Builder()
|
||||
.add("grant_type", "password")
|
||||
.add("username", username)
|
||||
.add("password", password)
|
||||
.add("client_id", "test_client")
|
||||
.add("scope", "openid offline_access profile sol_api")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
try {
|
||||
val resp = client.newCall(request).execute()
|
||||
val respString = resp.body?.string()
|
||||
val respJson = respString?.let { JSONObject(it) }
|
||||
respJson.toString()
|
||||
} catch (e: JSONException) {
|
||||
"""{"error": "json"}"""
|
||||
} catch (e: Exception) {
|
||||
//e.printStackTrace()
|
||||
"""{"error": "network"}"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStudentId(): String {
|
||||
return if (!userIsParent()) {
|
||||
JSONObject(userData).get("personID").toString()
|
||||
} else {
|
||||
JSONObject(userData).getJSONArray("children").getJSONObject(0).get("id").toString()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getUserDataJSON(): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val request = Request.Builder()
|
||||
.url(userDataUrl)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Authorization", "Bearer $token") // using Bearer auth token
|
||||
.get()
|
||||
.build()
|
||||
|
||||
try {
|
||||
val resp = client.newCall(request).execute()
|
||||
val respString = resp.body?.string().toString()
|
||||
respString
|
||||
} catch (e: IOException) {
|
||||
"""{"error": "network"}"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun userIsParent(): Boolean {
|
||||
return if (userData != "") JSONObject(userData).get("userType").toString() == "parent"
|
||||
else false
|
||||
}
|
||||
|
||||
private suspend fun getTimeTableJSON(): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val userIsParent = userIsParent()
|
||||
var url = timeTableUrl
|
||||
if (userIsParent) {
|
||||
url += "?studentId=$studentId"
|
||||
}
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Authorization", "Bearer $token")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
try {
|
||||
val resp = client.newCall(request).execute()
|
||||
resp.body?.string().toString()
|
||||
} catch (e: IOException) {
|
||||
"""{"error": "network"}"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseJSON(jsonString: String): List<Day> {
|
||||
val jsonObject = JSONObject(jsonString)
|
||||
|
||||
/*
|
||||
* return a List that looks like this:
|
||||
* [<name>, <date>, [<subj_name>, <subj_place>, <teacher>, <substitute>, <number>, <start_time>, <end_time>]]
|
||||
* ["Po", "8.1.", ["D", "207", "Strakova S.", 0, 1, "08:00", "08:45" ]]
|
||||
*/
|
||||
|
||||
val week = mutableListOf<Day>()
|
||||
|
||||
if (jsonObject.has("error")) {
|
||||
return week
|
||||
}
|
||||
|
||||
// Access the "body" array from the main JSON object
|
||||
val bodyArray = jsonObject.getJSONArray("days")
|
||||
|
||||
// Convert the JSON array to a list of JSON objects
|
||||
val jsonObjectList = mutableListOf<JSONObject>()
|
||||
for (i in 0 until bodyArray.length()) {
|
||||
val nestedJsonObject = bodyArray.getJSONObject(i)
|
||||
jsonObjectList.add(nestedJsonObject)
|
||||
}
|
||||
|
||||
for (obj in jsonObjectList) {
|
||||
// for each day
|
||||
val dateRAW = obj.get("date").toString()
|
||||
var dayStr = dateRAW.split("T")[0].split("-")[2]
|
||||
var monthStr = dateRAW.split("T")[0].split("-")[1]
|
||||
|
||||
if (dayStr[0] == '0') { dayStr = dayStr.slice(1..<dayStr.length) }
|
||||
if (monthStr[0] == '0') { monthStr = monthStr.slice(1..<monthStr.length) }
|
||||
|
||||
val date = "$dayStr. $monthStr."
|
||||
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")
|
||||
val localDateTime = LocalDateTime.parse(dateRAW, formatter)
|
||||
|
||||
val name = when (localDateTime.dayOfWeek.toString()) {
|
||||
"MONDAY" -> "Pondělí"
|
||||
"TUESDAY" -> "Úterý"
|
||||
"WEDNESDAY" -> "Středa"
|
||||
"THURSDAY" -> "Čtvrtek"
|
||||
"FRIDAY" -> "Pátek"
|
||||
"SATURDAY" -> "Sobota"
|
||||
"SUNDAY" -> "Neděle"
|
||||
else -> localDateTime.dayOfWeek.toString()
|
||||
}
|
||||
|
||||
val subjects = mutableListOf<Subject>()
|
||||
val auxSubjects = mutableListOf<Subject>()
|
||||
|
||||
val subjectJSONList = mutableListOf<JSONObject>()
|
||||
val subjectArray = obj.getJSONArray("schedules")
|
||||
for (i in 0 until subjectArray.length()) {
|
||||
subjectJSONList.add(subjectArray.getJSONObject(i))
|
||||
}
|
||||
|
||||
for (subj in subjectJSONList) {
|
||||
// for each subject in one particular day
|
||||
val subject = Subject()
|
||||
val kind = subj.getJSONObject("hourKind").get("id")
|
||||
val type = subj.getJSONObject("hourType").get("id")
|
||||
|
||||
if (
|
||||
(type == "SUPLOVANA") ||
|
||||
(type != "SUPLOVANI" && type != "ROZVRH")
|
||||
) {
|
||||
when (kind) {
|
||||
"ZRUSENI_VYUKY_ROZVRH" -> {
|
||||
subject.name = subj.get("title").toString()
|
||||
subject.place = ""
|
||||
subject.teacher = ""
|
||||
subject.substitute = "2" // 2 means school event
|
||||
|
||||
val hourspan = subj.getJSONArray("detailHours").length()
|
||||
subject.number = "1 - $hourspan"
|
||||
subject.start = subj.get("beginTime").toString()
|
||||
subject.start = subject.start.slice(0..subject.start.length-4)
|
||||
subject.end = subj.get("endTime").toString()
|
||||
subject.end = subject.end.slice(0..subject.end.length-4)
|
||||
|
||||
subjects.add(subject)
|
||||
}
|
||||
"S_NAHRADOU_SKOL_AKCE" -> {
|
||||
subject.name = subj.get("title").toString()
|
||||
subject.place = ""
|
||||
subject.teacher = ""
|
||||
subject.substitute = "2" // 2 means school event
|
||||
|
||||
val hourspan = subj.getJSONArray("detailHours").length()
|
||||
if (hourspan == 1) {
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
subject.number = detailHours.getJSONObject(0).get("id").toString()
|
||||
subject.start = detailHours.getJSONObject(0).get("timeFrom").toString()
|
||||
subject.start = subject.start.slice(0..subject.start.length - 4)
|
||||
subject.end = detailHours.getJSONObject(0).get("timeto").toString()
|
||||
subject.end = subject.end.slice(0..subject.end.length - 4)
|
||||
subjects.add(subject)
|
||||
} else {
|
||||
for (i in 0..<hourspan) {
|
||||
val newSubject = Subject()
|
||||
newSubject.name = subject.name
|
||||
newSubject.place = subject.place
|
||||
newSubject.teacher = subject.teacher
|
||||
newSubject.substitute = subject.substitute
|
||||
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
newSubject.number = detailHours.getJSONObject(i).get("id").toString()
|
||||
newSubject.start =
|
||||
detailHours.getJSONObject(i).get("timeFrom").toString()
|
||||
newSubject.start =
|
||||
newSubject.start.slice(0..newSubject.start.length - 4)
|
||||
newSubject.end = detailHours.getJSONObject(i).get("timeto").toString()
|
||||
newSubject.end = newSubject.end.slice(0..newSubject.end.length - 4)
|
||||
subjects.add(newSubject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
subject.name = subj.getJSONObject("subject").get("abbrev").toString()
|
||||
subject.place =
|
||||
subj.getJSONArray("rooms").getJSONObject(0).get("abbrev").toString()
|
||||
subject.teacher = "" +
|
||||
"${subj.getJSONArray("teachers").getJSONObject(0).get("name")} " +
|
||||
"${subj.getJSONArray("teachers").getJSONObject(0).get("surname")}"
|
||||
if (type == "ROZVRH") {
|
||||
subject.substitute = "0"
|
||||
} else if (type == "SUPLOVANI") {
|
||||
subject.substitute = "1"
|
||||
}
|
||||
val hourspan = subj.getJSONArray("detailHours").length()
|
||||
if (hourspan == 1) {
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
subject.number = detailHours.getJSONObject(0).get("id").toString()
|
||||
subject.start = detailHours.getJSONObject(0).get("timeFrom").toString()
|
||||
subject.start = subject.start.slice(0..subject.start.length - 4)
|
||||
subject.end = detailHours.getJSONObject(0).get("timeto").toString()
|
||||
subject.end = subject.end.slice(0..subject.end.length - 4)
|
||||
subjects.add(subject)
|
||||
} else {
|
||||
for (i in 0..<hourspan) {
|
||||
val newSubject = Subject()
|
||||
newSubject.name = subject.name
|
||||
newSubject.place = subject.place
|
||||
newSubject.teacher = subject.teacher
|
||||
newSubject.substitute = subject.substitute
|
||||
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
newSubject.number = detailHours.getJSONObject(i).get("id").toString()
|
||||
newSubject.start =
|
||||
detailHours.getJSONObject(i).get("timeFrom").toString()
|
||||
newSubject.start =
|
||||
newSubject.start.slice(0..newSubject.start.length - 4)
|
||||
newSubject.end = detailHours.getJSONObject(i).get("timeto").toString()
|
||||
newSubject.end = newSubject.end.slice(0..newSubject.end.length - 4)
|
||||
subjects.add(newSubject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// rust pseudocode
|
||||
|
||||
let mut res: Vec<i32> = vec![];
|
||||
for i in 0..v.len()-1 {
|
||||
if v[i] == v[i+1]-1 {
|
||||
res.push(v[i]);
|
||||
} else {
|
||||
res.push(v[i]);
|
||||
|
||||
while res[res.len()-1]+1 != v[i+1] {
|
||||
res.push(res[res.len()-1]+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.push(v[v.len()-1]);
|
||||
|
||||
res
|
||||
*/
|
||||
|
||||
if (subjects.size > 1) {
|
||||
for (i in 0..<subjects.size-1) {
|
||||
if (subjects[i].number == ((subjects[i + 1].number.toInt() - 1).toString())) {
|
||||
auxSubjects.add(subjects[i])
|
||||
} else {
|
||||
auxSubjects.add(subjects[i])
|
||||
|
||||
while ((auxSubjects[auxSubjects.size - 1].number.toInt() + 1).toString() != subjects[i + 1].number) {
|
||||
auxSubjects.add(
|
||||
Subject.freeSubject(
|
||||
(auxSubjects[auxSubjects.size - 1].number.toInt() + 1).toString(),
|
||||
"",
|
||||
""
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
auxSubjects.add(subjects[subjects.size - 1])
|
||||
} else {
|
||||
auxSubjects.add(subjects[0])
|
||||
}
|
||||
|
||||
for ((i, s) in auxSubjects.withIndex()) {
|
||||
if (s.place == "Volno") {
|
||||
s.start = auxSubjects[i-1].end
|
||||
s.end = auxSubjects[i+1].start
|
||||
}
|
||||
}
|
||||
|
||||
val day = Day()
|
||||
day.name = name
|
||||
day.date = date
|
||||
day.subjects = auxSubjects
|
||||
|
||||
week.add(day)
|
||||
}
|
||||
|
||||
return week
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Request
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
|
||||
class Network {
|
||||
companion object {
|
||||
suspend fun getToken(): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val request = Request.Builder()
|
||||
.url(Vars.tokenUrl)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.post(
|
||||
FormBody.Builder()
|
||||
.add("grant_type", "password")
|
||||
.add("username", Vars.username)
|
||||
.add("password", Vars.password)
|
||||
.add("client_id", "test_client")
|
||||
.add("scope", "openid offline_access profile sol_api")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
try {
|
||||
val resp = Vars.client.newCall(request).execute()
|
||||
val respString = resp.body?.string()
|
||||
val respJson = respString?.let { JSONObject(it) }
|
||||
respJson.toString()
|
||||
} catch (e: JSONException) {
|
||||
"""{"error": "json"}"""
|
||||
} catch (e: Exception) {
|
||||
//e.printStackTrace()
|
||||
"""{"error": "network"}"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun setupAPI() {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val token = getToken()
|
||||
Vars.token = "NETWORK_UNREACHABLE"
|
||||
if (!JSONObject(token).has("error")) {
|
||||
Vars.token = JSONObject(token).get("access_token").toString()
|
||||
}
|
||||
if (Vars.token == "NETWORK_UNREACHABLE") {
|
||||
Vars.userData = ""
|
||||
Vars.studentId = ""
|
||||
return@withContext
|
||||
}
|
||||
Vars.userData = getUserDataJSON()
|
||||
Vars.studentId = getStudentId()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStudentId(): String {
|
||||
return if (!Parsing.userIsParent()) {
|
||||
JSONObject(Vars.userData).get("personID").toString()
|
||||
} else {
|
||||
JSONObject(Vars.userData).getJSONArray("children").getJSONObject(0).get("id").toString()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getUserDataJSON(): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val request = Request.Builder()
|
||||
.url(Vars.userDataUrl)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Authorization", "Bearer ${Vars.token}") // using Bearer auth token
|
||||
.get()
|
||||
.build()
|
||||
|
||||
try {
|
||||
val resp = Vars.client.newCall(request).execute()
|
||||
val respString = resp.body?.string().toString()
|
||||
respString
|
||||
} catch (e: IOException) {
|
||||
"""{"error": "network"}"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getTimeTableJSON(): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val userIsParent = Parsing.userIsParent()
|
||||
var url = Vars.timeTableUrl
|
||||
if (userIsParent) {
|
||||
url += "?studentId=${Vars.studentId}"
|
||||
}
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Authorization", "Bearer ${Vars.token}")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
try {
|
||||
val resp = Vars.client.newCall(request).execute()
|
||||
resp.body?.string().toString()
|
||||
} catch (e: IOException) {
|
||||
"""{"error": "network"}"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import org.json.JSONObject
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
class Parsing {
|
||||
companion object {
|
||||
fun userIsParent(): Boolean {
|
||||
return if (Vars.userData != "") JSONObject(Vars.userData).get("userType")
|
||||
.toString() == "parent"
|
||||
else false
|
||||
}
|
||||
|
||||
fun parseTimeTableJSON(jsonString: String): List<Day> {
|
||||
val jsonObject = JSONObject(jsonString)
|
||||
|
||||
/*
|
||||
* return a List that looks like this:
|
||||
* [<name>, <date>, [<subj_name>, <subj_place>, <teacher>, <substitute>, <number>, <start_time>, <end_time>]]
|
||||
* ["Po", "8.1.", ["D", "207", "Strakova S.", 0, 1, "08:00", "08:45" ]]
|
||||
*/
|
||||
|
||||
val week = mutableListOf<Day>()
|
||||
|
||||
if (jsonObject.has("error")) {
|
||||
return week
|
||||
}
|
||||
|
||||
// Access the "body" array from the main JSON object
|
||||
val bodyArray = jsonObject.getJSONArray("days")
|
||||
|
||||
// Convert the JSON array to a list of JSON objects
|
||||
val jsonObjectList = mutableListOf<JSONObject>()
|
||||
for (i in 0 until bodyArray.length()) {
|
||||
val nestedJsonObject = bodyArray.getJSONObject(i)
|
||||
jsonObjectList.add(nestedJsonObject)
|
||||
}
|
||||
|
||||
for (obj in jsonObjectList) {
|
||||
// for each day
|
||||
val dateRAW = obj.get("date").toString()
|
||||
var dayStr = dateRAW.split("T")[0].split("-")[2]
|
||||
var monthStr = dateRAW.split("T")[0].split("-")[1]
|
||||
|
||||
if (dayStr[0] == '0') { dayStr = dayStr.slice(1..<dayStr.length) }
|
||||
if (monthStr[0] == '0') { monthStr = monthStr.slice(1..<monthStr.length) }
|
||||
|
||||
val date = "$dayStr. $monthStr."
|
||||
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")
|
||||
val localDateTime = LocalDateTime.parse(dateRAW, formatter)
|
||||
|
||||
val name = when (localDateTime.dayOfWeek.toString()) {
|
||||
"MONDAY" -> "Pondělí"
|
||||
"TUESDAY" -> "Úterý"
|
||||
"WEDNESDAY" -> "Středa"
|
||||
"THURSDAY" -> "Čtvrtek"
|
||||
"FRIDAY" -> "Pátek"
|
||||
"SATURDAY" -> "Sobota"
|
||||
"SUNDAY" -> "Neděle"
|
||||
else -> localDateTime.dayOfWeek.toString()
|
||||
}
|
||||
|
||||
val subjects = mutableListOf<Subject>()
|
||||
val auxSubjects = mutableListOf<Subject>()
|
||||
|
||||
val subjectJSONList = mutableListOf<JSONObject>()
|
||||
val subjectArray = obj.getJSONArray("schedules")
|
||||
for (i in 0 until subjectArray.length()) {
|
||||
subjectJSONList.add(subjectArray.getJSONObject(i))
|
||||
}
|
||||
|
||||
for (subj in subjectJSONList) {
|
||||
// for each subject in one particular day
|
||||
val subject = Subject()
|
||||
val kind = subj.getJSONObject("hourKind").get("id")
|
||||
val type = subj.getJSONObject("hourType").get("id")
|
||||
|
||||
if (
|
||||
(type == "SUPLOVANA") ||
|
||||
(type != "SUPLOVANI" && type != "ROZVRH")
|
||||
) {
|
||||
when (kind) {
|
||||
"ZRUSENI_VYUKY_ROZVRH" -> {
|
||||
subject.name = subj.get("title").toString()
|
||||
subject.place = ""
|
||||
subject.teacher = ""
|
||||
subject.substitute = "2" // 2 means school event
|
||||
|
||||
val hourspan = subj.getJSONArray("detailHours").length()
|
||||
subject.number = "1 - $hourspan"
|
||||
subject.start = subj.get("beginTime").toString()
|
||||
subject.start = subject.start.slice(0..subject.start.length-4)
|
||||
subject.end = subj.get("endTime").toString()
|
||||
subject.end = subject.end.slice(0..subject.end.length-4)
|
||||
|
||||
subjects.add(subject)
|
||||
}
|
||||
"S_NAHRADOU_SKOL_AKCE" -> {
|
||||
subject.name = subj.get("title").toString()
|
||||
subject.place = ""
|
||||
subject.teacher = ""
|
||||
subject.substitute = "2" // 2 means school event
|
||||
|
||||
val hourspan = subj.getJSONArray("detailHours").length()
|
||||
if (hourspan == 1) {
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
subject.number = detailHours.getJSONObject(0).get("id").toString()
|
||||
subject.start = detailHours.getJSONObject(0).get("timeFrom").toString()
|
||||
subject.start = subject.start.slice(0..subject.start.length - 4)
|
||||
subject.end = detailHours.getJSONObject(0).get("timeto").toString()
|
||||
subject.end = subject.end.slice(0..subject.end.length - 4)
|
||||
subjects.add(subject)
|
||||
} else {
|
||||
for (i in 0..<hourspan) {
|
||||
val newSubject = Subject()
|
||||
newSubject.name = subject.name
|
||||
newSubject.place = subject.place
|
||||
newSubject.teacher = subject.teacher
|
||||
newSubject.substitute = subject.substitute
|
||||
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
newSubject.number = detailHours.getJSONObject(i).get("id").toString()
|
||||
newSubject.start =
|
||||
detailHours.getJSONObject(i).get("timeFrom").toString()
|
||||
newSubject.start =
|
||||
newSubject.start.slice(0..newSubject.start.length - 4)
|
||||
newSubject.end = detailHours.getJSONObject(i).get("timeto").toString()
|
||||
newSubject.end = newSubject.end.slice(0..newSubject.end.length - 4)
|
||||
subjects.add(newSubject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
subject.name = subj.getJSONObject("subject").get("abbrev").toString()
|
||||
subject.place =
|
||||
subj.getJSONArray("rooms").getJSONObject(0).get("abbrev").toString()
|
||||
subject.teacher = "" +
|
||||
"${subj.getJSONArray("teachers").getJSONObject(0).get("name")} " +
|
||||
"${subj.getJSONArray("teachers").getJSONObject(0).get("surname")}"
|
||||
if (type == "ROZVRH") {
|
||||
subject.substitute = "0"
|
||||
} else if (type == "SUPLOVANI") {
|
||||
subject.substitute = "1"
|
||||
}
|
||||
val hourspan = subj.getJSONArray("detailHours").length()
|
||||
if (hourspan == 1) {
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
subject.number = detailHours.getJSONObject(0).get("id").toString()
|
||||
subject.start = detailHours.getJSONObject(0).get("timeFrom").toString()
|
||||
subject.start = subject.start.slice(0..subject.start.length - 4)
|
||||
subject.end = detailHours.getJSONObject(0).get("timeto").toString()
|
||||
subject.end = subject.end.slice(0..subject.end.length - 4)
|
||||
subjects.add(subject)
|
||||
} else {
|
||||
for (i in 0..<hourspan) {
|
||||
val newSubject = Subject()
|
||||
newSubject.name = subject.name
|
||||
newSubject.place = subject.place
|
||||
newSubject.teacher = subject.teacher
|
||||
newSubject.substitute = subject.substitute
|
||||
|
||||
val detailHours = subj.getJSONArray("detailHours")
|
||||
newSubject.number = detailHours.getJSONObject(i).get("id").toString()
|
||||
newSubject.start =
|
||||
detailHours.getJSONObject(i).get("timeFrom").toString()
|
||||
newSubject.start =
|
||||
newSubject.start.slice(0..newSubject.start.length - 4)
|
||||
newSubject.end = detailHours.getJSONObject(i).get("timeto").toString()
|
||||
newSubject.end = newSubject.end.slice(0..newSubject.end.length - 4)
|
||||
subjects.add(newSubject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// rust pseudocode
|
||||
|
||||
let mut res: Vec<i32> = vec![];
|
||||
for i in 0..v.len()-1 {
|
||||
if v[i] == v[i+1]-1 {
|
||||
res.push(v[i]);
|
||||
} else {
|
||||
res.push(v[i]);
|
||||
|
||||
while res[res.len()-1]+1 != v[i+1] {
|
||||
res.push(res[res.len()-1]+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.push(v[v.len()-1]);
|
||||
|
||||
res
|
||||
*/
|
||||
|
||||
if (subjects.size > 1) {
|
||||
for (i in 0..<subjects.size-1) {
|
||||
if (subjects[i].number == ((subjects[i + 1].number.toInt() - 1).toString())) {
|
||||
auxSubjects.add(subjects[i])
|
||||
} else {
|
||||
auxSubjects.add(subjects[i])
|
||||
|
||||
while ((auxSubjects[auxSubjects.size - 1].number.toInt() + 1).toString() != subjects[i + 1].number) {
|
||||
auxSubjects.add(
|
||||
Subject.freeSubject(
|
||||
(auxSubjects[auxSubjects.size - 1].number.toInt() + 1).toString(),
|
||||
"",
|
||||
""
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
auxSubjects.add(subjects[subjects.size - 1])
|
||||
} else {
|
||||
auxSubjects.add(subjects[0])
|
||||
}
|
||||
|
||||
for ((i, s) in auxSubjects.withIndex()) {
|
||||
if (s.free) {
|
||||
s.start = auxSubjects[i-1].end
|
||||
s.end = auxSubjects[i+1].start
|
||||
}
|
||||
}
|
||||
|
||||
val day = Day()
|
||||
day.name = name
|
||||
day.date = date
|
||||
day.subjects = auxSubjects
|
||||
|
||||
week.add(day)
|
||||
}
|
||||
|
||||
return week
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
|
||||
class SettingsFragment : DialogFragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NORMAL, android.R.style.Theme_Black_NoTitleBar_Fullscreen)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.activity_settings, container, false)
|
||||
|
||||
// Example: Dismiss the fragment when the back button is clicked
|
||||
view.findViewById<ImageView>(R.id.settingsBackButton).setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
// toolbar
|
||||
//val toolbar = view.findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar)
|
||||
//setSupportActionBar(toolbar)
|
||||
//toolbar.title = ""
|
||||
//toolbar.inflateMenu(R.menu.menu_main)
|
||||
|
||||
val button: Button = view.findViewById(R.id.setting_language_button_choice) // replace with your button ID
|
||||
button.setOnClickListener {
|
||||
Dialogs.LanguageChange(requireContext()).show()
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
|
||||
class SignInFragment : DialogFragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NORMAL, android.R.style.Theme_Black_NoTitleBar)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.activity_sign_in, container, false)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
|
||||
dialog.setCancelable(true)
|
||||
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import android.content.Context
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.WindowManager
|
||||
|
||||
class Util {
|
||||
companion object {
|
||||
fun getDisplayHeight(context: Context): Int {
|
||||
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
val displayMetrics = DisplayMetrics()
|
||||
|
||||
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
|
||||
return displayMetrics.heightPixels
|
||||
}
|
||||
|
||||
fun getDisplayWidth(context: Context): Int {
|
||||
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
val displayMetrics = DisplayMetrics()
|
||||
|
||||
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
|
||||
return displayMetrics.widthPixels
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.odweta.solen
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.File
|
||||
|
||||
class Vars {
|
||||
companion object {
|
||||
const val accountDataFilePath: String = "account_data.txt"
|
||||
lateinit var fileDir: File
|
||||
var width: Int = 0
|
||||
var height: Int = 0
|
||||
|
||||
val dayMap = mutableMapOf<Int, Day>()
|
||||
var days = listOf<Day>()
|
||||
|
||||
lateinit var username: String
|
||||
lateinit var password: String
|
||||
|
||||
val client = OkHttpClient()
|
||||
|
||||
private const val apiBaseUrl = "https://aplikace.skolaonline.cz/solapi/api"
|
||||
const val tokenUrl = "$apiBaseUrl/connect/token"
|
||||
lateinit var token: String
|
||||
const val userDataUrl = "$apiBaseUrl/v1/user"
|
||||
lateinit var userData: String
|
||||
lateinit var studentId: String
|
||||
const val timeTableUrl = "$apiBaseUrl/v1/timeTable"
|
||||
//val marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 122 KiB |
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#333333"
|
||||
android:alpha="0.6">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17.77,3.77l-1.77,-1.77l-10,10l10,10l1.77,-1.77l-8.23,-8.23z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF"
|
||||
android:alpha="0.8">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17.77,3.77l-1.77,-1.77l-10,10l10,10l1.77,-1.77l-8.23,-8.23z"/>
|
||||
</vector>
|
||||
|
After Width: | Height: | Size: 322 B |
|
After Width: | Height: | Size: 312 B |
|
After Width: | Height: | Size: 240 B |
|
After Width: | Height: | Size: 222 B |
|
After Width: | Height: | Size: 348 B |
|
After Width: | Height: | Size: 331 B |
|
After Width: | Height: | Size: 479 B |
|
After Width: | Height: | Size: 447 B |
@@ -1,21 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
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">
|
||||
|
||||
<Button
|
||||
android:id="@+id/settingsBackButton"
|
||||
android:layout_width="wrap_content"
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="315dp"
|
||||
android:layout_marginBottom="675dp"
|
||||
android:text="Zpět"
|
||||
android:fontFamily="@font/roboto_src"
|
||||
android:theme="@style/Theme.Solen.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/settingsToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:theme="@style/Theme.Solen.AppBarOverlay"
|
||||
app:popupTheme="@style/Theme.Solen.PopupOverlay">
|
||||
|
||||
<!-- Add an ImageView for the image in the Toolbar -->
|
||||
<ImageView
|
||||
android:id="@+id/settingsBackButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:contentDescription="@string/back_button"
|
||||
app:srcCompat="@drawable/ic_back_arrow" /> <!-- Adjust margin as needed -->
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView2"
|
||||
android:layout_width="409dp"
|
||||
android:layout_height="673dp"
|
||||
android:layout_marginStart="1dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginEnd="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setting_language"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_language_text"
|
||||
android:layout_width="180dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/lang_change"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/setting_language_button_choice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/lang_choose" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -5,44 +5,67 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="457dp"
|
||||
android:layout_height="307dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="17dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/usernameField"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/solen" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/usernameField"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_width="237dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="292dp"
|
||||
android:layout_marginBottom="52dp"
|
||||
android:background="@color/primary"
|
||||
android:ems="10"
|
||||
android:hint="Uživatelské jméno"
|
||||
android:inputType="text"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHighlight="@color/white"
|
||||
android:textColorHint="@color/white"
|
||||
android:textColorLink="@color/white"
|
||||
app:circularflow_radiusInDP="20dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/passwordField"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/passwordField"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_width="237dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginBottom="288dp"
|
||||
android:background="@color/primary"
|
||||
android:ems="10"
|
||||
android:hint="Heslo"
|
||||
android:inputType="textPassword"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHighlight="@color/white"
|
||||
android:textColorHint="@color/white"
|
||||
android:textColorLink="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/usernameField" />
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/signInButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginStart="137dp"
|
||||
android:layout_marginTop="72dp"
|
||||
android:layout_marginEnd="137dp"
|
||||
android:layout_marginBottom="88dp"
|
||||
android:text="Přihlásit se"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
android:contentDescription="@string/background"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.501"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.562"
|
||||
app:srcCompat="@color/black" />
|
||||
|
||||
<ImageView
|
||||
@@ -25,8 +27,10 @@
|
||||
android:contentDescription="@string/kolaonline_enhanced"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.495"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.16"
|
||||
app:srcCompat="@drawable/solen" />
|
||||
|
||||
<ImageView
|
||||
@@ -35,10 +39,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="205dp"
|
||||
android:layout_marginEnd="206dp"
|
||||
android:contentDescription="@string/splash_screen"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView3"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
app:layout_constraintVertical_bias="0.36" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -9,7 +9,7 @@
|
||||
android:id="@+id/layoutDayMain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:fontFamily="@font/roboto">
|
||||
</LinearLayout>
|
||||
android:background="@color/black"
|
||||
android:fontFamily="@font/roboto"
|
||||
android:orientation="vertical"></LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/settings_language_choice"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radio_button_language_cz"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/lang_cz" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radio_button_language_en"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/lang_en" />
|
||||
</RadioGroup>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
After Width: | Height: | Size: 719 B |
@@ -1,17 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/item_sign_in"
|
||||
android:title="@string/sign_in" />
|
||||
android:id="@+id/item_settings"
|
||||
android:title="@string/settings" />
|
||||
<item
|
||||
android:id="@+id/item_refresh"
|
||||
android:title="@string/refresh" />
|
||||
<item
|
||||
android:id="@+id/item_sign_out"
|
||||
android:title="@string/sign_out" />
|
||||
<item
|
||||
android:id="@+id/item_refresh"
|
||||
android:icon="@android:drawable/ic_popup_sync"
|
||||
android:title="@string/refresh" />
|
||||
<item
|
||||
android:id="@+id/item_settings"
|
||||
android:icon="@android:drawable/ic_menu_manage"
|
||||
android:title="@string/settings" />
|
||||
android:id="@+id/item_sign_in"
|
||||
android:title="@string/sign_in" />
|
||||
</menu>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/left_arrow_background"/>
|
||||
<foreground android:drawable="@mipmap/left_arrow_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/left_arrow_background"/>
|
||||
<foreground android:drawable="@mipmap/left_arrow_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 910 B |
|
After Width: | Height: | Size: 40 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 620 B |
|
After Width: | Height: | Size: 40 B |
|
After Width: | Height: | Size: 688 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 42 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 48 B |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 48 B |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
@@ -1,16 +0,0 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Solen" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,11 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFED7D31</color>
|
||||
<color name="purple_500">#FF6C5F5B</color>
|
||||
<color name="purple_700">#FF000000</color>
|
||||
<!--<color name="purple_700">#FF4F4A45</color>-->
|
||||
<color name="teal_200">#FFF6F1EE</color>
|
||||
<color name="teal_700">#FFF6F1EE</color>
|
||||
<color name="primary">#FFED7D31</color>
|
||||
<color name="secondary">#FF000000</color>
|
||||
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
|
||||
@@ -6,7 +6,13 @@
|
||||
<string name="splash_image">splash image</string>
|
||||
<string name="background">background</string>
|
||||
<string name="settings">Nastavení</string>
|
||||
<string name="sign_in">Přihlásit se</string>
|
||||
<string name="sign_in">Změnit přihlášení</string>
|
||||
<string name="sign_out">Odhlásit se</string>
|
||||
<string name="refresh">Znovu načíst data</string>
|
||||
<string name="back_button">Back button</string>
|
||||
<string name="lang_cz">Čeština</string>
|
||||
<string name="lang_en">English</string>
|
||||
<string name="lang_change">Změnit jazyk</string>
|
||||
<string name="lang_choose">Vybrat</string>
|
||||
<string name="splash_screen">splash screen</string>
|
||||
</resources>
|
||||
@@ -1,17 +1,21 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Solen" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryVariant">@color/primary</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorSecondary">@color/secondary</item>
|
||||
<item name="colorSecondaryVariant">@color/secondary</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:statusBarColor">@color/black</item>
|
||||
<!-- Background color -->
|
||||
<item name="android:colorBackground">@color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Solen.NoActionBar">
|
||||
@@ -24,4 +28,11 @@
|
||||
<style name="Theme.Solen.PopupOverlay" parent="ThemeOverlay.AppCompat.Light">
|
||||
<item name="android:textColor">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Solen.FullScreenDialog" parent="Theme.AppCompat.Dialog">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowIsFloating">false</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
</resources>
|
||||