Added further documentation, fixed slight spacing issue on course_card.xml when icon is set to R.drawable.ic_empty

This commit is contained in:
Deniz Düzgören
2019-09-22 19:46:20 +02:00
parent 49eefe8131
commit 088aa90aa4
14 changed files with 163 additions and 54 deletions
@@ -1,7 +1,6 @@
package com.denizd.substitutionplan.activities package com.denizd.substitutionplan.activities
import android.animation.LayoutTransition import android.animation.LayoutTransition
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
@@ -18,16 +17,19 @@ import android.view.animation.AnimationUtils
import android.widget.* import android.widget.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.denizd.substitutionplan.R import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.data.HelperFunctions
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import java.util.* import java.util.*
import kotlin.math.hypot import kotlin.math.hypot
/**
* This class is launched after a successful login and enables the user to set any preferences
* without scouring through the app's settings first
*/
internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) { internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
private lateinit var context: Context private lateinit var context: Context
@@ -42,21 +44,7 @@ internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
val parentLayout = findViewById<ConstraintLayout>(R.id.coordinatorLayout) val parentLayout = findViewById<ConstraintLayout>(R.id.coordinatorLayout)
parentLayout.layoutTransition.enableTransitionType(LayoutTransition.CHANGING) parentLayout.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
val barColour = when { HelperFunctions.setTheme(window = window, context = context)
Build.VERSION.SDK_INT < Build.VERSION_CODES.M -> ContextCompat.getColor(context, R.color.legacyBlack)
Build.VERSION.SDK_INT <= Build.VERSION_CODES.P -> ContextCompat.getColor(context, R.color.colorBackground)
else -> 0
}
if (barColour != 0) {
window.navigationBarColor = barColour
window.statusBarColor = barColour
}
if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_NO) {
if (Build.VERSION.SDK_INT in 23..28) {
@SuppressLint("InlinedApi")
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
}
prefs = PreferenceManager.getDefaultSharedPreferences(context) as SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context) as SharedPreferences
val edit = prefs.edit() val edit = prefs.edit()
@@ -89,6 +77,9 @@ internal class FirstTime : AppCompatActivity(R.layout.activity_first_time) {
val inflater = this.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val inflater = this.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
/**
* Setup is finished; trigger an animation and save the user's settings in Shared Preferences
*/
fab.setOnClickListener { fab.setOnClickListener {
fab.isClickable = false fab.isClickable = false
@@ -1,7 +1,6 @@
package com.denizd.substitutionplan.activities package com.denizd.substitutionplan.activities
import android.animation.LayoutTransition import android.animation.LayoutTransition
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.AnimatedVectorDrawable
@@ -18,15 +17,18 @@ import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.denizd.substitutionplan.R import com.denizd.substitutionplan.R
import com.denizd.substitutionplan.data.HelperFunctions
import com.denizd.substitutionplan.data.LoginWebViewClient import com.denizd.substitutionplan.data.LoginWebViewClient
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import kotlin.math.hypot import kotlin.math.hypot
/**
* This class will be instantiated upon first launch of the app to present the user with a WebView
* allowing them to log in to the school website to verify them as being occupied at the gymnasium.
*/
internal class Login : AppCompatActivity(R.layout.activity_login_webview), LoginWebViewClient.OnLoginSuccessListener { internal class Login : AppCompatActivity(R.layout.activity_login_webview), LoginWebViewClient.OnLoginSuccessListener {
private lateinit var prefs: SharedPreferences private lateinit var prefs: SharedPreferences
@@ -41,21 +43,7 @@ internal class Login : AppCompatActivity(R.layout.activity_login_webview), Login
val title = findViewById<TextView>(R.id.txtWelcome) val title = findViewById<TextView>(R.id.txtWelcome)
prefs = PreferenceManager.getDefaultSharedPreferences(this) prefs = PreferenceManager.getDefaultSharedPreferences(this)
val barColour = when { HelperFunctions.setTheme(window = window, context = this)
Build.VERSION.SDK_INT < Build.VERSION_CODES.M -> ContextCompat.getColor(this, R.color.legacyBlack)
Build.VERSION.SDK_INT <= Build.VERSION_CODES.P -> ContextCompat.getColor(this, R.color.colorBackground)
else -> 0
}
if (barColour != 0) {
window.navigationBarColor = barColour
window.statusBarColor = barColour
}
if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_NO) {
if (Build.VERSION.SDK_INT in 23..28) {
@SuppressLint("InlinedApi")
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
}
logInButton = findViewById(R.id.loginFab) logInButton = findViewById(R.id.loginFab)
logInButton.setOnClickListener { logInButton.setOnClickListener {
@@ -35,6 +35,12 @@ import com.google.firebase.FirebaseApp
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import java.util.* import java.util.*
/**
* The main class of the app. This controls navigation and enables the user to navigate through the
* app's fragments and view the information provided on the substitution table. Theming as well as
* setting up Firebase Cloud Messaging and Crashlytics are handled here. Also checks whether
* the user has completed the setup (logging in, setting preferences)
*/
internal class Main : AppCompatActivity(R.layout.app_bar_main) { internal class Main : AppCompatActivity(R.layout.app_bar_main) {
private lateinit var context: Context private lateinit var context: Context
@@ -49,11 +55,16 @@ internal class Main : AppCompatActivity(R.layout.app_bar_main) {
val login = Intent(context, Login::class.java) val login = Intent(context, Login::class.java)
Crashlytics.setUserIdentifier(prefs.getString("username", "") ?: "") Crashlytics.setUserIdentifier(prefs.getString("username", "") ?: "")
/**
* This cascade of if-else's allows the user to return to any point of the setup if they
* decide to postpone it, e.g. logging in but not setting up their preferences. Furthermore,
* it prevents existing users from upgrading from a previous version of the app and skip the
* mandatory login
*/
if (!prefs.getBoolean("successful_login", false)) { if (!prefs.getBoolean("successful_login", false)) {
startActivity(login) startActivity(login)
finish() finish()
} else if (prefs.getBoolean("firstTime", true)) { } else if (prefs.getBoolean("firstTime", true)) {
// } else if (true) {
startActivity(firstTime) startActivity(firstTime)
finish() finish()
} else { } else {
@@ -199,6 +210,12 @@ internal class Main : AppCompatActivity(R.layout.app_bar_main) {
} }
} }
/**
* Picks a greeting according to the time of day to present to the user if they choose to
* enable greetings
*
* @return the greeting
*/
private fun getGreetingString(): String { private fun getGreetingString(): String {
val gen = Random() val gen = Random()
return String.format(when (Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) { return String.format(when (Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) {
@@ -230,6 +247,9 @@ internal class Main : AppCompatActivity(R.layout.app_bar_main) {
return true return true
} }
/**
* Triggers FBPingService.kt to re-subscribe to the selected Firebase topics every 15 minutes
*/
private fun pingFirebaseTopics() { private fun pingFirebaseTopics() {
val componentName = ComponentName(this, FBPingService::class.java) val componentName = ComponentName(this, FBPingService::class.java)
val info = JobInfo.Builder(42, componentName) val info = JobInfo.Builder(42, componentName)
@@ -241,6 +261,12 @@ internal class Main : AppCompatActivity(R.layout.app_bar_main) {
scheduler.schedule(info) scheduler.schedule(info)
} }
/**
* Decides on the title for PersonalPlanFragment.kt. Picks a generic "Your plan" if greetings
* are enabled or "$user's Plan" if they are disabled
*
* @return the title
*/
private fun personalPlanTitle(): String { private fun personalPlanTitle(): String {
val user = prefs.getString("username", "") ?: "" val user = prefs.getString("username", "") ?: ""
return if (prefs.getBoolean("greeting", true) || user.isEmpty()) { return if (prefs.getBoolean("greeting", true) || user.isEmpty()) {
@@ -130,6 +130,13 @@ internal class CardAdapter(private var mSubst: List<Subst>, private val prefs: S
else -> R.color.colorText else -> R.color.colorText
} }
}) })
holder.iconView.visibility = if (icon == R.drawable.ic_empty) {
View.GONE
} else {
View.VISIBLE
}
holder.card.setCardBackgroundColor(ContextCompat.getColor(holder.iconView.context, cardBackgroundColour)) holder.card.setCardBackgroundColor(ContextCompat.getColor(holder.iconView.context, cardBackgroundColour))
holder.iconView.setImageResource(icon) holder.iconView.setImageResource(icon)
holder.iconView.setColorFilter(textColor) holder.iconView.setColorFilter(textColor)
@@ -32,7 +32,13 @@ import kotlin.collections.ArrayList
* *
* @param isPlan substitution plan will be downloaded and persisted in the database if true * @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 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 * @param isJobService enables sending of a notification if true
* @param context the context of the application that will be stored as a WeakReference
* to avoid memory leakage
* @param application a reference to the application that will be stored as a WeakReference
* @param parentView a reference to the parent view that will be stored as a WeakReference
* @param forced if true, times for food menu and substitution table will be overwritten
* with empty values to enable a forced refresh
*/ */
internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boolean, context: Context, application: Application, parentView: View?, forced: Boolean) : AsyncTask<Void, Void, Void>() { internal class DataFetcher(isPlan: Boolean, isMenu: Boolean, isJobService: Boolean, context: Context, application: Application, parentView: View?, forced: Boolean) : AsyncTask<Void, Void, Void>() {
@@ -1,6 +1,7 @@
package com.denizd.substitutionplan.data package com.denizd.substitutionplan.data
import android.Manifest import android.Manifest
import android.annotation.SuppressLint
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
@@ -8,9 +9,14 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Color import android.graphics.Color
import android.os.Build
import android.os.Environment import android.os.Environment
import android.view.View
import android.view.Window
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat.requestPermissions import androidx.core.app.ActivityCompat.requestPermissions
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
@@ -21,14 +27,24 @@ import java.io.FileOutputStream
import java.io.OutputStreamWriter import java.io.OutputStreamWriter
import javax.xml.parsers.DocumentBuilderFactory import javax.xml.parsers.DocumentBuilderFactory
/**
* An object class used for declaring functions and variables used by multiple classes throughout
* the project. As it is an object class, it can be accessed statically without initialisation
*/
internal object HelperFunctions { internal object HelperFunctions {
/**
* A language-independent list of all courses that is used to save the custom colours of any
* course in SharedPreferences without depending on the user's device language
*/
val languageIndependentCourses = arrayOf("German", "English", "French", "Spanish", "Latin", "Turkish", "Chinese", "Arts", "Music", val languageIndependentCourses = arrayOf("German", "English", "French", "Spanish", "Latin", "Turkish", "Chinese", "Arts", "Music",
"Theatre", "Geography", "History", "Politics", "Philosophy", "Religion", "Maths", "Biology", "Chemistry", "Theatre", "Geography", "History", "Politics", "Philosophy", "Religion", "Maths", "Biology", "Chemistry",
"Physics", "CompSci", "PhysEd", "GLL", "WAT", "Forder", "WP") "Physics", "CompSci", "PhysEd", "GLL", "WAT", "Forder", "WP")
val colourNames = arrayOf("default", "red", "orange", "yellow", "green", "teal", "cyan", "blue", "purple", "pink", val colourNames = arrayOf("default", "red", "orange", "yellow", "green", "teal", "cyan", "blue", "purple", "pink",
"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")
private val colourIntegers = intArrayOf( private val colourIntegers = intArrayOf(
0, 0,
R.color.bgRed, R.color.bgRed,
@@ -119,6 +135,14 @@ internal object HelperFunctions {
} }
} }
/**
* 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
* the code, as users from older versions (2.1.3 and below) may wish to upgrade without
* losing their settings
*
* @param prefs a reference to the app's SharedPreferences
*/
fun transferOldColourIntsToString(prefs: SharedPreferences) { fun transferOldColourIntsToString(prefs: SharedPreferences) {
var colour = "" var colour = ""
val edit = prefs.edit() val edit = prefs.edit()
@@ -135,6 +159,17 @@ internal object HelperFunctions {
edit.apply() edit.apply()
} }
/**
* 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
*
* @param subst the substitution item
* @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 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
*/
fun checkPersonalSubstitutions(subst: Subst, coursePreference: String, classPreference: String, psa: Boolean): Boolean { fun checkPersonalSubstitutions(subst: Subst, coursePreference: String, classPreference: String, psa: Boolean): Boolean {
val group = subst.group val group = subst.group
val course = subst.course val course = subst.course
@@ -157,6 +192,38 @@ internal object HelperFunctions {
return false return false
} }
/**
* This function is used throughout some classes to set the theme according to the device's
* Android version before an app theme may be picked in the settings. As Main.kt as well as
* SettingsFragment.kt are only accessible after the inital setup and serve slightly
* different purposes, they use their own implementations
*/
fun setTheme(window: Window, context: Context) {
val barColour = when {
Build.VERSION.SDK_INT < Build.VERSION_CODES.M -> ContextCompat.getColor(context, R.color.legacyBlack)
Build.VERSION.SDK_INT <= Build.VERSION_CODES.P -> ContextCompat.getColor(context, R.color.colorBackground)
else -> 0
}
if (barColour != 0) {
window.navigationBarColor = barColour
window.statusBarColor = barColour
}
if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_NO) {
if (Build.VERSION.SDK_INT in 23..28) {
@SuppressLint("InlinedApi")
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
}
}
/**
* Returns the notification channel that is reponsible for handling substitution notifications
* on Android Oreo and above. If it does not exist, this function creates it
*
* @param context the context used for gathering the notification system service
* @param prefs a reference to the app's SharedPreferences
* @return the notification channel
*/
@TargetApi(26) @TargetApi(26)
fun getNotificationChannel(context: Context, prefs: SharedPreferences): NotificationChannel { fun getNotificationChannel(context: Context, prefs: SharedPreferences): NotificationChannel {
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -175,6 +242,21 @@ internal object HelperFunctions {
} }
} }
fun checkStringForArray(s: String, checking: Array<String>): Boolean {
checking.forEach { check ->
if (s.contains(check)) return true
}
return false
}
/**
* The code below is used for saving and restoring user settings. However, it is currently
* inaccessible to the end user without entering a code in the hidden settings menu,
* as no interface has been created for it and it is not built around Android's Scoped Storage
* (which deprecates java.io.File and is likely to be required for file access on Android 11
* and above). The functions can still be accessed by entering "_READ" and "_WRITE"
* respectively in the hidden settings menu
*/
private val prefKeys = arrayOf("username", "classes", "courses", "greeting", "themeInt", "notif", private val prefKeys = arrayOf("username", "classes", "courses", "greeting", "themeInt", "notif",
"defaultPersonalised", "autoRefresh", "firstTimeDev", "launchDev", "pingFB", "defaultPersonalised", "autoRefresh", "firstTimeDev", "launchDev", "pingFB",
"subscribedToFBDebugChannel", "subscribedToiOSChannel") "subscribedToFBDebugChannel", "subscribedToiOSChannel")
@@ -255,11 +337,4 @@ internal object HelperFunctions {
requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42) requestPermissions(activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 42)
} }
} }
fun checkStringForArray(s: String, checking: Array<String>): Boolean {
checking.forEach { check ->
if (s.contains(check)) return true
}
return false
}
} }
@@ -5,7 +5,7 @@ import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
/** /**
* 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
* verify a login * verify a login
* *
* @param successListener a reference to the OnLoginSuccessListener implemented in an activity * @param successListener a reference to the OnLoginSuccessListener implemented in an activity
@@ -15,7 +15,7 @@ internal class LoginWebViewClient(successListener: OnLoginSuccessListener) : Web
private val mSuccessListener = successListener private val mSuccessListener = successListener
/** /**
* 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 view a reference to the WebView
@@ -32,14 +32,14 @@ internal class LoginWebViewClient(successListener: OnLoginSuccessListener) : Web
} }
/** /**
* 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
*/ */
internal interface OnLoginSuccessListener { internal interface OnLoginSuccessListener {
/** /**
* this function can be overridden to execute code when a login has been successful * This function can be overridden to execute code when a login has been successful
* *
* @param success boolean value that returns true if the cookie could be found, * @param success boolean value that is set to true if the cookie could be found,
* false otherwise * false otherwise
*/ */
fun onLoginSucceeded(success: Boolean) fun onLoginSucceeded(success: Boolean)
@@ -1,5 +1,9 @@
package com.denizd.substitutionplan.data package com.denizd.substitutionplan.data
/**
* Enum class for declaring Firebase Cloud Messaging topics without using the raw strings
* throughout the project
*/
internal enum class Topic(val tag: String) { internal enum class Topic(val tag: String) {
ANDROID("substitutions-android"), ANDROID("substitutions-android"),
BROADCAST("substitutions-broadcast"), BROADCAST("substitutions-broadcast"),
@@ -4,6 +4,10 @@ import com.denizd.substitutionplan.data.DataFetcher
import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage import com.google.firebase.messaging.RemoteMessage
/**
* This class handles Firebase data notifications and triggers a refresh of the substitution plan
* as well as the food menu upon arrival
*/
internal class FBNotificationService : FirebaseMessagingService() { internal class FBNotificationService : FirebaseMessagingService() {
override fun onMessageReceived(p0: RemoteMessage) { override fun onMessageReceived(p0: RemoteMessage) {
@@ -7,6 +7,13 @@ import com.denizd.substitutionplan.data.Topic
import com.google.firebase.FirebaseApp import com.google.firebase.FirebaseApp
import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.FirebaseMessaging
/**
* This class regularly resubscribes to keep the app in the background. This is supposed to resolve
* issues regarding the app being force-closed in the background and not being able to receive
* notifications.
*
* I am unsure about its effectiveness, though notifications do seem to arrive
*/
internal class FBPingService : JobService() { internal class FBPingService : JobService() {
private var jobCancelled = false private var jobCancelled = false
+1
View File
@@ -52,6 +52,7 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:maxLines="1"
app:layout_constraintTop_toBottomOf="@+id/text_greeting" app:layout_constraintTop_toBottomOf="@+id/text_greeting"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
+1 -1
View File
@@ -46,6 +46,7 @@
<ImageView <ImageView
android:id="@+id/iconView" android:id="@+id/iconView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_marginEnd="4dp"
android:layout_height="20dp" android:layout_height="20dp"
android:tint="@color/colorText"/> android:tint="@color/colorText"/>
@@ -53,7 +54,6 @@
android:id="@+id/course" android:id="@+id/course"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:textColor="@color/colorText"/> android:textColor="@color/colorText"/>
<TextView <TextView
+1 -1
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</string> <string name="licences_title">Lizenzen und 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>
+2 -2
View File
@@ -39,8 +39,8 @@
<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</string> <string name="licences_title">Licences and contributors</string>
<string name="licences_desc">What it took to create 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>
<string name="terms_and_conditions_title">Terms &amp; Conditions</string> <string name="terms_and_conditions_title">Terms &amp; Conditions</string>