diff --git a/.gitignore b/.gitignore index 19a033e..34199eb 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -*.pyc *.swp - TODO.md - -mobile/app/release/ +pc_linux/ diff --git a/README.md b/README.md index 73bacfa..0297449 100755 --- a/README.md +++ b/README.md @@ -2,57 +2,19 @@ An enhanced version of the ŠkolaOnLine (ŠOL) online school system. -Named after the Greek philosophist. - -It aims to provide more readable data than the web version and remove some of the annoyances of the mobile app. This project has two version, the main one is the Android app but secondly there is also a linux version written in Python, although it is kind of outdated. At the moment, development is mainly focused on the Android app. - -Insiration was taken from [@JakubAndrysek](https://github.com/JakubAndrysek)'s [skola-online-stahovani-znamek](https://github.com/JakubAndrysek/skola-online-stahovani-znamek) project. - - -# Android app - -The latest release is availible on [this](https://gitlab.com/Odweta/solon/-/raw/main/release/solon-v0.7.apk?ref_type=heads&inline=false) link. View all relases [here](https://gitlab.com/Odweta/solon/-/releases). - View your timetable and marks in a pleasing format. No extra bloat. Everything is colorful, animated and pretty, so your eyes should be pleased. +It aims to provide more readable data than the web version and remove some of the annoyances of the mobile app. -# PC Linux Python version +Named after the Greek philosopher. -This version of the project is not very relevant anymore, so its development is suspended (I won't work on it in the forseeable future). **Check out the Android app instead!** +Insiration was taken from [@JakubAndrysek](https://github.com/JakubAndrysek)'s [skola-online-stahovani-znamek](https://github.com/JakubAndrysek/skola-online-stahovani-znamek) project. -## Features -+ Shows the schedule for the current week -+ Shows all grades and their weighted average -+ Works offline (if the internet has been accessed at least once before) +
-## Installation +The latest release is availible on [this](https://gitlab.com/Odweta/solon/-/raw/main/release/solon.apk?ref_type=heads&inline=false) link. View all relases [here](https://gitlab.com/Odweta/solon/-/releases). -**Clone the repo** -`git clone https://gitlab.com/Odweta/solon` -**Navigate into the repo directory** -`cd solon/pc_linux` - -**Install** -`sudo make install` - -**Run the program** -`solon` - -## Usage - -Upon startup, the user is prompted to enter their ŠkolaOnLine credentials. If the login proccess is successful, the preconfigured functions in `/bin/solon` will execute. By default, the functions are: showing the schedule for this week, showing the schedule for today and tomorrow and showing the grades. You can edit that file if you want to change what is being displayed. Alternatively, you can edit the `src/main.py` and then do the install step (`sudo make install`). There are optional command-line arguments for filtering what should be displayed. Here they are: -| Optional argument | Description | -| ----------------------------------- | --------------------------------------------------------------------------------------- | -| -t, --today-tomorrow | show the schedule for today and tomorrow | -| -w, --week | show the schedule for this week | -| -g, --grades | show the grades | -| -u, --username | provide the username for login | -| -p, --password | provide the password for login | -| -l, --local | only show the local data without attempting to connect to the internet | -| -d, --delete-local-files | deletes all locally stored user data, including schedules, grades and login credentials | -| -b, --backup | backs up all local user data into a .tar.gz archive | -| -R, --backup-restore PATH | restores a backup previously made with SOLEn | diff --git a/mobile/.idea/deploymentTargetDropDown.xml b/mobile/.idea/deploymentTargetDropDown.xml index bd67f56..7a7d27b 100644 --- a/mobile/.idea/deploymentTargetDropDown.xml +++ b/mobile/.idea/deploymentTargetDropDown.xml @@ -3,7 +3,20 @@ - + + + + + + + + + + + + + + diff --git a/mobile/app/build.gradle.kts b/mobile/app/build.gradle.kts index e42c4ba..c114785 100644 --- a/mobile/app/build.gradle.kts +++ b/mobile/app/build.gradle.kts @@ -12,9 +12,7 @@ android { minSdk = 28 targetSdk = 34 versionCode = 1 - versionName = "0.7" - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + versionName = "0.8" } buildTypes { @@ -36,7 +34,6 @@ android { } dependencies { - implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.11.0") @@ -51,7 +48,4 @@ dependencies { implementation("com.github.bumptech.glide:glide:4.16.0") implementation("com.google.android.material:material:1.11.0") implementation("com.github.bumptech.glide:glide:4.16.0") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") - androidTestImplementation("androidx.test.ext:junit:1.1.5") - testImplementation("junit:junit:4.13.2") } \ No newline at end of file diff --git a/mobile/app/release/output-metadata.json b/mobile/app/release/output-metadata.json new file mode 100644 index 0000000..529b471 --- /dev/null +++ b/mobile/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.odweta.solon", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "0.7", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/mobile/app/src/main/AndroidManifest.xml b/mobile/app/src/main/AndroidManifest.xml index d66fbed..97ad91c 100644 --- a/mobile/app/src/main/AndroidManifest.xml +++ b/mobile/app/src/main/AndroidManifest.xml @@ -14,14 +14,14 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.Solen" + android:theme="@style/Theme.Solon" tools:targetApi="tiramisu" android:usesCleartextTraffic="true" android:enableOnBackInvokedCallback="true"> diff --git a/mobile/app/src/main/java/com/odweta/solon/AboutFragment.kt b/mobile/app/src/main/java/com/odweta/solon/AboutFragment.kt index 1e4b3ed..e8dd91a 100644 --- a/mobile/app/src/main/java/com/odweta/solon/AboutFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/AboutFragment.kt @@ -29,7 +29,7 @@ class AboutFragment : DialogFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setStyle(STYLE_NORMAL, R.style.Theme_Solen) + setStyle(STYLE_NORMAL, R.style.Theme_Solon) } override fun onCreateView( @@ -52,7 +52,7 @@ class AboutFragment : DialogFragment() { underlinedText.setSpan(UnderlineSpan(), 1, link.text.length, 0) link.text = underlinedText - makeTextViewClickable(link, "https://gitlab.com/Odweta", 1) + makeTextViewClickable(link, "https://gitlab.com/Odweta", 0) val srcLink = view.findViewById(R.id.sourceCode) @@ -67,7 +67,7 @@ class AboutFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) - dialog.window?.attributes?.windowAnimations = R.style.Theme_Solen_DialogAnimation + dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog.setCancelable(true) diff --git a/mobile/app/src/main/java/com/odweta/solon/CalWeekFragment.kt b/mobile/app/src/main/java/com/odweta/solon/CalWeekFragment.kt index 28d0e3e..bfa4e2c 100644 --- a/mobile/app/src/main/java/com/odweta/solon/CalWeekFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/CalWeekFragment.kt @@ -7,7 +7,6 @@ import android.graphics.drawable.GradientDrawable import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.Log import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -28,6 +27,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 import java.time.LocalDate import java.time.LocalTime +import kotlin.math.floor class CalWeekFragment : Fragment() { override fun onCreateView( @@ -237,7 +237,7 @@ class DayFragment(private val i: Int) : Fragment() { val customFont = context?.let { ResourcesCompat.getFont(it, R.font.roboto) } val scrollView = ScrollView(requireContext()) - scrollView.scrollBarStyle = R.style.Theme_Solen_Scrollbar + scrollView.scrollBarStyle = R.style.Theme_Solon_Scrollbar val dayLayout = LinearLayout(requireContext()) dayLayout.orientation = LinearLayout.VERTICAL @@ -391,7 +391,7 @@ class DayFragment(private val i: Int) : Fragment() { val subjectPlaceTextView = TextView(requireContext()) subjectPlaceTextView.typeface = customFont - subjectPlaceTextView.text = subj.place + subjectPlaceTextView.text = subj.place.replace("-", "–") if (subj.free) { // underline subjectPlaceTextView.setTypeface(customFont, Typeface.ITALIC) @@ -429,7 +429,7 @@ class DayFragment(private val i: Int) : Fragment() { timeTeacherLayout.gravity = Gravity.CENTER_VERTICAL val subjectTimeTextView = TextView(requireContext()) - val subjectTimeString = subj.start + " - " + subj.end + val subjectTimeString = subj.start + "–" + subj.end subjectTimeTextView.typeface = customFont subjectTimeTextView.text = subjectTimeString subjectTimeTextView.textSize = 20f @@ -446,7 +446,7 @@ class DayFragment(private val i: Int) : Fragment() { subjectTeacherTextView.text = subj.teacher subjectTeacherTextView.typeface = customFont subjectTeacherTextView.textSize = 12f - subjectTeacherTextView.setPadding(10, 0, 20, 10) + subjectTeacherTextView.setPadding(0, 0, 20, 10) subjectTeacherTextView.textAlignment = View.TEXT_ALIGNMENT_TEXT_END subjectTeacherTextView.setTextColor( ContextCompat.getColor( @@ -457,7 +457,7 @@ class DayFragment(private val i: Int) : Fragment() { timeTeacherLayout.addView(subjectTimeTextView) if (subj.name != "Volno") { - subjectTimeTextView.setPadding(10, 10, 20, 0) + subjectTimeTextView.setPadding(0, 10, 20, 0) timeTeacherLayout.addView(subjectTeacherTextView) } timeTeacherLayout.setPadding(0, 0, 10, 0) @@ -518,7 +518,12 @@ class DayFragment(private val i: Int) : Fragment() { LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT ) - subjectLayoutParams.setMargins(20, 0, 20, 0) + subjectLayoutParams.setMargins( + resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4, + 0, + resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4, + 0 + ) subjectLayout.layoutParams = subjectLayoutParams if (prevSubj.name != subj.name && i != 0) { @@ -534,7 +539,7 @@ class DayFragment(private val i: Int) : Fragment() { subjectSeparator.setImageResource(R.drawable.squiggly_line_100_yellow_transparent_one_color_edge) subjectSeparator.layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, - Util.pixelsToDp(20, resources) + resources.getDimensionPixelSize(R.dimen.cal_week_separator_height) ) dayLayout.addView(subjectSeparator) @@ -602,7 +607,12 @@ class DayFragment(private val i: Int) : Fragment() { LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT ) - endParams.setMargins(20, 0, 20, 0) + endParams.setMargins( + resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4, + 0, + resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/4, + 0 + ) dayEndTextView.layoutParams = endParams if (day.subjects[0].substitute != "2") { diff --git a/mobile/app/src/main/java/com/odweta/solon/MarkDetailsFragment.kt b/mobile/app/src/main/java/com/odweta/solon/MarkDetailsFragment.kt index b1d4100..d68eb67 100644 --- a/mobile/app/src/main/java/com/odweta/solon/MarkDetailsFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/MarkDetailsFragment.kt @@ -15,6 +15,7 @@ import android.view.View import android.view.ViewGroup import android.view.Window import android.widget.LinearLayout +import android.widget.ScrollView import android.widget.Space import android.widget.TableLayout import android.widget.TableRow @@ -31,13 +32,15 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: private lateinit var gestureDetector: GestureDetector private var initialTouchY: Float = 0f private val swipeThreshold = 100 - private val swipeVelocityThreshold = 100 private lateinit var rootLayout: LinearLayout + private lateinit var scrollView: ScrollView + private var isScrollingToDismissEnabled = false + private var isScrollingToTop = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set the style so the dialog is fullscreen - setStyle(STYLE_NORMAL, R.style.Theme_Solen) + setStyle(STYLE_NORMAL, R.style.Theme_Solon) } override fun onCreateView( @@ -64,10 +67,25 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: return view } + @SuppressLint("ClickableViewAccessibility") private fun gestures(view: View) { // Set up GestureDetector gestureDetector = GestureDetector(requireContext(), SwipeGestureListener()) + scrollView = view.findViewById(R.id.mark_detail_scroll) + scrollView.scrollY = 0 + + scrollView.setOnTouchListener { _, event -> + scrollView.performClick() + gestureDetector.onTouchEvent(event) + // Return false to allow normal scrolling + false + } + + scrollView.viewTreeObserver.addOnScrollChangedListener { + isScrollingToTop = !scrollView.canScrollVertically(-1) + } + // Attach the gesture detector to the root layout view.setOnTouchListener { _, event -> view.performClick() @@ -75,7 +93,32 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: } } - // TODO: MAKE CONSTRUCT() + inner class SwipeGestureListener : GestureDetector.SimpleOnGestureListener() { + override fun onDown(e: MotionEvent): Boolean { + // Reset the flag when the user touches the screen + isScrollingToDismissEnabled = false + return true + } + + override fun onFling( + e1: MotionEvent?, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { + val diffY = e1?.let { e2.y - it.y } ?: 0f + if (diffY > swipeThreshold) { + // Swiped down + if (isScrollingToTop) { + // Enable dismissing only if scrolled to top + isScrollingToDismissEnabled = true + dismiss() + } + } + return false + } + } + @SuppressLint("SetTextI18n", "ClickableViewAccessibility") private fun construct() { /* show all the marks in separate views with yet another onclick listener for even more @@ -88,33 +131,6 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: // FL == FrameLayout // SV == ScrollView - /*val bgColor = if (!marks.contains(Mark().none())) { - Util.getAvgBgColor(requireContext(), Util.avg(marks)) - } else { - ContextCompat.getColor( - requireContext(), - R.color.pastel_orange - ) - } - val bgStroke = if (!marks.contains(Mark().none())) { - Util.getAvgStrokeColor(requireContext(), Util.avg(marks)) - } else { - ContextCompat.getColor( - requireContext(), - R.color.pastel_orange - ) - } - val bg = GradientDrawable() - bg.setColor(bgColor) - bg.setStroke(10, bgStroke) - bg.cornerRadius = resources.getDimension(R.dimen.corner_radius)*/ - - /* subject: - * desc: - * date: - * weight: - */ - val font = context?.let { ResourcesCompat.getFont(it, R.font.roboto) } val gradeTV = TextView(requireContext()) gradeTV.text = marks[idx!!].grade @@ -123,26 +139,25 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: gradeTV.background = Util.getAvgDrawable(requireContext(), marks[idx!!].grade) gradeTV.gravity = Gravity.CENTER val pr = LinearLayout.LayoutParams( - 300, - 300 + Util.getDisplayWidth(requireContext())/3, + Util.getDisplayWidth(requireContext())/3 ) - pr.setMargins(0, 50, 0, 50) + pr.setMargins(0, 100, 0, 100) gradeTV.layoutParams = pr gradeTV.setPadding(20, 20, 20, 20) gradeTV.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) - val subjectDesc = makeTV("Předmět: ", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) - val subjectTV = makeTV(Vars.subjectNameMap[marks[idx!!].subject] ?: marks[idx!!].subject, Typeface.ITALIC, 22f) + val subjectDesc = makeTV("Předmět:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) + val subjectTV = makeTV(Vars.subjectNameMap[marks[idx!!].subject] ?: marks[idx!!].subject, Typeface.NORMAL, 22f) - val descDesc = makeTV("Popis: ", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) - val descTV = makeTV(marks[idx!!].theme, Typeface.ITALIC, 22f) + val descDesc = makeTV("Popis:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) + val descTV = makeTV(marks[idx!!].theme, Typeface.NORMAL, 22f) - val dateDesc = makeTV("Datum: ", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) - val dateTV = makeTV(getDate(marks[idx!!].date), Typeface.ITALIC, 22f) - - val weightDesc = makeTV("Váha: ", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) - val weightTV = makeTV(marks[idx!!].weight, Typeface.ITALIC, 22f) + val dateDesc = makeTV("Datum:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) + val dateTV = makeTV(getDate(marks[idx!!].date), Typeface.NORMAL, 22f) + val weightDesc = makeTV("Váha:", Typeface.BOLD, Util.getDisplayWidth(requireContext())/3) + val weightTV = makeTV(marks[idx!!].weight, Typeface.NORMAL, 22f) val subjectRow = makeRow(subjectDesc, subjectTV) val descRow = makeRow(descDesc, descTV) @@ -169,6 +184,7 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: val tv = TextView(requireContext()) tv.text = text tv.textSize = 25f + tv.textAlignment = View.TEXT_ALIGNMENT_CENTER tv.setPadding(40, 20, 20, 20) tv.setTypeface(font, typeface) tv.setTextColor( @@ -181,9 +197,7 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: width, LinearLayout.LayoutParams.MATCH_PARENT ) - p.setMargins(20, 60, 20, 20) tv.layoutParams = p - tv.background = ResourcesCompat.getDrawable(resources, R.drawable.button_background, null)!! tv.gravity = Gravity.CENTER_VERTICAL tv.ellipsize = null tv.setHorizontallyScrolling(false) @@ -196,13 +210,13 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: val tv = TextView(requireContext()) tv.text = text tv.textSize = size + tv.textAlignment = View.TEXT_ALIGNMENT_CENTER tv.setPadding(40, 20, 20, 20) - tv.background = ResourcesCompat.getDrawable(resources, R.drawable.pastel_yellow_no_stroke, null)!! val p = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT ) - p.setMargins(20, 0, 20, 0) + p.gravity = Gravity.CENTER tv.layoutParams = p tv.setTypeface(font, typeface) tv.setTextColor( @@ -221,42 +235,53 @@ class MarkDetailsFragment(private val marks: MutableList, private var idx: private fun makeRow(left: TextView, right: TextView): LinearLayout { val ll = LinearLayout(requireContext()) ll.orientation = LinearLayout.VERTICAL + ll.gravity = Gravity.CENTER_HORIZONTAL + val p = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + p.setMargins(20, 0, 20, 0) + ll.layoutParams = p + ll.background = ResourcesCompat.getDrawable(resources, R.drawable.pastel_yellow_no_stroke, null)!! ll.addView(left) ll.addView(right) - return ll + val otherLL = LinearLayout(requireContext()) + otherLL.orientation = LinearLayout.VERTICAL + otherLL.addView(createSpace()) + + val root = LinearLayout(requireContext()) + root.orientation = LinearLayout.VERTICAL + root.layoutParams = LinearLayout.LayoutParams( + Util.getDisplayWidth(requireContext())-40, + LinearLayout.LayoutParams.MATCH_PARENT + ) + + root.addView(ll) + root.addView(otherLL) + + return root + } + + private fun createSpace(): Space { + val space = Space(requireContext()) + space.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + resources.getDimensionPixelSize(R.dimen.cal_week_separator_height) + ) + + return space } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) - dialog.window?.attributes?.windowAnimations = R.style.Theme_Solen_DialogAnimation + dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog.setCancelable(true) return dialog } - inner class SwipeGestureListener : GestureDetector.SimpleOnGestureListener() { - override fun onDown(e: MotionEvent): Boolean { - return true - } - - override fun onFling( - e1: MotionEvent?, - e2: MotionEvent, - velocityX: Float, - velocityY: Float - ): Boolean { - val diffY = e1?.let { e2.y.minus(it.y) } ?: 0f - if (diffY > swipeThreshold && abs(velocityY) > swipeVelocityThreshold) { - // Swiped down - dismiss() - return true - } - return false - } - } - @Suppress("SameReturnValue") private fun handleTouchEvent(event: MotionEvent): Boolean { when (event.action) { diff --git a/mobile/app/src/main/java/com/odweta/solon/MarksFragment.kt b/mobile/app/src/main/java/com/odweta/solon/MarksFragment.kt index 2489587..58c3099 100644 --- a/mobile/app/src/main/java/com/odweta/solon/MarksFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/MarksFragment.kt @@ -66,6 +66,8 @@ class MarksFragment : Fragment() { root.addView(Vars.marks[subject]?.let { createMarkView(it) }) if (i < Vars.marks.keys.size-1) { root.addView(createSpace()) + } else { + root.addView(createSpace2()) } } @@ -83,6 +85,16 @@ class MarksFragment : Fragment() { return space } + private fun createSpace2(): Space { + val space = Space(requireContext()) + space.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + resources.getDimensionPixelSize(R.dimen.cal_week_separator_height)/2 + ) + + return space + } + private fun makeDR(marks: MutableList): GradientDrawable { val avgDrawable = GradientDrawable() avgDrawable.shape = GradientDrawable.RECTANGLE @@ -97,17 +109,6 @@ class MarksFragment : Fragment() { ) } ) - avgDrawable.setStroke( - 10, - if (!marks.contains(Mark().none())) { - Util.getAvgStrokeColor(requireContext(), Util.avg(marks)) - } else { - ContextCompat.getColor( - requireContext(), - R.color.pastel_orange - ) - } - ) return avgDrawable } @@ -183,9 +184,9 @@ class MarksFragment : Fragment() { } avgTextView.text = text avgTextView.textSize = 30f - avgTextView.setPadding(0, 20, 30, 20) + avgTextView.setPadding(20, 20, 20, 20) avgTextView.gravity = Gravity.CENTER // Center text vertically - avgTextView.setTypeface(customFont, Typeface.ITALIC) + avgTextView.setTypeface(customFont, Typeface.NORMAL) avgTextView.setTextColor( ContextCompat.getColor( requireContext(), @@ -219,18 +220,9 @@ class MarksFragment : Fragment() { val space = TextView(requireContext()) space.setPadding(0, 40, 0, 40) space.gravity = Gravity.CENTER_VERTICAL - space.text = " " + space.text = " " space.textSize = 22f - space.setTextColor( - if (!marks.contains(Mark().none())) { - Util.getAvgBgColor(requireContext(), Util.avg(marks)) - } else { - ContextCompat.getColor( - requireContext(), - R.color.pastel_orange_transparent - ) - } - ) + space.background = ResourcesCompat.getDrawable(resources, R.color.black, null) space.layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT @@ -260,7 +252,11 @@ class MarksFragment : Fragment() { R.color.white ) ) - marksTextView.gravity = Gravity.CENTER_VERTICAL + marksTextView.layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + marksTextView.gravity = Gravity.CENTER marksTextView.ellipsize = null marksTextView.setHorizontallyScrolling(false) diff --git a/mobile/app/src/main/java/com/odweta/solon/MarksOneSubjectFragment.kt b/mobile/app/src/main/java/com/odweta/solon/MarksOneSubjectFragment.kt index f18b4fa..8c11a86 100644 --- a/mobile/app/src/main/java/com/odweta/solon/MarksOneSubjectFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/MarksOneSubjectFragment.kt @@ -5,7 +5,6 @@ import android.app.Dialog import android.graphics.Typeface import android.graphics.drawable.GradientDrawable import android.os.Bundle -import android.util.Log import android.view.GestureDetector import android.view.Gravity import android.view.LayoutInflater @@ -14,11 +13,11 @@ import android.view.View import android.view.ViewGroup import android.view.Window import android.widget.LinearLayout +import android.widget.ScrollView import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.DialogFragment -import kotlin.math.abs // details for the marks of one subject @@ -26,11 +25,16 @@ import kotlin.math.abs class MarksOneSubjectFragment(private val marks: MutableList) : DialogFragment() { private var initialTouchY: Float = 0f private lateinit var rootLayout: LinearLayout + private lateinit var scrollView: ScrollView + private val swipeThreshold = 100 + private var isScrollingToDismissEnabled = false + private var isScrollingToTop = true + private lateinit var gestureDetector: GestureDetector override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set the style so the dialog is fullscreen - setStyle(STYLE_NORMAL, R.style.Theme_Solen) + setStyle(STYLE_NORMAL, R.style.Theme_Solon) } override fun onCreateView( @@ -53,7 +57,24 @@ class MarksOneSubjectFragment(private val marks: MutableList) : DialogFrag return view } + @SuppressLint("ClickableViewAccessibility") private fun gestures(view: View) { + gestureDetector = GestureDetector(requireContext(), SwipeGestureListener()) + + scrollView = view.findViewById(R.id.mark_details_scroll) + scrollView.scrollY = 0 + + scrollView.setOnTouchListener { _, event -> + scrollView.performClick() + gestureDetector.onTouchEvent(event) + // Return false to allow normal scrolling + false + } + + scrollView.viewTreeObserver.addOnScrollChangedListener { + isScrollingToTop = !scrollView.canScrollVertically(-1) + } + // Attach the gesture detector to the root layout view.setOnTouchListener { _, event -> view.performClick() @@ -162,7 +183,7 @@ class MarksOneSubjectFragment(private val marks: MutableList) : DialogFrag val markTV = TextView(requireContext()) markTV.text = mark.grade - markTV.setPadding(20, 20, 20, 20) + markTV.setPadding(30, 30, 30, 30) markTV.textSize = 30f markTV.setTextColor( ContextCompat.getColor( @@ -172,7 +193,7 @@ class MarksOneSubjectFragment(private val marks: MutableList) : DialogFrag ) val descTV = TextView(requireContext()) - descTV.text = "${mark.theme} " + descTV.text = mark.theme descTV.setPadding(20, 20, 20, 20) descTV.textSize = 20f descTV.setTextColor( @@ -185,7 +206,7 @@ class MarksOneSubjectFragment(private val marks: MutableList) : DialogFrag descTV.setOnTouchListener { _, event -> handleTouchEvent(event, i) } - descTV.setTypeface(customFont, Typeface.ITALIC) + descTV.setTypeface(customFont, Typeface.NORMAL) descTV.ellipsize = null descTV.setHorizontallyScrolling(false) @@ -206,7 +227,7 @@ class MarksOneSubjectFragment(private val marks: MutableList) : DialogFrag override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) - dialog.window?.attributes?.windowAnimations = R.style.Theme_Solen_DialogAnimation + dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog.setCancelable(true) @@ -246,4 +267,30 @@ class MarksOneSubjectFragment(private val marks: MutableList) : DialogFrag } return true } + + inner class SwipeGestureListener : GestureDetector.SimpleOnGestureListener() { + override fun onDown(e: MotionEvent): Boolean { + // Reset the flag when the user touches the screen + isScrollingToDismissEnabled = false + return true + } + + override fun onFling( + e1: MotionEvent?, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { + val diffY = e1?.let { e2.y - it.y } ?: 0f + if (diffY > swipeThreshold) { + // Swiped down + if (isScrollingToTop) { + // Enable dismissing only if scrolled to top + isScrollingToDismissEnabled = true + dismiss() + } + } + return false + } + } } \ No newline at end of file diff --git a/mobile/app/src/main/java/com/odweta/solon/MenuFragment.kt b/mobile/app/src/main/java/com/odweta/solon/MenuFragment.kt index 59e628c..0355ec1 100644 --- a/mobile/app/src/main/java/com/odweta/solon/MenuFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/MenuFragment.kt @@ -34,7 +34,7 @@ class MenuFragment : Fragment() { } signOut.setOnClickListener { - val builder: AlertDialog.Builder = AlertDialog.Builder(context, R.style.Theme_Solen_AlertDialog) + val builder: AlertDialog.Builder = AlertDialog.Builder(context, R.style.Theme_Solon_AlertDialog) builder .setView(LayoutInflater.from(context).inflate(R.layout.alert_dialog_sign_out, null)) .setPositiveButton("Ano") { dialog, _ -> diff --git a/mobile/app/src/main/java/com/odweta/solon/Network.kt b/mobile/app/src/main/java/com/odweta/solon/Network.kt index 8e02cde..8044d99 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Network.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Network.kt @@ -59,8 +59,6 @@ class Network { Vars.studentId = getStudentId() Vars.syid = getSYID() - //Log.d("SYID", Vars.syid) - Vars.marksUrl = "${Vars.apiBaseUrl}/v1/students/${Vars.studentId}/marks/list?SemesterId=${Vars.syid}" Vars.isAPISetUp = true @@ -78,7 +76,7 @@ class Network { syidNum += 1 } - // TODO: REMOVE LATER, JUST FOR DEBUG + // TODO: REMOVE LATER, JUST FOR DEBUG (NO MARKS IN THE CURRENT SEMESTER) syidNum -= 1 return syidStart.toString() + syidNum.toString() diff --git a/mobile/app/src/main/java/com/odweta/solon/SettingsFragment.kt b/mobile/app/src/main/java/com/odweta/solon/SettingsFragment.kt index ff0af71..1a96323 100644 --- a/mobile/app/src/main/java/com/odweta/solon/SettingsFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/SettingsFragment.kt @@ -27,7 +27,7 @@ class SettingsFragment : DialogFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setStyle(STYLE_NORMAL, R.style.Theme_Solen) + setStyle(STYLE_NORMAL, R.style.Theme_Solon) } inner class SwipeGestureListener : SimpleOnGestureListener() { @@ -88,7 +88,7 @@ class SettingsFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) - dialog.window?.attributes?.windowAnimations = R.style.Theme_Solen_DialogAnimation + dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation return dialog } } diff --git a/mobile/app/src/main/java/com/odweta/solon/SignInFragment.kt b/mobile/app/src/main/java/com/odweta/solon/SignInFragment.kt index eff045f..15fe472 100644 --- a/mobile/app/src/main/java/com/odweta/solon/SignInFragment.kt +++ b/mobile/app/src/main/java/com/odweta/solon/SignInFragment.kt @@ -23,7 +23,7 @@ class SignInFragment : DialogFragment() { super.onCreate(savedInstanceState) // Set up GestureDetector gestureDetector = GestureDetector(requireContext(), SwipeGestureListener()) - setStyle(STYLE_NORMAL, R.style.Theme_Solen) + setStyle(STYLE_NORMAL, R.style.Theme_Solon) } override fun onCreateView( @@ -43,7 +43,7 @@ class SignInFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { dialog = super.onCreateDialog(savedInstanceState) - dialog.window?.attributes?.windowAnimations = R.style.Theme_Solen_DialogAnimation + dialog.window?.attributes?.windowAnimations = R.style.Theme_Solon_DialogAnimation dialog.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog.setCancelable(true) diff --git a/mobile/app/src/main/java/com/odweta/solon/Util.kt b/mobile/app/src/main/java/com/odweta/solon/Util.kt index 1015240..138e5ad 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Util.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Util.kt @@ -127,10 +127,6 @@ class Util { return (pixels * resources.displayMetrics.density).toInt() } - /*fun dpToPixels(dp: Int, resources: Resources): Int { - return (dp / resources.displayMetrics.density).toInt() - }*/ - fun avg(marks: MutableList): String { var sum = 0f var len = 0 diff --git a/mobile/app/src/main/res/drawable/button_background.xml b/mobile/app/src/main/res/drawable/button_background.xml index dbcdc5b..c4a9fbd 100644 --- a/mobile/app/src/main/res/drawable/button_background.xml +++ b/mobile/app/src/main/res/drawable/button_background.xml @@ -5,5 +5,4 @@ - diff --git a/mobile/app/src/main/res/drawable/button_clicked_background.xml b/mobile/app/src/main/res/drawable/button_clicked_background.xml index a6fc358..ec73e18 100644 --- a/mobile/app/src/main/res/drawable/button_clicked_background.xml +++ b/mobile/app/src/main/res/drawable/button_clicked_background.xml @@ -5,5 +5,4 @@ - diff --git a/mobile/app/src/main/res/layout/activity_mark_details.xml b/mobile/app/src/main/res/layout/activity_mark_details.xml index bff995a..68f4017 100644 --- a/mobile/app/src/main/res/layout/activity_mark_details.xml +++ b/mobile/app/src/main/res/layout/activity_mark_details.xml @@ -5,15 +5,25 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:layout_constraintTop_toTopOf="parent"> + + + \ No newline at end of file diff --git a/mobile/app/src/main/res/layout/activity_marks_one_subject.xml b/mobile/app/src/main/res/layout/activity_marks_one_subject.xml index 41def3b..4000800 100644 --- a/mobile/app/src/main/res/layout/activity_marks_one_subject.xml +++ b/mobile/app/src/main/res/layout/activity_marks_one_subject.xml @@ -4,14 +4,24 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:layout_constraintTop_toTopOf="parent"> + + + \ No newline at end of file diff --git a/mobile/app/src/main/res/layout/activity_sign_in.xml b/mobile/app/src/main/res/layout/activity_sign_in.xml index ab8e036..daa0199 100644 --- a/mobile/app/src/main/res/layout/activity_sign_in.xml +++ b/mobile/app/src/main/res/layout/activity_sign_in.xml @@ -1,7 +1,6 @@ diff --git a/mobile/app/src/main/res/layout/radio_group_language.xml b/mobile/app/src/main/res/layout/radio_group_language.xml index e2a13a2..13b1ab2 100644 --- a/mobile/app/src/main/res/layout/radio_group_language.xml +++ b/mobile/app/src/main/res/layout/radio_group_language.xml @@ -15,7 +15,7 @@ android:text="@string/lang_cz" android:textColor="@color/white" android:layout_marginHorizontal="20dp" - android:theme="@style/Theme.Solen.RadioButton" /> + android:theme="@style/Theme.Solon.RadioButton" /> + android:theme="@style/Theme.Solon.RadioButton" /> \ No newline at end of file diff --git a/mobile/app/src/main/res/menu/menu_navbar.xml b/mobile/app/src/main/res/menu/menu_navbar.xml index 0936a77..7143130 100644 --- a/mobile/app/src/main/res/menu/menu_navbar.xml +++ b/mobile/app/src/main/res/menu/menu_navbar.xml @@ -19,64 +19,4 @@ android:layout_height="wrap_content" android:icon="@drawable/menu_240" android:title="@string/menu" /> - - - \ No newline at end of file + \ No newline at end of file diff --git a/mobile/app/src/main/res/values/dimens.xml b/mobile/app/src/main/res/values/dimens.xml index 19522fb..8a22b39 100644 --- a/mobile/app/src/main/res/values/dimens.xml +++ b/mobile/app/src/main/res/values/dimens.xml @@ -1,4 +1,4 @@ - 20dp + 16dp 8dp \ No newline at end of file diff --git a/mobile/app/src/main/res/values/strings.xml b/mobile/app/src/main/res/values/strings.xml index bd2061e..fd76659 100644 --- a/mobile/app/src/main/res/values/strings.xml +++ b/mobile/app/src/main/res/values/strings.xml @@ -14,7 +14,7 @@ Přehlednost je podstata. Vylepšená verze mobilní aplikace ŠkolaOnLine Odweta - Vývoj: + Vývoj: Zdrojový kód Uživatelské jméno Heslo diff --git a/mobile/app/src/main/res/values/themes.xml b/mobile/app/src/main/res/values/themes.xml index eaceeff..ad199f2 100644 --- a/mobile/app/src/main/res/values/themes.xml +++ b/mobile/app/src/main/res/values/themes.xml @@ -1,6 +1,6 @@ - - - - - - - \ No newline at end of file diff --git a/mobile/app/src/main/res/xml/data_extraction_rules.xml b/mobile/app/src/main/res/xml/data_extraction_rules.xml index 9ee9997..6cebe05 100644 --- a/mobile/app/src/main/res/xml/data_extraction_rules.xml +++ b/mobile/app/src/main/res/xml/data_extraction_rules.xml @@ -5,7 +5,7 @@ --> - diff --git a/mobile/settings.gradle.kts b/mobile/settings.gradle.kts index 388e18a..04a4c96 100644 --- a/mobile/settings.gradle.kts +++ b/mobile/settings.gradle.kts @@ -15,5 +15,5 @@ dependencyResolutionManagement { } } -rootProject.name = "Solen" +rootProject.name = "Solon" include(":app") \ No newline at end of file diff --git a/release/solon-v0.7.apk b/release/solon.apk similarity index 74% rename from release/solon-v0.7.apk rename to release/solon.apk index 5fb6fbf..21af7a2 100644 Binary files a/release/solon-v0.7.apk and b/release/solon.apk differ