Login page now disallows visiting a domain other than the school's login page to prevent a successful login from another domain allowing access to the app's content

This commit is contained in:
Deniz Düzgören
2019-09-23 17:23:59 +02:00
parent 088aa90aa4
commit fd40115549
21 changed files with 328 additions and 295 deletions
@@ -11,29 +11,33 @@ import com.denizd.substitutionplan.models.Colour
import com.denizd.substitutionplan.R import com.denizd.substitutionplan.R
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
internal class ColourAdapter(private var mColours: List<Colour>, onClickListener: OnClickListener) : RecyclerView.Adapter<ColourAdapter.ColourViewHolder>() { /**
* Adapter class used for displaying the courses as well as their associated colours in the
* customisation dialog. Used in SettingsFragment.kt
*
* @param colours a list of all courses and their associated colour
* @param onClickListener a reference to an OnClickListener
*/
internal class ColourAdapter(private var colours: List<Colour>, private val onClickListener: OnClickListener) : RecyclerView.Adapter<ColourAdapter.ColourViewHolder>() {
private val mOnClickListener = onClickListener internal class ColourViewHolder(view: View, private val clickListener: OnClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
class ColourViewHolder(view: View, clickListener: OnClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
val title: TextView = view.findViewById(R.id.item_text) val title: TextView = view.findViewById(R.id.item_text)
var image: ImageView = view.findViewById(R.id.item_image) var image: ImageView = view.findViewById(R.id.item_image)
val titleNoLang: TextView = view.findViewById(R.id.item_text_no_lang) val titleNoLang: TextView = view.findViewById(R.id.item_text_no_lang)
val cardView: MaterialCardView = view.findViewById(R.id.cardView) val cardView: MaterialCardView = view.findViewById(R.id.cardView)
private val mClickListener = clickListener
init { view.setOnClickListener(this) } init { view.setOnClickListener(this) }
override fun onClick(v: View?) { mClickListener.onClick(adapterPosition, title.text.toString(), titleNoLang.text.toString()) } override fun onClick(v: View?) { clickListener.onClick(adapterPosition, title.text.toString(), titleNoLang.text.toString()) }
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ColourViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ColourViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false) val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return ColourViewHolder(v, mOnClickListener) return ColourViewHolder(v, onClickListener)
} }
override fun onBindViewHolder(holder: ColourViewHolder, position: Int) { override fun onBindViewHolder(holder: ColourViewHolder, position: Int) {
val currentItem = mColours[position] val currentItem = colours[position]
holder.title.text = currentItem.title holder.title.text = currentItem.title
holder.titleNoLang.text = currentItem.titleNoLang holder.titleNoLang.text = currentItem.titleNoLang
@@ -54,7 +58,7 @@ internal class ColourAdapter(private var mColours: List<Colour>, onClickListener
holder.title.setTextColor(ContextCompat.getColor(holder.title.context, textColor)) holder.title.setTextColor(ContextCompat.getColor(holder.title.context, textColor))
} }
override fun getItemCount(): Int = mColours.size override fun getItemCount(): Int = colours.size
internal interface OnClickListener { internal interface OnClickListener {
fun onClick(position: Int, title: String, titleNoLang: String) fun onClick(position: Int, title: String, titleNoLang: String)
@@ -5,39 +5,38 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import java.util.ArrayList
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.denizd.substitutionplan.models.Food import com.denizd.substitutionplan.models.Food
import com.denizd.substitutionplan.R import com.denizd.substitutionplan.R
internal class FoodAdapter(food: ArrayList<Food>) : RecyclerView.Adapter<FoodAdapter.CardViewHolder>() { /**
private var mFood: List<Food>? = null * Adapter class used in FoodFragment.kt to display the food menu to the user
*
* @param foods a list of all food entries on the menu
*/
internal class FoodAdapter(private var foods: List<Food>) : RecyclerView.Adapter<FoodAdapter.FoodViewHolder>() {
class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { internal class FoodViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var mFood: TextView = itemView.findViewById(R.id.cardInfoText) var text: TextView = itemView.findViewById(R.id.cardInfoText)
} }
init { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FoodViewHolder {
mFood = food
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.just_a_card, parent, false) val v = LayoutInflater.from(parent.context).inflate(R.layout.just_a_card, parent, false)
return CardViewHolder(v) return FoodViewHolder(v)
} }
override fun onBindViewHolder(holder: CardViewHolder, position: Int) { override fun onBindViewHolder(holder: FoodViewHolder, position: Int) {
val currentItem = mFood!![position] val currentItem = foods[position]
holder.mFood.text = currentItem.food holder.text.text = currentItem.food
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return mFood!!.size return foods.size
} }
fun setFood(food: List<Food>) { fun setFood(foods: List<Food>) {
mFood = food this.foods = foods
notifyDataSetChanged() notifyDataSetChanged()
} }
} }
@@ -10,40 +10,39 @@ import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.models.Ringtone import com.denizd.substitutionplan.models.Ringtone
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
internal class RingtoneAdapter(private var _ringtones: List<Ringtone>, onClickListener: OnClickListener) : RecyclerView.Adapter<RingtoneAdapter.RingtoneViewHolder>() { /**
* Adapter class used for displaying all available notification ringtones to the user up to
* Android version 7 (Nougat). Used in SettingsFragment.kt
*
* @param ringtones a list of all ringtones including their URI
* @param onClickListener a reference to an OnClickListener to set the ringtone
*/
internal class RingtoneAdapter(private val ringtones: List<Ringtone>, private val onClickListener: OnClickListener) : RecyclerView.Adapter<RingtoneAdapter.RingtoneViewHolder>() {
private val _onClickListener = onClickListener internal class RingtoneViewHolder(view: View, private val clickListener: OnClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
class RingtoneViewHolder(view: View, clickListener: OnClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
val name: TextView = view.findViewById(R.id.item_text) val name: TextView = view.findViewById(R.id.item_text)
val uri: TextView = view.findViewById(R.id.item_text_no_lang) val uri: TextView = view.findViewById(R.id.item_text_no_lang)
val cardView: MaterialCardView = view.findViewById(R.id.cardView) val cardView: MaterialCardView = view.findViewById(R.id.cardView)
private val _clickListener = clickListener
init { view.setOnClickListener(this) } init { view.setOnClickListener(this) }
override fun onClick(v: View?) { _clickListener.onRingtoneClick(adapterPosition, name.text.toString(), uri.text.toString()) } override fun onClick(v: View?) { clickListener.onRingtoneClick(adapterPosition, name.text.toString(), uri.text.toString()) }
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RingtoneViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RingtoneViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false) val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return RingtoneViewHolder( return RingtoneViewHolder(v, onClickListener)
v,
_onClickListener
)
} }
override fun onBindViewHolder(holder: RingtoneViewHolder, position: Int) { override fun onBindViewHolder(holder: RingtoneViewHolder, position: Int) {
val currentItem = _ringtones[position] val currentItem = ringtones[position]
holder.name.text = currentItem.name holder.name.text = currentItem.name
holder.uri.text = currentItem.uri holder.uri.text = currentItem.uri
holder.cardView.setCardBackgroundColor(ContextCompat.getColor(holder.cardView.context, holder.cardView.setCardBackgroundColor(ContextCompat.getColor(holder.cardView.context, R.color.colorBackground))
R.color.colorBackground
))
} }
override fun getItemCount(): Int = _ringtones.size override fun getItemCount(): Int = ringtones.size
internal interface OnClickListener { internal interface OnClickListener {
fun onRingtoneClick(position: Int, name: String, uri: String) fun onRingtoneClick(position: Int, name: String, uri: String)
@@ -1,6 +1,7 @@
package com.denizd.substitutionplan.adapters package com.denizd.substitutionplan.adapters
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import android.text.SpannableString import android.text.SpannableString
@@ -16,18 +17,20 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.denizd.substitutionplan.data.HelperFunctions import com.denizd.substitutionplan.data.HelperFunctions
import com.denizd.substitutionplan.R import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import java.util.* import java.util.*
internal class CardAdapter(private var mSubst: List<Subst>, private val prefs: SharedPreferences) : RecyclerView.Adapter<CardAdapter.CardViewHolder>() { /**
* Adapter class for the Recycler View used to display the substitution plan in
* PlanFragment.kt and its subclasses
*
* @param substitutions a list of all substitutions of data type Substitution
* @param prefs a reference to the app's Shared Preferences used to get user-set colours
*/
internal class SubstitutionAdapter(private var substitutions: List<Substitution>, private val prefs: SharedPreferences) : RecyclerView.Adapter<SubstitutionAdapter.CardViewHolder>() {
private var colour = 0 internal class CardViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
private var colourString = ""
private var colorCheck = ""
private val cancellations = arrayOf("eigenverantwortliches arbeiten", "entfall", "entfällt", "fällt aus", "freisetzung", "vtr. ohne lehrer")
class CardViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
var iconView: ImageView = view.findViewById(R.id.iconView) var iconView: ImageView = view.findViewById(R.id.iconView)
var group: TextView = view.findViewById(R.id.group) var group: TextView = view.findViewById(R.id.group)
@@ -38,16 +41,16 @@ internal class CardAdapter(private var mSubst: List<Subst>, private val prefs: S
var additional: TextView = view.findViewById(R.id.additional) var additional: TextView = view.findViewById(R.id.additional)
var teacher: TextView = view.findViewById(R.id.teacher) var teacher: TextView = view.findViewById(R.id.teacher)
var spacer: TextView = view.findViewById(R.id.spacer) var spacer: TextView = view.findViewById(R.id.spacer)
var card: MaterialCardView = view.findViewById(R.id.planCard) var card: MaterialCardView = view.findViewById(R.id.planCard)
val context: Context = card.context
init { view.setOnClickListener(this) } init { view.setOnClickListener(this) }
override fun onClick(v: View?) { override fun onClick(v: View?) {
if (date.text.toString().length > 7 && date.text.toString().substring(3, 7) == "http") { if (date.text.toString().length > 7 && date.text.toString().substring(3, 7) == "http") {
try { try {
CustomTabsIntent.Builder().build().launchUrl(card.context, CustomTabsIntent.Builder().build().launchUrl(context,
Uri.parse(date.text.toString().substring(3))) Uri.parse(date.text.substring(3)))
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
Toast.makeText(card.context, card.context.getString(R.string.chrome_not_found), Toast.LENGTH_LONG).show() Toast.makeText(context, context.getString(R.string.chrome_not_found), Toast.LENGTH_LONG).show()
} }
} }
} }
@@ -59,12 +62,13 @@ internal class CardAdapter(private var mSubst: List<Subst>, private val prefs: S
} }
override fun onBindViewHolder(holder: CardViewHolder, position: Int) { override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
val currentItem = mSubst[position] val currentItem = substitutions[position]
var psa = false var psa = false
val strings = arrayOf(SpannableString(currentItem.group), SpannableString(currentItem.time), val strings = arrayOf(SpannableString(currentItem.group), SpannableString(currentItem.time),
SpannableString(currentItem.course), SpannableString(currentItem.room), SpannableString(currentItem.course), SpannableString(currentItem.room),
SpannableString(currentItem.teacher)) SpannableString(currentItem.teacher))
var cardBackgroundColour = 0 val cardBackgroundColour: Int
var colour = 0
for (string in strings) { for (string in strings) {
val questionMarkIndex = string.indexOf("?") val questionMarkIndex = string.indexOf("?")
@@ -76,11 +80,11 @@ internal class CardAdapter(private var mSubst: List<Subst>, private val prefs: S
val add = currentItem.additional.toLowerCase(Locale.ROOT) val add = currentItem.additional.toLowerCase(Locale.ROOT)
val type = currentItem.type.toLowerCase(Locale.ROOT) val type = currentItem.type.toLowerCase(Locale.ROOT)
if (add.isNotEmpty()) { if (add.isNotEmpty()) {
if (HelperFunctions.checkStringForArray(add, cancellations)) { if (HelperFunctions.checkStringForArray(add, HelperFunctions.cancellations)) {
strikeThrough(strings) strikeThrough(strings)
} }
} else { } else {
if (HelperFunctions.checkStringForArray(type, cancellations)) { if (HelperFunctions.checkStringForArray(type, HelperFunctions.cancellations)) {
strikeThrough(strings) strikeThrough(strings)
} }
} }
@@ -100,7 +104,7 @@ internal class CardAdapter(private var mSubst: List<Subst>, private val prefs: S
cardBackgroundColour = R.color.colorAccent cardBackgroundColour = R.color.colorAccent
View.GONE View.GONE
} else { } else {
colourString = getColourString(holder.course.text.toString()) val colourString = HelperFunctions.getColourString(holder.course.text.toString())
val colourPrefsInt = if (colourString.isNotEmpty()) { val colourPrefsInt = if (colourString.isNotEmpty()) {
prefs.getString("card$colourString", "") ?: "" prefs.getString("card$colourString", "") ?: ""
} else { } else {
@@ -150,58 +154,13 @@ internal class CardAdapter(private var mSubst: List<Subst>, private val prefs: S
holder.teacher.setTextColor(textColor) holder.teacher.setTextColor(textColor)
} }
override fun getItemCount(): Int = mSubst.size override fun getItemCount(): Int = substitutions.size
fun setSubst(subst: List<Subst>) { fun setSubst(substitutions: List<Substitution>) {
mSubst = subst this.substitutions = substitutions
notifyDataSetChanged() notifyDataSetChanged()
} }
private fun getColourString(course: String): String {
return try {
colorCheck = course.toLowerCase(Locale.ROOT).substring(0, 3)
when (colorCheck) {
"deu", "dep", "daz", "fda" -> "German"
"mat", "map" -> "Maths"
"eng", "enp", "ena" -> "English"
"spo", "spp", "spth" -> "PhysEd"
"pol", "pop" -> "Politics"
"dar", "dap" -> "Theatre"
"phy", "php" -> "Physics"
"bio", "bip", "nw1", "nw2", "nw3", "nw4" -> "Biology"
"che", "chp" -> "Chemistry"
"phi", "psp" -> "Philosophy"
"laa", "laf", "lat" -> "Latin"
"spa", "spf" -> "Spanish"
"fra", "frf", "frz" -> "French"
"inf" -> "CompSci"
"ges" -> "History"
"rel" -> "Religion"
"geg" -> "Geography"
"kun" -> "Arts"
"mus" -> "Music"
"tue" -> "Turkish"
"chi" -> "Chinese"
"gll" -> "GLL"
"wat" -> "WAT"
"för" -> "Forder"
"met", "wpb" -> "WP"
else -> ""
}
} catch (e: StringIndexOutOfBoundsException) {
try {
colorCheck = course.toLowerCase(Locale.ROOT).substring(0, 2)
when (colorCheck) {
"nw" -> "Biology"
"wp" -> "WP"
else -> ""
}
} catch (e2: StringIndexOutOfBoundsException) {
""
}
}
}
private fun strikeThrough(strings: Array<SpannableString>) { private fun strikeThrough(strings: Array<SpannableString>) {
for (i in 2..4) { strings[i].setSpan(StrikethroughSpan(), 0, strings[i].length, 0) } for (i in 2..4) { strings[i].setSpan(StrikethroughSpan(), 0, strings[i].length, 0) }
} }
@@ -18,7 +18,7 @@ import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.database.SubstRepository import com.denizd.substitutionplan.database.SubstRepository
import com.denizd.substitutionplan.activities.Main import com.denizd.substitutionplan.activities.Main
import com.denizd.substitutionplan.models.Food import com.denizd.substitutionplan.models.Food
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.jsoup.Jsoup import org.jsoup.Jsoup
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
@@ -57,7 +57,7 @@ internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boole
private val edit = prefs.edit() private val edit = prefs.edit()
private var currentTime = "" private var currentTime = ""
private var currentFoodTime = "" private var currentFoodTime = ""
private var substUrl = "https://djd4rkn355.github.io/subst.html" private var substUrl = "https://djd4rkn355.github.io/avh_substitutions.html"
private var foodUrl = "https://djd4rkn355.github.io/food.html" private var foodUrl = "https://djd4rkn355.github.io/food.html"
override fun doInBackground(vararg params: Void?): Void? { override fun doInBackground(vararg params: Void?): Void? {
@@ -149,7 +149,7 @@ internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boole
if (currentTime != prefs.getString("timeNew", "")) { if (currentTime != prefs.getString("timeNew", "")) {
val rows = doc.select("tr") val rows = doc.select("tr")
val paragraphs = doc.select("p") val paragraphs = doc.select("p")
val substArray = ArrayList<Subst>() val substArray = ArrayList<Substitution>()
val coursePreference = prefs.getString("courses", "") ?: "" val coursePreference = prefs.getString("courses", "") ?: ""
val classPreference = prefs.getString("classes", "") ?: "" val classPreference = prefs.getString("classes", "") ?: ""
@@ -169,7 +169,7 @@ internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boole
val row = rows[i] val row = rows[i]
val cols = row.select("th") val cols = row.select("th")
val subst = Subst( val subst = Substitution(
group = cols[0].text(), group = cols[0].text(),
date = cols[1].text(), date = cols[1].text(),
time = cols[2].text(), time = cols[2].text(),
@@ -237,11 +237,12 @@ internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boole
val notification = NotificationCompat.Builder(context, HelperFunctions.notificationChannelId) val notification = NotificationCompat.Builder(context, HelperFunctions.notificationChannelId)
.setStyle(NotificationCompat.DecoratedCustomViewStyle()) .setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout) .setCustomContentView(notificationLayout)
.setSmallIcon(R.drawable.ic_avhlogo)
.setContentIntent(openAppPending) .setContentIntent(openAppPending)
.setAutoCancel(true) .setAutoCancel(true)
.setColor(ContextCompat.getColor(context, R.color.colorAccent)) .setColor(ContextCompat.getColor(context, R.color.colorAccent))
notification.setSmallIcon(if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) R.drawable.ic_avh else R.drawable.ic_avhlogo)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
val sound = if ((prefs.getString("ringtoneUri", "") ?: "").isNotEmpty()) { val sound = if ((prefs.getString("ringtoneUri", "") ?: "").isNotEmpty()) {
Uri.parse(prefs.getString("ringtoneUri", "") ?: "") Uri.parse(prefs.getString("ringtoneUri", "") ?: "")
@@ -20,7 +20,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.checkSelfPermission import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.denizd.substitutionplan.R import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
import java.util.* import java.util.*
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -45,6 +45,12 @@ internal object HelperFunctions {
"brown", "grey", "pureWhite", "salmon", "tangerine", "banana", "flora", "spindrift", "sky", "orchid", "brown", "grey", "pureWhite", "salmon", "tangerine", "banana", "flora", "spindrift", "sky", "orchid",
"lavender", "carnation", "brown2", "pureBlack") "lavender", "carnation", "brown2", "pureBlack")
/**
* A list of all phrases used to describe that a course has been cancelled. Expand this
* if necessary. No further code changes required if this is expanded
*/
val cancellations = arrayOf("eigenverantwortliches arbeiten", "entfall", "entfällt", "fällt aus", "freisetzung", "vtr. ohne lehrer")
private val colourIntegers = intArrayOf( private val colourIntegers = intArrayOf(
0, 0,
R.color.bgRed, R.color.bgRed,
@@ -135,6 +141,60 @@ internal object HelperFunctions {
} }
} }
/**
* This function returns a string that is used to get the colour set for a specific course
* from Shared Preferences
*
* @param course the course abbreviation as a string
*
* @return the string used to store the course's colour in Shared Preferences
*/
fun getColourString(course: String): String {
var colorCheck: String
return try {
colorCheck = course.toLowerCase(Locale.ROOT).substring(0, 3)
when (colorCheck) {
"deu", "dep", "daz", "fda" -> "German"
"mat", "map" -> "Maths"
"eng", "enp", "ena" -> "English"
"spo", "spp", "spth" -> "PhysEd"
"pol", "pop" -> "Politics"
"dar", "dap" -> "Theatre"
"phy", "php" -> "Physics"
"bio", "bip", "nw1", "nw2", "nw3", "nw4" -> "Biology"
"che", "chp" -> "Chemistry"
"phi", "psp" -> "Philosophy"
"laa", "laf", "lat" -> "Latin"
"spa", "spf" -> "Spanish"
"fra", "frf", "frz" -> "French"
"inf" -> "CompSci"
"ges" -> "History"
"rel" -> "Religion"
"geg" -> "Geography"
"kun" -> "Arts"
"mus" -> "Music"
"tue" -> "Turkish"
"chi" -> "Chinese"
"gll" -> "GLL"
"wat" -> "WAT"
"för" -> "Forder"
"met", "wpb" -> "WP"
else -> ""
}
} catch (e: StringIndexOutOfBoundsException) {
try {
colorCheck = course.toLowerCase(Locale.ROOT).substring(0, 2)
when (colorCheck) {
"nw" -> "Biology"
"wp" -> "WP"
else -> ""
}
} catch (e2: StringIndexOutOfBoundsException) {
""
}
}
}
/** /**
* This function served as a way to transfer a deprecated method of storing user-defined * This function served as a way to transfer a deprecated method of storing user-defined
* course colours to a new method. It is not required otherwise, but still remains in * course colours to a new method. It is not required otherwise, but still remains in
@@ -163,17 +223,17 @@ internal object HelperFunctions {
* This function serves for checking whether a course on the substitution plan is relevant to * This function serves for checking whether a course on the substitution plan is relevant to
* the user and should be shown on their personal plan as well as in their notifications * the user and should be shown on their personal plan as well as in their notifications
* *
* @param subst the substitution item * @param substitution the substitution item
* @param coursePreference a string that represents the courses the user is enrolled in * @param coursePreference a string that represents the courses the user is enrolled in
* @param classPreference a string that represents the group the user is in * @param classPreference a string that represents the group the user is in
* @param psa decide whether or not any PSA items should be included in the filter * @param psa decide whether or not any PSA items should be included in the filter
* *
* @return true if the substitution is relevant to the user, false otherwise * @return true if the substitution is relevant to the user, false otherwise
*/ */
fun checkPersonalSubstitutions(subst: Subst, coursePreference: String, classPreference: String, psa: Boolean): Boolean { fun checkPersonalSubstitutions(substitution: Substitution, coursePreference: String, classPreference: String, psa: Boolean): Boolean {
val group = subst.group val group = substitution.group
val course = subst.course val course = substitution.course
if (psa && subst.date.isNotEmpty() && subst.date.substring(0, 3) == "psa") { if (psa && substitution.date.isNotEmpty() && substitution.date.substring(0, 3) == "psa") {
return true return true
} }
if (coursePreference.isEmpty() && classPreference.isNotEmpty()) { if (coursePreference.isEmpty() && classPreference.isNotEmpty()) {
@@ -1,8 +1,11 @@
package com.denizd.substitutionplan.data package com.denizd.substitutionplan.data
import android.graphics.Bitmap
import android.util.Log
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import java.lang.IllegalStateException
/** /**
* Class that extends WebViewClient to provide a function that checks the webpage's cookies to * Class that extends WebViewClient to provide a function that checks the webpage's cookies to
@@ -10,27 +13,40 @@ import android.webkit.WebViewClient
* *
* @param successListener a reference to the OnLoginSuccessListener implemented in an activity * @param successListener a reference to the OnLoginSuccessListener implemented in an activity
*/ */
internal class LoginWebViewClient(successListener: OnLoginSuccessListener) : WebViewClient() { internal class LoginWebViewClient(private val successListener: OnLoginSuccessListener) : WebViewClient() {
private val mSuccessListener = successListener /**
* onPageStarted has been overridden to prevent users from reaching a domain different from
* the school's login page
*/
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
if (url != "https://307.joomla.schule.bremen.de/index.php/component/users/profile?Itemid=171"
&& url != "https://307.joomla.schule.bremen.de/index.php/component/users/#top") {
reloadLoginPage(webView = view)
}
}
/** /**
* OnPageFinished has been overridden to check if the cookie "joomla_user_state=logged_in" * OnPageFinished has been overridden to check if the cookie "joomla_user_state=logged_in"
* exists, and returns true to the OnLoginSuccessListener * exists, and returns true to the OnLoginSuccessListener
*
* @param view a reference to the WebView
* @param url a reference to the url of the WebView
*/ */
override fun onPageFinished(view: WebView?, url: String?) { override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url) super.onPageFinished(view, url)
view?.scrollY = -4000 view?.scrollY = -10_000
val cookies = CookieManager.getInstance().getCookie(url) val cookies = CookieManager.getInstance().getCookie(url)
if (cookies.contains("joomla_user_state=logged_in")) { if (cookies.contains("joomla_user_state=logged_in")) {
mSuccessListener.onLoginSucceeded(true) successListener.onLoginSucceeded(true)
} }
} }
private fun reloadLoginPage(webView: WebView?) {
webView?.loadUrl("https://307.joomla.schule.bremen.de/index.php/component/users/#top")
}
/** /**
* The interface that provides onLoginSucceeded to the activity or fragment that implements it * The interface that provides onLoginSucceeded to the activity or fragment that implements it
*/ */
@@ -3,23 +3,23 @@ package com.denizd.substitutionplan.database
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.room.* import androidx.room.*
import com.denizd.substitutionplan.models.Food import com.denizd.substitutionplan.models.Food
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
@Dao @Dao
internal interface SubstDao { internal interface SubstDao {
// @get:Query("SELECT * FROM subst_table ORDER BY date ASC, `group` ASC, time ASC, priority DESC") // @get:Query("SELECT * FROM subst_table ORDER BY date ASC, `group` ASC, time ASC, priority DESC")
@get:Query("SELECT * FROM subst_table ORDER BY priority DESC") @get:Query("SELECT * FROM subst_table ORDER BY priority DESC")
val allSubst: LiveData<List<Subst>> val allSubstitutions: LiveData<List<Substitution>>
@Insert @Insert
fun insertSubst(subst: Subst) fun insertSubst(substitution: Substitution)
@Update @Update
fun updateSubst(subst: Subst) fun updateSubst(substitution: Substitution)
@Delete @Delete
fun deleteSubst(subst: Subst) fun deleteSubst(substitution: Substitution)
@Query("DELETE FROM subst_table") @Query("DELETE FROM subst_table")
fun deleteAllSubst() fun deleteAllSubst()
@@ -7,9 +7,9 @@ import androidx.room.RoomDatabase
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import com.denizd.substitutionplan.models.Food import com.denizd.substitutionplan.models.Food
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
@Database(entities = [Subst::class, Food::class], version = 7, exportSchema = false) @Database(entities = [Substitution::class, Food::class], version = 7, exportSchema = false)
internal abstract class SubstDatabase : RoomDatabase() { internal abstract class SubstDatabase : RoomDatabase() {
abstract fun substDao(): SubstDao abstract fun substDao(): SubstDao
@@ -4,32 +4,32 @@ import android.app.Application
import android.os.AsyncTask import android.os.AsyncTask
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
internal class SubstRepository(application: Application) { internal class SubstRepository(application: Application) {
private val substDao: SubstDao? private val substDao: SubstDao?
val allSubst: LiveData<List<Subst>>? val allSubstitutions: LiveData<List<Substitution>>?
init { init {
val database = val database =
SubstDatabase.getInstance(application) SubstDatabase.getInstance(application)
substDao = database?.substDao() substDao = database?.substDao()
allSubst = substDao?.allSubst allSubstitutions = substDao?.allSubstitutions
} }
fun insert(subst: Subst) { fun insert(substitution: Substitution) {
InsertSubstAsync(substDao).execute(subst) InsertSubstAsync(substDao).execute(substitution)
} }
fun deleteAllSubst() { fun deleteAllSubst() {
DeleteAllSubstAsync(substDao).execute() DeleteAllSubstAsync(substDao).execute()
} }
class InsertSubstAsync(substDao: SubstDao?) : AsyncTask<Subst, Void, Void>() { class InsertSubstAsync(substDao: SubstDao?) : AsyncTask<Substitution, Void, Void>() {
private val mSubstDao = substDao private val mSubstDao = substDao
override fun doInBackground(vararg substs: Subst): Void? { override fun doInBackground(vararg substitutions: Substitution): Void? {
mSubstDao?.insertSubst(substs[0]) mSubstDao?.insertSubst(substitutions[0])
return null return null
} }
} }
@@ -6,16 +6,16 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.denizd.substitutionplan.data.DataFetcher import com.denizd.substitutionplan.data.DataFetcher
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
internal class SubstViewModel(application: Application) : AndroidViewModel(application) { internal class SubstViewModel(application: Application) : AndroidViewModel(application) {
private val repository: SubstRepository = SubstRepository(application) private val repository: SubstRepository = SubstRepository(application)
val allSubst: LiveData<List<Subst>>? val allSubstitutions: LiveData<List<Substitution>>?
private val app = application private val app = application
init { init {
allSubst = repository.allSubst allSubstitutions = repository.allSubstitutions
} }
fun refresh(swipeRefreshLayout: SwipeRefreshLayout, rootView: View, refreshMenu: Boolean) { fun refresh(swipeRefreshLayout: SwipeRefreshLayout, rootView: View, refreshMenu: Boolean) {
@@ -35,18 +35,11 @@ internal class FoodFragment : Fragment(R.layout.food_layout) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val pullToRefresh = view.findViewById<SwipeRefreshLayout>(R.id.pullToRefresh) val pullToRefresh = view.findViewById<SwipeRefreshLayout>(R.id.pullToRefresh)
try {
recyclerView = view.findViewById(R.id.linear_food) recyclerView = view.findViewById(R.id.linear_food)
recyclerView.hasFixedSize() recyclerView.hasFixedSize()
recyclerView.layoutManager = GridLayoutManager(mContext, 1) recyclerView.layoutManager = GridLayoutManager(mContext, 1)
recyclerView.adapter = mAdapter recyclerView.adapter = mAdapter
try {
recyclerView.removeAllViews()
} catch (ignored: NullPointerException) {}
} catch (ignored: NullPointerException) {}
foodViewModel = ViewModelProviders.of(this).get(FoodViewModel::class.java) foodViewModel = ViewModelProviders.of(this).get(FoodViewModel::class.java)
foodViewModel.allFoods?.observe(this, Observer<List<Food>> { foodList -> foodViewModel.allFoods?.observe(this, Observer<List<Food>> { foodList ->
foodArrayList.clear() foodArrayList.clear()
@@ -3,14 +3,14 @@ package com.denizd.substitutionplan.fragments
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
internal class GeneralPlanFragment : PlanFragment() { internal class GeneralPlanFragment : PlanFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
substViewModel.allSubst?.observe(this, Observer<List<Subst>> { substitutions -> substViewModel.allSubstitutions?.observe(this, Observer<List<Substitution>> { substitutions ->
mAdapter.setSubst(substitutions) mAdapter.setSubst(substitutions)
recyclerView.scheduleLayoutAnimation() recyclerView.scheduleLayoutAnimation()
}) })
@@ -5,7 +5,7 @@ import android.view.View
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.denizd.substitutionplan.data.HelperFunctions import com.denizd.substitutionplan.data.HelperFunctions
import com.denizd.substitutionplan.R import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
internal class PersonalPlanFragment : PlanFragment() { internal class PersonalPlanFragment : PlanFragment() {
@@ -19,7 +19,7 @@ internal class PersonalPlanFragment : PlanFragment() {
val coursePreference = prefs.getString("courses", "") ?: "" val coursePreference = prefs.getString("courses", "") ?: ""
val classPreference = prefs.getString("classes", "") ?: "" val classPreference = prefs.getString("classes", "") ?: ""
substViewModel.allSubst?.observe(this, Observer<List<Subst>> { substitutions -> substViewModel.allSubstitutions?.observe(this, Observer<List<Substitution>> { substitutions ->
planCardList.clear() planCardList.clear()
personalPlanEmptyEmoticon.visibility = View.GONE personalPlanEmptyEmoticon.visibility = View.GONE
personalPlanEmptyText.visibility = View.GONE personalPlanEmptyText.visibility = View.GONE
@@ -15,15 +15,15 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.denizd.substitutionplan.* import com.denizd.substitutionplan.*
import com.denizd.substitutionplan.adapters.CardAdapter import com.denizd.substitutionplan.adapters.SubstitutionAdapter
import com.denizd.substitutionplan.database.SubstViewModel import com.denizd.substitutionplan.database.SubstViewModel
import com.denizd.substitutionplan.models.Subst import com.denizd.substitutionplan.models.Substitution
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
internal open class PlanFragment : Fragment(R.layout.plan) { internal open class PlanFragment : Fragment(R.layout.plan) {
lateinit var recyclerView: RecyclerView lateinit var recyclerView: RecyclerView
lateinit var mAdapter: CardAdapter lateinit var mAdapter: SubstitutionAdapter
var planCardList = ArrayList<Subst>() var planCardList = ArrayList<Substitution>()
lateinit var substViewModel: SubstViewModel lateinit var substViewModel: SubstViewModel
private lateinit var mContext: Context private lateinit var mContext: Context
lateinit var prefs: SharedPreferences lateinit var prefs: SharedPreferences
@@ -53,7 +53,7 @@ internal open class PlanFragment : Fragment(R.layout.plan) {
recyclerView = view.findViewById(R.id.linearRecycler) recyclerView = view.findViewById(R.id.linearRecycler)
recyclerView.hasFixedSize() recyclerView.hasFixedSize()
recyclerView.layoutManager = GridLayoutManager(mContext, getGridColumnCount(resources.configuration)) recyclerView.layoutManager = GridLayoutManager(mContext, getGridColumnCount(resources.configuration))
mAdapter = CardAdapter(planCardList, prefs) mAdapter = SubstitutionAdapter(planCardList, prefs)
recyclerView.adapter = mAdapter recyclerView.adapter = mAdapter
if (prefs.getInt("firstTimeOpening", 0) == 1) { if (prefs.getInt("firstTimeOpening", 0) == 1) {
@@ -188,69 +188,6 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
} }
} }
override fun onClick(v: View?) {
when (v?.id) {
R.id.chipHelpCourses -> createDialog(getString(R.string.courses_help_dialog_title), getString(R.string.courses_help_dialog_text))
R.id.chipHelpClasses -> createDialog(getString(R.string.grade_help_dialog_title), getString(
R.string.grade_help_dialog_text
))
R.id.btnCustomiseColours -> createColourDialog()
R.id.btnNoNotif -> createDialog(mContext.getString(R.string.no_notifications_title), mContext.getString(R.string.no_notifications_dialog_text))
R.id.btnCustomiseRingtone -> createRingtoneDialog()
R.id.btnWebsite -> {
try {
customTabsIntent.launchUrl(mContext, Uri.parse("http://307.joomla.schule.bremen.de"))
} catch (e: ActivityNotFoundException) {
makeToast(getString(R.string.chrome_not_found))
}
}
R.id.btnLicences -> {
createDialog(mContext.getString(R.string.licences_title), licences)
}
R.id.btnTerms -> {
createDialog("Terms & Conditions", termsAndConditions)
}
R.id.btnPrivacyP -> {
createDialog("Privacy Policy", privacyPolicy)
}
R.id.btnForceRefresh -> {
DataFetcher(
isPlan = true,
isMenu = true,
isJobService = false,
context = mContext,
application = activity!!.application,
parentView = view!!.rootView,
forced = true
).execute()
}
}
}
override fun onCheckedChanged(v: CompoundButton?, isChecked: Boolean) {
if (v?.isPressed == true) {
when (v.id) {
R.id.switchDisableGreeting -> {
prefs.edit().putBoolean("greeting", isChecked).apply()
}
R.id.switchNotifications -> {
prefs.edit().putBoolean("notif", isChecked).apply()
if (isChecked) {
subscribeToTopic(Topic.ANDROID)
} else {
unsubscribeFromTopic(Topic.ANDROID)
}
}
R.id.switchDefaultPlan -> {
prefs.edit().putBoolean("defaultPersonalised", isChecked).apply()
}
R.id.switchAutoRefresh -> {
prefs.edit().putBoolean("autoRefresh", isChecked).apply()
}
}
}
}
private fun createDialog(title: String, text: String) { private fun createDialog(title: String, text: String) {
val alertDialog = AlertDialog.Builder(mContext, val alertDialog = AlertDialog.Builder(mContext,
R.style.AlertDialog R.style.AlertDialog
@@ -262,7 +199,7 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
} }
private fun createColourDialog() { private fun createColourDialog() {
val colourCustomiserBuilder = AlertDialog.Builder(mContext, R.style.AlertDialog) val colourCustomisationDialogBuilder = AlertDialog.Builder(mContext, R.style.AlertDialog)
val dialogView = View.inflate(mContext, R.layout.recycler_dialog, null) val dialogView = View.inflate(mContext, R.layout.recycler_dialog, null)
val titleText = dialogView.findViewById<TextView>(R.id.empty_textviewtitle) val titleText = dialogView.findViewById<TextView>(R.id.empty_textviewtitle)
@@ -273,9 +210,9 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
layoutManager = GridLayoutManager(mContext, 1) layoutManager = GridLayoutManager(mContext, 1)
adapter = ColourAdapter(getColourList(), this@SettingsFragment) adapter = ColourAdapter(getColourList(), this@SettingsFragment)
} }
colourCustomiserBuilder.setView(dialogView) colourCustomisationDialogBuilder.setView(dialogView)
val colourCustomiserDialog = colourCustomiserBuilder.create() val colourCustomisationDialog = colourCustomisationDialogBuilder.create()
colourCustomiserDialog.show() colourCustomisationDialog.show()
} }
private fun getColourList(): ArrayList<Colour> { private fun getColourList(): ArrayList<Colour> {
@@ -371,15 +308,6 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
return alarms return alarms
} }
override fun onRingtoneClick(position: Int, name: String, uri: String) {
prefs.edit().apply {
putString("ringtoneName", name)
putString("ringtoneUri", uri)
}.apply()
setRingtoneText()
ringtoneCustomiserDialog.dismiss()
}
private fun setRingtoneText() { private fun setRingtoneText() {
currentRingtone.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { currentRingtone.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mContext.getString(R.string.pick_ringtone_desc_o) mContext.getString(R.string.pick_ringtone_desc_o)
@@ -393,36 +321,6 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
} }
} }
override fun onClick(position: Int, title: String, titleNoLang: String) {
val colourPickerBuilder = AlertDialog.Builder(mContext, R.style.AlertDialog)
val pickerDialogView = View.inflate(mContext, R.layout.empty_dialog, null)
val pickerTitleText = pickerDialogView.findViewById<TextView>(R.id.empty_textviewtitle)
val pickerLayout = pickerDialogView.findViewById<LinearLayout>(R.id.empty_linearlayout)
pickerTitleText.text = title
val picker = View.inflate(mContext, R.layout.bg_colour_picker, null)
pickerLayout.addView(picker)
colourPickerBuilder.setView(pickerDialogView)
val colourPickerDialog: AlertDialog = colourPickerBuilder.create()
val buttons = intArrayOf(R.id.def, R.id.red, R.id.orange, R.id.yellow, R.id.green,
R.id.teal, R.id.cyan, R.id.blue, R.id.purple, R.id.pink, R.id.brown, R.id.grey,
R.id.pureWhite, R.id.salmon, R.id.tangerine, R.id.banana, R.id.flora, R.id.spindrift,
R.id.sky, R.id.orchid, R.id.lavender, R.id.carnation, R.id.brown2, R.id.pureBlack)
val colours = HelperFunctions.colourNames
for (i2 in buttons.indices) {
picker.findViewById<MaterialButton>(buttons[i2]).setOnClickListener {
prefs.edit().putString("card$titleNoLang", colours[i2]).apply()
val recyclerViewState = colourRecycler.layoutManager?.onSaveInstanceState()
colourRecycler.adapter = ColourAdapter(getColourList(), this)
colourRecycler.layoutManager?.onRestoreInstanceState(recyclerViewState)
colourPickerDialog.dismiss()
}
}
colourPickerDialog.show()
}
private fun debugMenu(): Boolean { private fun debugMenu(): Boolean {
val alertDialog = AlertDialog.Builder(mContext, R.style.AlertDialog) val alertDialog = AlertDialog.Builder(mContext, R.style.AlertDialog)
val dialogView = View.inflate(mContext, R.layout.edittext_dialog, null) val dialogView = View.inflate(mContext, R.layout.edittext_dialog, null)
@@ -437,16 +335,14 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
) )
dialogButton.setOnClickListener { dialogButton.setOnClickListener {
when (dialogEditText.text.toString()) { when (dialogEditText.text.toString()) {
"_DIAGNOSTICS" -> { "_STATISTICS" -> {
val alertDialogDev = AlertDialog.Builder(mContext, R.style.AlertDialog) val alertDialogDev = AlertDialog.Builder(mContext, R.style.AlertDialog)
val devDialogView = View.inflate(mContext, R.layout.diagnostics_dialog, null) val devDialogView = View.inflate(mContext, R.layout.diagnostics_dialog, null)
val devDialogText = devDialogView.findViewById<TextView>(R.id.dialogtext) val devDialogText = devDialogView.findViewById<TextView>(R.id.dialogtext)
val resetLaunchBtn = devDialogView.findViewById<Button>(R.id.btnResetLaunch) val resetLaunchBtn = devDialogView.findViewById<Button>(R.id.btnResetLaunch)
val resetNotificationBtn = devDialogView.findViewById<Button>(R.id.btnResetNotif) val resetNotificationBtn = devDialogView.findViewById<Button>(R.id.btnResetNotif)
devDialogView.findViewById<TextView>(R.id.textviewtitle).text = getString( devDialogView.findViewById<TextView>(R.id.textviewtitle).text = getString(R.string.statistics_dialog_title)
R.string.diagnostics_dialog_title
)
devDialogText.text = getDiagnosticsText() devDialogText.text = getDiagnosticsText()
resetLaunchBtn.setOnClickListener { resetLaunchBtn.setOnClickListener {
@@ -462,15 +358,19 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
alertDialogDev.setView(devDialogView) alertDialogDev.setView(devDialogView)
alertDialogDev.show() alertDialogDev.show()
} }
"2018-04-20" -> { "2019-06-06" -> {
try { try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.youtube.com/watch?v=Jc2xfYuLWgE")) // Freak val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.youtube.com/watch?v=Jc2xfYuLWgE")) // 'Freak'
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setPackage("com.google.android.youtube") intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setPackage("com.google.android.youtube")
startActivity(intent) startActivity(intent)
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
makeToast(getString(R.string.youtube_not_found)) makeToast(getString(R.string.youtube_not_found))
} }
} }
"_LOGIN" -> {
prefs.edit().putBoolean("successful_login", false).apply()
makeToast("Login flag cleared")
}
"_FIRSTTIME" -> { "_FIRSTTIME" -> {
prefs.edit().putBoolean("firstTime", true).apply() prefs.edit().putBoolean("firstTime", true).apply()
makeToast("First time flag cleared") makeToast("First time flag cleared")
@@ -531,6 +431,115 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
private fun subscribeToTopic(topic: Topic) = FirebaseMessaging.getInstance().subscribeToTopic(topic.tag) private fun subscribeToTopic(topic: Topic) = FirebaseMessaging.getInstance().subscribeToTopic(topic.tag)
private fun unsubscribeFromTopic(topic: Topic) = FirebaseMessaging.getInstance().unsubscribeFromTopic(topic.tag) private fun unsubscribeFromTopic(topic: Topic) = FirebaseMessaging.getInstance().unsubscribeFromTopic(topic.tag)
override fun onClick(v: View?) {
when (v?.id) {
R.id.chipHelpCourses -> createDialog(getString(R.string.courses_help_dialog_title), getString(R.string.courses_help_dialog_text))
R.id.chipHelpClasses -> createDialog(getString(R.string.grade_help_dialog_title), getString(
R.string.grade_help_dialog_text
))
R.id.btnCustomiseColours -> createColourDialog()
R.id.btnNoNotif -> createDialog(mContext.getString(R.string.no_notifications_title), mContext.getString(R.string.no_notifications_dialog_text))
R.id.btnCustomiseRingtone -> createRingtoneDialog()
R.id.btnWebsite -> {
try {
customTabsIntent.launchUrl(mContext, Uri.parse("http://307.joomla.schule.bremen.de"))
} catch (e: ActivityNotFoundException) {
makeToast(getString(R.string.chrome_not_found))
}
}
R.id.btnLicences -> {
createDialog(mContext.getString(R.string.licences_title), licences)
}
R.id.btnTerms -> {
createDialog("Terms & Conditions", termsAndConditions)
}
R.id.btnPrivacyP -> {
createDialog("Privacy Policy", privacyPolicy)
}
R.id.btnForceRefresh -> {
DataFetcher(
isPlan = true,
isMenu = true,
isJobService = false,
context = mContext,
application = activity!!.application,
parentView = view!!.rootView,
forced = true
).execute()
}
}
}
override fun onRingtoneClick(position: Int, name: String, uri: String) {
prefs.edit().apply {
putString("ringtoneName", name)
putString("ringtoneUri", uri)
}.apply()
setRingtoneText()
ringtoneCustomiserDialog.dismiss()
}
/**
* OnClick method for colour customisation dialog
* @param position the position of the clicked item
* @param title the title of the clicked course
* @param titleNoLang the language-independent string used for storing the course's colour in
* Shared Preferences
*/
override fun onClick(position: Int, title: String, titleNoLang: String) {
val colourPickerBuilder = AlertDialog.Builder(mContext, R.style.AlertDialog)
val pickerDialogView = View.inflate(mContext, R.layout.empty_dialog, null)
val pickerTitleText = pickerDialogView.findViewById<TextView>(R.id.empty_textviewtitle)
val pickerLayout = pickerDialogView.findViewById<LinearLayout>(R.id.empty_linearlayout)
pickerTitleText.text = title
val picker = View.inflate(mContext, R.layout.bg_colour_picker, null)
pickerLayout.addView(picker)
colourPickerBuilder.setView(pickerDialogView)
val colourPickerDialog: AlertDialog = colourPickerBuilder.create()
val buttons = intArrayOf(R.id.def, R.id.red, R.id.orange, R.id.yellow, R.id.green,
R.id.teal, R.id.cyan, R.id.blue, R.id.purple, R.id.pink, R.id.brown, R.id.grey,
R.id.pureWhite, R.id.salmon, R.id.tangerine, R.id.banana, R.id.flora, R.id.spindrift,
R.id.sky, R.id.orchid, R.id.lavender, R.id.carnation, R.id.brown2, R.id.pureBlack)
val colours = HelperFunctions.colourNames
for (i2 in buttons.indices) {
picker.findViewById<MaterialButton>(buttons[i2]).setOnClickListener {
prefs.edit().putString("card$titleNoLang", colours[i2]).apply()
val recyclerViewState = colourRecycler.layoutManager?.onSaveInstanceState()
colourRecycler.adapter = ColourAdapter(getColourList(), this)
colourRecycler.layoutManager?.onRestoreInstanceState(recyclerViewState)
colourPickerDialog.dismiss()
}
}
colourPickerDialog.show()
}
override fun onCheckedChanged(v: CompoundButton?, isChecked: Boolean) {
if (v?.isPressed == true) {
when (v.id) {
R.id.switchDisableGreeting -> {
prefs.edit().putBoolean("greeting", isChecked).apply()
}
R.id.switchNotifications -> {
prefs.edit().putBoolean("notif", isChecked).apply()
if (isChecked) {
subscribeToTopic(Topic.ANDROID)
} else {
unsubscribeFromTopic(Topic.ANDROID)
}
}
R.id.switchDefaultPlan -> {
prefs.edit().putBoolean("defaultPersonalised", isChecked).apply()
}
R.id.switchAutoRefresh -> {
prefs.edit().putBoolean("autoRefresh", isChecked).apply()
}
}
}
}
private val licences = "Libraries:" + private val licences = "Libraries:" +
"\n • jsoup HTML parser © 2009-2018 Jonathan Hedley, licensed under the open source MIT Licence" + "\n • jsoup HTML parser © 2009-2018 Jonathan Hedley, licensed under the open source MIT Licence" +
"\n\nFont:" + "\n\nFont:" +
@@ -4,7 +4,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "subst_table") @Entity(tableName = "subst_table")
internal data class Subst( internal data class Substitution(
val group: String, val group: String,
val date: String, val date: String,
val time: String, val time: String,
+1 -1
View File
@@ -106,7 +106,7 @@
android:id="@+id/temporary" android:id="@+id/temporary"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="subst plan" android:text="substitution plan"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
-6
View File
@@ -14,10 +14,6 @@
android:layout_marginBottom="3dp" android:layout_marginBottom="3dp"
app:cardElevation="2dp"> app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/cardInfoText" android:id="@+id/cardInfoText"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -28,6 +24,4 @@
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"/> android:layout_marginBottom="16dp"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
+2 -2
View File
@@ -33,7 +33,7 @@
<string name="setup_successful">Viel Spaß!</string> <string name="setup_successful">Viel Spaß!</string>
<string name="greeting_title">Aktiviere Begrüßungen</string> <string name="greeting_title">Aktiviere Begrüßungen</string>
<string name="greeting_desc">Falls du möchtest, dass ich nett bin :)</string> <string name="greeting_desc">Falls du möchtest, dass ich nett bin :)</string>
<string name="licences_title">Lizenzen und Mitwirkende</string> <string name="licences_title">Lizenzen &amp; Mitwirkende</string>
<string name="licences_desc">Was die App ausmacht (Englisch)</string> <string name="licences_desc">Was die App ausmacht (Englisch)</string>
<string name="version">Versionsnummer</string> <string name="version">Versionsnummer</string>
<string name="privacy_policy_title">Datenschutzerklärung</string> <string name="privacy_policy_title">Datenschutzerklärung</string>
@@ -52,7 +52,7 @@
<string name="apply">Anwenden</string> <string name="apply">Anwenden</string>
<string name="invalid_code">Ungültiger Code</string> <string name="invalid_code">Ungültiger Code</string>
<string name="experimental_menu_dialog_title">Experimentalmenü</string> <string name="experimental_menu_dialog_title">Experimentalmenü</string>
<string name="diagnostics_dialog_title">Diagnosemenü</string> <string name="statistics_dialog_title">Statistikmenü</string>
<string name="enable_course_notifications">Kursbenachrichtigungen aktivieren</string> <string name="enable_course_notifications">Kursbenachrichtigungen aktivieren</string>
<string name="no_personal_substitutions">Kein Entfall für dich</string> <string name="no_personal_substitutions">Kein Entfall für dich</string>
<string name="customise_colours_title">Benutzerdefinierte Farben</string> <string name="customise_colours_title">Benutzerdefinierte Farben</string>
+2 -3
View File
@@ -10,7 +10,6 @@
<string name="personal">Personal</string> <string name="personal">Personal</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
<!-- for Settings fragment -->
<string name="enable_course_notifications_title">Enable course notifications</string> <string name="enable_course_notifications_title">Enable course notifications</string>
<string name="enable_course_notifications_desc">This does not affect broadcasts</string> <string name="enable_course_notifications_desc">This does not affect broadcasts</string>
@@ -39,7 +38,7 @@
<string name="greeting_title">Enable greeting</string> <string name="greeting_title">Enable greeting</string>
<string name="greeting_desc">If you want me to be nice :)</string> <string name="greeting_desc">If you want me to be nice :)</string>
<string name="licences_title">Licences and contributors</string> <string name="licences_title">Licences &amp; contributors</string>
<string name="licences_desc">What went into making this app</string> <string name="licences_desc">What went into making this app</string>
<string name="version">Version Number</string> <string name="version">Version Number</string>
@@ -59,7 +58,7 @@
<string name="courses_help_dialog_title">How to enter your courses</string> <string name="courses_help_dialog_title">How to enter your courses</string>
<string name="courses_help_dialog_text">You need to enter the course abbreviation for every course you want in your personal plan and want to be notified about. You can find them on your time table.\n\nHere\'s a few examples:\n\nMathematics in 5th grade (juniors) would be MAT.\n\nAdvanced English in E-Phase (seniors) for the English profile may be ENP1.\n\nBasic Biology in Q1 could be bio1.\n\nIn most cases, entering courses while in junior phase is redundant.\nSeparate multiple courses by spaces, commas are optional.\nCapitalisation is required.</string> <string name="courses_help_dialog_text">You need to enter the course abbreviation for every course you want in your personal plan and want to be notified about. You can find them on your time table.\n\nHere\'s a few examples:\n\nMathematics in 5th grade (juniors) would be MAT.\n\nAdvanced English in E-Phase (seniors) for the English profile may be ENP1.\n\nBasic Biology in Q1 could be bio1.\n\nIn most cases, entering courses while in junior phase is redundant.\nSeparate multiple courses by spaces, commas are optional.\nCapitalisation is required.</string>
<string name="experimental_menu_dialog_title">Experimental Menu</string> <string name="experimental_menu_dialog_title">Experimental Menu</string>
<string name="diagnostics_dialog_title">Diagnostics Menu</string> <string name="statistics_dialog_title">Statistics Menu</string>
<string name="experimental_menu_dialog_text">This menu is currently intended for testing only. Additional features may be added in the future. Feel free to try arbitrary codes.</string> <string name="experimental_menu_dialog_text">This menu is currently intended for testing only. Additional features may be added in the future. Feel free to try arbitrary codes.</string>
<string name="apply">Apply</string> <string name="apply">Apply</string>
<string name="invalid_code">Invalid code</string> <string name="invalid_code">Invalid code</string>