Firebase topic subscription changes are now immediate, added backup option in experimental dialog

This commit is contained in:
Deniz Düzgören
2019-09-03 18:18:21 +02:00
parent c1c025f616
commit 152004026c
15 changed files with 194 additions and 36 deletions
@@ -24,6 +24,7 @@ import com.denizd.substitutionplan.R
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.textfield.TextInputEditText
import java.util.*
import kotlin.math.hypot
internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
@@ -78,6 +79,7 @@ internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
val pers = findViewById<CheckBox>(R.id.cbPersonalised)
val helpClasses = findViewById<ImageButton>(R.id.chipHelpClasses)
val helpCourses = findViewById<ImageButton>(R.id.chipHelpCourses)
val greeting = findViewById<CheckBox>(R.id.cbGreetings)
helpClasses.setOnClickListener {
createDialog(getString(R.string.enterGradeHelpTitle), getString(
@@ -92,6 +94,23 @@ internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
val inflater = this.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
// findViewById<MaterialButton>(R.id.btnRestoreFromFile).setOnClickListener {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42)
// }
// HelperFunctions.readPrefsFromXml(prefs)
// name.setText(prefs.getString("username", ""))
// grade.setText(prefs.getString("classes", ""))
// courses.setText(prefs.getString("courses", ""))
// notif.isChecked = prefs.getBoolean("notif", false)
// dark.isChecked = when (prefs.getInt("themeInt", 0)) {
// 0 -> true
// else -> false
// }
// greeting.isChecked = prefs.getBoolean("greeting", false)
// pers.isChecked = prefs.getBoolean("defaultPersonalised", false)
// }
fab.setOnClickListener {
fab.isClickable = false
@@ -105,7 +124,7 @@ internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
edit.putInt("themeInt", 0)
}
edit.putBoolean("notif", notif.isChecked)
.putBoolean("greeting", findViewById<CheckBox>(R.id.cbGreetings).isChecked)
.putBoolean("greeting", greeting.isChecked)
.putBoolean("defaultPersonalised", pers.isChecked)
.putBoolean("firstTime", false)
.putBoolean("colourTransferred", true)
@@ -116,7 +135,7 @@ internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
val x: Int = fab.right - fab.width / 2
val y: Int = fab.bottom- fab.height / 2
val endRadius = Math.hypot(cLayout.width.toDouble(), cLayout.height.toDouble()).toInt()
val endRadius = hypot(cLayout.width.toDouble(), cLayout.height.toDouble()).toInt()
inflater.inflate(R.layout.welcome_screen, linearInflation, true)
val anim = ViewAnimationUtils.createCircularReveal(linearInflation, x, y, 0F, endRadius.toFloat())
val handler = Handler()
@@ -20,7 +20,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.recyclerview.widget.RecyclerView
import com.crashlytics.android.Crashlytics
import com.denizd.substitutionplan.data.MiscData
import com.denizd.substitutionplan.data.HelperFunctions
import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.fragments.FoodFragment
import com.denizd.substitutionplan.fragments.GeneralPlanFragment
@@ -30,6 +30,7 @@ import com.denizd.substitutionplan.services.FBPingService
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.FirebaseApp
import com.jaredrummler.android.device.DeviceName
import java.lang.IllegalArgumentException
import java.util.*
@@ -54,13 +55,14 @@ internal class Main : AppCompatActivity(R.layout.app_bar_main) {
} else {
if (!prefs.getBoolean("colourTransferred", false)) {
MiscData.transferOldColourIntsToString(prefs)
HelperFunctions.transferOldColourIntsToString(prefs)
edit.putBoolean("colourTransferred", true).apply()
}
edit.putInt("launchDev", prefs.getInt("launchDev", 0) + 1)
edit.apply()
FirebaseApp.initializeApp(this)
pingFirebaseTopics()
val appbarlayout = findViewById<AppBarLayout>(R.id.appbarlayout)
@@ -119,10 +121,7 @@ internal class Main : AppCompatActivity(R.layout.app_bar_main) {
if (!prefs.getBoolean("autoRefresh", false) && prefs.getInt("firstTimeOpening", 0) != 0) {
try {
val sb = StringBuilder()
val updated = sb.append(getText(R.string.lastUpdated)).append(prefs.getString("timeNew", "")).toString()
Snackbar.make(contextView, updated, Snackbar.LENGTH_LONG).show()
Snackbar.make(contextView, "${getString(R.string.lastUpdated)} ${prefs.getString("timeNew", "")}", Snackbar.LENGTH_LONG).show()
} catch (e: IllegalArgumentException) {}
}
@@ -14,7 +14,7 @@ import android.widget.Toast
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.denizd.substitutionplan.data.MiscData
import com.denizd.substitutionplan.data.HelperFunctions
import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.models.Subst
import com.google.android.material.card.MaterialCardView
@@ -74,7 +74,7 @@ internal class CardAdapter(private var mSubst: List<Subst>) : RecyclerView.Adapt
}
holder.mImageView.setImageResource(
MiscData.getIconForCourse(
HelperFunctions.getIconForCourse(
currentItem.course
)
)
@@ -105,7 +105,7 @@ internal class CardAdapter(private var mSubst: List<Subst>) : RecyclerView.Adapt
} else {
""
}
colour = MiscData.getColourForString(colourPrefsInt)
colour = HelperFunctions.getColourForString(colourPrefsInt)
if (colour != 0) {
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(holder.mCourse.context, colour))
} else {
@@ -166,7 +166,7 @@ internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boole
if (jobService && prefs.getBoolean("notif", true)) {
substArray.filter { substitution ->
MiscData.checkPersonalSubstitutions(
HelperFunctions.checkPersonalSubstitutions(
substitution,
coursePreference,
classPreference,
@@ -202,10 +202,10 @@ internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boole
val manager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
MiscData.getNotificationChannel(mContext, prefs)
HelperFunctions.getNotificationChannel(mContext, prefs)
val notification = NotificationCompat.Builder(mContext,
MiscData.notificationChannelId
HelperFunctions.notificationChannelId
)
.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout)
@@ -1,16 +1,29 @@
package com.denizd.substitutionplan.data
import android.Manifest
import android.annotation.TargetApi
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.requestPermissions
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.fragment.app.FragmentActivity
import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.models.Subst
import java.util.*
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStreamWriter
import javax.xml.parsers.DocumentBuilderFactory
internal object MiscData {
internal object HelperFunctions {
val languageIndependentCourses = arrayOf("German", "English", "French", "Spanish", "Latin", "Turkish", "Chinese", "Arts", "Music",
"Theatre", "Geography", "History", "Politics", "Philosophy", "Religion", "Maths", "Biology", "Chemistry",
@@ -164,4 +177,86 @@ internal object MiscData {
manager.getNotificationChannel(notificationChannelId)
}
}
private val prefKeys = arrayOf("username", "classes", "courses", "greeting", "themeInt", "notif",
"defaultPersonalised", "autoRefresh", "firstTimeDev", "launchDev", "pingFB",
"subscribedToFBDebugChannel", "subscribedToiOSChannel")
private val prefTypes = arrayOf("string", "string", "string", "bool", "int", "bool", "bool", "bool",
"string", "int", "int", "bool", "bool")
private fun getPrefValue(prefs: SharedPreferences, type: String, key: String): Any {
return when (type) {
"string" -> prefs.getString(key, "")
"int" -> prefs.getInt(key, 0)
"bool" -> prefs.getBoolean(key, false)
else -> ""
}
}
private fun setPrefValue(prefs: SharedPreferences, key: String, value: String, type: String) {
when (type) {
"int" -> prefs.edit().putInt(key, value.toInt()).apply()
"string" -> prefs.edit().putString(key, value).apply()
"bool" -> prefs.edit().putBoolean(key, value.toBoolean()).apply()
}
}
fun writePrefsToXml(prefs: SharedPreferences, context: Context, activity: FragmentActivity) {
if (checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
val localPrefKeys = ArrayList<String>()
val localPrefTypes = ArrayList<String>()
for (i in prefKeys.indices) {
localPrefKeys.add(prefKeys[i])
localPrefTypes.add(prefTypes[i])
}
for (i in languageIndependentCourses.indices) {
localPrefKeys.add("card${languageIndependentCourses[i]}")
localPrefTypes.add("string")
}
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
val file = File(dir, "avh_plan_data.xml")
val out = OutputStreamWriter(FileOutputStream(file, false))
var input = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<items>"
for (i in localPrefKeys.indices) {
input += "\n\t<key>${localPrefKeys[i]}</key>" +
"\n\t<value>${getPrefValue(prefs, localPrefTypes[i], localPrefKeys[i])}</value>"
}
input += "\n</items>"
out.write(input)
out.flush()
out.close()
Toast.makeText(context, context.getString(R.string.success), Toast.LENGTH_LONG).show()
} else {
Toast.makeText(context, context.getString(R.string.permissionDenied), Toast.LENGTH_LONG).show()
requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42)
}
}
fun readPrefsFromXml(prefs: SharedPreferences, context: Context, activity: FragmentActivity) {
if (checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
val localPrefTypes = ArrayList<String>()
for (i in prefKeys.indices) {
localPrefTypes.add(prefTypes[i])
}
for (i in languageIndependentCourses.indices) {
localPrefTypes.add("string")
}
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
val file = File(dir, "avh_plan_data.xml")
val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
val document = documentBuilder.parse(file)
for (i in localPrefTypes.indices) {
val key = document.getElementsByTagName("key").item(i).textContent
val value = document.getElementsByTagName("value").item(i).textContent
Log.d("VALUES", "$key $value")
setPrefValue(prefs, key, value, localPrefTypes[i])
}
Toast.makeText(context, context.getString(R.string.success), Toast.LENGTH_LONG).show()
} else {
Toast.makeText(context, context.getString(R.string.permissionDenied), Toast.LENGTH_LONG).show()
requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42)
}
}
}
@@ -3,7 +3,7 @@ package com.denizd.substitutionplan.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
import com.denizd.substitutionplan.data.MiscData
import com.denizd.substitutionplan.data.HelperFunctions
import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.models.Subst
@@ -28,7 +28,7 @@ internal class PersonalPlanFragment : PlanFragment() {
recyclerView.visibility = View.VISIBLE
substitutions.filter { substitution ->
MiscData.checkPersonalSubstitutions(
HelperFunctions.checkPersonalSubstitutions(
substitution,
coursePreference,
classPreference,
@@ -27,7 +27,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.denizd.substitutionplan.*
import com.denizd.substitutionplan.adapters.ColourAdapter
import com.denizd.substitutionplan.adapters.RingtoneAdapter
import com.denizd.substitutionplan.data.MiscData
import com.denizd.substitutionplan.data.HelperFunctions
import com.denizd.substitutionplan.models.Colour
import com.denizd.substitutionplan.models.Ringtone
import com.google.android.material.bottomnavigation.BottomNavigationView
@@ -116,7 +116,7 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
edit.putString("username", txtName.text.toString()).apply()
edit.putString("username", txtName.text.toString().trim()).apply()
}
})
@@ -255,9 +255,9 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
R.id.switchNotifications -> {
edit.putBoolean("notif", isChecked)
if (isChecked) {
FirebaseMessaging.getInstance().subscribeToTopic("substitutions-android")
subToTopic("android")
} else {
FirebaseMessaging.getInstance().unsubscribeFromTopic("substitutions-android")
unsubFromTopic("android")
}
}
R.id.switchDefaultPlan -> {
@@ -290,11 +290,11 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
val titleText = dialogView.findViewById<TextView>(R.id.empty_textviewtitle)
titleText.text = getString(R.string.customiseColoursTitle)
colourRecycler = dialogView.findViewById(R.id.recyclerView)
colourRecycler.hasFixedSize()
colourRecycler.layoutManager = GridLayoutManager(mContext, 1)
colourRecycler.adapter = ColourAdapter(getColourList(), this)
colourRecycler.apply {
hasFixedSize()
layoutManager = GridLayoutManager(mContext, 1)
adapter = ColourAdapter(getColourList(), this@SettingsFragment)
}
colourCustomiserBuilder.setView(dialogView)
val colourCustomiserDialog = colourCustomiserBuilder.create()
colourCustomiserDialog.show()
@@ -302,7 +302,7 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
private fun getColourList(): ArrayList<Colour> {
val colours = ArrayList<Colour>()
val coursesNoLang = MiscData.languageIndependentCourses
val coursesNoLang = HelperFunctions.languageIndependentCourses
val courses = arrayOf(getString(R.string.courseDeu), getString(
R.string.courseEng
), getString(R.string.courseFra), getString(R.string.courseSpa), getString(
@@ -335,7 +335,7 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
courses[i],
coursesNoLang[i],
coursesIcons[i],
MiscData.getColourForString(prefs.getString("card${coursesNoLang[i]}", "") ?: "")
HelperFunctions.getColourForString(prefs.getString("card${coursesNoLang[i]}", "") ?: "")
)
)
}
@@ -344,7 +344,7 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
private fun createRingtoneDialog() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
MiscData.getNotificationChannel(mContext, prefs)
HelperFunctions.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")
@@ -431,7 +431,7 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
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 = MiscData.colourNames
val colours = HelperFunctions.colourNames
for (i2 in 0 until buttons.size) {
picker.findViewById<MaterialButton>(buttons[i2]).setOnClickListener {
@@ -516,8 +516,10 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
}
"_DEVCHANNEL" -> {
val subbed = if (prefs.getBoolean("subscribedToFBDebugChannel", false)) {
unsubFromTopic("debug")
"Unsubscribed from"
} else {
subToTopic("debug")
"Subscribed to"
}
edit.putBoolean("subscribedToFBDebugChannel", !prefs.getBoolean("subscribedToFBDebugChannel", false)).apply()
@@ -525,13 +527,17 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
}
"_IOSCHANNEL" -> {
val subbed = if (prefs.getBoolean("subscribedToiOSChannel", false)) {
unsubFromTopic("ios")
"Unsubscribed from"
} else {
subToTopic("ios")
"Subscribed to"
}
edit.putBoolean("subscribedToiOSChannel", !prefs.getBoolean("subscribedToiOSChannel", false)).apply()
makeToast("$subbed iOS channel")
}
"_WRITE" -> HelperFunctions.writePrefsToXml(prefs, mContext, activity!!)
"_READ" -> HelperFunctions.readPrefsFromXml(prefs, mContext, activity!!)
else -> makeToast(getString(R.string.invalidCode))
}
}
@@ -554,4 +560,7 @@ internal class SettingsFragment : Fragment(R.layout.content_settings), View.OnCl
"\n\nSubscribed to notification channel: ${prefs.getBoolean("notif", false)}" +
"\n\nSubscribed to dev channel: ${prefs.getBoolean("subscribedToFBDebugChannel", false)}"
}
private fun subToTopic(topic: String) = FirebaseMessaging.getInstance().subscribeToTopic("substitutions-$topic")
private fun unsubFromTopic(topic: String) = FirebaseMessaging.getInstance().unsubscribeFromTopic("substitutions-$topic")
}
@@ -1,3 +1,3 @@
package com.denizd.substitutionplan.models
internal class Colour(val title: String, val titleNoLang: String, val icon: Int, val colour: Int)
internal data class Colour(val title: String, val titleNoLang: String, val icon: Int, val colour: Int)
@@ -4,4 +4,4 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "food_table")
internal class Food(val food: String, @PrimaryKey val priority: Int)
internal data class Food(val food: String, @PrimaryKey val priority: Int)
@@ -1,3 +1,3 @@
package com.denizd.substitutionplan.models
internal class Ringtone(val name: String, val uri: String)
internal data class Ringtone(val name: String, val uri: String)
@@ -4,7 +4,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "subst_table")
internal class Subst(val group: String, val date: String, val time: String, val course: String, val room: String, val additional: String, val priority: Int) {
internal data class Subst(val group: String, val date: String, val time: String, val course: String, val room: String, val additional: String, val priority: Int) {
@PrimaryKey(autoGenerate = true)
var id: Int = 0