Code optimisations, using ArrayList#filter for performance improvements and code reductions, fixed notification channel not having been created before opening ringtone settings

This commit is contained in:
Deniz Düzgören
2019-08-31 16:12:36 +02:00
parent 8ee0288c46
commit f139f28137
10 changed files with 206 additions and 185 deletions
@@ -3,7 +3,6 @@ package com.denizd.substitutionplan
import android.app.*
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.media.RingtoneManager
import android.net.Uri
import android.os.AsyncTask
@@ -18,22 +17,24 @@ import com.google.android.material.snackbar.Snackbar
import org.jsoup.Jsoup
import kotlin.collections.ArrayList
class DataFetcher(isplan: Boolean, ismenu: Boolean, isjobservice: Boolean, context: Context, application: Application, parentview: View?) : AsyncTask<Void, Void, Void>() {
/**
* This class gets data from the djd4rkn355.github.io domain asynchronously and persists it in the
* database. By using booleans as parameters, network usage and speed can be improved as only
* requested data will be downloaded and processed.
*
* @param isPlan substitution plan will be downloaded and persisted in the database if true
* @param isMenu food menu will be downloaded and persisted in the database if true
* @param isJobService a notification will be prepared and sent if this and isPlan is true
*/
class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boolean, context: Context, application: Application, parentView: View?) : AsyncTask<Void, Void, Void>() {
private var jobservice = isjobservice
private var plan = isplan
private var menu = ismenu
/* booleans to improve speed and decrease network usage when fetching data by only grabbing the required HTML pages
"plan" = true is required to fetch the substitution data
"menu" = true is required to fetch the food menu
"jobservice" = true is required to send a notification
"jobservice" = true has no effect unless "plan" is also true
*/
private var jobService = isJobService
private var plan = isPlan
private var menu = isMenu
private var mContext = context
private var mApplication = application
private var mView = parentview
private var mView = parentView
private var priority = 200
private var notifText = ""
private var informational = ""
@@ -46,146 +47,21 @@ class DataFetcher(isplan: Boolean, ismenu: Boolean, isjobservice: Boolean, conte
override fun doInBackground(vararg params: Void?): Void? {
try {
if (prefs.getBoolean("testUrls", false)) {
substUrl = "https://djd4rkn355.github.io/subst_test.html"
foodUrl = "https://djd4rkn355.github.io/food_test.html"
}
if (menu) {
val docFood = Jsoup.connect(foodUrl).get()
currentFoodTime = docFood.select("h1")[0].text()
if (currentFoodTime != prefs.getString("timeFoodNew", "")) {
val foodRepository = FoodRepository(mApplication)
val foodElements = docFood.select("th")
foodRepository.deleteAll()
val indices = ArrayList<Int>()
val daysAndVon = arrayOf("Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "von")
for (i in 0 until foodElements.size) {
if (checkStringForArray(foodElements[i].text(), daysAndVon)) indices.add(i)
}
indices.add(foodElements.size)
for ((priority, l) in (0 until indices.size - 1).withIndex()) {
var s = ""
for (i2 in indices[l] until indices[l + 1]) {
if (s.isEmpty()) {
s = foodElements[i2].text()
} else {
s += "\n${foodElements[i2].text()}"
}
}
foodRepository.insert(Food(s, priority))
}
edit.putString("timeFoodNew", currentFoodTime).apply()
}
}
if (plan) {
val doc = Jsoup.connect(substUrl).get()
currentTime = doc.select("h1")[0].text()
if (currentTime != prefs.getString("timeNew", "")) {
val substRepo = SubstRepository(mApplication)
val rows = doc.select("tr")
val paragraphs = doc.select("p")
val coursePreference = prefs.getString("courses", "") ?: ""
val classPreference = prefs.getString("classes", "") ?: ""
for (i in 0 until paragraphs.size) {
if (i == 0) {
informational = paragraphs[i].text()
} else {
informational += "\n\n" + paragraphs[i].text()
}
}
edit.putString("informational", informational).apply()
substRepo.deleteAllSubst()
for (i in 0 until rows.size) {
val row = rows[i]
val cols = row.select("th")
val group = cols[0].text()
val course = cols[3].text()
val additional = cols[5].text()
substRepo.insert(Subst(group = group, date = cols[1].text(), time = cols[2].text(), course = course,
room = cols[4].text(), additional = additional, priority = priority))
priority--
if (jobservice && prefs.getBoolean("notif", true)) {
if (coursePreference.isEmpty() && classPreference.isNotEmpty()) {
if (group.isNotEmpty() && group != "") {
if (classPreference.contains(group) || group.contains(classPreference)) {
notifText += "${if (notifText.isNotEmpty()) ",\n" else ""}$course: ${if (additional.isNotEmpty()) additional else "---"}"
}
}
} else if (classPreference.isNotEmpty() && coursePreference.isNotEmpty()) {
if (group != "" && course != "") {
if (coursePreference.contains(course)) {
if (classPreference.contains(group) || group.contains(classPreference)) {
notifText += "${if (notifText.isNotEmpty()) ",\n" else ""}$course: ${if (additional.isNotEmpty()) additional else "---"}"
}
}
}
}
}
}
if (notifText.isNotEmpty()) {
val openApp = Intent(mContext, Main::class.java)
openApp.flags = Intent.FLAG_ACTIVITY_NEW_TASK
openApp.flags += Intent.FLAG_ACTIVITY_CLEAR_TASK
val openAppPending = PendingIntent.getActivity(mContext, 0, openApp, 0)
val notificationLayout = RemoteViews(mContext.packageName, R.layout.notification)
notificationLayout.setTextViewText(R.id.notification_title, mContext.getString(R.string.substitutionPlan))
notificationLayout.setTextViewText(R.id.notification_textview, notifText)
val manager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "general"
val channelName = mContext.getString(R.string.general)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
channel.enableLights(true)
channel.lightColor = Color.BLUE
manager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(mContext, channelId)
.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout)
.setSmallIcon(R.drawable.ic_avh)
.setContentIntent(openAppPending)
.setAutoCancel(true)
.setColor(ContextCompat.getColor(mContext, R.color.colorAccent))
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
val sound = if ((prefs.getString("ringtoneUri", "") ?: "").isNotEmpty()) {
Uri.parse(prefs.getString("ringtoneUri", "") ?: "")
} else {
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
}
notification.setSound(sound)
}
manager.notify(1, notification.build())
}
edit.putString("timeNew", currentTime).apply()
when {
prefs.getBoolean("testUrls", false) -> {
substUrl = "https://djd4rkn355.github.io/subst_test.html"
foodUrl = "https://djd4rkn355.github.io/food_test.html"
}
menu -> requestFoodMenuData()
plan -> requestSubstPlanAndNotification()
}
if (plan || menu) {
mView?.let { v: View ->
val snackText = mContext.getText(R.string.lastUpdated).toString() + when {
val snackText = "${mContext.getText(R.string.lastUpdated)} ${when {
menu -> currentFoodTime
else -> currentTime
}
}}"
val snackBarView = v.findViewById<View>(R.id.coordination)
Snackbar.make(snackBarView, snackText, Snackbar.LENGTH_LONG).show()
}
@@ -207,6 +83,121 @@ class DataFetcher(isplan: Boolean, ismenu: Boolean, isjobservice: Boolean, conte
}
}
private fun requestFoodMenuData() {
val docFood = Jsoup.connect(foodUrl).get()
currentFoodTime = docFood.select("h1")[0].text()
if (currentFoodTime != prefs.getString("timeFoodNew", "")) {
val foodRepository = FoodRepository(mApplication)
val foodElements = docFood.select("th")
foodRepository.deleteAll()
val indices = ArrayList<Int>()
val daysAndVon = arrayOf("Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "von")
for (i in 0 until foodElements.size) {
if (checkStringForArray(foodElements[i].text(), daysAndVon)) indices.add(i)
}
indices.add(foodElements.size)
for ((priority, l) in (0 until indices.size - 1).withIndex()) {
var s = ""
for (i2 in indices[l] until indices[l + 1]) {
if (s.isEmpty()) {
s = foodElements[i2].text()
} else {
s += "\n${foodElements[i2].text()}"
}
}
foodRepository.insert(Food(s, priority))
}
edit.putString("timeFoodNew", currentFoodTime).apply()
}
}
private fun requestSubstPlanAndNotification() {
val doc = Jsoup.connect(substUrl).get()
currentTime = doc.select("h1")[0].text()
if (currentTime != prefs.getString("timeNew", "")) {
val substRepo = SubstRepository(mApplication)
val rows = doc.select("tr")
val paragraphs = doc.select("p")
val substArray = ArrayList<Subst>()
val coursePreference = prefs.getString("courses", "") ?: ""
val classPreference = prefs.getString("classes", "") ?: ""
for (i in 0 until paragraphs.size) {
if (i == 0) {
informational = paragraphs[i].text()
} else {
informational += "\n\n" + paragraphs[i].text()
}
}
edit.putString("informational", informational).apply()
substRepo.deleteAllSubst()
for (i in 0 until rows.size) {
val row = rows[i]
val cols = row.select("th")
val group = cols[0].text()
val course = cols[3].text()
val additional = cols[5].text()
val subst = Subst(group = group, date = cols[1].text(), time = cols[2].text(), course = course,
room = cols[4].text(), additional = additional, priority = priority)
substArray.add(subst)
substRepo.insert(subst)
priority--
}
if (jobService && prefs.getBoolean("notif", true)) {
substArray.filter { MiscData.checkPersonalSubstitutions(it, coursePreference, classPreference, false) }.forEach { substItem ->
notifText += "${if (notifText.isNotEmpty()) ",\n" else ""}$substItem.course: ${if (substItem.additional.isNotEmpty()) substItem.additional else "---"}"
}
}
if (notifText.isNotEmpty()) {
sendNotification()
}
edit.putString("timeNew", currentTime).apply()
}
}
private fun sendNotification() {
val openApp = Intent(mContext, Main::class.java)
openApp.flags = Intent.FLAG_ACTIVITY_NEW_TASK
openApp.flags += Intent.FLAG_ACTIVITY_CLEAR_TASK
val openAppPending = PendingIntent.getActivity(mContext, 0, openApp, 0)
val notificationLayout = RemoteViews(mContext.packageName, R.layout.notification)
notificationLayout.setTextViewText(R.id.notification_title, mContext.getString(R.string.substitutionPlan))
notificationLayout.setTextViewText(R.id.notification_textview, notifText)
val manager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
MiscData.getNotificationChannel(mContext, prefs)
val notification = NotificationCompat.Builder(mContext, MiscData.notificationChannelId)
.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout)
.setSmallIcon(R.drawable.ic_avh)
.setContentIntent(openAppPending)
.setAutoCancel(true)
.setColor(ContextCompat.getColor(mContext, R.color.colorAccent))
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
val sound = if ((prefs.getString("ringtoneUri", "") ?: "").isNotEmpty()) {
Uri.parse(prefs.getString("ringtoneUri", "") ?: "")
} else {
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
}
notification.setSound(sound)
}
manager.notify(1, notification.build())
}
private fun checkStringForArray(s: String, checking: Array<String>): Boolean {
for (i in checking.indices) {
if (s.contains(checking[i])) {
@@ -6,7 +6,7 @@ import com.google.firebase.messaging.RemoteMessage
class FBNotificationService : FirebaseMessagingService() {
override fun onMessageReceived(p0: RemoteMessage) {
DataFetcher(isplan = true, ismenu = true, isjobservice = true, context = applicationContext,
application = application, parentview = null).execute()
DataFetcher(isPlan = true, isMenu = true, isJobService = true, context = applicationContext,
application = application, parentView = null).execute()
}
}
@@ -9,8 +9,8 @@ class GeneralPlanFragment : PlanFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
substViewModel.allSubst?.observe(this, Observer<List<Subst>> {
mAdapter.setSubst(it)
substViewModel.allSubst?.observe(this, Observer<List<Subst>> { substitutions ->
mAdapter.setSubst(substitutions)
recyclerView.scheduleLayoutAnimation()
})
}
@@ -1,6 +1,11 @@
package com.denizd.substitutionplan
import android.annotation.TargetApi
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Color
import java.util.*
object MiscData {
@@ -15,11 +20,10 @@ object MiscData {
R.color.bgTeal, R.color.bgCyan, R.color.bgBlue, R.color.bgPurple, R.color.bgPink, R.color.bgBrown, R.color.bgGrey,
R.color.bgPureWhite, R.color.bgSalmon, R.color.bgTangerine, R.color.bgBanana, R.color.bgFlora, R.color.bgSpindrift,
R.color.bgSky, R.color.bgOrchid, R.color.bgLavender, R.color.bgCarnation, R.color.bgBrown2, R.color.bgPureBlack)
fun emoji(unicode: Int): String { return String(Character.toChars(unicode)) }
const val notificationChannelId = "general"
fun getColourForString(name: String): Int {
return when(name) {
return when (name) {
"red" -> R.color.bgRed
"orange" -> R.color.bgOrange
"yellow" -> R.color.bgYellow
@@ -95,4 +99,43 @@ object MiscData {
}
edit.apply()
}
fun checkPersonalSubstitutions(subst: Subst, coursePreference: String, classPreference: String, psa: Boolean): Boolean {
val group = subst.group
val course = subst.course
if (psa && subst.date.isNotEmpty() && subst.date.substring(0, 3) == "psa") {
return true
}
if (coursePreference.isEmpty() && classPreference.isNotEmpty()) {
if (group.isNotEmpty() && group != "") {
if (classPreference.contains(group) || group.contains(classPreference)) {
return true
}
}
} else if (classPreference.isNotEmpty() && coursePreference.isNotEmpty()) {
if (group != "" && course != "") {
if (coursePreference.contains(course)) {
if (classPreference.contains(group) || group.contains(classPreference)) {
return true
}
}
}
}
return false
}
@TargetApi(26)
fun getNotificationChannel(context: Context, prefs: SharedPreferences): NotificationChannel {
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
return if (!prefs.getBoolean("notifChannelCreated", false)) {
val channel = NotificationChannel(notificationChannelId, context.getString(R.string.general), NotificationManager.IMPORTANCE_DEFAULT)
channel.enableLights(true)
channel.lightColor = Color.BLUE
manager.createNotificationChannel(channel)
prefs.edit().putBoolean("notifChannelCreated", true).apply()
channel
} else {
manager.getNotificationChannel(notificationChannelId)
}
}
}
@@ -13,41 +13,28 @@ class PersonalPlanFragment : PlanFragment() {
smileydowntext = view.findViewById(R.id.smileydowntext)
linearsmiley = view.findViewById(R.id.linearsmiley)
substViewModel.allSubst?.observe(this, Observer<List<Subst>> {
val coursePreference = prefs.getString("courses", "") ?: ""
val classPreference = prefs.getString("classes", "") ?: ""
substViewModel.allSubst?.observe(this, Observer<List<Subst>> { substitutions ->
planCardList.clear()
smileydown.visibility = View.GONE
smileydowntext.visibility = View.GONE
linearsmiley.visibility = View.GONE
persPlanEmpty = true
recyclerView.visibility = View.VISIBLE
for (i in 0 until it.size) {
if (it[i].date.isNotEmpty() && it[i].date.substring(0, 3) == "psa") {
planCardList.add(it[i])
} else if ((prefs.getString("courses", "") ?: "").isEmpty() && (prefs.getString("classes", "") ?: "").isNotEmpty()) {
if (it[i].group.isNotEmpty() && it[i].group != "") {
if ((prefs.getString("classes", "") ?: "").contains(it[i].group) || it[i].group.contains((prefs.getString("classes", "") ?: "").toString())) {
planCardList.add(it[i])
persPlanEmpty = false
}
}
} else if ((prefs.getString("classes", "") ?: "").isNotEmpty() && (prefs.getString("courses", "") ?: "").isNotEmpty()) {
if (it[i].group != "" && it[i].course != "") {
if ((prefs.getString("courses", "") ?: "").contains(it[i].course)) {
if ((prefs.getString("classes", "") ?: "").contains(it[i].group) || it[i].group.contains((prefs.getString("classes", "") ?: "").toString())) {
planCardList.add(it[i])
persPlanEmpty = false
}
}
}
}
substitutions.filter { MiscData.checkPersonalSubstitutions(it, coursePreference, classPreference, true) }.forEach { substItem ->
planCardList.add(substItem)
}
planCardList.sortWith(Comparator { lhs, rhs -> Integer.compare(rhs.priority, lhs.priority) })
persPlanEmpty = (planCardList.size == 1 && planCardList[0].date.substring(0, 3) == "psa") || planCardList.isEmpty()
planCardList.sortWith(Comparator { lhs, rhs -> rhs.priority.compareTo(lhs.priority) })
recyclerView.scheduleLayoutAnimation()
mAdapter.setSubst(planCardList)
handler.postDelayed({
if (persPlanEmpty) {
// recyclerView.visibility = View.GONE
smileydown.visibility = View.VISIBLE
smileydowntext.visibility = View.VISIBLE
linearsmiley.visibility = View.VISIBLE
@@ -20,7 +20,7 @@ open class PlanFragment : Fragment(R.layout.plan) {
lateinit var recyclerView: RecyclerView
lateinit var mAdapter: CardAdapter
private lateinit var layoutManager: GridLayoutManager
val planCardList = ArrayList<Subst>()
var planCardList = ArrayList<Subst>()
lateinit var substViewModel: SubstViewModel
lateinit var mContext: Context
lateinit var prefs: SharedPreferences
@@ -36,7 +36,6 @@ open class PlanFragment : Fragment(R.layout.plan) {
super.onAttach(context)
mContext = context
prefs = PreferenceManager.getDefaultSharedPreferences(mContext)
edit = prefs.edit()
}
override fun onConfigurationChanged(newConfig: Configuration) {
@@ -83,11 +82,11 @@ open class PlanFragment : Fragment(R.layout.plan) {
mAdapter = CardAdapter(planCardList)
recyclerView.adapter = mAdapter
if (prefs.getInt("firstTimeOpening", 0) == 2) {
if (prefs.getInt("firstTimeOpening", 0) == 1) {
if (!prefs.getBoolean("notif", false)) {
pullToRefresh.isRefreshing = true
DataFetcher(true, false, false, mContext, activity!!.application, view.rootView).execute()
edit.putInt("firstTimeOpening", 3).apply()
DataFetcher(true, true, false, mContext, activity!!.application, view.rootView).execute()
prefs.edit().putInt("firstTimeOpening", 2).apply()
}
}
@@ -313,6 +313,7 @@ class SettingsFragment : Fragment(R.layout.content_settings), View.OnClickListen
private fun createRingtoneDialog() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
MiscData.getNotificationChannel(mContext, prefs)
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, mContext.applicationContext.packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, "general")
+1 -1
View File
@@ -94,7 +94,7 @@
<string name="courseWp">WP &amp; Methodenstunde</string>
<string name="chromeCompatibleNotFound">Kein Chrome-kompatibler Browser gefunden</string>
<string name="youtubeNotFound">YouTube-App nicht gefunden</string>
<string name="lastUpdated">"Zuletzt aktualisiert: "</string>
<string name="lastUpdated">Zuletzt aktualisiert:</string>
<string name="nightMode">Aktiviert den Nachtmodus</string>
<string name="yourPlan">Dein Plan</string>
<string-array name="greetingsMorning">
+1 -1
View File
@@ -107,7 +107,7 @@
<string name="chromeCompatibleNotFound">No Chrome-compatible browser found</string>
<string name="youtubeNotFound">YouTube app not found</string>
<string name="lastUpdated">"Last updated: "</string>
<string name="lastUpdated">Last updated:</string>
<string name="nightMode">Enables night mode</string>
<string name="yourPlan">Your Plan</string>